Ribbon是Netflix开源的一款用于客户端负载均衡的工具软件,目前ribbon是使用轮询的方式进行负载均衡的。
何为负载均衡? 可以参考我的一篇博文:CentOS 6.5 下 Nginx的反向代理和负载均衡的实现
上述的Nginx是基于服务端的反向代理+负载均衡器
基于前两篇,我用一张图来说明一下什么是eureka(服务注册+发现中心),其实我是想画个鱼塘的,哈哈
在上图中:
处在顶层的是我们的eureka服务注册中心(省略发现,先服务注册,随后才有服务被发现,心知肚明,我这里就省略了)
处在两边的是eureka的client部分,所谓的client就是服务的提供者,也就是要向服务中心发起服务注册的一方,这里我们只模拟了两个客户端,服务名都叫“hello-service”,为嘛叫一样的了?因为他俩本来就是一个模子刻出来的啊,这里等于做了一个service集群(集群就是相同的服务部署到不同的机子上,说白了就是这部分 localhost:port 不一样,也有可能环境不一样,项目中的配置文件也不一样,比如一个服务部署在Windows下,一个部署在Linux下),集群的好处就是,一个服务挂掉了,另一个服务可以顶上,不至于出现无服务可用的尴尬局面,就好好比连锁店一样,为什么会有那么多连锁店,还不是有大量消费者买单吗?另外,这里的eureka客户端也可以充当服务消费者的身份,也就是,它不仅自身提供服务,而且还可以通过服务注册中心的发现服务功能,去消费xxx服务。
处在下面的,我用了一个笑脸来代替我们自己,因为服务是死的,不会开口说话,不会动脑筋,但是我们可以啊,我们作为服务消费方借助eureka的服务发现功能,成功的获取了服务列表,但是拿到服务,我们就傻眼了
“两个服务,我用哪个呢?”
于是我们思考了片刻,觉得看8022的服务比较顺眼,于是我们就使用了8022的服务
过了一会,我们又要消费服务了,这时候,我们又要思考片刻
“哎哟我去,要是服务中心每次在我使用服务的时候帮我选好服务了该多好啊?”
“算了,刚才用了8022,这次用8080的服务吧”
于是,我们又在浏览器中访问8080的服务
上面说过,对于人来说,我们有选择的能力,但是对于程序来说,它连思考的能力都没有,如何做出选择?
上面还说过,如果服务中心能为服务消费者在使用服务的时候(这里的消费者可不再是我们自己了)提供具体的某一项服务,而不是提供一个服务列表,如果这样的话,那就相当的嗨皮了,如果这样的话,我看eureka可以改名了,叫什么
“服务注册发现定项送货上门中心” --- 还不如干脆叫“服务一条龙中心”得了
现在知道ribbon为什么是客户端级别的了吧
好了,玩笑归玩笑,毕竟eureka能干好自己的事情就好了,我们就不调戏它了
回到本篇,ribbon才是我们今天的主角,有了它,我们再也不用担心该调哪个服务了,轻松消费服务,so easy!
关于ribbon,只要记住一点:在eureka server不给我们提供服务的负载均衡下,我们的ribbon担负起了这一职责,帮助eureka client选择(choose)服务并消费
最开始说过了,ribbon默认采用的方式是轮询方式(Ribbon提供了多重策略,例如轮询round robin、随机Random、根据相应时间加权等)
所谓的轮询就是
“第一天我吃肉,第二天我吃青菜,第三天我吃肉,第四天我再吃青菜.....”一次类推,第五天显然我吃肉的(有迹可循,轮流着来)
所谓的随机就是
“第一天我吃青菜,第二天我吃肉,第三天我还吃肉,第四天我吃青菜....”一次类推,你根本无法断定第五天我是吃肉还是吃青菜
所谓的加权就是
“第一天我吃肉,第二天我吃肉,第三天我吃肉,第四天我吃青菜....”一次类推,你可能发现我比较喜欢吃肉,因此第五天吃肉的概率较大,那什么时候会出现加权的策略呢?比如服务所在的服务器性能比较优,那理所当然这个服务就应该被ribbon选择的多一些,如果这个服务所在的服务器是一台年代久远的PC机,你还让它访问的次数多,这不是自找麻烦吗?
OK,我们说的有点多了,我们直接进入demo案列演示部分
一、项目目录结构树
eureka-server : 服务注册发现中心(Spring-Cloud-Eureka服务注册发现中心server+client案列模拟说明)
eureka-client: 服务提供者及消费者( Spring-Boot+Eureka服务注册发现中心server+client案列demo)
service-ribbon:服务消费者+ribbon客户端负载均衡器提供服务选择功能
二、service-ribbon创建 ---Pom.xml依赖添加
(1)由于ribbon是提供服务选择的,因此,没有eureka的服务发现功能,ribbon也是无力可施啊,所以,pom如下
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.appleyk</groupId>
<artifactId>service-ribbon</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>ribbon</name>
<description>基于Spring-Boot快速开发模式的ribbon--客户端负载均衡</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 添加Eureka依赖,启用服务发现功能,再结合ribbon使用 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<!-- 监控 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- ribbon -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
</dependencies>
</project>
二、service-ribbon创建 ---启用euraka服务发现功能
(1)Application.java
package com.appleyk;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.support.SpringBootServletInitializer;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
//@EnableDiscoveryClient: 客户端启用服务发现功能
@EnableDiscoveryClient
@SpringBootApplication
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
/**
* 注入一个Bean name : "restTemplate"
*
* @return
*/
@Bean
@LoadBalanced//开启客户端负载均衡
RestTemplate restTemplate() {
return new RestTemplate();
}
}
什么是RestTemplate,待会在具体的demo中进行注释解释
使用ribbon的服务choose,真的很简单,简单的只是一个注解@LoadBalance就能搞定(可ribbon源码里面要干的事情是很多的)
Choose a ServiceInstance from the LoadBalancer for the specified service
从负载均衡器中为指定的服务选择一个实例(ribbon会向eureka服务中心发请求,获得相应服务实例的列表,随后choose一个)
@return a ServiceInstance that matches the serviceId
当然,返回的服务实例可不是乱来的,必须是serviceID能够匹配到的
choose之前,ribbon还是有很多工作要做的,再细点,就得研究源码了,我..........
三、service-ribbon创建 ---配置eureka客户端参数
server.port=8033
#当前项目也是一个服务提供者,给自身的serviceID起名
spring.application.name=service-ribbon
#指定eureka服务注册发现中心的访问地址
eureka.client.service-url.defaultZone=http://localhost:8011/eureka/
四、service-ribbon创建 ---创建service,实现负载均衡
(1)HelloService.java
package com.appleyk.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Service
public class HelloService {
@Autowired
private RestTemplate restTemplate;
public String hiService(){
/**
* restTemplate:简化了发起HTTP请求以及处理响应的过程,并且支持REST
* get请求
* getForObject(方法重载)
* 第一个参数:string url -- 格式 : ..//eureka服务名称/RequestMapping....
* 第一个参数:支持字符串占位符 比如 ...//HELLO-SERVICE/hello/{name}/{age},name 和 age 都可以被替换成等价的变量
* 第二个参数:返回数据的类型
*/
return restTemplate.getForObject("http://HELLO-SERVICE/hello", String.class);
}
}
由于我们在Spring-Boot的application.java里面开启了ribbon的客户端负载均衡功能(使用注解@LoadBalanced自动实现),因此,上述请求的地址,表面上看不合理,因为我们知道eureka服务注册中心有两个名称为“HELLO-SERVICE”的服务实例,但是不用担心,ribbon会帮我们选择一个进行请求,不信的话,我们继续往下走....
五、service-ribbon创建 ---创建Controller
(1)HelloController.java
package com.appleyk.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.appleyk.service.HelloService;
/**
* @author yukun24@126.com
* @blob http://blog.csdn.net/appleyk
* @date 2018年2月9日-下午4:38:26
*/
@RestController
public class HelloController {
@Autowired
HelloService helloService;
@Autowired
private LoadBalancerClient loadBalancerClient;
@RequestMapping(value = "/hello")
public String hi(){
return helloService.hiService();
}
@RequestMapping("/service/get")
public String GetService(){
ServiceInstance serviceInstance = loadBalancerClient.choose("hello-service");
String info = "猜猜我是哪个服务:"+serviceInstance.getHost()+":"+serviceInstance.getPort();
System.err.println(info);
return info;
}
}
(2)
至此,一切就绪(eureka-server、eureka-client、service-ribbon),只差run
六、启动eureka-server
(1)
(2)浏览器访问eureka中心
目前,中心只有一个服务实例(Port:8080),部署在本机的虚拟机下
七、启动本地eureka-client
(1)
(2)刷新eureka服务注册中心
八、启动service-ribbon
(1)注意service-ribbon本身也是一个服务提供者
(2)因此,eureka服务注册中心的服务实例列表里也有我们刚刚注册的service-ribbon
九、利用LoadBalancerClient实例测试服务选择
(1)
(2) 测试工具Insomnia测试
A.
B.第一次
C.第二次
下面再调用,就是服务A和服务B的交替出现了,不再做演示
十、ribbon登场,隐式开启负载均衡功能
(1)
(2) 测试工具Insomnia测试
A.第一次
B.第二次
下面再调用,就是服务A和服务B的交替出现了,不再做演示
结束语
坚持写博客,你会发现,你善于搜集资料了,你善于思考了,你也善于整理了,你变得对某项技术不再只局限于使用那么简单了,你渴望去了解它、并用自己的理解去阐述它甚至还有种想要扒开源码一探究竟的欲望,只奈自身能力有限,暂时扒不动也没有足够的时间去细细研究,还是,多读书多学习吧!