SpringCloud Netfilx Ribbon负载均衡工具使用方法介绍
作者:幽默涵养miss u
一、介绍
Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现。通过Spring Cloud的封装,可以让我们轻松地将面向服务的REST模版请求自动转换成客户端负载均衡的服务调用。Spring Cloud Ribbon虽然只是一个工具类框架,它不像服务注册中心、配置中心、API网关那样需要独立部署,但是它几乎存在于每一个Spring Cloud构建的微服务和基础设施中。因为微服务间的调用,API网关的请求转发等内容,实际上都是通过Ribbon来实现的,包括后续我们将要学习的OpenFeign,它也是基于Ribbon实现负载均衡的远程服务调用工具。所以,对Spring Cloud Ribbon的理解和使用,对于我们使用Spring Cloud来构建微服务非常重要.
二、使用
@Service public class ApplicationClientServiceImpl implements ApplicationClientService { /** * Ribbon提供的负载均衡器 */ @Autowired private LoadBalancerClient loadBalancerClient; @Override public String client() { ServiceInstance si = loadBalancerClient.choose("application-service"); // 获取Application Service IP。 System.out.println(si.getHost()); // 获取Ip及端口。 System.out.println(si.getInstanceId()); // 端口 System.out.println(si.getPort()); // 应用程序名 application-service System.out.println(si.getServiceId()); // URI http://Application Service IP:端口 System.out.println(si.getUri().toString()); return null; } }
三、SpringWeb之RestTemplate基于Http协议的远程访问
要想使用RestRemplate必须自己配置
package com.bjsxt.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; @Configuration public class MyConfig { @Bean public RestTemplate restTemplate(){ return new RestTemplate(); } }
(1)控制器
@RestController public class ServerController { @Value("${server.port}") private int port; /** * 返回类型为集合,泛型为自定类型。 */ @RequestMapping("/returnUsers") public List<User> returnUsers(int nums){ List<User> result = new ArrayList<>(); for(int i = 0; i < nums; i++){ result.add(new User(100 + i, "姓名-" + i, 20+i)); } return result; } /** * 任意请求方式, 返回值类型是集合。相对复杂的Java类型。 * @return */ @RequestMapping("/returnList") public List<String> returnList(int nums){ List<String> result = new ArrayList<>(); for(int i = 0; i < nums; i++){ result.add("返回结果 - " + i); } return result; } /** * 任意请求,传递路径地址参数 * @param name * @param age * @return */ @RequestMapping("/restfulParams/{name}/{age}") public String restfulParams(@PathVariable("name") String name, @PathVariable int age){ System.out.println("端口号: " + port + ", 任意请求方式,restful参数, name = " + name + " ; age = " + age); return "restful参数, name = " + name + " ; age = " + age; } /** * post请求,请求体传递参数。参数使用@RequestBody处理。 */ @PostMapping("/postBodyParams") public String postBodyParams(@RequestBody Map<String, String> params){ System.out.println("端口号: " + port + " , post请求,有请求体参数, params = " + params); return "post请求,请求体参数, params = " + params; } /** * post请求,有参数 */ @PostMapping("/postWithParams") public String postWithParams(String name, int age){ System.out.println("端口号: " + port + " , post请求,有参数, name = " + name + " ; age = " + age); return "post请求有参数 : name = " + name + " ; age = " + age; } /** * post请求,没有参数 */ @PostMapping("/postNoParams") public String postNoParams(){ System.out.println("端口号: " + port + " , post请求,没有参数"); return "post请求,没有参数"; } /** * get请求,包含参数 */ @GetMapping("/getWithParams") public String getWithParams(String name, int age){ System.out.println("端口号: " + port + " 。 get请求,有参数, name = " + name + " ; age = " + age); return "get请求,包含参数 : name = " + name + " ; age = " + age; } /** * get请求,没有参数 * @return */ @GetMapping("/getNoParams") public String getNoParams(){ System.out.println("端口号:" + port + "。 get请求,无参数。"); return "get请求,无参数。"; } }
(2)无参数GET请求-getForObject
/** * 发get请求。 * 没有请求参数 * * <T> T getForObject(String url, Class<T> returnValueType, Object... params) * url - 要访问的具体地址 * returnValueType - 服务器返回的响应体数据类型 * params - 可选的请求参数 * return - 响应体中的具体内容。 */ @Test public void testGetNoParams(){ // 定义要访问的地址是什么 String url = baseUrl + "/getNoParams"; // 发get请求。没有参数 String result = restTemplate.getForObject(url, String.class); System.out.println("服务器返回:" + result); }
(3)有参数GET请求-getForObject
/** * get请求,有参数 * <T> T getForObject(String url, Class<T> returnValueType, Object... params) * <T> T getForObject(String url, Class<T> returnValueType, Map params) * 传递参数,就是处理请求地址url,并传递需要的参数。 * 要传递参数,则处理请求地址url。使用{名字}作为占位变量。传递参数的时候, * 可以根据占位变量从左至右的顺序,使用可变长数组依次传递。 * 也可以根据占位变量名称,做指定传递,使用map集合传递,map的key就是占位变量名, * map的value,是要传递的请求参数。 */ @Test public void testGetWithParams(){ String url = baseUrl + "/getWithParams?name={x}&age={y}"; String result1 = restTemplate.getForObject(url, String.class, "张三", "20"); System.out.println("可变长数组传递参数,服务器返回:" + result1); System.out.println("=========================================="); Map<String, Object> params = new HashMap<>(); params.put("x", "李四"); params.put("y", 25); String result2 = restTemplate.getForObject(url, String.class, params); System.out.println("Map集合传递参数,服务器返回:" + result2); }
(4)GET请求-getForEntity
/** * 在RestTemplate中。除方法getForObject以外,还有getForEntity。 * 方法除返回值类型,其他一致。 * <T> ResponseEntity<T> getForEntity(String url, Class<T> returnValueType, * Object... params) * ResponseEntity - 包含响应头和响应体。 * 如果需要对响应头做特殊处理,使用getForEntity方法。 */ @Test public void testGetForEntity(){ String url = baseUrl + "/getNoParams"; ResponseEntity<String> entity = restTemplate.getForEntity(url, String.class); HttpHeaders headers = entity.getHeaders(); for(String headerName : headers.keySet()){ System.out.println("响应头: " + headerName + " = " + headers.get(headerName)); } System.out.println("响应状态码: " + entity.getStatusCodeValue()); System.out.println("响应体数据: " + entity.getBody()); }
(5)无参数POST请求
/** * post请求,没有参数 * <T> T postForObject(String url, Object body, Class<T> returnBodyType, * Object... params); * <T> T postForObject(String url, Object body, Class<T> returnBodyType, * Map params); * body参数 - 使用post请求方式发请求的时候,请求体是什么? * 建议传递的对象类型是HttpEntity。 * 如果对请求体没有要求,可以传递null。 */ @Test public void testPostNoParams(){ String url = baseUrl + "/postNoParams"; String result = restTemplate.postForObject(url, null, String.class); System.out.println("post请求,无参数,服务器返回:" + result); }
(6)post请求路径地址传递参数
/** * post请求,有参数 * 1. 请求地址传递参数。 请求头传参。 传参方式和get一样。 * 2. 请求体表单参数。 请求体传参。 需要提供请求体数据 */ @Test public void testPostWithParamsPath(){ String url = baseUrl + "/postWithParams?name={1}&age={2}"; String result = restTemplate.postForObject(url, null, String.class, "王五", 30); System.out.println(result); }
(7)post请求表单传递参数
/** * spring web在处理请求头和请求体的时候, * 需要HttpMessageConverter提供数据转换处理。 * HttpMessageConverter是接口,由Spring WEB定义。 * 具体实现,需要依赖不同的具体技术实现。 * 一般都使用jackson做实现。请求头和体数据,都是基于JSON转换的。 */ @Test public void testPostWithParamsForm(){ String url = baseUrl + "/postWithParams"; // 创建请求体信息。 // 请求头, 表单请求。 application/x-www-form-urlencoded HttpHeaders headers = new HttpHeaders(); headers.add("content-type", "application/x-www-form-urlencoded"); // 表单 MultiValueMap<String, Object> form = new LinkedMultiValueMap<>(); // add 提供一个 请求参数 名 = 值。 form.add("name", "尼古拉斯.赵四"); form.add("age", 40); // put 提供 键值对 //form.put("name", Arrays.asList("尼古拉斯.赵四")); //List<Object> ages = new ArrayList<>(); //ages.add(40); //form.put("age", ages); HttpEntity<MultiValueMap<String, Object>> entity = new HttpEntity<>(form, headers); String result = restTemplate.postForObject(url, entity, String.class); System.out.println(result); }
(8)post请求请求体传递参数
/** * post请求,有参数 * 使用请求体传递参数。@RequestBody处理请求参数。 * 1. 请求参数只能在请求体中,以字符串描述,且是一个完整的请求参数。此参数没有名称。如:JSON * 2. 请求头 content-type的设置,和具体的参数值的格式相关,如: JSON对应的content-type是application/json * 如:xml字符串对应的content-type是application/xml * 3. 请求体传递的参数,只能由唯一的一个完整参数。可以同时携带表单参数和地址栏参数。 */ @Test public void testPostBodyParams(){ String url = baseUrl + "/postBodyParams"; // 创建请求参数, 使用JSON格式的字符串,描述一个Map集合。 String params = "{\"key1\":\"value1\", \"key2\":\"value2\"}"; // 创建请求时,使用的请求体描述 HttpHeaders headers = new HttpHeaders(); headers.add("content-type", "application/json;charset=utf-8"); HttpEntity<String> entity = new HttpEntity<>(params, headers); String result = restTemplate.postForObject(url, entity, String.class); System.out.println("返回结果:" + result); } @Test public void testPostBodyParams2(){ // 可以使用简单的处理方案,实现请求体传递JSON格式的数据 // 使用postForObject(),第二个参数,直接传递一个Java对象,作为请求体中传递的参数。 // 参数没有名字,由RestTemplate做数据转换,默认使用JSON格式字符串做转换结果。 String url = baseUrl + "/postBodyParams"; // 创建Map类型的参数对象 Map<String, String> params = new HashMap<>(); params.put("name", "测试"); params.put("gender", "男"); // 请求服务器。 直接传递java对象,默认请求头content-type=application/json;charset=utf-8 String result = restTemplate.postForObject(url, params, String.class); System.out.println("返回结果:" + result); }
(9)RestFUL传递参数
/** * 使用get请求方式,传递restful参数 */ @Test public void testRestfulParams(){ String url = baseUrl + "/restfulParams/{name}/{age}"; // 访问 String result = restTemplate.getForObject(url, String.class, "restful", "15"); System.out.println(result); }
(10)Exchange通用处理方案(处理相对复杂的结果类型例如自定义类型数组)
/** * RestTemplate类型中的通用方法,exchange。可以提交任意方式的请求。 * 且可以定义相对复杂的返回类型。如:带有泛型要求的集合。 * * <T> ResponseEntity<T> exchange(String url, HttpMethod requestMethod, HttpEntity http, * Class<T> returnType, Object... params) * <T> ResponseEntity<T> exchange(String url, HttpMethod requestMethod, HttpEntity http, * Class<T> returnType, Map params) * <T> ResponseEntity<T> exchange(String url, HttpMethod requestMethod, HttpEntity http, * ParameterizedTypeReference<T> returnType, Object... params) * <T> ResponseEntity<T> exchange(String url, HttpMethod requestMethod, HttpEntity http, * ParameterizedTypeReference<T> returnType, Map... params) */ @Test public void testExchangeMethod(){ String url = baseUrl + "/returnList?nums={1}"; // 相对复杂的返回结果类型描述对象 url = baseUrl + "/returnUsers?nums={1}"; ParameterizedTypeReference<List<User>> type = new ParameterizedTypeReference<List<User>>() {}; // 访问远程 ResponseEntity<List<User>> entity = restTemplate.exchange(url, HttpMethod.GET, null, type, 3); List<User> body = entity.getBody(); System.out.println(body); }
四、调用Application Service集群
基于RestTemplate和Ribbon实现Application Client调用Application Service集群
(1)编写配置类
@Configuration public class AppClientConfiguration { /** * 创建RestTemplate对象的方法,增加注解 * LoadBalanced - 把Spring Cloud封装的LoadBalancerClient于RestTemplate整合。 * 让RestTemplate自带负载均衡能力。仅在当前的Ribbon环境中生效。 * 逻辑是: * 请求 http://服务名称/具体地址. RestTemplate解析请求地址。 * 把服务名称解析出,作为参数,调用LoadBalancerClient中的choose方法。 * 把返回的ServiceInstance.getUri().toASCIIString()作为服务器地址,拼接上 * 请求的具体地址。访问远程。 * @return */ @Bean @LoadBalanced public RestTemplate restTemplate(){ return new RestTemplate(); } }
(2)发起远程调用
@Service public class ApplicationClientServiceImpl implements ApplicationClientService { @Autowired private RestTemplate restTemplate; //http://服务名 private final String baseUrl = "http://eureka-client-app-service"; @Override public String getNoParams() { String url = baseUrl + "/getNoParams"; // 访问 String result = restTemplate.getForObject(url, String.class); System.out.println(result); return result; } }
五、Ribbon负载均衡算法
Ribbon的负载均衡策略是通过不同的类型来实现的(都是IRule接口的实现),下表详细介绍一些常用负载均衡策略及对应的Ribbon策略类。
编号 | 策略名称 | 策略对应的类名 | 实现原理 |
---|---|---|---|
1 | 轮询策略(默认) | RoundRobinRule | 轮询策略表示每次都按照顺序取下一个application service,比如一共有5个application service,第1次取第1个,第2次取第2个,第3次取第3个,以此类推 |
2 | 权重轮询策略(常用,中小型项目使用) | WeightedResponseTimeRule | 1.根据每个application service的响应时间分配一个权重,响应时间越长,权重越小,被选中的可能性越低。 2.原理:一开始为轮询策略,并开启一个计时器,每30秒收集一次每个application service的平均响应时间,当信息足够时,给每个application service附上一个权重,并按权重随机选择application service,权重越高的application service会被高概率选中。 |
3 | 随机策略(不推荐,测试使用,开发使用) | RandomRule | 从application service列表中随机选择一个 |
4 | 最少并发数策略(应用在硬件软件环境一致的情况下,中小型项目使用) | BestAvailableRule | 选择正在请求中的并发数最小的application service,除非这个application service在熔断中。 |
5 | 重试策略。在“选定的负载均衡策略”基础上进行重试机制 | RetryRule | 1.“选定的负载均衡策略”这个策略是轮询策略RoundRobinRule 2.该重试策略先设定一个阈值时间段,如果在这个阈值时间段内当选择application service不成功,则一直尝试采用“选定的负载均衡策略:轮询策略”最后选择一个可用的application service |
6 | 可用性敏感策略(一般在同区域内服务集群环境中使用) | AvailabilityFilteringRule | 过滤性能差的application service,有2种: 第一种:过滤掉在eureka中处于一直连接失败application service 第二种:过滤掉高并发的application service |
7 | 区域敏感性策略(应用在大型的,物理隔离分布式环境中) | ZoneAvoidanceRule | 1.以一个区域为单位考察可用性,对于不可用的区域整个丢弃,从剩下区域中选可用的application service 2.如果这个ip区域内有一个或多个实例不可达或响应变慢,都会降低该ip区域内其他ip被选中的权重。 |
指定负载均衡策略,新增Bean对象管理方法
@Bean public IRule iRule(){ return new RandomRule(); }
到此这篇关于SpringCloud Netfilx Ribbon负载均衡工具使用方法介绍的文章就介绍到这了,更多相关SpringCloud Netfilx Ribbon内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!