3、负载均衡 Ribbon

目录

3、Ribbon

3.1、Ribbon 官方解释

3.2、负载均衡策略

3.3、Spring Boot 使用 Ribbon

3.4、参照 Ribbon 自定义负载均衡

3.4.1、Ribbon 的负载均衡 IRule 接口的实现

3.4.2、自定义负载均衡代码实现

3.4.3、使用自定义负载均衡器


3、Ribbon

3.1、Ribbon 官方解释

Ribbon is a client side load balancer which gives you a lot of control over the behaviour of HTTP and TCP clients. Feign already uses Ribbon, so if you are using @FeignClient then this section also applies.

A central concept in Ribbon is that of the named client. Each load balancer is part of an ensemble of components that work together to contact a remote server on demand, and the ensemble has a name that you give it as an application developer (for example, by using the @FeignClient annotation). On demand, Spring Cloud creates a new ensemble as an ApplicationContext for each named client by using RibbonClientConfiguration. This contains (amongst other things) an ILoadBalancer, a RestClient, and a ServerListFilter.

Ribbon 是一个客户端负载均衡器,可以让您对 HTTP 和 TCP 客户端的行为进行大量控制。 Feign 已经使用了 Ribbon,因此如果您使用的是 @FeignClient,那么本节也适用。

Ribbon 中的一个核心概念是指定客户端的概念。 每个负载均衡器都是一组组件的一部分,这些组件一起工作以按需联系远程服务器,并且该集合具有您作为应用程序开发人员提供的名称(例如,通过使用 @FeignClient 批注)。 根据需要,Spring Cloud 通过使用 RibbonClientConfiguration 为每个命名客户端创建一个新的集合作为 ApplicationContext。 这包含(除其他外)ILoadBalancer,RestClient和ServerListFilter。

3.2、负载均衡策略

  • 随机(Random)
  • 轮询(RoundRobin)
  • 一致性哈希(ConsistentHash)
  • 哈希(Hash)
  • 加权(Weighted)
  • 最小请求数(Available)

3.3、Spring Boot 使用 Ribbon

因为在 eureka 或 Fegin 的包都有依赖 Ribbon,所以不需要依赖 jar,下面是 RestTemplate + LoadBalance 的实现

//1、定义RestTemplate
@Bean
@LoadBalanced
public RestTemplate loadBalancedRestTemplate(){
    return new RestTemplate();
}
//可以使用自定义负载策略,不定义的话会默认
@Bean
public IRule ribbonRule(){
    return new RoundRobinRule();
}
//2、使用 LoadBalanced
@Autowired
@LoadBalanced
private RestTemplate abRestTemplate;

@GetMapping("/lb/invoke/{serviceName}/say")
public String invokeLoadBalanceSay(@PathVariable String serviceName, @RequestParam String message){
    //调用服务端的接口
    return abRestTemplate.getForObject("http://" + serviceName + "/say?message=" + message, String.class);
}

3.4、参照 Ribbon 自定义负载均衡

3.4.1、Ribbon 的负载均衡 IRule 接口的实现

  • RandomRule:随机策略
  • RoundRobinRule:轮询策略
  • WeightedResponseTimeRule:加权策略 可以与轮询策略组合成加权轮询策略
  • BestAvailableRule:请求数最少策略
  • AvailabilityFilteringRule:过滤多次访问故障,以及并发超过阈值的服务,最后使用轮询
  • RetryRule:对选定的负载均衡机上重试机制
  • ZoneAvoidanceRule:复合判断 server 所在区域的性能和 server 的可用性来选择 server

70.png

3.4.2、自定义负载均衡代码实现

public class LoadBalanceRequestInterceptor implements ClientHttpRequestInterceptor {
    private volatile Map<String,Set<String>> targetUrlsCache = new HashMap<>();
    @Autowired
    private DiscoveryClient discoveryClient;
    
    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        //URI:/${app-name}/uri
        URI requestURI = request.getURI();
        String path = requestURI.getPath();
        String [] parts = StringUtils.split(path.substring(1),"/");
        String serviceName = parts[0];
        String uri = parts[1];

        List<String> targetUrls = new LinkedList<>(targetUrlsCache.get(serviceName));
        int size = targetUrls.size();
        int index = new Random().nextInt(size);//使用简单的随机策略,可以使用IRule拓展
        String targetURL = targetUrls.get(index);
        String actualURL = targetURL +"/"+ uri + "?" + requestURI.getQuery();

        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity<InputStream> entity = restTemplate.getForEntity(actualURL,InputStream.class);
        HttpHeaders httpHeaders = entity.getHeaders();
        InputStream responseBody = entity.getBody();
        return new SimpleClentHttpResponse(httpHeaders,responseBody);
    }
    //更新缓存中的服务地址
    @Scheduled(fixedRate = 10 * 1000)
    public void updateTargetUrlsCache(){
        Map<String,Set<String>> newTargetUrisCache = new HashMap<>();
    
        discoveryClient.getServices().forEach(serviceName -> {
            List<ServiceInstance> serviceInstances = discoveryClient.getInstances(serviceName);
            Set<String> newTargetUrls = serviceInstances
                    .stream()
                    .map(s ->
                            s.isSecure() ? "https://" + s.getHost() + ":" + s.getPort() :
                                    "http://" + s.getHost() + ":" + s.getPort()
                    ).collect(Collectors.toSet());
            newTargetUrisCache.put(serviceName,newTargetUrls);
        });
        this.targetUrlsCache = newTargetUrisCache;
    }
    
    private class SimpleClentHttpResponse implements ClientHttpResponse {
        private HttpHeaders httpHeaders;
        private InputStream body;
        public SimpleClentHttpResponse(HttpHeaders httpHeaders, InputStream responseBody) {
            this.httpHeaders = httpHeaders;
            this.body = responseBody;
        }
        @Override
        public HttpStatus getStatusCode() throws IOException {
            return HttpStatus.OK;
        }
        @Override
        public int getRawStatusCode() throws IOException {
            return 200;
        }
        @Override
        public String getStatusText() throws IOException {
            return "OK";
        }
        @Override
        public void close() {
        }
        @Override
        public InputStream getBody() throws IOException {
            return body;
        }
        @Override
        public HttpHeaders getHeaders() {
            return httpHeaders;
        }
    }
}

3.4.3、使用自定义负载均衡器

在spring boot 项目中使用 restTempate + 自定义的 loadBalance 实现负载均衡调用

//1、定义自定义的负载拦截器
@Bean
public ClientHttpRequestInterceptor interceptor(){
    return new LoadBalanceRequestInterceptor();
}
@Bean
@Autowired
public RestTemplate restTemplate(ClientHttpRequestInterceptor interceptor){
    RestTemplate restTemplate = new RestTemplate();
    //2、将自己定义的负载拦截器添加到 restTemplate 的拦截器中
    restTemplate.setInterceptors(Arrays.asList(interceptor));
    return restTemplate;
}
//3、使用自定义的 RestTemplate
@Autowired
private RestTemplate restTemplate;
@GetMapping("/invoke/{serviceName}/say")
public String invokeSay(@PathVariable String serviceName, @RequestParam String message){
    //TODO 存在JsonParseException 的异常
    return restTemplate.getForObject("/" + serviceName + "/say?message=" + message, String.class);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值