一:Ribbon(消费者调用多个提供者时-负载均衡)
1.实际环境中,我们往往会开启很多个service的集群。此时我们获取的服务列表中就会有多个,到底该访问哪一个呢?一般这种情况下我们就需要编写负载均衡算法,在多个实例列表中进行选择。不过Eureka中已经帮我们集成了负载均衡组件:Ribbon,简单修改代码即可使用。
2.开启负载均衡
①:因为Eureka中已经集成了Ribbon,所以我们无需引入新的依赖。直接修改代码:
②:在RestTemplate的配置方法上添加@LoadBalanced注解:
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
③:修改调用方式,不再手动获取ip和端口,而是直接通过服务名称调用:
@GetMapping("{id}")
public User queryById(@PathVariable("id") Long id){
String url = "http://user-service/user/" + id;
User user = restTemplate.getForObject(url, User.class);
return user;
}
3.修改负载均衡的规则
①:SpringBoot也帮我们提供了修改负载均衡规则的配置入口:
user-service:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
格式是:{服务名称}.ribbon.NFLoadBalancerRuleClassName,值就是IRule的实现类。
附加:
Ribbon默认是采用懒加载,即第一次访问时才会去创建负载均衡客户端。往往会出现超时。如果需要采用饥饿加载,即项目启动即创建,可以这样配置:
ribbon:
eager-load:
enabled: true
clients: user-service
4.流程图
5.总结
①:Eureka中已经集成了Ribbon
②:加注解即可实现ribbon
③:ribbon的主要作用-实现负载均衡(集群中的服务用某种策略算法进行访问)
④:ribbon的负载均衡算法常用俩种(1.轮询 2.随机)
⑤:ribbon底层是用拦截器原理实现的
二:Hystrix(服务之间的调用保护)
1.雪崩问题
①:微服务中,服务间调用关系错综复杂,一个请求,可能需要调用1个或多个微服务接口才能实现,会形成非常复杂的调用链路,
如图,一次业务请求,需要调用A、P、H、I四个服务,这四个服务又可能调用其它服务。如果此时,某个服务出现异常:
②:微服务I发生异常,请求阻塞,用户不会得到响应,则tomcat的这个线程不会释放,于是越来越多的用户请求到来,越来越多的线程会阻塞。
③:服务器支持的线程和并发数有限,请求一直阻塞,会导致服务器资源耗尽,从而导致所有其它服务都不可用,形成雪崩效应。
2.线程隔离,服务降级
①:Hystrix为每个依赖服务调用分配一个小的线程池,如果线程池已满调用将被立即拒绝,默认不采用排队.加速失败判定时间。用户的请求将不再直接访问服务,而是通过线程池中的空闲线程来访问服务,如果线程池已满,或者请求超时,则会进行降级处理
②:服务降级虽然会导致请求失败,但是不会导致阻塞,而且最多会影响这个依赖服务对应的线程池中的资源,对其它服务没有响应。
③:触发Hystix服务降级的情况:
- 线程池已满
- 请求超时
④:实战演示( 在消费者端做线程隔离)
a.引入依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
b.在启动类上添加注解:@EnableCircuitBreaker(这个组合注解包含了他们@SpringCloudApplication)
@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
public class ConsumerApplication {
// ...
}
c.编写降级逻辑
@GetMapping("{id}")
@HystrixCommand(fallbackMethod = "queryByIdFallBack")
public String queryById(@PathVariable("id") Long id){
String url = "http://user-service/user/" + id;
String user = restTemplate.getForObject(url, String.class);
return user;
}
public String queryByIdFallBack(Long id){
log.error("查询用户信息失败,id:{}", id);
return "对不起,网络太拥挤了!";
}
⑤:总结
a.当调用提供方出现故障,或者时间超过1秒,就会走失败降级方法。
b.超时设置:
hystrix:
command:
default:
execution.isolation.thread.timeoutInMilliseconds: 2000
3.服务熔断:
①:熔断状态有3个状态
- Closed:关闭状态(断路器关闭),所有请求都正常访问。
- Open:打开状态(断路器打开),所有请求都会被降级。Hystix会对请求情况计数,当一定时间内失败请求百分比达到阈值,则触发熔断,断路器会完全关闭。默认失败比例的阈值是50%,请求次数最少不低于20次。
- Half Open:半开状态,open状态不是永久的,打开后会进入休眠时间(默认是5S)。随后断路器会自动进入半开状态。此时会释放1次请求通过,若这个请求是健康的,则会关闭断路器,否则继续保持打开,再次进行5秒休眠计时。
②:服务熔断在原来注解的基础上已经实现了(线程隔离实现的注解),只是需要触发熔断的条件:
circuitBreaker.requestVolumeThreshold=10
circuitBreaker.sleepWindowInMilliseconds=10000
circuitBreaker.errorThresholdPercentage=50
解读:
- requestVolumeThreshold:触发熔断的最小请求次数,默认20
- errorThresholdPercentage:触发熔断的失败请求最小占比,默认50%
- sleepWindowInMilliseconds:休眠时长,默认是5000毫秒
③:配置(可以在方法,可以在类上,可以全局配置文件中)
hystrix:
command:
default:
execution.isolation.thread.timeoutInMilliseconds: 2000
4.总结:
①:hystrix作为一种保护雪崩的机制
②:有俩种保护形势
a.线程隔离,服务降级(触发:1.服务故障 2.超时1秒)
b.熔断(触发:访问20次,超过50%访问失败)
返回友好提示,不用等待,不影响其它服务。
③:用相同注解实现(引依赖,启动类加注解,实现类加注解)
④:配置参数(方法上,类上,配置文件中都可以)
三:Feign(底层集成ribbon和集成Hystix的服务间的调用)–重要
1.Feign的理解:可以把一个远程调用的请求,伪造的像一个本地方法调用一样
2.实现
①:导入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
②:Feign的客户端
@FeignClient("user-service")
public interface UserClient {
@GetMapping("/user/{id}")
User queryById(@PathVariable("id") Long id);
}
③:开启Feign功能(我们在启动类上,添加注解,开启Feign功能)
@SpringCloudApplication
@EnableFeignClients // 开启Feign功能
public class ConsumerDemoApplication {
public static void main(String[] args) {
SpringApplication.run(UserConsumerDemoApplication.class, args);
}
}
3.对ribbon集成
①:配置
ribbon:
ReadTimeout: 2000 # 读取超时时长
ConnectTimeout: 1000 # 建立链接的超时时长
ribbon:
ConnectTimeout: 500 # 连接超时时长
ReadTimeout: 2000 # 数据通信超时时长
MaxAutoRetries: 0 # 当前服务器的重试次数
MaxAutoRetriesNextServer: 1 # 重试多少次服务
OkToRetryOnAllOperations: false # 是否对所有的请求方式都重试
4.对hystrix集成
①:默认情况下是关闭的。我们需要通过下面的参数来开启:
feign:
hystrix:
enabled: true # 开启Feign的熔断功能
②:首先,我们要定义一个类,实现刚才编写的UserFeignClient,作为fallback的处理类
@Component
public class UserClientFallback implements UserClient {
@Override
public User queryById(Long id) {
User user = new User();
user.setId(id);
user.setName("用户查询出现异常!");
return user;
}
}
③:然后在UserFeignClient中,指定刚才编写的实现类
@FeignClient(value = "user-service", fallback = UserFeignClientFallback.class)
public interface UserClient {
@GetMapping("/user/{id}")
User queryById(@PathVariable("id") Long id);
}
5.请求压缩
①:Spring Cloud Feign 支持对请求和响应进行GZIP压缩,以减少通信过程中的性能损耗。通过下面的参数即可开启请求与响应的压缩功能:
feign:
compression:
request:
enabled: true # 开启请求压缩
response:
enabled: true # 开启响应压缩
同时,我们也可以对请求的数据类型,以及触发压缩的大小下限进行设置:
feign:
compression:
request:
enabled: true # 开启请求压缩
mime-types: text/html,application/xml,application/json # 设置压缩的数据类型
min-request-size: 2048 # 设置触发压缩的大小下限
6.日志级别
①:设置cn.itcast包下的日志级别都为debug
logging:
level:
cn.itcast: debug
②:编写配置类,定义日志级别
@Configuration
public class FeignConfig {
@Bean
Logger.Level feignLoggerLevel(){
return Logger.Level.FULL;
}
}
③:在FeignClient中指定配置类:
@FeignClient(value = "user-service", fallback = UserClientFallback.class, configuration = FeignConfig.class)
public interface UserClient {
@GetMapping("/user/{id}")
User queryById(@PathVariable("id") Long id);
}
7.总结
①:feign本质的功能微服务之间的调用(佯装)
②:feign集成ribbon(负载均衡-拉取eureka上的服务列表进行算法访问)
③:feign集成hystrix(微服务调用时的保护机制)
④:请求响应(压缩)
⑤:日志(打印)