实现微服务负载均衡(Ribbon)

实现微服务负载均衡(Ribbon)

1、什么是负载均衡?

通俗讲,负载均衡就是将负载(工作任务,访问请求)进行分摊到多个操作单元(服务器,组件)上进行执行。

根据负载均衡发生位置的不同,一般分为服务端负载均衡客户端负载均衡

服务端负载均衡指的是发生在服务提供者一方,比如:常见的nginx负载均衡;而客户端负载均衡指的是发生在服务请求的一方,也就是在发送请求之前已经选好了由哪个实例处理请求。

实现微服务负载均衡(Ribbon)_第1张图片

我们在微服务调用关系中一般会选择客户端负载均衡,也就是在服务调用的一方来决定服务由哪个提供者执行。

2、自定义实现负载均衡

第1步:启动多个相同的微服务。

第2步:通过Nacos查看微服务的注册启动情况。

第3步:修改服务调用代码,实现负载均衡

@RestControllerpublic class OrderController{    
    @Autowried    private RestTemplate restTemplate;    
    @Autowired    private DiscoveryClient discoveryClient;        
    @GetMapping("/order/prod/{pid}")    
    public Order order(@PathVariable("pid")Integer pid){        
        //从nacos中获取服务地址        //自定义规则实现随机挑选服务       
        List<ServiceInstance> instances = discoveryClient.getInstances("service-provider");        
        int index = new Random().nextInt(instances.size());        
        ServiceInstance serviceInstance = instances.get(index);        
        String url = servieInstance.getHost() + ":" + serviceInstance.getPort();        
        Product product = restTemplate.getForObject("http://"+url+"/product"+pid, Product.class);            }}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1ctd7dEP-1632894199343)(C:\Users\14565\AppData\Roaming\Typora\typora-user-images\image-20210915180536907.png)]

第4步:启动多个服务提供者和一个服务消费者,多访问几次消费者测试其结果。

3、基于Ribbon实现负载均衡

3.1、Ribbon是什么?

Ribbon是Spring Cloud的一个组件,它可以让我们使用一个注解就能轻松的实现负载均衡; 是 Netflix 公司的一个开源的负载均衡 项目;是一个基于HTTP 和 TCP的客户端/进程内负载均衡器,**运行在消费者端**

3.2、Ribbon的使用

第1步:在RestTemplate的生成方法上添加@LoadBalanced注解

@Bean
@LoadBalanced
public RestTemplate restTemplate(){    
    return new RestTemplate();
}

第2步:修改服务调用的方法

@RestControllerpublic class OrderController{    
    @Autowired    
    private RestTemplate restTemplate;       
    @GetMapping("/order/prod/{pid}")    
    public Order order(@PathVariable("pid")Integer pid){        
        String url = "service-product";        
        Product product = restTeplate.getForObject("http://"+url+"/product/"+pid,Product.class);    
    }
}

3.3、Ribbon支持的负载均衡策略

Ribbon内置了多种负载均衡策略,内部负载均衡的顶级接口为com.netflix.loadbalancer.IRule,具体的负载策略如下:

策略名 策略描述 实现说明
BestAvailableRule 选择一个最小的并发请求的server 逐个考察Server,如果Server被tripped了,则忽略,再选择其中ActiveRequestCount最小的server。
AvailabilityFilteingRule 过滤掉哪些因为一直连接失败的被标记为circuit tripped的后端server,并过滤掉那些高并发的后端server(active connections超过配置的阈值) 使用一个AvailabilityPredicate来包含过滤server的逻辑,其实就是检查status里纪录的各个server的运行状态
WeightedResponseTimeRule 根据响应时间分配一个weight,响应时间越长,weight越小,被选中的可能性越低。 一个后台线程定期的从status里面读取评价响应时间,为每个server计算一个weight。weight的计算也比较简单repsponse time 减去每个server自己平均的response time是server的权重。当开始运行,没有形成status时,使用roubine策略选择server。
RetryRule 对选定的负载均衡策略server上执行重试机制。 在一个配置时间段内,当选择server失败,则一直尝试使用subRule的方式选择一个可用的server。
RoundRobinRule 轮询方式选择server 轮询index,选择index对应位置的server。
RandomRule 随机选择一个server 在index上随机,选择index对应位置的server。
ZoneAvoidanceRule 复合判断server所在区域的性能和server的可用性选择server 使用ZoneAvoidancePredicate和AvailabilityPredicate来判断是否选择某个server,前一个判断判定一个zone的运行性能是否可用,剔除不可用的zone(的所有server),AvailabilityPredicate用于过滤掉连接数过多的server。

