Spring Cloud与Docker微服务架构实战简单学习笔记(三)
1. 服务消费者整合Feign
1.1 引入依赖包
SpringCloud2.0以后引入以下包:
<!-- 添加Feign依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
1.2 新建FeignClient接口
@FeignClient(name = "microservice-simple-provider-user")
public interface UserFeignClient {
@GetMapping(value="/user/{id}")
public UserEntity findByIdFeign(@PathVariable("id") int id);
}
1.3 修改controller,使用Feign调用
@Autowired
private UserFeignClient userFeignClient;
@GetMapping("/user/feign/{id}")
public UserEntity findByIdFeign(@PathVariable int id) {
return userFeignClient.findByIdFeign(id);
}
1.4 启动类加注解
在启动类上加@EnableFeignClients注解,开启Feign Client功能。
2. Hystrix实现容错(断路器)
断路器可理解为对容易导致错误的操作的代理。
这种代理能够统计一段时间内调用失败的次数,并决定是正常请求依赖的服务还是直接放回。
断路器可以实现快速失败,如果它在一段时间内检测到许多类似的错误(例如超时),就会在之后的一段时间内,强迫对该服务的调用快速失败,即不再请求所依赖的服务。
断路器也可以自动诊断依赖的服务是否已经恢复正常。
断路器状态转换图
2.1 Hystrix简介
Hystrix是一个实现了超时机制和断路器模式的工具类库。
3 如何整合Hystrix
3.1 添加依赖包
<!-- 添加Hystrix依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
3.2 启动类加注解
@EnableHystrix
3.3 修改Controller
让方法“findByIdFeign”具备容错能力,并为它添加一个回退方法“findByIdFeignFallback”,这两个方法具有相同的参数和返回值。
@HystrixCommand(fallbackMethod = "findByIdFeignFallback")
@GetMapping("/user/feign/{id}")
public UserEntity findByIdFeign(@PathVariable int id) {
return userFeignClient.findByIdFeign(id);
}
public UserEntity findByIdFeignFallback(int id) {
UserEntity user = new UserEntity();
user.setId(-1l);
user.setAge(20);
user.setUsername("account-1");
user.setName("默认用户");
user.setBalance(new BigDecimal(9999));
return user;
}
3.4 验证
先正常启动user1、user2,访问“http://localhost:8032/movie/user/feign/2”可正常返回id=2的用户信息。
停掉user1或user2,再访问,可以看到能获取到“默认用户”的信息。
4. Hystrix线程隔离策略与传播上下文
Hystrix隔离策略有两种:
1.线程隔离(THREAD):使用该方式,HystrixCommand将会在单独的线程上执行,并发请求受线程池中的线程数量限制。
2.信号量隔离(SEMAPHORE):使用该方式,HystrixCommand将会在调用线程上执行,开销相对较小,并发请求受到信号量个数的限制。
Hystrix中默认并且推荐使用线程隔离,因为这种方式,除了网络超时以外,还有一个额外的保护层。
一般来说,只有当调用负载非常高时,(例如每个实例每秒调用数百次)才需要使用信号量隔离,因为这种场景下使用thread开销会比较高。 信号量隔离一般仅适用于非网络调用的隔离。
可以使用“execution.isolation.strategy”属性指定隔离策略:
@HystrixCommand(fallbackMethod = "findByIdFeignFallback",
commandProperties = {
@HystrixProperty(name="execution.isolation.strategy", value="SEMAPHORE")
})
public UserEntity findByIdFeign(@PathVariable int id) {
return userFeignClient.findByIdFeign(id);
}
5. Feign整合Hystrix
前面是使用注解@HystrixCommand的fallbackMethod属性实现回退的。然而,Feign是以接口形式工作的,它没有方法体,前面讲解的方式显然不适用于Feign。
事实上,Feign已默认整合了Hystrix,只要Hystrix在项目的classPath中,Feign默认就会用断路器包裹所有方法。
5.1 FeignClient上添加fallback属性
注意:fallback上配的是class
@FeignClient(name = "microservice-simple-provider-user", fallback = UserFeignClientFallback.class)
public interface UserFeignClient {
@GetMapping(value="/user/{id}")
public UserEntity findByIdFeign(@PathVariable("id") int id);
}
5.2 新建FeignClient的实现类
@Component
public class UserFeignClientFallback implements UserFeignClient {
@Override
public UserEntity findByIdFeign(int id) {
UserEntity user = new UserEntity();
user.setId(-1l);
user.setAge(20);
user.setUsername("account-1");
user.setName("默认用户(feign接口注解)");
user.setBalance(new BigDecimal(9999));
return user;
}
}
5.3 为Feign开启Hystrix
默认情况下,feign是不启动hystrix功能,需要我们启动此功能,通过以下配置:
feign.hystrix.enabled=true
如果没有开启,则会报错:
This application has no explicit mapping for /error, so you are seeing this as a fallback.
整合完成,可以测试验证。
.
6. 通过Fallback Factory检查回退原因
很多场景下,需要了解回退的原因,可使用注解@FeignClient的fallbackFactory属性。
6.1 FeignClient上添加fallbackFactory属性
@FeignClient(name = "microservice-simple-provider-user", fallbackFactory = UserFeignClientFallbackFactory.class)
public interface UserFeignClient {
@GetMapping(value="/user/{id}")
public UserEntity findByIdFeign(@PathVariable("id") int id);
}
6.2 新建FallbackFactory的实现类
@Component
public class UserFeignClientFallbackFactory implements FallbackFactory<UserFeignClient>{
private static final Logger log = LoggerFactory.getLogger(UserFeignClientFallbackFactory.class);
@Override
public UserFeignClient create(Throwable cause) {
return new UserFeignClient() {
@Override
public UserEntity findByIdFeign(int id) {
// 日志最好放在各个fallback方法中,不要直接放在create方法中
// 否则在引用启动时,就会打印该日志
UserFeignClientFallbackFactory.log.error("=========== fallback reason is :", cause);
UserEntity user = new UserEntity();
user.setId(-1l);
user.setAge(20);
user.setUsername("account-1");
user.setName("默认用户(打印fallback原因)");
user.setBalance(new BigDecimal(9999));
return user;
}
};
}
}
6.3 注意的问题
fallback属性 和 fallbackFactory不要同时存在(虽然不报错),同时存在的话fallbackFactory不生效,不会打印日志。
7. Hystrix的监控
除了实现容错外,Hystrix还提供了近乎实时的监控。
HystrixCommand和HystrixObservableCommand在执行时,会生成执行结果和运行指标,比如:每秒执行的请求数、成功数等。
使用Hystrix的模块“hystrix-metrics-event-stream”,就可将这些监控的指标信息以“text/event-stream”的格式暴露给外部系统。
spring-cloud-starter-hystrix已包含该模块,在此基础上,只需要添加“spring-boot-starter-actuator”,就可以使用“/hystrix.stream”端点获取Hystrix的监控信息了。
7.1 添加依赖包
<!-- 添加Actuator监控依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
7.2 启动类加方法
低版本的直接访问:http://localhost:8032/hystrix.stream
高版本的在启动类中添加下列方法,再访问:http://localhost:8032/hystrix.stream,即可。
/**
* 低版本直接启动即可使用 http://ip:port/hystrix.stream 查看监控信息
* 高版本需要添加本方法方可使用 http://ip:port/hystix.stream 查看监控信息
*
* @return
*/
@Bean
public ServletRegistrationBean getServlet() {
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
registrationBean.setLoadOnStartup(1);
registrationBean.addUrlMappings("/hystrix.stream");
registrationBean.setName("HystrixMetricsStreamServlet");
return registrationBean;
}
7.3 Feign项目的Hystrix监控(还未测试成功)
添加依赖:
<!-- 添加Hystrix依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
启动类加注解“@EnableCircuitBreaker”:
@EnableCircuitBreaker
8. 使用Hystrix Dashboard可视化监控数据
要保证http://localhost:8032/hystrix.stream可用,Hystrix Dashboard只是把它可视化。
8.1 添加依赖包spring-cloud-starter-netflix-hystrix-dashboard
<!-- 添加Hystrix可视化监控依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
<version>2.1.2.RELEASE</version>
</dependency>
8.2 启动类添加注解@EnableHystrixDashboard
@EnableHystrixDashboard
8.3 测试,看效果
访问http://localhost:8032/hystrix,进入Hystrix Dashboard主页:
红框处填值,title可以随便写,然后点击按钮“Monitor Stream”:
8.4 Hystrix Dashboard指标解释
8.5 Hystrix Dashboard如何注册到Eureka Server上,有什么好处(待补)
9. 使用Turbine聚合监控数据
前文使用“/hystrix.stream”端点监控单个微服务实例。
Turbine是一个聚合Hystrix监控数据的工具,他可将所有相关/hystrix.stream端点的数据聚合到一个组合的/turbine.stream中,从而让集群的监控更加方便。
引入Turbine后,架构图如下:
9.1 新建一个项目“microservice-simple-turbine”专门做聚合监控
依赖包:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 添加Eureka Client依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>
<!-- 添加turbine依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-turbine</artifactId>
<version>2.1.2.RELEASE</version>
</dependency>
配置项: 注意看配置项的描述
server.port=2031
spring.application.name=microservice-simple-turbine
eureka.client.serviceUrl.defaultZone=http://hht:123456@localhost:8761/eureka/,http://hht:123456@localhost:8762/eureka/
eureka.client.instance.prefer-ip-address=true
#指定要监控的微服务名称,用逗号分隔
turbine.app-config=microservice-simple-consumer-movie,microservice-simple-consumer-books
#指定集群名称
turbine.cluster-name-expression="default"
#表示同一主机上的服务通过host和port的组合来进行区分,默认情况下是使用host来区分,这样会使本地调试有问题
turbine.combine-host-port=true
#springboot2.0,默认路径不是/hystrix.stream,而turbine默认路径是这个,所以要修改一下
turbine.instanceUrlSuffix=/hystrix.stream
启动类: 加注解 @EnableTurbine
@EnableTurbine
@SpringBootApplication
public class TurbineApplication
{
public static void main( String[] args )
{
SpringApplication.run(TurbineApplication.class, args);
}
}
测试:http://localhost:2031/turbine.stream
9.2 用Hystrix Dashboard让监控数据可视化
上文中在microservice-simple-consumer-movie中整合了Dashboard,其实没必要这么做(movie、books里可以删除)。
不用再每个微服务里整合Hystrix Dashboard,只需要新建一个项目整合就可以了,或者和turbine做在一起。
本列就是做在turbine里。
添加依赖包:spring-cloud-starter-netflix-hystrix-dashboard
<!-- 添加Hystrix Dashboard仪表盘监控依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
<version>2.1.2.RELEASE</version>
</dependency>
启动类加注解: @EnableHystrixDashboard
@EnableTurbine
@EnableHystrixDashboard
@SpringBootApplication
public class TurbineApplication
{
public static void main( String[] args )
{
SpringApplication.run(TurbineApplication.class, args);
}
}
测试: http://localhost:2031/hystrix
在打开的页面中输入: http://localhost:2031/turbine.stream, 可以看到2个监控信息。
10. 使用消息中间件RabbitMQ收集数据(待补)
一些场景下,前文的方式无法正常工作(例如微服务和Turbine网络不通),此时,可借助消息中间件实现数据收集。
各个微服务将Hystrix Command的监控数据发送至消息中间件,Turbine消费消息中间件中的数据。
本例,新建2个工程来演示:
microservice-simple-consumer-food
microservice-simple-turbine-RabbitMQ