6.5、自定义负载均衡算法
1.介绍接口IRule
getLoaderBalance()可设置不同的负载均衡方法。
RoundRobinRule:轮询
RandomRule:随机
AvailabilityFilteringRule:先过滤掉跳闸、访问故障的服务,对剩下的进行轮询
RetryRule:会先按照轮询,如果服务获取失败,则会在指定时间内进行重试
2.实现随机轮询
在消费者的配置类中添加
@Bean
public IRule myRule(){
return new RandomRule();
}
3.实现自定义Ribbon类
创建myrule包并放在muzi目录下,创建MuziRule类
@Configuration
public class MuziRule {
@Bean
public IRule myRule(){
return new RandomRule();
}
}
在消费者的启动类中添加注解:
//在微服务启动的时候就能去加载我们自定义的Ribbon类
@RibbonClient(name = "SPRINGCLOUD-PROVIDER-DEPT",configuration = MuziRule.class)
故可覆盖之前的默认轮询
改成自定义算法:
myrule包的结构:
建MuziRandomRule类:
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
public class MuziRandomRule extends AbstractLoadBalancerRule {
public MuziRandomRule() {
}
//total=0 默认为0,如果=5,我们指向下一个服务节点
private int total=0;
//指定当前是哪个节点提供服务
private int currentIndex=0;
/*
每个服务访问5次,换下一个服务(3个)
*/
//@SuppressWarnings({"RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE"})
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
return null;
} else {
Server server = null;
while(server == null) {
if (Thread.interrupted()) {
return null;
}
//获得还活着的服务
List<Server> upList = lb.getReachableServers();
//获得全部的服务
List<Server> allList = lb.getAllServers();
int serverCount = allList.size();
if (serverCount == 0) {
return null;
}
//*****************************
//自定义部分
if(total<5){
server=upList.get(currentIndex);
total++;
}else{
total=0;
currentIndex++;
if(currentIndex>upList.size()){
currentIndex=0;
}
server = upList.get(currentIndex);
}
//*******************************
if (server == null) {
Thread.yield();
} else {
if (server.isAlive()) {
return server;
}
server = null;
Thread.yield();
}
}
return server;
}
}
protected int chooseRandomInt(int serverCount) {
return ThreadLocalRandom.current().nextInt(serverCount);
}
public Server choose(Object key) {
return this.choose(this.getLoadBalancer(), key);
}
public void initWithNiwsConfig(IClientConfig clientConfig) {
}
}
再返回自定义的负载均衡算法类:
@Bean
public IRule myRule(){
//返回自定义的负载均衡算法
return new MuziRandomRule();
}
7.Feign负载均衡
7.1、简介
feign是申明式的web service客户端,它让微服务之间的调用变得更简单了,类似controller调用service.SpringCloud集成了Ribbon和Eureka,可在使用Feign时提供负载均衡的http客户端。
只需要创建一个接口,然后添加注解即可。
feign,主要是社区,大家都习惯面向接口编程。这个是很多开发人员额规范。调用微服务访问两种方法。
1.微服务名字【ribbon】
2.接口和注解【feign】
7.2、Feign能干什么?
○Feign旨在使编写Java Http客户端变得更容易。
○在Feign的实现下,我们只需要创建一个接口并使用注解的方式配置它(类似于以前Dao接口上标注Mapper注解,现在是一个微服务接口上面标注一个Feign注解即可。)
Feign集成了Ribbon
○利用Ribbon维护了MicroServiceCloud-Dept的服务列表信息,并且通过轮询实现了客户端的负载均衡,而与Ribbon不同的是,通过Feign只需要定义服务绑定接口且以声明式的方式,优雅而且简单的实现了服务调用。
7.3、Feign的使用
1.创建一个springcloud-consumer-dept-fegn模块充当消费者(直接复制粘贴)
模块结构:
2.在feign消费者及模块api内导入feign依赖
<!--feign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
3.在api模块内springcloud包下创建service包,并在其目录下,建DeptClientService类
@Component
@FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT")
public interface DeptClientService {
@GetMapping("/dept/get/{id}")
public Dept queryById(@PathVariable("id") Long id);
@GetMapping("/dept/list")
public List<Dept> queryAll();
@PostMapping("/dept/list")
public boolean addDept(Dept dept);
}
4.修改feign消费者的DeptConsumerController类
@RestController
public class DeptConsumerController {
@Autowired
private DeptClientService service;
@RequestMapping("/consumer/dept/add")
public boolean add(Dept dept) {
return this.service.addDept(dept);
}
@RequestMapping("/consumer/dept/list")
public List<Dept> list(){
return this.service.queryAll();
}
@RequestMapping("/consumer/dept/get/{id}")
public Dept get(@PathVariable("id") Long id){
return this.service.queryById(id);
}
}
5.在Feign的启动类上添加注解,开启Feign
@EnableFeignClients(basePackages = {"com.muzi.springcloud"})
@ComponentScan("com.muzi.springcloud")
6.启动一系列服务
8.Hystrix
分布式系统面临的问题
复杂分布式结构中的应用程序有数十个依赖关系,每个依赖关系在某些时候将不可避免的失败!
服务雪崩
多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其他的微服务,这就是所谓的扇出,如果扇出的链路上某个微服务的调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统崩溃,所谓的雪崩效应。
对于高流量的应用来说,单一的后端依赖可能会导致所有服务器上的所有资源都在几秒钟内饱和。比失败更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,备份队列,线程和其他资源紧张,导致整个系统发生更多的级联故障,这些都表示需要对故障和延迟进行隔离和管理,以便单个依赖关系的失败,不能取消整个应用程序或系统 。
什么是Hystrix?
Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统中,许多依赖不可避免的会调用失败,比如超时,异常等,Hystrix能够保证在一个依赖出问题额情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。
“断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个服务预期的,可处理的备选响应,而不是长时间的等待或者抛出调用方法无法处理的异常,这样就可以保证了服务调用方额线程不会被长时间,不必要的占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。
能干嘛?
○服务降级
○服务熔断
○服务限流
○接近实时的监控
○。。。。
使用Hystrix
1.创建springcloud-provider-depthystrix-8001模块(复制粘贴)
并修改一定的配置名字
模块结构:
2.导入Hystrix依赖
<!--Hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
3.重新买Controller的类
@RestController
public class DeptController {
@Autowired
private DeptService deptService;
@GetMapping("/dept/get/{id}")
//设置备选方法
@HystrixCommand(fallbackMethod = "hystrixGet")
public Dept get(@PathVariable("id") Long id){
Dept dept = deptService.queryById(id);
if (dept==null){
throw new RuntimeException("id=>"+id+",不存在该用户");
}
return dept;
}
//备选方案
public Dept hystrixGet(@PathVariable("id") Long id){
return new Dept()
.setDeptno(id)
.setDname("id=>"+id+"没有对应的信息,null--@Hystrix")
.setDb_source("no this database in MySQL");
}
}
4.在Hystrix提供者的启动类中添加注解,启动Hystrix支持
//添加对熔断的支持
@EnableCircuitBreaker