• 极客专栏正式上线!欢迎访问 https://www.jikewenku.com/topic.html
  • 极客专栏正式上线!欢迎访问 https://www.jikewenku.com/topic.html

SpringCloud微服务架构第三篇

技术杂谈 勤劳的小蚂蚁 2个月前 (02-08) 80次浏览 已收录 0个评论 扫描二维码

Ribbon是什么?
Ribbon是基于Netflix Ribbon实现的一套客户端 负载均衡的工具。
简单的说,Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法,将Netflix的中间层服务连接在一起。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。
简单的说,就是在配置文件中列出Load Balancer(简称LB)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器。我们也很容易使用Ribbon实现自定义的负载均衡算法。

Ribbon能干什么?

LB,即负载均衡(Load Balance),在微服务或分布式集群中经常用的一种应用。 
负载均衡简单的说就是将用户的请求平摊的分配到多个服务上,从而达到系统的HA。 
常见的负载均衡有软件Nginx,LVS,硬件 F5等。
相应的在中间件,例如:dubbo和SpringCloud中均给我们提供了负载均衡,SpringCloud的负载均衡算法可以自定义。
集中式LB:
即在服务的消费方和提供方之间使用独立的LB设施(可以是硬件,如F5, 也可以是软件,如nginx), 由该设施负责把访问请求通过某种策略转发至服务的提供方。
进程内LB:
将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务器。
Ribbon就属于进程内LB,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址。

Ribbon的相关资料

Ribbon初步的配置

1.修改microservicecloud-consumer-dept-80工程
1).修改pom文件,增加以下内容
<!– Ribbon相关 –>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>

2.修改application.yml 追加eureka的服务注册地址
2).修改application.yml资源文件,增加以下内容:
eureka:
 client:
   register-with-eureka:false
   service-url:

3.对ConfigBean类进行新注解@LoadBalanced 获得Rest时加入Ribbon的配置。
3).修改ConfigBean类,增加以下内容:
@Bean
@LoadBalanced//要求客户端通过Rest去访问微服务的时候自带负载均衡
public RestTemplate getRestTemplate(){
returnnew RestTemplate();
}
4.主启动类DeptConsumer80_App添加@EnableEurekaClient注解。
4).修改主启动类DeptConsumer80_App,增加以下内容:
@SpringBootApplication
@EnableEurekaClient  
public class DeptConsumer80_App {
publicstaticvoidmain(String[] args) {
SpringApplication.run(DeptConsumer80_App.class, args);
}
}
5.修改DeptController_Consumer客户端访问类。
5).修改DeptController_Consumer,增加以下内容:
//private static final String REST_URL_PREFIX = “http://localhost:8001&#8221;;
//从Eureka上面有一个叫MICROSERVICECLOUD-DEPT微服务名字,按名字访问的微服务
privatestaticfinal String REST_URL_PREFIX = http://MICROSERVICECLOUD-DEPT&#8221;;
6.测试:
1).先启动3个eureka集群,也就是microservicecloud-eureka-7001,microservicecloud-eureka-7002和microservicecloud-eureka-7003。
2).再启动microservicecloud-provider-dept-8001并注册进eureka。
3).再启动microservicecloud-consumer-dept-80。
输入到浏览器: http://localhost/consumer/dept/get/1 进行测试
输入到浏览器: http://localhost/consumer/dept/list 进行测试
查看我们的mysql数据库看是否添加了大数据部:
添加成功效果图:
总结:Ribbon和Eureka整合后Consumer可以直接调用服务而不用再关心地址和端口号。

Ribbon负载均衡

Ribbon负载均衡架构图:
Ribbon在工作时分成两步 
  • 第一步先选择 EurekaServer ,它优先选择在同一个区域内负载较少的server. 
  • 第二步再根据用户指定的策略,在从server取到的服务注册列表中选择一个地址。 
    其中Ribbon提供了多种策略:比如轮询、随机和根据响应时间加权。
