在我们使用之前的RestTemplate远程调用的时候,是这样的:
它存在一个问题就是:
•代码可读性差,编程体验不统一
•参数复杂URL难以维
Feign是一个声明式的http客户端,官方地址:https://github.com/OpenFeign/feign
其作用就是帮助我们优雅的实现http请求的发送,解决上面提到的问题。
Feign: 代替消费者向提供者发送请求,并接受返回结果
openfeign: SpringCloud官方提供的组件
第一步:导入依赖
org.springframework.cloud
spring-cloud-starter-openfeign
第二步:在启动类上添加注解开启Feign功能
创建Feign的客户端:
@FeignClient("userservice")
public interface UserFeignClient {
@GetMapping("/user/{id}")
User queryById(@PathVariable("id") Long id);
}
也就是代表访问的地址就是:http://userservice/user/id
也就是说:@FeignClient("userservice")+GetMapping("/user/{id}")里面的内容拼成的一个链接
然后我们再使用的时候就可以直接注入这个类调用这一层的方法就可以完成远程调用了
配置文件方式:
基于配置文件修改feign的日志级别可以针对单个服务:
如果在这个项目中配置了,那么当我们只有远程调用userservice的都会促发这个配置。
feign:
client:
config:
userservice: # 针对某个微服务的配置
loggerLevel: FULL # 日志级别
将那个userservice去掉也就可以根据配置全局配置,也就是说如果在这个项目中配置了,那么当我们远程调用所有的都会促发这个配置。
feign:
client:
config:
default: # 这里用default就是全局配置,如果是写服务名称,则是针对某个微服务的配置
loggerLevel: FULL # 日志级别
public class DefaultFeignConfiguration {
@Bean
public Logger.Level feignLogLevel(){
return Logger.Level.BASIC; // 日志级别为BASIC
}
}
如果要全局生效,将其放到启动类的@EnableFeignClients这个注解中:
@MapperScan("cn.itcast.order.mapper")
@SpringBootApplication
// 配置类的方式开启全局日志记录
@EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration.class) // 开启feign客户端的支持
@EnableFeignClients // 开启feign客户端的支持
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
//......
}
如果是局部生效,则把它放到对应的@FeignClient这个注解中:
// 指定服务日志配置
@FeignClient(value = "userservice",configuration = DefaultFeignConfiguration.class)
@FeignClient(value = "userservice")
public interface UserFeignClient {
@GetMapping("/user/{id}")
User queryById(@PathVariable("id") Long id);
}
Feign底层发起http请求,依赖于其它的框架。其底层客户端实现包括:
•URLConnection:默认实现,不支持连接池
•Apache HttpClient :支持连接池
•OKHttp:支持连接池
其实也就是Feign远程调用还是使用的RestTmplate,但是默认使用的时URLConnection,是不支持连接池的,那么我们想要使用连接池的话就需要使用HttpClient和OkHttp,这两个支持连接池
引入依赖
io.github.openfeign
feign-httpclient
配置连接池
feign:
client:
config:
default: # default全局的配置
loggerLevel: BASIC # 日志级别,BASIC就是基本的请求和响应信息
httpclient:
enabled: true # 开启feign对HttpClient的支持
max-connections: 200 # 最大的连接数
max-connections-per-route: 50 # 每个路径的最大连接数
注意:我们在导入了HttpClient的依赖以后就会直接将默认的给替换掉,即使不配置那么也会提换掉,个人认为只要导入了HttpClient依赖bean的工厂就有这个实例,那么Feign就会发现有这个实例就会优先调用HttpClirnt,如果有连接词的这个两种都没有导入依赖那么就是没有实例,Feign发现没有实例就会使用默认的
总结,Feign的优化:
日志级别尽量用basic
2.使用HttpClient或OKHttp代替URLConnection
① 引入feign-httpClient依赖
② 配置文件开启httpClient功能,设置连接池参数
所谓最近实践,就是使用过程中总结的经验,最好的一种使用方式。
仔细观察可以发现,Feign的客户端与服务提供者的controller代码非常相似:
也就是将feign单独分离出来做成一个jar包依赖导入我们的i项目中。
Gateway网关是我们服务的守门神,所有微服务的统一入口。
网关的核心功能特性:
请求路由
权限控制
限流
也就是说我们的集群全部交给网关调用,我们只需要访问网关就行,网关给我们负载均衡,替我们访问
权限控制:网关作为微服务入口,需要校验用户是是否有请求资格,如果没有则进行拦截。
路由和负载均衡:一切请求都必须先经过gateway,但网关不处理业务,而是根据某种规则,把请求转发到某个微服务,这个过程叫做路由。当然路由的目标服务有多个时,还需要做负载均衡。
限流:当请求流量过高时,在网关中按照下流的微服务能够接受的速度来放行请求,避免服务压力过大
引入依赖:
org.springframework.cloud
spring-cloud-starter-gateway
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
编写路由规则:
server:
port: 10010 # 网关端口
spring:
application:
name: gateway # 服务名称
cloud:
nacos:
server-addr: localhost:80 # nacos地址
gateway:
routes: # 网关路由配置
- id: user-service # 路由id,自定义,只要唯一即可
# uri: http://127.0.0.1:8081 # 路由的目标地址 http就是固定地址
uri: lb://userservice # 路由的目标地址 lb就是负载均衡,后面跟服务名称
predicates: # 路由断言,也就是判断请求是否符合路由规则的条件
- Path=/user/** # 这个是按照路径匹配,只要以/user/开头就符合要求
filters:
- AddRequestHeader=Truth, Itcast is freaking awesome! # 给这个请求添加请求头后面是信息,请求头的标签为Truth 内容为:Itcast is freaking awesome!
- id: order-service # 路由id,自定义,只要唯一即可
# uri: http://127.0.0.1:8081 # 路由的目标地址 http就是固定地址
uri: lb://orderservice # 路由的目标地址 lb就是负载均衡,后面跟服务名称
predicates: # 路由断言,也就是判断请求是否符合路由规则的条件 断言有多种,可以去查看
- Path= /** # 这个是按照路径匹配,这个是匹配全部
default-filters: # 默认过滤项
#也就是说,比如上边的id为user-service他有指定的请求头,但是他们的请求头的key是一样的,那么就是用他自己的,但是下边的order-service没有指定,那么就用默认的
- AddRequestHeader=Truth, Itcast is freaking awesomedefault!
# 。。。
globalcors: # 全局的跨域处理
add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题
corsConfigurations:
'[/**]':
allowedOrigins: # 允许哪些网站的跨域请求
- "http://localhost:8090"
allowedMethods: # 允许的跨域ajax的请求方式
- "GET"
- "POST"
- "DELETE"
- "PUT"
- "OPTIONS"
allowedHeaders: "*" # 允许在请求中携带的头信息
allowCredentials: true # 是否允许携带cookie
maxAge: 360000 # 这次跨域检测的有效期
上边有两个路由:
一个是
- Path=/user/**
还有一个是
- Path= /**
当第二个的放在第一个的位置时,那么我们不管访问什么都会出现一个问题,就是第二个无论什么路径都会满足,这时候就不会进入第一个- Path=/user/**,即使携带user的也会被拦击,这个如果有共同路径时要考虑先后顺序
我们在配置文件中写的断言规则只是字符串,这些字符串会被Predicate Factory读取并处理,转变为路由判断的条件
例如Path=/user/**是按照路径匹配,这个规则是由
org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory
类来处理的,像这样的断言工厂在SpringCloudGateway还有十几个
路由过滤器
过滤器的作用是什么?
① 对路由的请求或响应做加工处理,比如添加请求头
② 配置在路由下的过滤器只对当前路由的请求生效
defaultFilters的作用是什么?
① 对所有路由都生效的过滤器
网关提供了31种,但每一种过滤器的作用都是固定的。如果我们希望拦截请求,做自己的业务逻辑则没办法实现。
全局过滤器的作用也是处理一切进入网关的请求和微服务响应,与GatewayFilter的作用一样。区别在于GatewayFilter通过配置定义,处理逻辑是固定的;而GlobalFilter的逻辑需要自己写代码实现。
定义方式是实现GlobalFilter接口。
代码实现
@Order(-1)
@Component
public class AuthorizeFilter implements GlobalFilter {
@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 1.获取请求参数
MultiValueMap params = exchange.getRequest().getQueryParams();
// 2.获取authorization参数
String auth = params.getFirst("authorization");
// 3.校验
if ("admin".equals(auth)) {
// 放行
return chain.filter(exchange);
}
// 4.拦截
// 4.1.禁止访问,设置状态码
exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
// 4.2.结束处理
return exchange.getResponse().setComplete();
}
}
其中那个order是我们过滤器的在过滤链中的执行先后顺序,order越小那么执行的越靠前,如果没指定那么就是按照代码顺序从上往下的顺序
过滤器执行顺序
请求进入网关会碰到三类过滤器:当前路由的过滤器、DefaultFilter、GlobalFilter
请求路由后,会将当前路由过滤器和DefaultFilter、GlobalFilter,合并到一个过滤器链(集合)中,排序后依次执行每个过滤器:
排序的规则是什么呢?
每一个过滤器都必须指定一个int类型的order值,order值越小,优先级越高,执行顺序越靠前。
GlobalFilter通过实现Ordered接口,或者添加@Order注解来指定order值,由我们自己指定
路由过滤器和defaultFilter的order由Spring指定,默认是按照声明顺序从1递增。
当过滤器的order值一样时,会按照 defaultFilter > 路由过滤器 > GlobalFilter的顺序执行。
也就是说一个网页如果他的内容同时出现了来自两个端口的,那么就是跨域
协议,域名,端口三者中任意一者不同即为跨域
跨域:域名不一致就是跨域,主要包括:
域名不同: www.taobao.com 和 www.taobao.org 和 www.jd.com 和 miaosha.jd.com
域名相同,端口不同:localhost:8080和localhost8081
跨域问题:浏览器禁止请求的发起者与服务端发生跨域ajax请求,请求被浏览器拦截的问题
解决方案:CORS,这个以前应该学习过,这里不再赘述了。不知道的小伙伴可以查看跨域资源共享 CORS 详解 - 阮一峰的网络日志
在gateway服务的application.yml文件中,添加下面的配置:
spring:
cloud:
gateway:
# 。。。
globalcors: # 全局的跨域处理
add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题
corsConfigurations:
'[/**]':
allowedOrigins: # 允许哪些网站的跨域请求
- "http://localhost:8090"
allowedMethods: # 允许的跨域ajax的请求方式
- "GET"
- "POST"
- "DELETE"
- "PUT"
- "OPTIONS"
allowedHeaders: "*" # 允许在请求中携带的头信息
allowCredentials: true # 是否允许携带cookie
maxAge: 360000 # 这次跨域检测的有效期
Nacos组件:
概述: alibaba提供的一个组件
作用:
Nacos注册中心
Nacos配置中心
注意: nacos注册中心和配置中心各有各的依赖,用哪个导哪个
安装: 安装nacos,并启动
startup.cmd -m standalone
Nacos注册中心:
问题由来:
两个微服务远程调用时存在硬编码问题
如果保存多个微服务的地址
如何监控微服务的健康状态
作用:
...
内容:
namespace:
group:
XxxService:
异地容灾:
将一个服务下的多个实例,存放在不同的地区,形成不同的集群
微服务在调用时,优先本集群(就近原则)
使用步骤:
1.导入注册中心依赖
2.配置nacos的地址
配置微服务集群时,需要设置优先本集群
Nacos配置中心
问题由来:
在一个微服务集群中,每一个微服务内都可能存在相同的配置,修改起来不方便
作用:
将配置文件从微服务中分离,存放在nacos中
微服务启动时,从nacos中加载配置文件,并使用配置文件中的配置
配置文件的热更新: 2种
配置文件中的多种编写方式:
服务名-环境.yaml > 服务名.yaml > application.yaml
在SpringBoot中外部配置(指定配置大于默认配置) 大于 内部配置
使用步骤:
0.在nacos中编写配置文件
1.导入注册中心依赖
2.配置nacos
在bootstrap.yml文件中配置nacos配置中心的相关信息