目录
接上一篇 “微服务之SpringCloud简介及Eureka注册中心”
1.负载均衡Ribbon
1.1.什么是Ribbon
官方解释:Ribbon 是 Netflix 发布的负载均衡器,它有助于控制HTTP和TCP 客户端的行为。为Ribbon配置服务提供地址列表后,Ribbon就可基于某种负载均衡算法,自动的帮助服务消费者去请求。Ribbon默认为我们提供了很多负载均衡算法,例如轮询、随机等,当然,我们也可以为Ribbon实现自定义的负载均衡算法。
1.2.代码使用Ribbon实现负载均衡
首先我们启动两个user-server实例,一个8081,一个8082
Eureka监控面板:
1.3.开启负载均衡
因为Eureka中已经集成了Ribbon,所以我们无需引入新的依赖。直接修改代码
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate(new OkHttp3ClientHttpRequestFactory());
}
1.4.源码追踪
LoadBalancerInterceptor 帮我们根据server名称,获取到了服务实例的ip和端口。
我们进行源码跟踪:
继续跟入execute方法:发现获取了8082端口的服务
再跟下一次,发现获取的是8081:
1.5.负载均衡策略
Ribbon默认的负载均衡策略是简单的轮询,我们可以测试一下
@RunWith(SpringRunner.class)
@SpringBootTest(classes = UserConsumerDemoApplication.class)
public class LoadBalanceTest {
@Autowired
RibbonLoadBalancerClient client;
@Test
public void test(){
for (int i = 0; i < 100; i++) {
ServiceInstance instance = this.client.choose("user-service");
System.out.println(instance.getHost() + ":" + instance.getPort());
}
}
}
结果:确实是轮询方式
修改负载均衡策略
继续跟踪源码,
这里的rule默认值是一个RoundRobinRule
,看类的介绍:
翻译:最广为人知的基本负载平衡策略,即循环法则。
SpringBoot也帮我们提供了修改负载均衡规则的配置入口:
user-service:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
格式是:{服务名称}.ribbon.NFLoadBalancerRuleClassName
,值就是IRule的实现类。
再次测试,发现结果变成了随机:
1.6.重试机制
Eureka的服务治理强调的CAP原则中的AP,即(可用性和可靠性)。它与Zookeeper这一类强调CP(一致性,可靠性)的服务治理框架最大的区别在于:Eureka为了实现更高的服务可用性,牺牲了一定的一致性。极端情况下它宁愿接受故障实例也不愿丢掉健康实例,正如Eureka的自我保护机制。但是此时如果我们调用这些不正常的服务,调用就会失败,从而导致其它服务不能正常工作。
因为服务剔除的延迟,consumer并不会立即得到最新的服务列表,此时再次访问你会得到错误提示:
但是此时,8081服务其实是正常的。
因此Spring Cloud 整合了Spring Retry 来增强RestTemplate的重试能力,当一次服务调用失败后,不会立即抛出一次,而是再次重试另一个服务。
只需要简单配置即可实现Ribbon的重试:
spring:
cloud:
loadbalancer:
retry:
enabled: true # 开启Spring Cloud的重试功能
user-service:
ribbon:
ConnectTimeout: 250 # Ribbon的连接超时时间
ReadTimeout: 1000 # Ribbon的数据读取超时时间
OkToRetryOnAllOperations: true # 是否对所有操作都进行重试
MaxAutoRetriesNextServer: 1 # 切换实例的重试次数
MaxAutoRetries: 1 # 对当前实例的重试次数
根据如上配置,当访问到某个服务超时后,它会再次尝试访问下一个服务实例,如果不行就再换一个实例,如果不行,则返回失败。切换次数取决于 MaxAutoRetriesNextServer
参数的值
引入spring-retry依赖
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
我们重启user-consumer-demo,测试,发现即使user-service2宕机,也能通过另一台服务实例获取到结果!
2.Hystrix 熔断器
2.1.简介
Hystrix,即熔断器。主页:https://github.com/Netflix/Hystrix/。
Hystix是Netflix开源的一个延迟和容错库,是容错管理工具。用于隔离访问远程服务、第三方库,防止出现级联失败,通过隔离、控制服务从而对延迟和故障提供更强大的容错能力,避免整个系统被拖垮。
复杂分布式架构通常都具有很多依赖,当一个应用高度耦合其他服务时非常危险且容易导致失败,这种失败很容易伤害服务的调用者,最后导致一个接一个的连续错误,应用本身就处于被拖垮的风险中,最后失去控制。当在系统高峰时期,大量对微服务的调用可能会堵塞远程服务器的线程池,如果这个线程池没有和主应用服务器的现场隔离,就可能导致整个服务器挂机。
Hystirx使用自己的线程池,这样和主应用服务器线程池隔离,如果调用花费很长时间,会停止调用,不同的命令或命令组能够被配置使用它们各自的线程池,可以隔离不同的服务。
2.2.熔断器的工作机制
熔断机制的原理很简单,像家里的电路熔断器,如果电路发生短路能立刻熔断电路,避免发生灾难。在分布式系统中应用这一模式之后,服务调用方可以自己进行判断某些服务反应慢或者存在大量超时的情况时,能够主动熔断,防止整个系统被拖垮。
不同于电路熔断只能断不能自动重连,Hystrix 可以实现天性容错,当情况好转之后,可以自动重连。通过短路的方式,可以将后续请求直接拒绝掉,一段时候之后允许部分请求通过,如果调用成功则回到电路闭合状态,否则继续断开。
正常工作的情况下,客户端请求调用服务API接口:
当有服务出现宜昌市,直接进行失败回滚,服务降级处理:
当服务繁忙时,如果服务出现异常,不是粗暴的直接报错,而是返回一个友好的提示,虽然拒绝了用户的访问,但是会返回一个结果。
3.Feign(服务调用)
3.1.简介
Feign:伪装,假装;Feign可以把Rest的请求进行隐藏,伪装成类似SpringMvc的Controller一样,不用自己拼接url,拼接参数等等操作,一切交给Feign去做。官网:https://github.com/OpenFeign/feign。
Feign 是 Netflix 开发声的声明式、模板化的HTTP客户端,其灵感来自Retrofit、JAXRS-2.0 以及 WebSocket。Feign 可帮助我们更加办结。优雅的调用HTTP API。
SpringCloud中,使用 Feign 非常简单:创建一个接口,并在接口上添加一些注释,代码就完成了。Feign 支持多种注解,例如 Feign自带的注解或者 JAX-RS 注解。SpringCloud 对 Feign 进行了增强,使 Feign 支持了 Spring MVC 注解,并整合了 Ribbon 和 Eureka,从而让 Feign 的使用更加方便。
3.2.快速入门
3.2.1.导入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
3.2.2.Feign的客户端
@FeignClient("user-service")
public interface UserFeignClient {
@GetMapping("/user/{id}")
User queryUserById(@PathVariable("id") Long id);
}
首先之二是一个接口,Feign会通过动态代理,帮我们生产实现类。这点跟myBatis的mapper很像。
@FeignClient,声明这是一个Feign客户端,类似@Mapper注解。同时通过value属性指定服务名称。
接口中的定义方法,完全采用SpringMVC的注解,Feign会根据注解帮我们生成URL,并访问获取结果。
3.2.3.开启Feign功能
@SpringBootApplication
@EnableDiscoveryClient
@EnableHystrix
@EnableFeignClients // 开启Feign功能
public class UserConsumerDemoApplication {
public static void main(String[] args) {
SpringApplication.run(UserConsumerDemoApplication.class, args);
}
}
3.3.负载均衡
Feign中本身已经集成了Ribbon依赖和自动配置:
因此我们不需要引入额外依赖
3.4.Hystrix支持
Feign默认也有对Hystix的集成:
只不过,默认情况下是关闭的。我们需要通过下面的参数来开启:
feign:
hystrix:
enabled: true # 开启Feign的熔断功能
但是,Feign中的Fallback配置不像Ribbon中那样简单了。
1)首先,我们要定义一个类,实现刚才编写的UserFeignClient,作为fallback的处理类
@Component
public class UserFeignClientFallback implements UserFeignClient {
@Override
public User queryUserById(Long id) {
User user = new User();
user.setId(id);
user.setName("用户查询出现异常!");
return user;
}
}
2)然后在UserFeignClient中,指定刚才编写的实现类
@FeignClient(value = "user-service", fallback = UserFeignClientFallback.class)
public interface UserFeignClient {
@GetMapping("/user/{id}")
User queryUserById(@PathVariable("id") Long id);
}
3.5.请求压缩
Spring Cloud Feign 支持对请求和相应进行GZIP压缩,以减少通信过程中的性能损耗。
feign:
compression:
request:
enabled: true # 开启请求压缩
response:
enabled: true # 开启响应压缩
mime-types: text/html,application/xml,application/json # 设置压缩的数据类型
min-request-size: 2048 # 设置触发压缩的大小下限
3.6.日志级别
通过logging.level.xx=debug
来设置日志级别。然而这个对Fegin客户端而言不会产生效果。因为@FeignClient
注解修改的客户端在被代理时,都会创建一个新的Fegin.Logger实例。我们需要额外指定这个日志的级别才可以。
1)设置com.xx 包下的日志级别都为debug
logging:
level:
com.xx: debug
2) 编写配置类,定义日志级别
@Configuration
public class FeignConfig {
@Bean
Logger.Level feignLoggerLevel(){
return Logger.Level.FULL;
}
}
-
NONE:不记录任何日志信息,这是默认值。
-
BASIC:仅记录请求的方法,URL以及响应状态码和执行时间
-
HEADERS:在BASIC的基础上,额外记录了请求和响应的头信息
-
FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据。
3)在FeignClient中指定配置类:
@FeignClient(value = "user-service", fallback = UserFeignClientFallback.class, configuration = FeignConfig.class)
public interface UserFeignClient {
@GetMapping("/user/{id}")
User queryUserById(@PathVariable("id") Long id);
}