• Spring Cloud Netfilx Ribbon(负载均衡工具)


    一、介绍

    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来构建微服务非常重要.

     二、使用

    1. @Service
    2. public class ApplicationClientServiceImpl implements ApplicationClientService {
    3. /**
    4. * Ribbon提供的负载均衡器
    5. */
    6. @Autowired
    7. private LoadBalancerClient loadBalancerClient;
    8. @Override
    9. public String client() {
    10. ServiceInstance si = loadBalancerClient.choose("application-service");
    11. // 获取Application Service IP。
    12. System.out.println(si.getHost());
    13. // 获取Ip及端口。
    14. System.out.println(si.getInstanceId());
    15. // 端口
    16. System.out.println(si.getPort());
    17. // 应用程序名 application-service
    18. System.out.println(si.getServiceId());
    19. // URI http://Application Service IP:端口
    20. System.out.println(si.getUri().toString());
    21. return null;
    22. }
    23. }

    三、Spring Web 之 RestTemplate 基于Http协议的远程访问

    要想使用RestRemplate必须自己配置

    1. package com.bjsxt.config;
    2. import org.springframework.context.annotation.Bean;
    3. import org.springframework.context.annotation.Configuration;
    4. import org.springframework.web.client.RestTemplate;
    5. @Configuration
    6. public class MyConfig {
    7. @Bean
    8. public RestTemplate restTemplate(){
    9. return new RestTemplate();
    10. }
    11. }

    (1)控制器

    1. @RestController
    2. public class ServerController {
    3. @Value("${server.port}")
    4. private int port;
    5. /**
    6. * 返回类型为集合,泛型为自定类型。
    7. */
    8. @RequestMapping("/returnUsers")
    9. public List returnUsers(int nums){
    10. List result = new ArrayList<>();
    11. for(int i = 0; i < nums; i++){
    12. result.add(new User(100 + i, "姓名-" + i, 20+i));
    13. }
    14. return result;
    15. }
    16. /**
    17. * 任意请求方式, 返回值类型是集合。相对复杂的Java类型。
    18. * @return
    19. */
    20. @RequestMapping("/returnList")
    21. public List returnList(int nums){
    22. List result = new ArrayList<>();
    23. for(int i = 0; i < nums; i++){
    24. result.add("返回结果 - " + i);
    25. }
    26. return result;
    27. }
    28. /**
    29. * 任意请求,传递路径地址参数
    30. * @param name
    31. * @param age
    32. * @return
    33. */
    34. @RequestMapping("/restfulParams/{name}/{age}")
    35. public String restfulParams(@PathVariable("name") String name,
    36. @PathVariable int age){
    37. System.out.println("端口号: " + port + ", 任意请求方式,restful参数, name = " +
    38. name + " ; age = " + age);
    39. return "restful参数, name = " + name + " ; age = " + age;
    40. }
    41. /**
    42. * post请求,请求体传递参数。参数使用@RequestBody处理。
    43. */
    44. @PostMapping("/postBodyParams")
    45. public String postBodyParams(@RequestBody Map params){
    46. System.out.println("端口号: " + port + " , post请求,有请求体参数, params = " +
    47. params);
    48. return "post请求,请求体参数, params = " + params;
    49. }
    50. /**
    51. * post请求,有参数
    52. */
    53. @PostMapping("/postWithParams")
    54. public String postWithParams(String name, int age){
    55. System.out.println("端口号: " + port + " , post请求,有参数, name = " +
    56. name + " ; age = " + age);
    57. return "post请求有参数 : name = " +
    58. name + " ; age = " + age;
    59. }
    60. /**
    61. * post请求,没有参数
    62. */
    63. @PostMapping("/postNoParams")
    64. public String postNoParams(){
    65. System.out.println("端口号: " + port + " , post请求,没有参数");
    66. return "post请求,没有参数";
    67. }
    68. /**
    69. * get请求,包含参数
    70. */
    71. @GetMapping("/getWithParams")
    72. public String getWithParams(String name, int age){
    73. System.out.println("端口号: " + port + " 。 get请求,有参数, name = "
    74. + name + " ; age = " + age);
    75. return "get请求,包含参数 : name = " + name + " ; age = " + age;
    76. }
    77. /**
    78. * get请求,没有参数
    79. * @return
    80. */
    81. @GetMapping("/getNoParams")
    82. public String getNoParams(){
    83. System.out.println("端口号:" + port + "。 get请求,无参数。");
    84. return "get请求,无参数。";
    85. }
    86. }

    (2)无参数GET请求-getForObject

    1. /**
    2. * 发get请求。
    3. * 没有请求参数
    4. *
    5. * T getForObject(String url, Class returnValueType, Object... params)
    6. * url - 要访问的具体地址
    7. * returnValueType - 服务器返回的响应体数据类型
    8. * params - 可选的请求参数
    9. * return - 响应体中的具体内容。
    10. */
    11. @Test
    12. public void testGetNoParams(){
    13. // 定义要访问的地址是什么
    14. String url = baseUrl + "/getNoParams";
    15. // 发get请求。没有参数
    16. String result = restTemplate.getForObject(url, String.class);
    17. System.out.println("服务器返回:" + result);
    18. }

    (3)有参数GET请求-getForObject

    1. /**
    2. * get请求,有参数
    3. * T getForObject(String url, Class returnValueType, Object... params)
    4. * T getForObject(String url, Class returnValueType, Map params)
    5. * 传递参数,就是处理请求地址url,并传递需要的参数。
    6. * 要传递参数,则处理请求地址url。使用{名字}作为占位变量。传递参数的时候,
    7. * 可以根据占位变量从左至右的顺序,使用可变长数组依次传递。
    8. * 也可以根据占位变量名称,做指定传递,使用map集合传递,map的key就是占位变量名,
    9. * map的value,是要传递的请求参数。
    10. */
    11. @Test
    12. public void testGetWithParams(){
    13. String url = baseUrl + "/getWithParams?name={x}&age={y}";
    14. String result1 =
    15. restTemplate.getForObject(url, String.class,
    16. "张三", "20");
    17. System.out.println("可变长数组传递参数,服务器返回:" + result1);
    18. System.out.println("==========================================");
    19. Map params = new HashMap<>();
    20. params.put("x", "李四");
    21. params.put("y", 25);
    22. String result2 =
    23. restTemplate.getForObject(url, String.class, params);
    24. System.out.println("Map集合传递参数,服务器返回:" + result2);
    25. }

    (4)GET请求-getForEntity

    1. /**
    2. * 在RestTemplate中。除方法getForObject以外,还有getForEntity。
    3. * 方法除返回值类型,其他一致。
    4. * ResponseEntity getForEntity(String url, Class returnValueType,
    5. * Object... params)
    6. * ResponseEntity - 包含响应头和响应体。
    7. * 如果需要对响应头做特殊处理,使用getForEntity方法。
    8. */
    9. @Test
    10. public void testGetForEntity(){
    11. String url = baseUrl + "/getNoParams";
    12. ResponseEntity entity =
    13. restTemplate.getForEntity(url, String.class);
    14. HttpHeaders headers = entity.getHeaders();
    15. for(String headerName : headers.keySet()){
    16. System.out.println("响应头: " + headerName + " = "
    17. + headers.get(headerName));
    18. }
    19. System.out.println("响应状态码: " + entity.getStatusCodeValue());
    20. System.out.println("响应体数据: " + entity.getBody());
    21. }

    (5)无参数POST请求

    1. /**
    2. * post请求,没有参数
    3. * T postForObject(String url, Object body, Class returnBodyType,
    4. * Object... params);
    5. * T postForObject(String url, Object body, Class returnBodyType,
    6. * Map params);
    7. * body参数 - 使用post请求方式发请求的时候,请求体是什么?
    8. * 建议传递的对象类型是HttpEntity。
    9. * 如果对请求体没有要求,可以传递null。
    10. */
    11. @Test
    12. public void testPostNoParams(){
    13. String url = baseUrl + "/postNoParams";
    14. String result =
    15. restTemplate.postForObject(url, null, String.class);
    16. System.out.println("post请求,无参数,服务器返回:" + result);
    17. }

    (6)post请求路径地址传递参数

    1. /**
    2. * post请求,有参数
    3. * 1. 请求地址传递参数。 请求头传参。 传参方式和get一样。
    4. * 2. 请求体表单参数。 请求体传参。 需要提供请求体数据
    5. */
    6. @Test
    7. public void testPostWithParamsPath(){
    8. String url = baseUrl + "/postWithParams?name={1}&age={2}";
    9. String result =
    10. restTemplate.postForObject(url, null, String.class,
    11. "王五", 30);
    12. System.out.println(result);
    13. }

    (7)post请求表单传递参数

    1. /**
    2. * spring web在处理请求头和请求体的时候,
    3. * 需要HttpMessageConverter提供数据转换处理。
    4. * HttpMessageConverter是接口,由Spring WEB定义。
    5. * 具体实现,需要依赖不同的具体技术实现。
    6. * 一般都使用jackson做实现。请求头和体数据,都是基于JSON转换的。
    7. */
    8. @Test
    9. public void testPostWithParamsForm(){
    10. String url = baseUrl + "/postWithParams";
    11. // 创建请求体信息。
    12. // 请求头, 表单请求。 application/x-www-form-urlencoded
    13. HttpHeaders headers = new HttpHeaders();
    14. headers.add("content-type", "application/x-www-form-urlencoded");
    15. // 表单
    16. MultiValueMap form =
    17. new LinkedMultiValueMap<>();
    18. // add 提供一个 请求参数 名 = 值。
    19. form.add("name", "尼古拉斯.赵四");
    20. form.add("age", 40);
    21. // put 提供 键值对
    22. //form.put("name", Arrays.asList("尼古拉斯.赵四"));
    23. //List ages = new ArrayList<>();
    24. //ages.add(40);
    25. //form.put("age", ages);
    26. HttpEntity> entity = new HttpEntity<>(form, headers);
    27. String result =
    28. restTemplate.postForObject(url, entity, String.class);
    29. System.out.println(result);
    30. }
    31. (8)post请求请求体传递参数

      1. /**
      2. * post请求,有参数
      3. * 使用请求体传递参数。@RequestBody处理请求参数。
      4. * 1. 请求参数只能在请求体中,以字符串描述,且是一个完整的请求参数。此参数没有名称。如:JSON
      5. * 2. 请求头 content-type的设置,和具体的参数值的格式相关,如: JSON对应的content-type是application/json
      6. * 如:xml字符串对应的content-type是application/xml
      7. * 3. 请求体传递的参数,只能由唯一的一个完整参数。可以同时携带表单参数和地址栏参数。
      8. */
      9. @Test
      10. public void testPostBodyParams(){
      11. String url = baseUrl + "/postBodyParams";
      12. // 创建请求参数, 使用JSON格式的字符串,描述一个Map集合。
      13. String params = "{\"key1\":\"value1\", \"key2\":\"value2\"}";
      14. // 创建请求时,使用的请求体描述
      15. HttpHeaders headers = new HttpHeaders();
      16. headers.add("content-type", "application/json;charset=utf-8");
      17. HttpEntity entity = new HttpEntity<>(params, headers);
      18. String result = restTemplate.postForObject(url, entity, String.class);
      19. System.out.println("返回结果:" + result);
      20. }
      21. @Test
      22. public void testPostBodyParams2(){
      23. // 可以使用简单的处理方案,实现请求体传递JSON格式的数据
      24. // 使用postForObject(),第二个参数,直接传递一个Java对象,作为请求体中传递的参数。
      25. // 参数没有名字,由RestTemplate做数据转换,默认使用JSON格式字符串做转换结果。
      26. String url = baseUrl + "/postBodyParams";
      27. // 创建Map类型的参数对象
      28. Map params = new HashMap<>();
      29. params.put("name", "测试");
      30. params.put("gender", "男");
      31. // 请求服务器。 直接传递java对象,默认请求头content-type=application/json;charset=utf-8
      32. String result = restTemplate.postForObject(url, params, String.class);
      33. System.out.println("返回结果:" + result);
      34. }

      (9)RestFUL传递参数

      1. /**
      2. * 使用get请求方式,传递restful参数
      3. */
      4. @Test
      5. public void testRestfulParams(){
      6. String url = baseUrl + "/restfulParams/{name}/{age}";
      7. // 访问
      8. String result = restTemplate.getForObject(url, String.class, "restful", "15");
      9. System.out.println(result);
      10. }

      (10)Exchange通用处理方案(处理相对复杂的结果类型例如自定义类型数组)

      1. /**
      2. * RestTemplate类型中的通用方法,exchange。可以提交任意方式的请求。
      3. * 且可以定义相对复杂的返回类型。如:带有泛型要求的集合。
      4. *
      5. * ResponseEntity exchange(String url, HttpMethod requestMethod, HttpEntity http,
      6. * Class returnType, Object... params)
      7. * ResponseEntity exchange(String url, HttpMethod requestMethod, HttpEntity http,
      8. * Class returnType, Map params)
      9. * ResponseEntity exchange(String url, HttpMethod requestMethod, HttpEntity http,
      10. * ParameterizedTypeReference returnType, Object... params)
      11. * ResponseEntity exchange(String url, HttpMethod requestMethod, HttpEntity http,
      12. * ParameterizedTypeReference returnType, Map... params)
      13. */
      14. @Test
      15. public void testExchangeMethod(){
      16. String url = baseUrl + "/returnList?nums={1}";
      17. // 相对复杂的返回结果类型描述对象
      18. url = baseUrl + "/returnUsers?nums={1}";
      19. ParameterizedTypeReference> type =
      20. new ParameterizedTypeReference>() {};
      21. // 访问远程
      22. ResponseEntity> entity =
      23. restTemplate.exchange(url, HttpMethod.GET, null, type, 3);
      24. List body = entity.getBody();
      25. System.out.println(body);
      26. }

      四、基于RestTemplate和Ribbon实现Application Client调用Application Service集群

       (1)编写配置类

      1. @Configuration
      2. public class AppClientConfiguration {
      3. /**
      4. * 创建RestTemplate对象的方法,增加注解
      5. * LoadBalanced - 把Spring Cloud封装的LoadBalancerClient于RestTemplate整合。
      6. * 让RestTemplate自带负载均衡能力。仅在当前的Ribbon环境中生效。
      7. * 逻辑是:
      8. * 请求 http://服务名称/具体地址. RestTemplate解析请求地址。
      9. * 把服务名称解析出,作为参数,调用LoadBalancerClient中的choose方法。
      10. * 把返回的ServiceInstance.getUri().toASCIIString()作为服务器地址,拼接上
      11. * 请求的具体地址。访问远程。
      12. * @return
      13. */
      14. @Bean
      15. @LoadBalanced
      16. public RestTemplate restTemplate(){
      17. return new RestTemplate();
      18. }
      19. }

      (2)发起远程调用

      1. @Service
      2. public class ApplicationClientServiceImpl implements ApplicationClientService {
      3. @Autowired
      4. private RestTemplate restTemplate;
      5. //http://服务名
      6. private final String baseUrl = "http://eureka-client-app-service";
      7. @Override
      8. public String getNoParams() {
      9. String url = baseUrl + "/getNoParams";
      10. // 访问
      11. String result = restTemplate.getForObject(url, String.class);
      12. System.out.println(result);
      13. return result;
      14. }
      15. }

      五、Ribbon负载均衡算法

      Ribbon的负载均衡策略是通过不同的类型来实现的(都是IRule接口的实现),下表详细介绍一些常用负载均衡策略及对应的Ribbon策略类。

      编号策略名称策略对应的类名实现原理
      1轮询策略(默认)RoundRobinRule轮询策略表示每次都按照顺序取下一个application service,比如一共有5个application service,第1次取第1个,第2次取第2个,第3次取第3个,以此类推
      2权重轮询策略(常用,中小型项目使用)WeightedResponseTimeRule1.根据每个application service的响应时间分配一个权重,响应时间越长,权重越小,被选中的可能性越低。 2.原理:一开始为轮询策略,并开启一个计时器,每30秒收集一次每个application service的平均响应时间,当信息足够时,给每个application service附上一个权重,并按权重随机选择application service,权重越高的application service会被高概率选中。
      3随机策略(不推荐,测试使用,开发使用)RandomRule从application service列表中随机选择一个
      4最少并发数策略(应用在硬件软件环境一致的情况下,中小型项目使用)BestAvailableRule选择正在请求中的并发数最小的application service,除非这个application service在熔断中。
      5重试策略。在“选定的负载均衡策略”基础上进行重试机制RetryRule1.“选定的负载均衡策略”这个策略是轮询策略RoundRobinRule 2.该重试策略先设定一个阈值时间段,如果在这个阈值时间段内当选择application service不成功,则一直尝试采用“选定的负载均衡策略:轮询策略”最后选择一个可用的application service
      6可用性敏感策略(一般在同区域内服务集群环境中使用)AvailabilityFilteringRule过滤性能差的application service,有2种: 第一种:过滤掉在eureka中处于一直连接失败application service 第二种:过滤掉高并发的application service
      7区域敏感性策略(应用在大型的,物理隔离分布式环境中)ZoneAvoidanceRule1.以一个区域为单位考察可用性,对于不可用的区域整个丢弃,从剩下区域中选可用的application service 2.如果这个ip区域内有一个或多个实例不可达或响应变慢,都会降低该ip区域内其他ip被选中的权重。

      指定负载均衡策略,新增Bean对象管理方法

      1. @Bean
      2. public IRule iRule(){
      3. return new RandomRule();
      4. }
    32. 相关阅读:
      [TAPL] 概念笔记
      考研英语语法(十)
      以太坊是什么? 以及以太坊如何工作的?
      maven仓库改国内源
      PPTP、L2TP、IPSec、IPS 有什么区别?
      lodash的merge()方法
      WinRAR如何保护压缩包的文件不被修改
      不要在Python中使用iterrows()循环
      3.4、可靠传输
      List获取差集产生的问题
    33. 原文地址:https://blog.csdn.net/weixin_53455615/article/details/128079825