服务器负载均衡是什么意思(nginx负载均衡原理)

1.什么是负载均衡

首先我们来看看维基百科对负载均衡的说明:

负载平衡(Load balancing)是一种计算机技术,用来在多个计算机(计算机集群)、网络连接、CPU、磁盘驱动器或其他资源中分配负载,以达到最优化资源使用、最大化吞吐率、最小化响应时间、同时避免过载的目的。使用带有负载平衡的多个服务器组件,取代单一的组件,可以通过冗余提高可靠性。负载平衡服务通常是由专用软件和硬件来完成。主要作用是将大量作业合理地分摊到多个操作单元上进行执行,用于解决互联网架构中的高并发和高可用的问题。

举个例子来解释下负载均衡

下图中,一群人在银行排队办理业务,假设只有一个服务窗口,那么一个服务窗口来处理所有人员业务办理,人少的时候,肯定是能够办理完的,如果人特别多的时候恩?一个服务窗口肯定是没有办法处理完这么多业务办理的。于是就出现如下图这样,一大堆人排队。

服务器负载均衡是什么意思(nginx负载均衡原理)

在我们负载均衡中 ,客户端(PC/移动/第三方)发起请求,就好比办理业务的人员。负载均衡器(load balancer)就好比取号机,当他接收到一个请求的时候,负载均衡器就会给这个请求,分配一个服务来处理请求。就像取号机一样,负载均衡器按照一定规则分配一个窗口来办理业务。

服务器负载均衡是什么意思(nginx负载均衡原理)

此时,我们的商品服务和订单服务都只是一个服务,那么如何注册多个商品服务呢?

2.1向注册中心注册多个服务

以同样的项目代码,设定端口为8091,其余配置均和之前的商品服务保持一致,然后启动商品服务。

server:port:8091#服务端口spring:application:name:hutao-microservice-item#指定服务名eureka:client:registerWithEureka:true#是否将自己注册到Eureka服务中,默认为truefetchRegistry:true#是否从Eureka中获取注册信息,默认为trueserviceUrl:#Eureka客户端与Eureka服务端进行交互的地址defaultZone:http://127.0.0.1:9090/eureka/instance:prefer-ip-address:true#将自己的ip地址注册到Eureka服务中

点击如下所示,可以看到我们启动了1个注册中心,1个订单服务,2个商品服务

服务器负载均衡是什么意思(nginx负载均衡原理)

也就是说,我们通过服务ID:hutao-microservice-item在注册中心里面,能够找到两个服务

2.2通过服务ID找到服务

在微服务Spring Cloud快速入门中我们定义了如下Feign接口,在这个接口上面,我们添加了一个注解@FeignClient,指定了服务ID:hutao-microservice-item。这个Feign接口会给我发起Http调用。当然在这里我们可能无法看到负载均衡的效果,因此我们需要稍微深入下底层代码。

@FeignClient(value="hutao-microservice-item")@RequestMapping("/itemservice")publicinterfaceFeignOrderService{/***@Description:使用声明式HTTP客户端发起请求:根据ID查询订单*@authorhutao*@mailhutao_2017@aliyun.com*@date2020年8月30日*/@GetMapping(value="item/{itemId}")ItemsqueryItem(@PathVariable("itemId")StringitemId);}

大家应该还记得,我们的商品服务和订单服务,注册到注册中心的时候,我们在启动类上面添加了一个注解:@EnableDiscoveryClient。

现在我们来看下DiscoveryClient这个接口

2.2.1.DiscoveryClient 解读

DiscoveryClient接口源码如下,他是用来发现服务的,比如发现Netflix服务,其中有个方法List getInstances(String serviceId)是根据服务ID获取服务实例集合。

/***RepresentsreadoperationscommonlyavailabletodiscoveryservicessuchasNetflix*Eurekaorconsul.io.**@authorSpencerGibb*@authorOlgaMaciaszek-Sharma*/publicinterfaceDiscoveryClientextendsOrdered{/***Defaultorderofthediscoveryclient.*/intDEFAULT_ORDER=0;/***Ahuman-readabledescriptionoftheimplementation,usedinHealthIndicator.*@returnThedescription.*/Stringdescription();/***GetsallServiceInstancesassociatedwithaparticularserviceId.*@paramserviceIdTheserviceIdtoquery.*@returnAListofServiceInstance.*/List<ServiceInstance>getInstances(StringserviceId);/***@returnAllknownserviceIDs.*/List<String>getServices();/***Defaultimplementationforgettingorderofdiscoveryclients.*@returnorder*/@OverridedefaultintgetOrder(){returnDEFAULT_ORDER;}}

我们可以看到DiscoveryClient 有4个实现,当然这里我们用的肯定是Eureka。

服务器负载均衡是什么意思(nginx负载均衡原理)

查看每个实例,我们发现我们能够看到每个服务的地址,端口等信息

服务器负载均衡是什么意思(nginx负载均衡原理)

上述案例中,我们获取到的实例是两个,那么每次调用的时候,我们怎么来确定,我要调用的是哪一个服务?,因为这时候我们拿到的两个服务,也就是两个不同的IP地址,这时候就需要一个负载均衡器来帮我们选择一个IP进行访问。