1.在总父工程下创建microservicecloud-provider-dept-8002的maven Module
1).然后再参考microservicecloud-provider-dept-8001的pom文件,将pom文件里的配置复制一份到microservicecloud-provider-dept-8002 pom文件中
2.在总父工程下再创建microservicecloud-provider-dept-8003的maven Module
1).然后再参考microservicecloud-provider-dept-8001的pom文件,将pom文件里的配置复制一份到microservicecloud-provider-dept-8003 pom文件中
3.将microservicecloud-provider-dept-8001的java源码复制两份分别给microservicecloud-provider-dept-8002和microservicecloud-provider-dept-8003,并且分别修改主启动类名字
修改成功后的效果图:
4.然后再将microservicecloud-provider-dept-8001工程下的resources资源文件夹里面的两个配置文件都复制到microservicecloud-provider-dept-8002和microservicecloud-provider-dept-8003
效果图如下:
然后我们修改microservicecloud-provider-dept-8002和microservicecloud-provider-dept-8003的application.yml文件里面的端口,都分别改为8002和8003端口。
5.新建两个数据库给microservicecloud-provider-dept-8002和microservicecloud-provider-dept-8003这两个工程连接和使用
备注:创建cloudDB02数据库,执行以下sql语句
DROPDATABASEIFEXISTS cloudDB02;
CREATEDATABASE cloudDB02 CHARACTERSET UTF8;
USE cloudDB02;
CREATETABLE dept
(
deptno BIGINTNOTNULL PRIMARY KEY AUTO_INCREMENT,
dname VARCHAR(60),
db_source   VARCHAR(60)
);
INSERTINTO dept(dname,db_source) VALUES(‘开发部’,DATABASE());
INSERTINTO dept(dname,db_source) VALUES(‘人事部’,DATABASE());
INSERTINTO dept(dname,db_source) VALUES(‘财务部’,DATABASE());
INSERTINTO dept(dname,db_source) VALUES(‘市场部’,DATABASE());
INSERTINTO dept(dname,db_source) VALUES(‘运维部’,DATABASE());
SELECT * FROM dept;

再创建一个名为cloudDB03的数据库,执行以下sql语句。
DROPDATABASEIFEXISTS cloudDB03;
CREATEDATABASE cloudDB03 CHARACTERSET UTF8;
USE cloudDB03;
CREATETABLE dept
(
deptno BIGINTNOTNULL PRIMARY KEY AUTO_INCREMENT,
dname VARCHAR(60),
db_source   VARCHAR(60)
);
INSERTINTO dept(dname,db_source) VALUES(‘开发部’,DATABASE());
INSERTINTO dept(dname,db_source) VALUES(‘人事部’,DATABASE());
INSERTINTO dept(dname,db_source) VALUES(‘财务部’,DATABASE());
INSERTINTO dept(dname,db_source) VALUES(‘市场部’,DATABASE());
INSERTINTO dept(dname,db_source) VALUES(‘运维部’,DATABASE());
SELECT * FROM dept;
然后再改microservicecloud-provider-dept-8002和microservicecloud-provider-dept-8003这两个工程的application.yml文件下的数据库源的url指定的数据库名字
  • microservicecloud-provider-dept-8002工程连 cloudDB02,
  • microservicecloud-provider-dept-8003工程连 cloudDB03。
好了,我们做了那么多配置和修改,现在要进入测试我们的负载均衡了!!!!
测试的步骤:
1.启动3个eureka集群配置(microservicecloud-eureka-7001系列)
2.启动3个Dept微服务并各自测试通过(microservicecloud-provider-dept-8001系列)
 启动3个Dept微服务成功后先进行一次测试
 1).先在浏览器输入第一个网址:http://localhost:8001/dept/list 进行测试
 2).再从浏览器中输入第二个网址:http://localhost:8002/dept/list 进行测试
 3).再从浏览器中输入第三个网址:http://localhost:8003/dept/list 进行测试
 如果三个网址都成功出现JSON字符串,并且db_source的数据都不同,那么说明成功了!!!
3.启动microservicecloud-consumer-dept-80
启动成功以后,在浏览器输入:http://localhost/consumer/dept/list
就可以体现负载均衡的效果了。
启动成功后的负载均衡效果图: 这是第一次访问的结果,
我们在http://localhost/consumer/dept/list 这个页面上刷新一次该页面就会看到clouddb03会变成clouddb02再刷新一次就会变成clouddb01。依次这样,这种模式采用的是轮询算法,这里就不演示了。
备注:注意观察看到返回的数据库名字,各不相同,负载均衡实现
然后再打开http://eureka7001.com:7001/ 你就可以看到在一个微服务下面挂着三个实例
总结:Ribbon其实就是一个软负载均衡的客户端组件,他可以和其他所需请求的客户端结合使用,和eureka结合只是其中的一个实例。

Ribbon核心组件之IRule

