SpringCloud
Feign
Feign替代RestTemplate(快速入门)
存在问题
- 代码可读性差,编程体验不统一
- 参数复杂,URL难以维护
Feign是声明式客户端,帮助我们优雅的完成请求发送
步骤1:引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
步骤2:开启Feign
在启动类上加 @EnableFeignClients
@SpringBootApplication
@EnableFeignClients //标注这个
public class ClNacosApplication {
public static void main(String[] args) {
SpringApplication.run(ClNacosApplication.class, args);
}
}
步骤3:新建 feign 客户端
- user1service1 是服务名
@FeignClient("user1service1")
public interface UserClient {
//根据 ID 查询
@GetMapping("/user/{userId}")
User findById(@PathVariable Integer userId);
}
如果报以下错误:
由于SpringCloud Feign在Hoxton.M2 RELEASED版本之后不再使用Ribbon
而是使用spring-cloud-loadbalancer,所以不引入spring-cloud-loadbalancer会报错
增加依赖
<!-- 去掉ribbon -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 新增 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
</dependency>
自定义Feign的配置
- 修改日志级别
- feign.Logger.Level(修改日志级别)有以下四种
- NONE:不打印日志
- BASIC:发起请求的时候,记录开始,结束,耗时时长
- HEADERS:包含以上,带上请求头响应头信息
- FULL:包含以上,并且带上请求体和响应体
- 响应结果的解析器
- feign.codec.Decoder(响应结果的解析器)
- 说明:http远程调用的结果做解析,比如json转java对象
- 请求参数编码
- feign.codec.Encoder(请求参数编码)
- 说明:将请求参数编码,便于通过http请求发送
- 支持的注解格式
- feign. Contract(支持的注解格式)
- 说明:默认是SpringMVC的注解
- 失败重试机制
- feign. Retryer(重试机制)
- 说明:请求失败的重试机制,默认是没有,不过会使用Ribbon的重试
第一种修改方式:配置文件
spring:
application:
name: order1service
profiles:
active: dev
cloud:
nacos:
discovery:
server-addr: localhost:8845
config:
file-extension: yaml
# 下面都需要配置
enabled: false
logging:
level:
com.orderservice: debug
feign:
client:
config:
default:
loggerLevel: full #这是全局,写服务名就是局部
java代码实现
首先
import feign.Logger;
import org.springframework.context.annotation.Bean;
public class DefaultFeignConfiguration {
@Bean
public Logger.Level loglevel() {
return Logger.Level.FULL; // 这是日志级别
}
}
如果是全局,就要在启动类上面加载
@SpringBootApplication
// 在下面
@EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration.class)
@MapperScan("com.orderservice.dao")
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
如果是局部就要在服务上面加载
@FeignClient("user1service1")
@EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration.class)
public interface UserClient {
配置文件中增加
spring:
application:
name: order1service
profiles:
active: dev
cloud:
nacos:
discovery:
server-addr: localhost:8845
config:
file-extension: yaml
# 加入以下这些
enabled: false #禁用
logging:
level: # 下面是哪些包需要打印日志
com.orderservice: debug
Feign 性能优化
Feign底层实现:
- URLConnection:默认实现,不支持连接池
- Apache HttpClient :支持连接池(推荐)
- OKHttp:支持连接池(推荐)
优化主要包括:
- 使用连接池代替默认的URLConnection
- 日志级别,最好用basic或none
步骤1:引入依赖
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
步骤2:打开连接池
feign:
httpclient:
enabled: true # 支持httpClient的开关
max-connections: 200 # 最大连接数
max-connections-per-route: 50 # 单个路径最大连接数
Feign 最佳实践
方案1:继承实现
给消费者的FeignClient和提供者的controller定义统一的父接口作为标准
缺点:
- 官方不推荐,共享接口,因为紧耦合
方案2:引入依赖
方式二(抽取):将FeignClient抽取为独立模块,并且把接口有关的POJO、默认的Feign配置都放到这个模块中,提供给所有消费者使用
实现方式二
- 首先创建module,命令为 feign-api,然后引入feign的starter依赖(openfeign)
- 把clients,config,domain复制过去
// 需要指定以下客户端,因为不在一个项目会报错
@EnableFeignClients(clients = {UserClient.class})
// 方式二扫包
@EnableFeignClients(basePackages = "com.feign.clients")
- 其他包抽取、调用即可
Feign 传参
Feign客户端必须加@PathVariable 或者 @RequestParam ,否则报错405
Gateway 网关
统一网关
微服务都要访问数据库完成自己业务
微服务都要去nacos中去注册,配置的管理
如果有相互调用,可以用feign实现
外部怎么办,直接调用不太安全,就要使用网关了
网关功能:
- 身份认证和权限校验
- 服务路由、负载均衡
- 请求限流
Zuul是基于Servlet的实现,属于阻塞式编程。
而SpringCloudGateway则是基于Spring5中提供的WebFlux,属于响应式编程的实现,具备更好的性能。
网关搭建(基本路由配置)
- 创建一个新的Module叫 gateway,引入两个依赖
<!--网关-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency><!--nacos服务发现依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--负载均衡, 因为新版本不用ribbon了-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
- 编写基本路由配置
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://user1service1 # 路由的目标地址 lb就是负载均衡,后面跟服务名称
predicates: # 路由断言,也就是判断请求是否符合路由规则的条件
- Path=/user/** # 这个是按照路径匹配,只要以/user/开头就符合要求
- 执行过程
路由断言工厂
读取用户配置的路由规则,不通过就404
predicates:
- Path=/order/**
- Before=2022-04-15T15:14:33.133+08:00[Asia/Shanghai]
过滤器
GatewayFilter 可以对网关请求和微服务的响应做处理
添加请求头
spring:
application:
name: gateway # 服务名称
cloud:
nacos:
server-addr: localhost:80 # nacos地址
gateway:
routes: # 网关路由配置
- id: order-service
uri: lb://order1service
predicates:
- Path=/order/**
- Before=2022-04-15T15:14:33.133+08:00[Asia/Shanghai]
# 配置过滤--添加请求头 AddRequestHeader=名称,内容
filters:
- AddRequestHeader=Truth,Itcast is freaking aowsome!
添加默认请求,对所有路由都生效
default-filters:
- AddRequestHeader=Truth,Itcast is freaking aowsome!
全局过滤器(GlobalFilter)
定义在 Gateway 服务类中
全局过滤器,是过滤一切进入路由的请求和响应
用java代码实现,可以做逻辑处理
实现未登录禁止访问(简介的)
- @Order(-1) 表示优先级,越小越高
@Component
@Order(-1)
public class AuthorizeFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 1. 获取请求参数
ServerHttpRequest request = exchange.getRequest();
MultiValueMap<String, String> queryParams = request.getQueryParams();
// 2. 获取 authorization
String s = queryParams.getFirst("authorization");
if("admin".equals(s)){
// 放行
return chain.filter(exchange);
}
// 设置状态码
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); // 401
return exchange.getResponse().setComplete();
}
}
过滤器的执行顺序
请求进入网关会碰到三类过滤器:当前路由的过滤器、DefaultFilter、GlobalFilter
会将当前路由过滤器和DefaultFilter,GlobalFilter合并到一个过滤器链(集合)中 排序后会依次执行每个过滤器
globalfilter的优先级由我们自己指定
每一个过滤器都必须指定一个Order,Order值越小,优先级越高
defaultFilter的优先级由spring指定,默认按照顺序依次递增
当过滤器值一样,会按照defaultfilter > 当前过滤器 > globalfilter依次执行
跨域问题处理(解决CORS)
跨域:域名不一致就是跨域,主要包括
- 域名后缀不同
- 端口不同
浏览器禁止请求于服务端发生跨域的Ajax请求。被浏览器拦截问题,添加到Gateway
spring:
cloud:
gateway:
globalcors: # 全局的跨域处理
add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题
corsConfigurations:
'[/**]': # 拦截一切请求
allowedOrigins: # 允许哪些网站的跨域请求
- "http://localhost:8090"
- "http://www.leyou.com"
allowedMethods: # 允许的跨域ajax的请求方式
- "GET"
- "POST"
- "DELETE"
- "PUT"
- "OPTIONS"
allowedHeaders: "*" # 允许在请求中携带的头信息,*表示全部
allowCredentials: true # 是否允许携带cookie
maxAge: 360000 # 这次跨域检测的有效期,浏览器不再发起询问,在有效期内
注意事项:
nacos 服务中必须和服务的分组在一块,否则 503,无映射