SpringCloud系列五:Ribbon 负载均衡(Ribbon 基本使用、Ribbon 负载均衡、自定义 Ribbon 配置、禁用 Eureka 实现 Ribbon 调用)
声明:本文来源于MLDN培训视频的课堂笔记,写在这里只是为了方便查阅。
1、概念:Ribbon 负载均衡
2、具体内容
现在所有的服务已经通过了 Eureka 进行了注册,那么使用 Eureka 注册的目的是希望所有的服务都统一归属到 Eureka 之中进 行处理,但是现在的问题,所有的微服务汇集到了 Eureka 之中,而客户端的调用也应该通过 Eureka 完成。而这种调用就可以利用 Ribbon 技术来实现。
Ribbon 是一个服务调用的组件,并且是一个客户端实现负载均衡处理的组件。服务器端实现负载均衡可以使用 Nginx、 HAProxy、LVS 等。
2.1、Ribbon 基本使用
1、 【microcloud-consumer-80】修改 pom.xml 配置文件,追加 Ribbon 相关依赖支持包:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-ribbon</artifactId> </dependency>
2、 【microcloud-consumer-80】修改 RestConfig 配置类,在获取 RestTemplate 对象的时候加入 Ribbon 的配置注解@LoadBalanced。
package cn.study.microcloud.config; import java.nio.charset.Charset; import java.util.Base64; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpHeaders; import org.springframework.web.client.RestTemplate; @Configuration public class RestConfig { @Bean public HttpHeaders getHeaders() { // 要进行一个Http头信息配置 HttpHeaders headers = new HttpHeaders(); // 定义一个HTTP的头信息 String auth = "studyjava:hello"; // 认证的原始信息 byte[] encodedAuth = Base64.getEncoder() .encode(auth.getBytes(Charset.forName("US-ASCII"))); // 进行一个加密的处理 // 在进行授权的头信息内容配置的时候加密的信息一定要与“Basic”之间有一个空格 String authHeader = "Basic " + new String(encodedAuth); headers.set("Authorization", authHeader); return headers; } @Bean @LoadBalanced public RestTemplate getRestTemplate() { return new RestTemplate(); } }
3、 【microcloud-consumer-80】修改 aplication.yml 配置文件,追加 Eureka 的服务注册地址配置。
server:
port: 80
eureka:
client:
register-with-eureka: false #客户端不注册到eureka,只是进行服务的调用
service-url:
defaultZone: http://edmin:studyjava@eureka-7001.com:7001/eureka,http://edmin:studyjava@eureka-7002.com:7002/eureka,http://edmin:studyjava@eureka-7003.com:7003/eureka
4、 【microcloud-consumer-80】修改项目的启动类,追加 Eureka 客户端的配置注解:
package cn.study.microcloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; @SpringBootApplication @EnableEurekaClient public class Consumer_80_StartSpringCloudApplication { public static void main(String[] args) { SpringApplication.run(Consumer_80_StartSpringCloudApplication.class, args); } }
5、 【microcloud-consumer-80】修改控制器调用类;
· 现在在 eureka 之中注册的所有服务的名称都是大写字母:MICROCLOUD-PROVIDER-DEPT;
package cn.study.microcloud.controller; import java.util.List; import javax.annotation.Resource; import org.springframework.cloud.netflix.ribbon.RibbonClient; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import cn.study.vo.Dept; @RestController public class ConsumerDeptController { public static final String DEPT_GET_URL = "http://MICROCLOUD-PROVIDER-DEPT/dept/get/"; public static final String DEPT_LIST_URL = "http://MICROCLOUD-PROVIDER-DEPT/dept/list/"; public static final String DEPT_ADD_URL = "http://MICROCLOUD-PROVIDER-DEPT/dept/add?dname="; @Resource private RestTemplate restTemplate; @Resource private HttpHeaders headers; @RequestMapping(value = "/consumer/dept/get") public Object getDept(long id) { Dept dept = this.restTemplate .exchange(DEPT_GET_URL + id, HttpMethod.GET, new HttpEntity<Object>(this.headers), Dept.class) .getBody(); return dept; } @SuppressWarnings("unchecked") @RequestMapping(value = "/consumer/dept/list") public Object listDept() { List<Dept> allDepts = this.restTemplate .exchange(DEPT_LIST_URL, HttpMethod.GET, new HttpEntity<Object>(this.headers), List.class) .getBody(); return allDepts; } @RequestMapping(value = "/consumer/dept/add") public Object addDept(Dept dept) throws Exception { Boolean flag = this.restTemplate.exchange(DEPT_ADD_URL, HttpMethod.POST, new HttpEntity<Object>(dept, this.headers), Boolean.class) .getBody(); return flag; } }
访问地址:http://client.com/consumer/dept/list。这个时候有了 Ribbon 与 Eureka 整合之后用户不再去关注具体的 Rest 服务的地 址与端口号了,所有的信息获取都通过 Eureka 完成。
2.2、Ribbon 负载均衡
通过上面的代码可以发现在 Ribbon 里面有一个负载均衡的注解:@LoadBalanced,那么就意味着现在就可以实现负载均衡的处理了。ribbon负载均衡的原理图如下
所有的服务都注册到eureka中心,并且都是同一个名字microcloud-provider-dept,用户通过eureka找到服务microcloud-provider-dept,然后通过ribbon负载均衡进行调用
1、 【microcloud-provider-dept-8001】将此项目复制为两份“microcloud-provider-dept-8002”、 “microcloud-provider-dept-8003”;
2、 【microcloud-provider-dept-*】执行各自的数据库脚本,随后修改各自的数据库连接配置;
3、 【microcloud-provider-dept-*】修改各自服务的 application.yml 配置文件;
server:
port: 8001
eureka:
client: # 客户端进行Eureka注册的配置
service-url:
defaultZone: http://edmin:studyjava@eureka-7001.com:7001/eureka,http://edmin:studyjava@eureka-7002.com:7002/eureka,http://edmin:studyjava@eureka-7003.com:7003/eureka
instance:
lease-renewal-interval-in-seconds: 2 # 设置心跳的时间间隔(默认是30秒)
lease-expiration-duration-in-seconds: 5 # 如果现在超过了5秒的间隔(默认是90秒)
instance-id: dept-8001.com # 在信息列表时显示主机名称
prefer-ip-address: true # 访问的路径变为IP地址
info:
app.name: study-microcloud
company.name: www.study.cn
build.artifactId: $project.artifactId$
build.version: $project.verson$
mybatis:
config-location: classpath:mybatis/mybatis.cfg.xml # mybatis配置文件所在路径
type-aliases-package: cn.study.vo # 定义所有操作类的别名所在包
mapper-locations: # 所有的mapper映射文件
- classpath:mybatis/mapper/**/*.xml
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 配置当前要使用的数据源的操作类型
driver-class-name: org.gjt.mm.mysql.Driver # 配置MySQL的驱动程序类
url: jdbc:mysql://localhost:3306/study8001 # 数据库连接地址
username: root # 数据库用户名
password: mysqladmin # 数据库连接密码
dbcp2: # 进行数据库连接池的配置
min-idle: 5 # 数据库连接池的最小维持连接数
initial-size: 5 # 初始化提供的连接数
max-total: 5 # 最大的连接数
max-wait-millis: 200 # 等待连接获取的最大超时时间
application:
name: microcloud-provider-dept
千万记住,现在所有的服务名字一定要保持一致,如果不一致则会认为是两个服务,无法进行负载均衡。
4、 修改项目中的 hosts 配置文件,在这个配置文件之中追加新的域名:
127.0.0.1 dept-8001.com
127.0.0.1 dept-8002.com
127.0.0.1 dept-8003.com
5、 【microcloud-provider-dept-*】在保证 Eureka 已经正确启动之后启动所有的部门微服务信息;
6、 【microcloud-consumer】启动消费端,消费端在 RestTemplate 配置的时候使用了负载均衡的注解。
· 访问地址:http://client.com/consumer/dept/list;
现在发现每一次获取数据都是通过不同的微服务获得的,所以现在同一个消费端就可以通过 Ribbon 实现了负载均衡配置处理。
2.3、自定义 Ribbon 配置
在之前使用了一个“@LoadBalanced”注解,该注解描述的是一个负载均衡,但是对于负载均衡也是可以由用户修改负载均衡策略的。那么如果说现在要想去修改这样的策略,也是可以的,可以使用自定义的 LoadBalacne 的配置类。
1、 【microcloud-consumer-80】追加一个 LoadBalance 的配置类,这个类应该放在 SpringCloud 启动后找不到的位置上,所以应该 做一个新的包;
package cn.study.commons.config; import org.springframework.context.annotation.Bean; import com.netflix.loadbalancer.IRule; public class MyLoadBalanceConfig { @Bean public IRule ribbonRule() { // 其中IRule就是所有规则的标准 return new com.netflix.loadbalancer.RandomRule() ; } }
2、 【microcloud-consumer-80】修改程序主类,追加 Ribbon 的配置操作:
package cn.study.microcloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.cloud.netflix.ribbon.RibbonClient; import cn.study.commons.config.MyLoadBalanceConfig; @SpringBootApplication @EnableEurekaClient @RibbonClient(name="ribbonClient",configuration=MyLoadBalanceConfig.class) public class Consumer_80_StartSpringCloudApplication { public static void main(String[] args) { SpringApplication.run(Consumer_80_StartSpringCloudApplication.class, args); } }
那么此时就实现了负载均衡的配置处理操作。
3、 【microcloud-consumer-80】修改控制器程序类,在这个程序类之中可以通过负载均衡的客户端获取服务器的相关信息。
package cn.study.microcloud.controller; import java.util.List; import javax.annotation.Resource; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import cn.study.vo.Dept; @RestController public class ConsumerDeptController { public static final String DEPT_GET_URL = "http://MICROCLOUD-PROVIDER-DEPT/dept/get/"; public static final String DEPT_LIST_URL = "http://MICROCLOUD-PROVIDER-DEPT/dept/list/"; public static final String DEPT_ADD_URL = "http://MICROCLOUD-PROVIDER-DEPT/dept/add?dname="; @Resource private RestTemplate restTemplate; @Resource private HttpHeaders headers; @Resource private LoadBalancerClient loadBalancerClient ; @RequestMapping(value = "/consumer/dept/get") public Object getDept(long id) { ServiceInstance serviceInstance = this.loadBalancerClient.choose("MICROCLOUD-PROVIDER-DEPT") ; System.out.println( "【*** ServiceInstance ***】host = " + serviceInstance.getHost() + "、port = " + serviceInstance.getPort() + "、serviceId = " + serviceInstance.getServiceId()); Dept dept = this.restTemplate .exchange(DEPT_GET_URL + id, HttpMethod.GET, new HttpEntity<Object>(this.headers), Dept.class) .getBody(); return dept; } }
那么此时后台通过指定的负载均衡的配置程序,就可以取得即将操作的主机的信息。
2.4、禁用 Eureka 实现 Ribbon 调用
在之前使用的技术都是通过 Eureka 访问了 Eureka 之中的指定名称的服务而获得了所需要的数据,但是在 Ribbon 设计的时候 考虑到了一个脱离 Eureka 使用的环境。如果要真进行了 Eureka 的脱离,代码将变为如下的形式。
1、 【microcloud-consumer-ribbon】修改 application.yml 配置文件:
server: port: 80 ribbon: eureka: enabled: false microcloud-provider-dept: ribbon: listOfServers: http://dept-8001.com:8001,http://dept-8002.com:8002,http://dept-8003.com:8003
2、 【microcloud-consumer-ribbon】修改 RestConfig 配置程序类,不再需要使用“@LoadBalanced”注解:
package cn.study.microcloud.config; import java.nio.charset.Charset; import java.util.Base64; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpHeaders; import org.springframework.web.client.RestTemplate; @Configuration public class RestConfig { @Bean public HttpHeaders getHeaders() { // 要进行一个Http头信息配置 HttpHeaders headers = new HttpHeaders(); // 定义一个HTTP的头信息 String auth = "studyjava:hello"; // 认证的原始信息 byte[] encodedAuth = Base64.getEncoder() .encode(auth.getBytes(Charset.forName("US-ASCII"))); // 进行一个加密的处理 // 在进行授权的头信息内容配置的时候加密的信息一定要与“Basic”之间有一个空格 String authHeader = "Basic " + new String(encodedAuth); headers.set("Authorization", authHeader); return headers; } @Bean // @LoadBalanced public RestTemplate getRestTemplate() { return new RestTemplate(); } }
3、 【microcloud-consumer-ribbon】修改 ConsumerDeptController控制器,直接通过获取主机和端口调用后台微服务:
package cn.study.microcloud.controller; import java.net.URI; import javax.annotation.Resource; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.cloud.netflix.ribbon.RibbonClient; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import cn.study.commons.config.MyLoadBalanceConfig; import cn.study.vo.Dept; @RestController @RibbonClient(name = "microcloud-provider-dept", configuration = MyLoadBalanceConfig.class) public class ConsumerDeptController { public static final String DEPT_REST_TOPIC = "microcloud-provider-dept"; @Resource private RestTemplate restTemplate; @Resource private HttpHeaders headers; @Resource private LoadBalancerClient loadBalancerClient; @RequestMapping(value = "/consumer/dept/get") public Object getDept(long id) { ServiceInstance serviceInstance = this.loadBalancerClient .choose(DEPT_REST_TOPIC); System.out.println( "【*** ServiceInstance ***】host = " + serviceInstance.getHost() + "、port = " + serviceInstance.getPort() + "、serviceId = " + serviceInstance.getServiceId() + "、uri = " + serviceInstance.getUri()); URI deptUri = URI.create(String.format("http://%s:%s/dept/get/" + id, serviceInstance.getHost(), serviceInstance.getPort())); Dept dept = this.restTemplate .exchange(deptUri, HttpMethod.GET, new HttpEntity<Object>(this.headers), Dept.class) .getBody(); return dept; } }
这种模式并不标准,只能够说是 Ribbon 自己所具备的一项功能而已,实际之中如果不是非常必要,不建议使用。