上一章Spring Cloud项目中Nacos作为注册中心 在使用Naocs作为注册中心的时候我们使用 RestTemplate+Ribbon 的方式访问到了procider发布的服务, 但是在实际开发中不可能使用这种方式进行服务调用,所以这章我们讲一下:使用 Naocs集成OpenFeign 来完成调用服务发布的接口。
官网地址: https://docs.spring.io/spring-cloud-openfeign/docs/2.2.10.BUILD-SNAPSHOT/reference/html
OpenFeign: 自身整合了Ribbon合Hystrix, 为服务调用提供了更优雅的方式
Ribbon:负载均衡
Hystrix: 断路器,Hystrix能够保证在一个依赖出现问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。
这里简单说一下Feign
Feign集成了Ribbon、RestTemplate实现了负载均衡的执行Http调用,只不过对原有的方式(Ribbon+RestTemplate)进行了封装,开发者不必手动使用RestTemplate调服务,而是定义一个接口,在这个接口中标注一个注解即可完成服务调用,这样更加符合面向接口编程的宗旨,简化了开发。但遗憾的是Feign现在停止迭代了。
OpenFeign是SpringCloud在Feign 的基础上支持了SpringMVC的注解,如@RequestMappong等,OpenFeign的@FeignClient 可以解析SpringMVC的@RequstMapping注解下的接口,并通过动态代理的方式产生实现类,实现类中做负载均衡应调用其他服务。
OpenFeign 的传参方式
注解 | 参数形式 |
---|---|
@RequestBody | JSON传参方式 默认 |
@SpringQueryMap | POJO表单传参方式 |
@RequestHeader | 请求头传参 |
@RequestParam | GET请求传参 |
@PathVariable | 请求路径上 |
OpenFeign 还提供了很多propertires 、yml参数配置
官方地址: https://docs.spring.io/spring-cloud-openfeign/docs/2.2.10.BUILD-SNAPSHOT/reference/html/appendix.html
OpenFeign 默认开启Apache HttpClient
如果想使用 OkHttpClient 的话 就需要配置 feign.okhttp.enabled 默认false
上一章 创建了 服务发布者 provider 服务消费者 consumer
这一章 我们重新创建一个新的 集成OpenFeign的消费者
demo项目git地址: https://gitee.com/DianHaiShiYuDeMing/nacos-demo
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.example</groupId>
<artifactId>nacos-demo</artifactId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>open-fegin</artifactId>
<groupId>com.example.consumer</groupId>
<version>0.0.1-SNAPSHOT</version>
<name>open-fegin</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
server.port=8030
spring.application.name=open-fegin
###nacos 配置
## 名称空间
spring.cloud.nacos.discovery.namespace=evone
## nacos 链接账号 没有设置不可以不选
spring.cloud.nacos.username=evone
# nacos 链接 密码 没有设置不可以不选
spring.cloud.nacos.password=evone123456
# spring.cloud.nacos.server-addr=192.168.104.110:8848 使用的nacos改了端口号, 默认端口 8848
spring.cloud.nacos.server-addr=192.168.104.110:16848
###ribbon 配置
#请求处理的超时时间
ribbon.ReadTimeout=6000
#请求连接的超时时间
ribbon.ConnectTimeout=4000
#对当前实例的重试次数
ribbon.MaxAutoRetries=2
#切换实例的重试的次数 1 就切换一次实例 2 切换两次(如果只有两个服务就会切换会刚刚请求的那个服务) 一次类推
ribbon.MaxAutoRetriesNextServer=1
###hystrix 配置
# 开启 hystrix
feign.hystrix.enabled=true
# hystrix 超时时间 一般 hystrix超时时间 = (1 + MaxAutoRetries + MaxAutoRetriesNextServer+MaxAutoRetries) * ReadTimeout
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=9000
#设置所有实例的默认值
hystrix.command.default.execution.timeout.enabled=true
package com.example.fegin;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableDiscoveryClient// 开启nacos 服务发现
@EnableFeignClients(basePackages = "com.example.fegin")
public class OpenFeginApplication {
public static void main(String[] args) {
SpringApplication.run(OpenFeginApplication.class, args);
}
}
package com.example.fegin.controller;
import com.example.fegin.service.DemoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class FeginContoller {
@Autowired
private DemoService demoService;
@GetMapping("/test/{id}")
public Object test(@PathVariable String id) {
String forObject = demoService.test(id);
return forObject;
}
}
令牌中继通俗的讲则是让令牌在微服务链路调用中传递下去,保证各个微服务能够获取令牌中的用户信息。在通俗点就是 将请求头信息透传到被调用的微服务中
客户端携带令牌请求网关,网关鉴权成功后会将令牌中的用户信息解析出来放在请求头中下发给订单服务,同样的,订单服务需要将用户信息传递给账户服务获取该用户的账户信息。
令牌在OpenFeign调用过程中是不能自动中继的,因此必须手动的将令牌信息传递下去。
/**
* 用于实现令牌信息中继
*/
@Component
public class FeignRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
//当feign 开启 hyxtr
//当Feign开启Hystrix支持时, RequestContextHolder.getRequestAttributes 就会为null
//原因在于 Hystrix的默认隔离策略是THREAD 。而 RequestContextHolder 源码中,使用了两个血淋淋的ThreadLocal 。
//Hystrix官方强烈建议使用THREAD作为隔离策略
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();
//获取RequestContextHolder中的信息
Map headers = getHeaders(request);
//放入feign的RequestTemplate中
for (Map.Entry entry : headers.entrySet()) {
System.out.println("令牌中继 机制 key:"+entry.getKey()+" value:" +entry.getValue());
template.header(entry.getKey(), entry.getValue());
}
}
/**
* 获取原请求头
*/
private Map getHeaders(HttpServletRequest request) {
Map map = new LinkedHashMap();
Enumeration enumeration = request.getHeaderNames();
if (enumeration != null) {
while (enumeration.hasMoreElements()) {
String key = enumeration.nextElement();
String value = request.getHeader(key);
map.put(key, value);
}
}
return map;
}
}
注意: OpenFeign 在开启熔断降级后内部调用开启了子线程,因此传统方案直接下RequestInterceptor中设置是不可行的。也就是说: OpenFeign 开启Hystrix后就拿不到上下文
原因在于 Hystrix的默认隔离策略是THREAD 。而 RequestContextHolder 源码中,使用了两个血淋淋的ThreadLocal 。
解决方案
Spring Cloud Openfegin 自动集成了 Ribbon 与 Hystrix;
Openfegin 默认开启 Ribbon
Openfegin 默认不开启 Hystrix
Openfegin 默认 HttpClient
在pom坐标使用过程中 OpenFegin 要使用与 nacos-discovery 的版本不一样的话 可能造成依赖jar包坐标版本冲突,导致依赖jar 下不下来 出现各种各样的问题 例如
java.lang.ClassNotFoundException: com.netflix.config.CachedDynamicIntProperty
需要单独引入引入
<dependency>
<groupId>com.netflix.archaius</groupId>
<artifactId>archaius-core</artifactId>
<version>0.7.6</version>
<exclusions>
<exclusion>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</exclusion>
</exclusions>
</dependency>