文章目录
- 一、 Ribbon简介
- 二、 使用Ribbon开发微服务
- 1 创建springcloud工程 和 commons子模块
- 2 开发服务提供者 - ribbonappservice
- 3 开发服务消费者 - ribbonappclient
- 三、 集中式与进程内负载均衡区别
- 四、 Ribbon常见的负载均衡策略
- 1 Ribbon中的常用负载均衡简介
- 2 配置负载均衡策略
- 五、 Ribbon的点对点直连
一、 Ribbon简介
Ribbon 是一个基于Http和TCP的客服端负载均衡工具,它是基于Netflix Ribbon实现的。它不像spring cloud服务注册中心、配置中心、API网关那样独立部署,但是它几乎存在于每个spring cloud 微服务中。包括feign提供的声明式服务调用也是基于该Ribbon实现的。ribbon默认提供很多种负载均衡算法,例如 轮询、随机 等等。甚至包含自定义的负载均衡算法。Ribbon可以用于解决并提供微服务的负载均衡的问题。
二、 使用Ribbon开发微服务
在Spring Cloud中,使用Ribbon技术开发Eureka Client组件还是非常方便的。我们在开发过程中,不需要像Dubbo那样关注服务的角色。无论是Provider还是Consumer都是一个微服务客户端,只是在编码层面上,服务消费者代码的开发相对比较麻烦。我们通过简单案例测试一下Spring Cloud中的微服务开发过程。
因为现在的Eureka Server部署在Linux中,并已为Linux定义了新的主机域名,需要先修改开发测试环境中的hosts文件。windows中的hosts文件位于:C:\windows\system32\dirvers\etc\hosts。新增内容如下:(IP根据具体情况配置)
192.168.14.128 eureka1
192.168.14.129 eureka2
1 创建springcloud工程 和 commons子模块
1.1 POM依赖
<?xml version="1.0" encoding="UTF-8"?>
<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><packaging>pom</packaging><modules><module>commons</module><module>ribbonappservice</module><module>ribbonappclient</module></modules><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.2.2.RELEASE</version></parent><groupId>com.bjsxt</groupId><artifactId>springcloud</artifactId><version>1.0-SNAPSHOT</version><dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>Hoxton.SR1</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement>
</project>
1.2 commons 的pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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"><parent><artifactId>springcloud</artifactId><groupId>com.bjsxt</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>commons</artifactId>
</project>
1.3 commons子模块中user类 (包含set,get等方法)
2 开发服务提供者 - ribbonappservice
2.1 POM依赖
<?xml version="1.0" encoding="UTF-8"?>
<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"><parent><artifactId>springcloud</artifactId><groupId>com.bjsxt</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><!-- 服务提供方项目, 使用Ribbon技术开发 --><artifactId>ribbonappservice</artifactId><dependencies><!-- spring cloud是通过http协议对外发布一个基于REST规则的微服务通过SpringMVC技术中的Controller对外提供微服务的。--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- Eureka客户端依赖,spring cloud中,服务的提供者和消费者都是Eureka客户端。必须依赖此资源 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency><dependency><groupId>com.bjsxt</groupId><artifactId>commons</artifactId><version>1.0-SNAPSHOT</version></dependency></dependencies></project>
2.2 服务提供者代码
package com.bjsxt.userservice.controller;import com.bjsxt.entity.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;import java.util.HashMap;
import java.util.Map;/*** 提供服务的控制器*/
@Controller
public class UserController {@RequestMapping("/user/save")@ResponseBodypublic Map<String, Object> save(User user){System.out.println("新增用户数据: " + user);Map<String, Object> result = new HashMap<>();result.put("code", "200"); // 返回的状态码result.put("message", "新增用户成功"); // 返回的处理结果消息。return result;}
}
2.3 配置文件application.yml
server:port: 8082spring:application:name: ribbon-app-serviceeureka: # Eureka客户端,启动的时候,如果未配置Eureka服务端地址列表,则在localhost:8761注册client:service-url: # 配置Eureka服务端地址,注册中心地址,多个地址使用逗号 ',' 分隔。defaultZone: http://localhost:8761/eureka/
2.4 启动类
package com.bjsxt.userservice;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;/*** 服务提供方启动类* 在Spring Cloud低版本中,如果开发的代码是Eureka Client(服务提供者和消费者),* 那么启动类上需要增加注解* @EnableEurekaClient - 当前应用是一个Eureka客户端* @EnableDiscoveryClient - 当前应用需要启动发现机制,就是找到Eureka服务端,并注册发现服务。*/
@SpringBootApplication
public class RibbonAppServiceApp {public static void main(String[] args) {SpringApplication.run(RibbonAppServiceApp.class, args);}
}
2.5 检查Eureka Server中的服务注册情况(Eureka Server项目使用SpringCloud Netflix Eureka文章中的)
3 开发服务消费者 - ribbonappclient
3.1 POM依赖
<?xml version="1.0" encoding="UTF-8"?>
<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"><parent><artifactId>springcloud</artifactId><groupId>com.bjsxt</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>ribbonappclient</artifactId><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency><dependency><groupId>com.bjsxt</groupId><artifactId>commons</artifactId><version>1.0-SNAPSHOT</version></dependency></dependencies></project>
3.2 控制器
package com.bjsxt.userclient.controller;import com.bjsxt.entity.User;
import com.bjsxt.userclient.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;import java.util.Map;/*** 服务消费端*/
@Controller
public class UserController {@Autowiredprivate UserService userService;@RequestMapping("/user/save")@ResponseBodypublic Map<String, Object> save(User user){// 调用本地服务代码,本地服务代码远程调用application service服务提供方。Map<String, Object> result = this.userService.save(user);System.out.println("远程调用返回的结果:" + result);return result;}}
3.3 服务接口
package com.bjsxt.userclient.service;import com.bjsxt.entity.User;import java.util.Map;public interface UserService {Map<String, Object> save(User user);
}
3.4 服务实现
package com.bjsxt.userclient.service.impl;import com.bjsxt.entity.User;
import com.bjsxt.userclient.service.UserService;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;import java.util.Map;@Service
public class UserServiceImpl implements UserService {/*** 是Ribbon技术中的负载均衡客户端对象。其中封装了从Eureka Server上发现的所有的服务地址列表* 包括服务的名称,IP,端口等。*/@Autowiredprivate LoadBalancerClient loadBalancerClient;/*** 远程方法调用。访问application service,访问的地址是:http://localhost:8080/user/save* @param user* @return*/@Overridepublic Map<String, Object> save(User user) {// 根据服务的名称,获取服务实例。服务名称就是配置文件yml中的spring.application.name// 服务实例包括,这个名称的所有服务地址和端口。ServiceInstance instance = this.loadBalancerClient.choose("ribbon-app-service");// 访问地址拼接StringBuilder builder = new StringBuilder("");builder.append("http://").append(instance.getHost()).append(":").append(instance.getPort()).append("/user/save").append("?username=").append(user.getUsername()).append("&password=").append(user.getPassword()).append("&remark=").append(user.getRemark());System.out.println("本地访问地址:" + builder.toString());// 创建一个Rest访问客户端模板对象。RestTemplate template = new RestTemplate();// 约束响应结果类型ParameterizedTypeReference<Map<String, Object>> responseType =new ParameterizedTypeReference<Map<String, Object>>() {};// 远程访问application service。ResponseEntity<Map<String, Object>> response =template.exchange(builder.toString(), HttpMethod.GET,null, responseType);Map<String, Object> result = response.getBody();return result;}
}
3.5 配置文件application.yml
server:port: 8081spring:application:name: ribbon-app-clienteureka:client:service-url:defaultZone: http://localhost:8761/eureka/
3.6 启动类
package com.bjsxt.userclient;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class RibbonAppClientApp {public static void main(String[] args) {SpringApplication.run(RibbonAppClientApp.class, args);}
}
三、 集中式与进程内负载均衡区别
业界主流的负载均衡解决方案有:集中式负载均衡和进程内负载均衡。
1 集中式负载均衡
即在客户端和服务端之间使用独立的负载均衡设施(可以是硬件,如F5, 也可以是软件,如nginx), 由该设施负责把访问请求通过某种策略转发至服务端。
2 进程内负载均衡
将负载均衡逻辑集成到客户端组件中,客户端组件从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务端发起请求。Ribbon就是一个进程内的负载均衡实现。
四、 Ribbon常见的负载均衡策略
Ribbon就属于进程内负载均衡,它只是一个类库,集成于Eureka Client进程,Eureka Client进程通过访问注册中心Eureka Server发现服务列表,发现的服务列表信息是由ribbon来管理的。当访问Application Service的时候,Application Client会通过ribbon来找到合适的Application Service地址信息,并发起远程调用请求。
1 Ribbon中的常用负载均衡简介
1 轮询策略(默认) RoundRobinRule
轮询策略表示每次都顺序取下一个provider,比如一共有5个provider,第1次取第1个,第2次取第2个,第3次取第3个,以此类推
2 权重轮询策略(常用) WeightedResponseTimeRule
1.根据每个provider的响应时间分配一个权重,响应时间越长,权重越小,被选中的可能性越低。
2.原理:一开始为轮询策略,并开启一个计时器,每30秒收集一次每个provider的平均响应时间,当信息足够时,给每个provider附上一个权重,并按权重随机选择provider,高权越重的provider会被高概率选中。
3 随机策略(不推荐) RandomRule
从provider列表中随机选择一个provider
4 最少并发数策略(应用在硬件软件环境一致的情况下) BestAvailableRule
选择正在请求中的并发数最小的provider,除非这个provider在熔断中。
5 在“选定的负载均衡策略”基础上进行重试机制 RetryRule
1.“选定的负载均衡策略”这个策略是轮询策略RoundRobinRule
2.该重试策略先设定一个阈值时间段,如果在这个阈值时间段内当选择provider不成功,则一直尝试采用“选定的负载均衡策略:轮询策略”最后选择一个可用的provider
6 可用性敏感策略(一般在同区域内服务集群环境中使用) AvailabilityFilteringRule
过滤性能差的provider,有2种:
第一种:过滤掉在eureka中处于一直连接失败provider
第二种:过滤掉高并发的provider
7 区域敏感性策略(应用在大型的,物理隔离分布式环境中) ZoneAvoidanceRule
1.以一个区域为单位考察可用性,对于不可用的区域整个丢弃,从剩下区域中选可用的provider
2.如果这个ip区域内有一个或多个实例不可达或响应变慢,都会降低该ip区域内其他ip被选中的权重。
2 配置负载均衡策略
可以通过修改 ribbonappclient 应用的全局配置文件来改变当前环境中使用的Ribbon负载均衡策略。
server:port: 8081spring:application:name: ribbon-app-clienteureka:client:service-url:defaultZone: http://localhost:8761/eureka/ribbon-app-service: # 远程访问这个命名的服务ribbon: # 底层Ribbon配置NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 就是具体的负载均衡策略类型全名
五、 Ribbon的点对点直连
Ribbon也可以脱离Eureka Server注册中心,通过配置的方式指定要调用的远程服务信息,实现Ribbon点对点直连。修改的配置内容如下:
server:port: 8081spring:application:name: ribbon-app-client#eureka:
# client:
# service-url:
# defaultZone: http://localhost:8761/eureka/ribbon-app-service: # 远程访问这个命名的服务ribbon: # 底层Ribbon配置NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 就是具体的负载均衡策略类型全名listOfServers: localhost:8080 # 多个地址用逗号分隔。ribbon: # 关闭Ribbon自动访问Eureka服务端。eureka:enabled: false