根据特定算法中从服务列表中选取一个要访问的服务。
SpringCloud结合Ribbon,它默认出厂自带了7种算法。
第一种是:RoundRobinRule 轮询
第二种是:RandomRule 随机
第三种是:AvailabilityFilteringRule 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务, 
还有并发的连接数量超过阈值的服务,然后对剩余的服务列表按照轮询策略进行访问。
第四种是:WeightedResponseTimeRule 根据平均响应时间计算所有服务的权重,响应时间越快服务权重越大被选中的概率越高。刚启动时如果统计信息不足,则使用RoundRobinRule策略,等统计信息足够, 
会切换到WeightedResponseTimeRule。
第五种是:RetryRule 先按照RoundRobinRule的策略获取服务,如果获取服务失败则在指定时间内会进行重试,获取可用的服务。
第六种是:BestAvailableRule 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务。
第七种是:ZoneAvoidanceRule 默认规则,复合判断server所在区域的性能和server的可用性选择服务器。
那么目前,我们实现了第一种算法RoundRobinRule 轮询的方式。
那我们就来做第二种随机算法的实现吧。
在microservicecloud-consumer-dept-80工程ConfigBean类下做一个随机算法的方法
@Bean
public IRule myRule(){
//用重新选择的随机算法替代默认的轮询算法。
returnnew RandomRule();
}
我们来测试一下:
1.启动3个eureka集群配置(microservicecloud-eureka-7001系列)。
2.启动3个Dept微服务并各自测试通过(microservicecloud-provider-dept-8001系列)。
3.启动microservicecloud-consumer-dept-80工程进行随机算法的测试。
请在浏览器输入:http://localhost/consumer/dept/list
随机的效果我不截图了,因为用了随机算法,随机的效果的体现你们就能体会的到。
那么其他的一些算法,我就不一一进行展示了。

Ribbon自定义算法

如果上面的7种算法,都不够出来业务逻辑,那么可以来自定义算法。
1.修改microservicecloud-consumer-dept-80的主启动类
1).在主启动类上添加一个注解@RibbonClient()
/*在启动该微服务的时候就能去加载我们的自定义Ribbon配置类,从而使配置生效。
并且官方文档明确给出了警告:
   这个自定义配置类不能放在@ComponentScan所扫描的当前包下以及子包下,
   否则我们自定义的这个配置类就会被所有的Ribbon客户端所共享,也就是说
   我们达不到特殊化定制的目的了。*/
//那么解决方案是重新在java包下再建一个包名,并把MySelfRule类放入该包内。
@RibbonClient(name=“MICROSERVICECLOUD-DEPT”,configuration=MySelfRule.class)
请看下面的效果图:
编写MySelfRule自定义配置类的:
@Configuration
publicclassMySelfRule{
@Bean
public IRule myRule()
 {
returnnew RandomRule();//Ribbon默认是轮询,我自定义为随机
}
}
然后进行测试:
1.启动3个eureka集群配置(microservicecloud-eureka-7001系列)。
2.启动3个Dept微服务并各自测试通过(microservicecloud-provider-dept-8001系列)。
3.启动microservicecloud-consumer-dept-80工程进行随机算法的测试。
请在浏览器输入:http://localhost/consumer/dept/get/1
测试的效果我不截图了,因为用的是自定义随机算法,随机的效果的实现你们就能体会的到。

自定义规则深度解析

