1、Ribbon负载均衡
Spring Cloud Ribbon是一套客户端的负载均衡工具。
LB:负载均衡
分类:
-
集中式LB
服务消费者和服务提供者之间,单独设置一个设施
比如Nginx
-
进程式LB
将LB集成到消费者,消费者从服务注册中心获得有哪些可用的地址,从中选取一个
Ribbon就是属于进程式LB
Ribbon配置
1、导入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
2、向程序的spring ioc容器注入一个bean: restTemplate
@Configuration
public class ConfigBean
{
@Bean
@LoadBalanced // 支持负载均衡的功能
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
通过restTemplate基于服务名调用微服务
RestController
public class DeptConsumerController {
@Autowired
private RestTemplate restTemplate;
// 这里现在是死的地址
// private static final String REST_URL_PREFIX = "http://localhost:8001";
// 一旦集成了Ribbon,这里可以直接编写服务名即可 springcloud-provider-dept
// 可以基于微服务的名称来访问微服务
// springcloud-provider-dept 8001 8002 8003 (集群)
private static final String REST_URL_PREFIX = "http://SPRINGCLOUD-PROVIDER-DEPT";
@RequestMapping("/consumer/dept/discovery")
public Object discovery(){
return restTemplate.getForObject(REST_URL_PREFIX+"/dept/discovery",Object.class);
}
@RequestMapping("/consumer/dept/add")
public boolean add(Dept dept){
return restTemplate.postForObject(REST_URL_PREFIX+"/dept/add",dept,Boolean.class);
}
@RequestMapping("/consumer/dept/get/{id}")
public Dept get(@PathVariable("id") Long id) {
System.out.println("===>"+id);
return restTemplate.getForObject(REST_URL_PREFIX+"/dept/get/"+id,Dept.class);
}
@RequestMapping("/consumer/dept/list")
public List<Dept> list() {
return restTemplate.getForObject(REST_URL_PREFIX+"/dept/list",List.class);
}
IRule 负载均衡算法
ribbon的负载均衡算法都是实现了IRule接口
Ribbon中的7中负载均衡算法:
(1)RoundRobinRule:轮询;
(2)RandomRule:随机;
(3)AvailabilityFilteringRule:会先过滤掉由于多次访问故障而处于断路器状态的服务,还有并发的连接数量超过阈值的服务,然后对剩余的服务列表按照轮询策略进行访问;
(4)WeightedResponseTimeRule:根据平均响应时间计算所有服务的权重,响应时间越快的服务权重越大被选中的概率越大。刚启动时如果统计信息不足,则使用RoundRobinRule(轮询)策略,等统计信息足够,会切换到WeightedResponseTimeRule;
(5)RetryRule:先按照RoundRobinRule(轮询)策略获取服务,如果获取服务失败则在指定时间内进行重试,获取可用的服务;
(6)BestAvailableRule:会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务;
(7)ZoneAvoidanceRule:复合判断Server所在区域的性能和Server的可用性选择服务器;
在ConfigBean 修改LB策略
@Configuration
public class ConfigBean {
// 支持负载均衡的功能
@LoadBalanced // SpringCloud Ribbon 是基于客户端实现的一套负载均衡的工具
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
// 修改LB策略
@Bean
public IRule myRule(){
return new RetryRule();
}
}
自定义负载均衡算法
注意点:一定不要在主启动类同级或子级下写算法,不能被Spring扫描!
1、配置类MySelfRule
@Configuration
public class MySelfRule {
// 修改LB策略
@Bean
public IRule myRule(){
return new CodingRandomRule();
}
}
2、自定义负载均衡规则CodingRandomRule
public class CodingRandomRule extends AbstractLoadBalancerRule {
// 8001 8002 8003 , 每个服务调用5次,轮询升级版
// 逻辑
// 1、total = 0,当total数量为5,就切换到下一个服务,重置为0
// 2、index = 0. 对外提供的服务选择 ,当index>3, 重置为0
private int total = 0;
private int currentIndex = 0;
public Server choose(ILoadBalancer lb, Object key) {
System.out.println("codingRandom rule choice");
if (lb == null) {
return null;
}
// 最终选择的服务
Server server = null;
while (server == null) {
if (Thread.interrupted()) {
return null;
}
// 获取所有上线的服务 8001 8002 8003
List<Server> upList = lb.getReachableServers();
// 获取所有所有的服务 8001 8002 8003 ......
List<Server> allList = lb.getAllServers();
// 获取服务数量
int serverCount = allList.size();
if (serverCount == 0) {
return null;
}
// 随机选择一个 (算法的具体位置)
if (total < 5){ // 如果当前total小于5
server = upList.get(currentIndex);
total++;
} else { // 如果当前total大于5
total = 1;
currentIndex++;
// >=
if (currentIndex >= upList.size()){
currentIndex = 0;
}
server = upList.get(currentIndex);
}
// ======= END =======
if (server == null) {
Thread.yield();
continue;
}
if (server.isAlive()) {
return (server);
}
server = null;
Thread.yield();
}
return server;
}
protected int chooseRandomInt(int serverCount) {
return ThreadLocalRandom.current().nextInt(serverCount);
}
@Override
public Server choose(Object key) {
return choose(getLoadBalancer(), key);
}
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
// TODO Auto-generated method stub
}
}
3、主启动类,加上注解@RibbonClient
@SpringBootApplication
@EnableEurekaClient // 客户端
@EnableFeignClients
// 配置Ribbon客户端, 多个 *
@RibbonClient(name = "provider-dept",configuration = MySelfRule.class)
public class ConsumerDeptApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerDeptApplication.class,args);
}
}
2、Feign调用
Feign 声明式的web service客户端,调用微服务更加简单,类似controller 调用sevice
feign集成类ribbon,也支持负载均衡,调用ribbon的负载均衡算法。
Feign 其实不是做负载均衡的,负载均衡的是ribbon的功能,feign只是集成了ribbon
使用
1、导入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2、定义一个feign接口,通过@ FeignClient(“服务名”),来指定调用哪个服务
@FeignClient(value = "provider-dept",fallback = DeptClientServiceFallbackFactory.class)
public interface DeptClientService {
@RequestMapping(value = "/dept/get/{id}",method = RequestMethod.GET)
R getByid(@PathVariable("id") String id);
}
3、Web层conroller调用接口
@RestController
public class DeptConsumerController {
//编译器报错,无视。 因为这个Bean是在程序启动的时候注入的,编译器感知不到,所以报错。
@Autowired
DeptClientService deptClientService;
@RequestMapping("/consumer/dept/get/{id}")
public Dept get(@PathVariable("id") Long id) {
return deptClientService.getById(id);
}
}
4、启动类加上注解@EnableFeignClients开启Feign的功能:
@SpringBootApplication
@EnableEurekaClient // 客户端
@EnableFeignClients
// 配置Ribbon客户端, 多个 *
@RibbonClient(name = "provider-dept",configuration = MySelfRule.class)
public class ConsumerDeptApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerDeptApplication.class,args);
}
}
3、Hystrix断路器
服务雪崩
分布式系统面临的问题:
一个复杂系统中,经常会有数十个依赖相互调用,有时候依赖可能会调用失败
服务雪崩
多个微服务之间的调用,A调用B,B调用C,C又调用….,这就是所谓的扇出
假设扇出的链路上,某一个微服务调用响应时间过长,或者直接不可用,对于微服务A,这个时候就会占用越来越多的资源,从而可能引起系统崩溃,这就是所谓的“雪崩效应”。
对于高流量的应用来说,单例的后端依赖可能会导致所有服务上的资源在几秒内就饱和。比失败更糟糕的是,导致服务之间的延迟增加,队列,线程资源都紧张,可能导致更多的问题出现。
Hystrix的出现
Hystrix (豪猪,断路器)是一个用于处理分布式系统的延迟和容错的库。在分布式系统中,许多的依赖,不可避免会调用失败,比如超时,异常,Hystrix就可以保证一个依赖出现了问题的时候,不会导致整体的服务失败,避免级联故障,从而提高分布式系统的弹性。
它的作用:
-
服务降级(客户端)
-
服务熔断(服务端)
-
服务限流
- 实时监控(hystrix dashboard 监控@HystrixCommand指定的断路点,反应实例断路点调用健康情况)
关于hystrix的监控,可以参考之前我写的两篇文章:
服务熔断(提供方,服务端)
熔断就是一种应对服务雪崩的链路保护机制,在服务提供方设置的。
测试
1、导入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
2、在controller对应的类上,处理问题,编写解决方案
@RestController
public class DeptController {
@Autowired
private DeptService deptService;
// 面对这种情况,就可以使用Hystrix,
// 使用 @HystrixCommand 然后配置 fallbackMethod
// 一旦调用服务失败,抛出了错误信息后,就自动会调用 fallbackMethod的方法!
@HystrixCommand(fallbackMethod = "hystrixFallbackGet")
@GetMapping("/dept/get/{id}")
public Dept get(@PathVariable("id") Long id){
Dept dept = deptService.queryById(id);
if (dept==null){
throw new RuntimeException("该id"+id + "没有查询到对应的信息");
}
return dept;
}
// 出现问题的解决方案!备用
public Dept hystrixFallbackGet(@PathVariable("id") Long id){
return new Dept().setDeptno(id)
.setDname("该id"+id+"没有对应的信息null=>hystrix_get_fallbackMethod")
.setDb_source("no this data in mysql");
}
}
3、启动类增加注解@EnableHystrix或者@EnableCircuitBreaker
@SpringBootApplication
@EnableEurekaClient // 本服务启动之后,就会自动注册到 Eureka 中
@EnableDiscoveryClient // 开启服务发现
@EnableCircuitBreaker // 对Hystrix熔断机制的支持
//@EnableHystrix // 源码已包含@EnableCircuitBreaker注解
public class ProviderDeptApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderDeptApplication.class,args);
}
}
服务降级(消费方,客户端)
理解:在某种情况下,调节服务,保证最高性能
上图为服务应用,在做秒杀活动60%的请求都是秒杀,只有10%的请求是退款的,为保证秒杀的请求的资源,可以把退款调用的微服务关闭,通过服务降级快速结束请求,把资源让处理,活动过后,再把退款的微服务应用开启。
Feign集成了hystrix,不用导入hystrix依赖
这里使用FallbackFactory编写降级方法,也可以使用Fallback,很简单的,参考我另外一篇文章
1、编写降级Factory类
// 客户端调用的失败的处理!
// 1、客户端编写 FallbackFactory 类,实现 FallbackFactory接口,参数就是要处理的业务方法
// 2、编写对应的逻辑(处理,接口调用这个 FallbackFactory)
// 3、丢到spring中
@Component
public class DeptClientServiceFallbackFactory implements FallbackFactory<DeptClientService> {
// 处理错误逻辑, 统一的处理方式
public DeptClientService create(Throwable throwable) {
return new DeptClientService() {
public Dept queryById(Long id) {
return new Dept().setDeptno(id)
.setDname("请明天退款!")
.setDb_source("no this data in mysql");
}
public List<Dept> queryAll() {
return null;
}
};
}
}
2、DeptClientServiceFallbackFactory配置feign接口中
@FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT",fallbackFactory = DeptClientServiceFallbackFactory.class)
public interface DeptClientService {
@GetMapping("/dept/get/{id}")
public Dept queryById(@PathVariable("id") Long id); // 根据id查询
@GetMapping("/dept/list")
public List<Dept> queryAll(); // 查询所有
}
3、开启hystrix支持
feign:
hystrix:
enabled: true
小结
服务熔断:服务端设定,一般是因为一个服务故障,或者异常引起的,类似保险丝,触发了异常,就直接熔断,不会等待服务超时。
服务降级:客户端设定,从整体的负荷考虑,将某个服务挂掉啦,不能再被调用了,前面举的秒杀活动例子,这个时候客户端可以准备一个自己的本地fallback进行回调,返回缺省值。虽然整体架构上,服务水平下降了,但是比直接挂掉强。
4、服务监控-可视化面板
单个服务监控
/springcloud/2019/11/05/springcloud-hystrix-dashboard.html
多个服务监控
/springcloud/2019/11/06/springcloud-hystrix-turbine.html