可以通过修改配置来调整Ribbon的负载均衡策略,具体代码如下:

service-provider: #调用的服务提供者的服务名称	ribbon:		NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule		

4、基于LoadBalancer实现负载均衡

4.1、LoadBalancer是什么?

Spring Cloud原有的客户端负载均衡方案Ribbon已经被废弃(因为Netflix公司将Ribbon闭源),因此,Spring Cloud定义了LoadBalancer来替换Ribbon。

4.2、LoadBalancer的使用

第1步:添加LoadBalancer依赖

<dependency>    
    <groupId>org.springframework.cloudgroupId>    
    <artifactId>spring-cloud-starter-loadbalancerartifactId>
    <version>2.2.1.RELEASEversion>
dependency>

第2步:在RestTemplate的生成方法上添加@LoadBalanced注解

@Bean
@LoadBalanced
public RestTemplate restTemplate(){    
    return new RestTemplate();}

或者 使用LoadBalancerClient的客户端API

//在服务消费者的业务逻辑代码中注入LoadBalancerClient 的实例
@Autowired
LoadBalancerClient lbc;

@Autowired
RestTemplate rt;

public List<String> getOders(){
    ServiceInstance si = lbc.choose("order-service");
    rt.getForObject(si.getUri() + "/order/list", List.class);
}

如果项目中引入了Ribbon,Spring Cloud默认优先使用Ribbon,因此使用Spring Cloud Load Balancer需要将Ribbon依赖排除掉或者关闭Ribbon,可以使用以下配置关闭Ribbon功能。

spring:	
 cloud:		
  loadbalancer:			
   ribbon:				
    enabled: false

关闭ribbon之后,Spring Cloud LoadBalancer就会加载成默认的负载均衡器(BlockingLoadBalancerClient), 默认负载均衡器是轮询选择。

4.3、自定义负载均衡策略

可以在启动类上面使用@LoadBalancerClient注解,或者@LoadBalancerClients注解,指定服务级别的负载均衡策略。

@LoadBalancerClient(value = "demo-provider", configuration = RandomLoadbalancerConfig.class)
public class RandomLoadbalancerConfig {	
    @Bean	
    public ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(Environment environment,			LoadBalancerClientFactory loadBalancerClientFactory) {		
        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);		
        return new RandomLoadBalancer(				
            loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);	
    }
}

自定义灰度负载均衡策略示例:

@Slf4j
public class GrayRoundRobinLoadBalancer extends RoundRobinLoadBalancer {	
    private ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;	
    private String serviceId;	
    @Override	
    public Mono<Response<ServiceInstance>> choose(Request request) {		
        ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider				.getIfAvailable(NoopServiceInstanceListSupplier::new);		
        return supplier.get(request).next().map(serviceInstances -> getInstanceResponse(serviceInstances, request));	
    }	
    Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances, Request request) {		// 注册中心无可用实例 抛出异常		
        if (CollUtil.isEmpty(instances)) {			
            log.warn("No instance available {}", serviceId);			
            return new EmptyResponse();		
        }		
        DefaultRequestContext requestContext = (DefaultRequestContext) request.getContext();	
        RequestData clientRequest = (RequestData) requestContext.getClientRequest();		
        HttpHeaders headers = clientRequest.getHeaders();		
        String reqVersion = headers.getFirst(CommonConstants.VERSION);		
        if (StrUtil.isBlank(reqVersion)) {			
            return super.choose(request).block();		
        }		// 遍历可以实例元数据,若匹配则返回此实例		
        for (ServiceInstance instance : instances) {			
            NacosServiceInstance nacosInstance = (NacosServiceInstance) instance;			
            Map<String, String> metadata = nacosInstance.getMetadata();			
            String targetVersion = MapUtil.getStr(metadata, CommonConstants.VERSION);			
            if (reqVersion.equalsIgnoreCase(targetVersion)) {				
                log.debug("gray requst match success :{} {}", reqVersion, nacosInstance);				     
                return new DefaultResponse(nacosInstance);			
            }		
        }		// 降级策略,使用轮询策略		
        return super.choose(request).block();	
    }
}

你可能感兴趣的:(微服务框架,运维,java,nginx)