需求:依旧轮询策略,但是加上新需求,每个服务器要求被调用5次。也即以前是每台机器一次,现在是每台机器5次。
需求代码实现: 在MySelfRule类中的myRule方法里面添加以下内容:
@Configuration
publicclassMySelfRule{
@Bean
public IRule myRule()
   {
//return new RandomRule();//Ribbon默认是轮询,自定义为随机
returnnew RandomRule();
}
然后在myrule包下创建一个RandomRule_hhf的类,并且添加以下内容:
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
//需求是5次,但是微服务只有8001,8002,8003三台机器
publicclassRandomRule_hhfextendsAbstractLoadBalancerRule{
//总共被调用的次数,目前要求每台被调用5次  
privateint total = 0;
//当前提供服务的机器号
privateint currentIndex = 0;
public Server choose(ILoadBalancer lb, Object key){
//ILoadBalancer哪一种负载均衡算法如果等于null的话就返回null,那么自然而然,它肯定会加载一种算法,所以它不会变成null。
if (lb == null) {
returnnull;
}
//现在还不知道是哪个算法来响应server
Server server = null;
//如果说server等于null,那么就看线程是否中断了,如果被中断的话就返回null
while (server == null) {
if (Thread.interrupted()) {
returnnull;
}
//upList的意思就是现在活着的可以对外提供的机器,然后.get()方法通过
//int index = rand.nextInt(serverCount); 那么就是随机到几就返回第几的值
List<Server> upList = lb.getReachableServers();
List<Server> allList = lb.getAllServers();
//如果serverCount目前有三台,那么就不等于0,那么就是flase。
int serverCount = allList.size();
if (serverCount == 0) {
/*
                * No servers. End regardless of pass, because subsequent passes
                * only get more restrictive.
                */
returnnull;
}
//这个的意思就是说如果serverCount有三台,那么index就得到了从下标0和1和2数组
// int index = rand.nextInt(serverCount);
// server = upList.get(index);
//当第一次total < 5的时候
//当第二次total < 5的时候
//当第五次total < 5的时候(那么第五次就不小于5),那么if(total < 5)这段里面的代码就不执行了
if(total < 5){
//那么第一次的server是0号机
//那么第二次的server也是0号机
server = upList.get(currentIndex);
//第一次的总的计数次数是加一个1
//第二次的总的计数次数是再加一个1
total++;
//当第五次total < 5的时候就走else
}else {
//那么total等于0
total = 0;
//而currentIndex就加一个1
currentIndex++;
//那么1大于等于upList.size(),目前假设有三台机器,那么1就不大于等于upList.size()
//那么现在就是给0号机给1号机进行服务了,以此类推。。
//但是如果currentIndex等于下标3的时候并且>= upList.size(),但我们按照数组下标来算的话只               //有0和1和2的下标,那么当currentIndex等于下标3的时候这样就是超过第三台了,那么                        //currentIndex就重新等于0。以此类推。。
if(currentIndex >= upList.size())
{
currentIndex = 0;
}
}
//如果这个server等于null,那么线程中断一会,下一轮继续
if (server == null) {
/*
                * The only time this should happen is if the server list were
                * somehow trimmed. This is a transient condition. Retry after
                * yielding.
                */
Thread.yield();
continue;
}
//如果活着好好的,那么就返回server回去
if (server.isAlive()) {
return (server);
}
// Shouldn’t actually happen.. but must be transient or a bug.
server = null;
Thread.yield();
}
//返回对应该响应服务是8001,还是8002还是8003
return server;
}
protectedintchooseRandomInt(int serverCount){
return ThreadLocalRandom.current().nextInt(serverCount);
}
@Override
public Server choose(Object key){
return choose(getLoadBalancer(), key);
}
@Override
publicvoidinitWithNiwsConfig(IClientConfig clientConfig){
}
}
那么我们的自定义算法定义好了以后,我们再回到MySelfRule类中添加RandomRule_hhf类进行自定义算法的测试了
在MySelfRule类中添加RandomRule_hhf类:
@Configuration
publicclassMySelfRule{
@Bean
public IRule myRule()
   {
//return new RandomRule();//Ribbon默认是轮询,自定义为随机
//return new RandomRule();
returnnew RandomRule_hhf();  //自定义每台机器5次
}
}
进入测试:
1.启动3个eureka集群配置(microservicecloud-eureka-7001系列)。
2.启动3个Dept微服务并各自测试通过(microservicecloud-provider-dept-8001系列)。
3.启动microservicecloud-consumer-dept-80工程进行自定义负载均衡算法的测试。
请在浏览器输入:http://localhost/consumer/dept/get/1
测试的效果我不截图了,只要在页面刷新5次即可看出效果!!

丨极客文库, 版权所有丨如未注明 , 均为原创丨
本网站采用知识共享署名-非商业性使用-相同方式共享 3.0 中国大陆许可协议进行授权
转载请注明原文链接:SpringCloud微服务架构第三篇
喜欢 (0)
[247507792@qq.com]
分享 (0)
勤劳的小蚂蚁
关于作者:
温馨提示:本文来源于网络,转载文章皆标明了出处,如果您发现侵权文章,请及时向站长反馈删除。

您必须 登录 才能发表评论!

  • 精品技术教程
  • 编程资源分享
  • 问答交流社区
  • 极客文库知识库

客服QQ


QQ:2248886839


工作时间:09:00-23:00