【SpringCloud】<微服务>Ribbon负载均衡

【SpringCloud】<微服务>Ribbon负载均衡

 目录

一、负载均衡原理:

负载均衡流程:

LoadBalancerInterceptor:

LoadBalancerClient: 

负载均衡策略IRule :

完整流程: 

二、负载均衡策略:

三、自定义负载均衡策略:

1.方式一:Bean组件

2.方式二:配置文件

四、饥饿加载:


一、负载均衡原理:

负载均衡流程:

【SpringCloud】<微服务>Ribbon负载均衡

        为什么我们只输入了service名称就可以访问了呢?

        之前还要获取ip和端口。

        显然有人帮我们根据service名称,获取到了服务实例的ip和端口。它就是LoadBalancerInterceptor,这个类会在对RestTemplate的请求进行拦截,然后从Eureka根据服务id获取服务列表,随后利用负载均衡算法得到真实的服务地址信息,替换服务id。

        进行源码跟踪!

LoadBalancerInterceptor:

public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
    private LoadBalancerClient loadBalancer;
    private LoadBalancerRequestFactory requestFactory;

    public LoadBalancerInterceptor(LoadBalancerClient loadBalancer, LoadBalancerRequestFactory requestFactory) {
        this.loadBalancer = loadBalancer;
        this.requestFactory = requestFactory;
    }

    public LoadBalancerInterceptor(LoadBalancerClient loadBalancer) {
        this(loadBalancer, new LoadBalancerRequestFactory(loadBalancer));
    }

    public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException {
        URI originalUri = request.getURI();
        String serviceName = originalUri.getHost();
        Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
        return (ClientHttpResponse)this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution));
    }
}

可以看到这里的intercept方法,拦截了用户的HttpRequest请求,然后做了几件事:

  • request.getURI():获取请求uri,本例中就是 http://user-service/user/8

  • originalUri.getHost():获取uri路径的主机名,其实就是服务id,user-service

  • this.loadBalancer.execute():处理服务id,和用户请求。

这里的this.loadBalancerLoadBalancerClient类型,我们继续跟入。  

【SpringCloud】<微服务>Ribbon负载均衡

        LoadBalancerInterceptor实现了ClientHttpRequestInterceptor,ClientHttpRequestInterceptor是一个客户端的HTTP拦截器,他会拦截由客户端发起的HTTP请求。

【SpringCloud】<微服务>Ribbon负载均衡

        LoadBalancerInterceptor重写了intercept方法:

    public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException {
        URI originalUri = request.getURI();
        String serviceName = originalUri.getHost();
        Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
        return (ClientHttpResponse)this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution));
    }

【SpringCloud】<微服务>Ribbon负载均衡

        intercept方法中,拿到了客户端的请求url,解析url获得了模块的服务名称,通过把服务名称交给Eureka拉取服务!这里intercept方法把serviceName(模块的服务名称)交给了loadBalancer进行执行!

【SpringCloud】<微服务>Ribbon负载均衡

LoadBalancerClient: 

        进入execute方法,找到具体实现:

    public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint) throws IOException {
        ILoadBalancer loadBalancer = this.getLoadBalancer(serviceId);
        Server server = this.getServer(loadBalancer, hint);
        if (server == null) {
            throw new IllegalStateException("No instances available for " + serviceId);
        } else {
            RibbonLoadBalancerClient.RibbonServer ribbonServer = new RibbonLoadBalancerClient.RibbonServer(serviceId, server, this.isSecure(server, serviceId), this.serverIntrospector(serviceId).getMetadata(server));
            return this.execute(serviceId, (ServiceInstance)ribbonServer, (LoadBalancerRequest)request);
        }
    }
  • getLoadBalancer(serviceId):根据服务id获取ILoadBalancer,而ILoadBalancer会拿着服务id去eureka中获取服务列表并保存起来。
  • getServer(loadBalancer):利用内置的负载均衡算法,从服务列表中选择一个。本例中,可以看到获取了8082端口的服务 

【SpringCloud】<微服务>Ribbon负载均衡

        execute方法中,先拉取到了Eureka上的服务列表:

【SpringCloud】<微服务>Ribbon负载均衡

负载均衡策略IRule :

根据负载均衡的方式,选取对应的服务节点: 

Server server = this.getServer(loadBalancer, hint);

【SpringCloud】<微服务>Ribbon负载均衡

【SpringCloud】<微服务>Ribbon负载均衡

进入chooseServer方法:

【SpringCloud】<微服务>Ribbon负载均衡

我们看看这个rule是谁:

【SpringCloud】<微服务>Ribbon负载均衡

这里的rule默认值是一个RoundRobinRule,看类的介绍:

【SpringCloud】<微服务>Ribbon负载均衡

这不就是轮询的意思嘛。

完整流程: 

【SpringCloud】<微服务>Ribbon负载均衡

基本流程如下:

  • 拦截我们的RestTemplate请求http://userservice/user/1

  • RibbonLoadBalancerClient会从请求url中获取服务名称,也就是user-service

  • DynamicServerListLoadBalancer根据user-service到eureka拉取服务列表

  • eureka返回列表,localhost:8081、localhost:8082

  • IRule利用内置负载均衡规则,从列表中选择一个,例如localhost:8081

  • RibbonLoadBalancerClient修改请求地址,用localhost:8081替代userservice,得到http://localhost:8081/user/1,发起真实请求  


二、负载均衡策略:

Ribbon的负载均衡规则是一个叫做IRule的接口来定义的,每一个子接口都是一种规则:

【SpringCloud】<微服务>Ribbon负载均衡

【SpringCloud】<微服务>Ribbon负载均衡

默认的实现就是ZoneAvoidanceRule,是一种轮询方案。


三、自定义负载均衡策略:

1.方式一:Bean组件

在order-service中的OrderApplication类中,定义一个新的IRule:

@Bean
public IRule randomRule(){
    return new RandomRule();
}

2.方式二:配置文件

在order-service的application.yml文件中,添加新的配置也可以修改规则:

userservice: # 给某个微服务配置负载均衡规则,这里是userservice服务
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 负载均衡规则 

注意:一般用默认的负载均衡规则,不做修改。


四、饥饿加载:

        Ribbon默认是采用懒加载,即第一次访问时才会去创建LoadBalanceClient,请求时间会很长。而饥饿加载则会在项目启动时创建,降低第一次访问的耗时,通过下面配置开启饥饿加载:

ribbon:
  eager-load:
    enabled: true
    clients: userservice #服务名称

上一篇:zabbix4.0-安装:yum安装(zabbix_proxy、zabbix_agent)


下一篇:prometheus监控介绍1.0(故事的结局总是这样,花开两朵,天各一方。)