Spring Cloud微服务之loadbalancer负载平衡
小学时候,曾经做过这样的数学题。
说有一个水池子,上面有一个排水管,下面有一个进水管,开一个进水管,6个小时灌满水池,开一个排水管,10个小时放光整个水池的水。现在同时打开排水管和进水管,多少个小时能灌满整个池子。
带来的问题是:为什么不节约用水,浪费多少水费,现在水费这么贵。
不过问题就是一个问题,现在问题在升级,如果有上面还是那个进水管,只不过有两个出水管,一个排水管还是6个小时灌满水池,一个排水管还是10个小时放光整个水池的水,同时打开这个进水管和两个排水管,多少时间灌满整个水池。
带来的问题是:这会更浪费水,因为灌不满这个水池了。
当然也带来了负载平衡的道理。
一、负载平衡的概念
负载均衡,英文名称为Load Balance,其含义就是指将负载(工作任务)进行平衡、分摊到多个操作单元上进行运行。
同样是进水,利用负载平衡的道理,不能让每一个进水管歇着,你工作所以我工作。
某些工作出现的早中晚三班导,采用了不固定的方式,每个人都不会固定,说不定你上早班,说不定你上中班,说不定你上晚班,下次就进行轮值了。负载平衡的良好写照。
听说有皇帝的时候,皇帝是后宫佳丽三千,每天晚上靠“翻牌子”来决定谁是被宠爱的。如果当时有懂“负载平衡”道理的太监就会发现,三千佳丽就会协调安排,皇帝也会照顾到每一位佳丽,但也有一个问题,一个佳丽要想重复被庞幸,大概需要十年。就是怕,一首歌唱的“十年之后,我们是朋友,还可以问候,只是那种温柔,再也找不到拥抱的理由”。
还是谈回负载平衡。
负载均衡,是指将负载(工作任务)进行平衡、分摊到多个操作单元上进行运行。
你讨厌的领导下发一个超级变态的任务,工作量大到交给一个人无法完成,于是就需要几个天选的打工人过来一起处理,把大任务分解成多个小任务分配给不同的人,完成分配任务的就叫负载均衡。
负载平衡表现有微服务中,当用户访问一个请求,需要获取服务器上的资源及相关信息,有很多服务器有这样的资源和相关信息,具体由哪个服务器来提供服务,就由负载平衡来决定,如下图所示。
二、负载平衡的算法
谈完负载平衡的概念。来谈一下负载平衡的算法。
还是从一个数学问题谈起吧。
一个飞机上乘载了20个人,结果在飞行过程中出现了故障,需要20个人跳伞求生,只有一个舱门,然后就会一个一个从舱门跳下去。
上面是一个简单的处理负载平衡的方法,一个用户访问资源,每个服务器轮流服务。
现在数学问题升级。
飞机上乘载了20个人,只有19个降落伞,飞机上有前端工程师,有后端工程师,有前台,有项目经理,有产品经理,有UI设计师,有测试人员,你觉得跳伞的顺序是什么,或者说谁是哪个没有降落伞的人。
问题也来了,好像不是一个数学问题,是一个逻辑的问题。
还有,就是必须对这些人的重要性做一些对比,还是一个舱门,还是一个一个打开降落伞下落,只不过需要把下落的人一个一个按照重要性站好队,然后一个一个轮流跳下去。
这里就是负载平衡的算法思维。
负载均衡的算法有多种分类,这里介绍为大家介绍三种常见的,分别是轮询、加权轮询、最小连接。
轮询
简单的轮询,就是把来自用户的请求轮流分配给内部的服务器:从服务器1开始,直到服务器N,然后重新开始循环。
这就是飞机上的降落伞够数,一个一个从舱门中跳下去。
也就是说,从第一个服务器开始分配用户,到每个服务器都能分到一位用户,等到全部服务器都有被分到用户后,就开始第二轮分配,以此类推。
加权轮询
这里需要给服务器设置权重并遵循轮询的模式,根据服务器的不同处理能力,给每个服务器分配不同的权值,使其能够接受相应权值数的服务请求。
假设这里有服务器A、B、C、D,分别设置2、2、1、1的权重,当有一个用户访问时,首先有A或B来服务,这两个服务器权重相同,如有4个用户来访问,就需要每个服务器进行工作,如果有100位用户分配给这四个服务器时,分配顺序会根据处理的速度及权重来决定,不过最开始服务的应该是A或B。
这就是前面数学问题中飞机上降落伞不够数你要让谁先跳伞去逃生。
最小连接
指每次有用户访问时,看当前哪个服务器的连接数最少就分配给哪个服务器。最小连接法是动态负载均衡算法,适用于各个服务器处理的性能相似的时候。
最小连接数法是根据服务器当前的连接情况进行负载均衡的,当请求到来时,会选取当前连接数最少的一台服务器来处理请求。
这个算法相当于飞机故障,还是要完成跳伞,只不过谁系好了伞包,谁做好了心理准备,谁就走到舱门去跳下去。
三、微服务EurekaServer的建立
1、点击File--->New---->Project.....,如下图。
2、在弹出的地话框中,左边点击Spring Initializr,表示Spring的初始程序,右边在默认的地方可能会初始化失败,需要选择自定义的地址:http://start.aliyun.com。如下图所示。
3、在接下来的弹出框中输入spring cloud项目名称的Group和artifactId。如下图所示。
4、点击Next进入下一步,左边先点击Web,右边点击Spring web可以建立Web应用程序。如下图。
5、继续在这个对话框中,左边点击Spring Cloud Discovery,右边点击Eureka Server,如下图所示。
6、然后点击Next进入到“下一步”,会出现对话框。如下图所示。
7、在出现的对话框中,点击Finish后完成项目的构建向导。
项目框架建立后,修改主程序中的主类,如下图所示。
8、在打开的主程序中,加入注解@EnableEurekaServer。如下图所示。
最终,eurekaserver的程序代码如下。
package com.myfirsteurekabalance.myfirsteurekabalance;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class MyfirsteurekabalanceApplication {
public static void main(String[] args) {
SpringApplication.run(MyfirsteurekabalanceApplication.class, args);
}
}
9、下面需要编辑resources下面的application.yml文件,在原来的resouces目录下只有application.properties,需要把application.properties改成application.yml。然后编辑该文件。
10、application.yml的文件内容如下。
# 应用名称
spring:
application:
name: myfirsteurekabalance
# 应用服务 WEB 访问端口
server:
port: 8011
eureka:
client:
register-with-eureka: true
fetch-registry: false
service-url:
defaultZone: http://localhost:8011/eureka
instance:
hostname: myprovider1
其文件内容的解释如下 。
四、Eureka客户端的搭建
1、同样点击File--->New----->Project....,如下图所示。
2、在打开的对话框中,左边继续点击Spring Initializr,表示Spring的初始程序,右边在默认的地方可能会初始化失败,需要选择自定义的地址:http://start.aliyun.com。如下图所示。
3、点击Next,然后在出现的对话框中输入项目名称。如下图所示。
4、在接下来出现的对话框中,左边选择Web,右边选择Spring Web。如下图所示。
5、继续在这个对话框中,左边选择Spring Cloud Discovery,右边选择Eureka Discovery Client。如下图所示。
6、然后点击下一步即Next,在出现的对话框中直接点击Finish完成项目建立的向导。如下图所示。
7、在主程序中输入EnableEurekeClient或者是@EnableDiscoveryClient都可以实现Eureka客户端的发现。
客户端的代码如下。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class Myfirsteurekaclient01Application {
public static void main(String[] args) {
SpringApplication.run(Myfirsteurekaclient01Application.class, args);
}
}
然后在客户端的package包下面建立一个config,并在config下面建立RestTemplateConfig的类文件,然后也要建立一个controller的包,在controller的包下面建立controller的类文件ConsumerController。结构如下图。
编辑config包下面的RestTemplateConfig文件,具体内容需要使用RestTemplate模板,然后重写RestTemplate模板。
代码如下。
package com.myeurekaclientexample01.myeurekaclientexample01.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestTemplateConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
这里指出LoadBalanced,其实就是做负载平衡。Bean需要指出java bean,此java bean主要是把RestTemplate转成Bean。
在Controller的类方法中把RestTemplate做为Autowired的注入,然后通过restTemplate的getForObject获取服务器的地址,这里设置负载平衡。
package com.myeurekaclientexample01.myeurekaclientexample01.controller;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.client.RestTemplate;
import java.util.Map;
@RestController
@RequestMapping("/consumer")
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
@RequestMapping("/getall/{id}")
public Map getall(@PathVariable("id")int id){
String url="http://myproviderexample/provider/getdata/"+id;
return restTemplate.getForObject(url,Map.class);
}
}
这里做的负载平衡,意思是服务器有很多都具备provider的controller地址映射,请求url地址中的myproviderexample是在服务器中注册的controller服务地址。需要服务器中的application.yml中的application中的name是一致的。
这里可以实现多个客户端来提供provider生产者的控制器服务,application.yml中的instance中的name不同,但是application中的name选项一致。
具体其中一个客户端的controller服务下面描述。
五、loadbalance实现负载平衡的服务客户端设置
其建立方法仍然是客户端的设置方法,项目结构图如下。
其controller控制器的具体代码如下。
import java.util.Map;
import java.util.HashMap;
import java.util.UUID;
@RestController
@RequestMapping("/provider")
public class ProviderController {
@Autowired
private Environment env;
@RequestMapping("/getdata/{id}")
public Map getdata(@PathVariable("id")int id){
String providerName=env.getProperty("eureka.instance.hostname")+":"+env.getProperty("server.port");
Map result=new HashMap();
result.put("status",0);
result.put("msg","success");
result.put("provider",providerName);
result.put("get_id_value",id);
result.put("version",UUID.randomUUID().toString());
return result;
}
}
在请求的地址中获取环境的主机,并构建json数据,json数据中可以提供微服务中服务的服务器名称及服务器端口号。
在客户端的yml文件设置如下。
# 应用名称
spring:
application:
name: myprovider
# 应用服务 WEB 访问端口
server:
port: 8094
eureka:
client:
service-url:
defaultZone: http://localhost:8088/eureka
instance:
hostname: myprovider1
其设置的说明如下图所示。
关于windows中hosts的路径如下图所示。
图中所示的路径是: C:\windows\system32\drivers\etc。
在etc目录下有一个hosts文件,打开后,标明服务器名字的映射方式。如下图。
依照第五个主题的方式可以继续产生下一个提供provider服务的客户端,代码是一致的,只是application.yml中的instance选项下的hostname名字不同而已。
六、运行服务器
分别运行注册服务器,提供provider服务的若干服务端,负载平衡的服务端。
运行后,可以先从浏览器中请求地址,访问第一次。如下图所示。
再从浏览器中输入地址,访问第二次。如下图所示。
提供的服务器地址不同,采用了轮询的负载平衡的技术实现。