在Spring Cloud中,netfix提供一个负载均衡器Ribbon,该负载均衡器是声明式的,其用法如下所示,在我们注入到Spring容器中的RestTemplate添加注解@LoadBalanced,这时候我们的RestTemplate就具备了负载均衡的功能。

注意:RestTemplate底层默认使用的jdk的标准实现,如果我们想让RestTemplate的底层使用okhttp,可以替换实现的。如下源码所示,RestTemplate提供了三个构造方法。

服务器负载均衡是什么意思(nginx负载均衡原理)

在上面的一个案例中,我们通过从服务实例中,获取到服务,然后再从服务中心获取具体的IP地址信息,发起请求。

Stringurl=serviceInstance.getHost()+":"+serviceInstance.getPort();Itemsitems=restTemplate.getForObject("http://"+url+"/itemservice/item/1",

但是,现在我们不需要这么做了,因为我们对restTemplate声明了是需要负载均衡的,因此我们发起请求的时候,就不需要指定IP地址了,我们可以用服务ID来代替IP地址,然后由restTemplate来帮我选择需要调用的IP。因此上述代码会被简化为如下所示,被注释掉的代码就是被优化的代码。

@GetMapping(value="order/{orderId}")publicOrderqueryOrderById(@PathVariable("orderId")StringorderId){StringserviceId="hutao-microservice-item";//List<ServiceInstance>instances=discoveryClient.getInstances(serviceId);//ServiceInstanceserviceInstance=instances.get(0);//Stringurl=serviceInstance.getHost()+":"+serviceInstance.getPort();//Itemsitems=restTemplate.getForObject("http://"+url+"/itemservice/item/1",Items.class);Itemsitems=restTemplate.getForObject("http://"+serviceId+"/itemservice/item/1",Items.class);System.out.println(items);returnnull;}

重启服务后,商品服务仍然可用,那么这时候怎么来验证我们的负载均衡成功了?

2.2.5.简单验证负载均衡

其实最简单的验证方式就是,在商品服务中,添加日志,看看哪个服务记录了日志或者debug调试,看走哪一个服务的代码。即可验证我们的负载均衡是否成功。

当然这里我们就不做上述方式的演示,来做一点高难度的代码分析。

3.分析@LoadBalanced实现负载均衡源码解析

3.1.RestTemplate源码解析

1、首先看org.springframework.web.client.RestTemplate类。

当我们执行如下代码时

restTemplate.getForObject("http://"+serviceId+"/itemservice/item/1",Items.class);

最终会执行到如下方法

doExecute(URI,HttpMethod,RequestCallback,ResponseExtractor<T>)

服务器负载均衡是什么意思(nginx负载均衡原理)

逐步深入代码,我们找到了如下的拦截器。LoadBalancerInterceptororg.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor

服务器负载均衡是什么意思(nginx负载均衡原理)

接着执行完毕getServer这个方法后,我们发现,只有一个服务实例了为8090。第二次调试代码后发现获取的是8091.多次调试后发现是8090和8091是轮询出现的,

服务器负载均衡是什么意思(nginx负载均衡原理)

也就是说getServer这个方法给我们实现了负载均衡。看源代码知道,如果未设置负载均衡参数,就使用default。否则就根据配置的参数选择服务。

protectedServergetServer(ILoadBalancerloadBalancer,Objecthint){if(loadBalancer==null){returnnull;}//Use'default'onanullhint,orjustpassiton?returnloadBalancer.chooseServer(hint!=null?hint:"default");}

服务器负载均衡是什么意思(nginx负载均衡原理)

在我们之前调试代码的时候我们发现源码中定义了IRule.choose(Object objet)这个接口,并且如下几个实现。

服务器负载均衡是什么意思(nginx负载均衡原理)

如果我们不想要默认的轮询机制,我们可以采用如上实现中的任何一种

比如,我们设置为随机策略。

  • hutao-microservice-item是我们需要对某个服务设定
  • NFLoadBalancerRuleClassName是我们的策略(也就是实现类的路径)
hutao-microservice-item:ribbon:NFLoadBalancerRuleClassName:com.netflix.loadbalancer.RandomRule

重启服务后,再看我们的负载均衡,是随机的,而不是轮询的。

服务器负载均衡是什么意思(nginx负载均衡原理)

6.总结

  1. 注册多个服务:将项目部署在不同的服务器,或者同一个服务器不同端口号,然后注册到注册中心的服务ID(serviceId)保持一致即可;
  2. 负载均衡实现:通过对RestTemplate添加注解@LoadBalanced,使用代理模式,拦截RestTemplate请求时,访问指定的负载服务,从而实现负载均衡。
  3. DiscoveryClient接口他是用来发现服务的,比如发现Netflix服务,其中有个方法List getInstances(String serviceId)是根据服务ID获取服务实例集合。
  4. LoadBalancerInterceptor进行请求拦截处理
  5. RibbonLoadBalancerClient首先通过getLoadBalancer根据服务ID获取到负载,然后getServer会从多个负载中获取到适合的服务。其实现chooseServer如果没有设置负载均衡规则,则使用默认的轮询算法。
  6. IRule接口定义了choose方法。
(0)
小多多的头像小多多创始人

相关推荐

发表回复

登录后才能评论