https://blog.youkuaiyun.com/qq_35835624/article/details/78837112
Spring Cloud Ribbon介绍:
Ribbon是Netflix开源的一款用于客户端软负载均衡的工具软件。
Spring Cloud对Ribbon进行了一些封装以更好的使用Spring Boot的自动化配置理念。
服务端负载均衡和客户端负载均衡
首先,负载均衡分为硬件负载均衡和软件负载均衡两种,这里只说软件负载均衡。软件负载均衡中又分为两种,即服务端负载均衡和客户端负载均衡。无论是那种负载均衡,都是需要维护一个服务的清单,并通过心跳机制来定期清理那些故障的服务断点。服务端负载均衡和客户端负载均衡的却别在于其所维护的服务清单所存储的位置不同,一个在服务端一个在客户端。
https://blog.youkuaiyun.com/u014401141/article/details/78676296
1、服务器端负载均衡:例如Nginx,通过Nginx进行负载均衡,先发送请求,然后通过负载均衡算法,在多个服务器之间选择一个进行访问;即在服务器端再进行负载均衡算法分配。
服务端负载均衡
负载均衡是我们处理高并发、缓解网络压力和进行服务端扩容的重要手段之一,但是一般情况下我们所说的负载均衡通常都是指服务端负载均衡,服务端负载均衡又分为两种,一种是硬件负载均衡,还有一种是软件负载均衡。
硬件负载均衡主要通过在服务器节点之间安装专门用于负载均衡的设备,常见的如F5。
软件负载均衡则主要是在服务器上安装一些具有负载均衡功能的软件来完成请求分发进而实现负载均衡,常见的就是Nginx。
无论是硬件负载均衡还是软件负载均衡,它的工作原理都不外乎下面这张图:
无论是硬件负载均衡还是软件负载均衡都会维护一个可用的服务端清单,然后通过心跳机制来删除故障的服务端节点以保证清单中都是可以正常访问的服务端节点,此时当客户端的请求到达负载均衡服务器时,负载均衡服务器按照某种配置好的规则从可用服务端清单中选出一台服务器去处理客户端的请求。这就是服务端负载均衡。
2、客户端负载均衡:例如spring cloud中的ribbon,客户端会有一个服务器地址列表,在发送请求前通过负载均衡算法选择一个服务器,然后进行访问,这是客户端负载均衡;即在客户端就进行负载均衡算法分配
我们在Spring Cloud中服务的发现与消费一文中涉及到了客户端负载均衡,在那篇文章中我们提到
“Ribbo是一个基于HTTP和TCP的客户端负载均衡器,当我们将Ribbon和Eureka一起使用时,Ribbon会从Eureka注册中心去获取服务端列表,然后进行轮询访问以到达负载均衡的作用,客户端负载均衡中也需要心跳机制去维护服务端清单的有效性,当然这个过程需要配合服务注册中心一起完成。”
从上面的描述我们可以看出,客户端负载均衡和服务端负载均衡最大的区别在于服务清单所存储的位置。在客户端负载均衡中,所有的客户端节点都有一份自己要访问的服务端清单,这些清单统统都是从Eureka服务注册中心获取的。在Spring Cloud中我们如果想要使用客户端负载均衡,方法很简单,开启@LoadBalanced
注解即可,这样客户端在发起请求的时候会先自行选择一个服务端,向该服务端发起请求,从而实现负载均衡。具体小伙伴们可以参考Spring Cloud中服务的发现与消费这篇文章
在spring cloud ribbon中使用客户端负载均衡只需要完成以下两个步骤:
- 服务的提供者只需要启动多个服务的实例,并将实例注册到一个注册中心或者注册到多个相互关联的注册中心中
- 服务的消费者直接调用被@LoadBalanced注解修饰过的RestTemplate来实现
面向服务的接口调用。
RestTemplate
如上图所示,其实在之前实现的服务消费者项目中就已经使用了RestTemplate,与此同时也使用了@LoadBalanced注解修饰RestTemplate。也就是说在之前的项目中我们已经使用了spring cloud ribbon的负载均衡,并实现了一个简单的服务访问。下面就详细的说一下RestTemplate在服务调用时的用法。
get请求
getForEntity(String url,Class responseType,Object… urlVariable);
这个方法有三个形参,第一个形参url就是请求的Url地址,第二个形参responseType为请求响应体body的包装类型,第三个形参urlVariable为url中的参数绑定。在HTTP中,get请求的请求参数是绑定在url后面的,比如http://url:port/application?param1=xxx。
使用方式如下:
@RequestMapping(value="/ribbon-consumer",method = RequestMethod.GET)
@ResponseBody
public String helloConsumer(){
ResponseEntity<String> entity = restTemplate.getForEntity("http://eureka-service/hello?name={1}", String.class, "xg");
String body = entity.getBody();
return body;
}
其中{1}表示请求参数中的一个占位符,而第三个形参Object… urlVariable是一个可变参数。需要注意的是,请求参数的占位符和可变参数中的顺序要对应起来。
例如:
@RequestMapping(value="/ribbon-consumer",method = RequestMethod.GET)
@ResponseBody
public String helloConsumer(){
ResponseEntity<String> entity = restTemplate.getForEntity("http://eureka-service/hello?name={1}&sex={2}", String.class,new String[]{"xg","male"});
String body = entity.getBody();
return body;
}
getForEntity(String url,Class responseType,Map urlVariables);
该方法与上面方法唯一的不同在于第三个形参由一可变参数变成了一个Map,使用起来也很简单,url中参数名即为Map中的key,而value就是请求的实际参数。例如上面的代码可以改写为
@RequestMapping(value="/ribbon-consumer",method = RequestMethod.GET)
@ResponseBody
public String helloConsumer(){
Map<String,String> params = new HashMap<>();
params.put("name","xg");
params.put("sex","male");
ResponseEntity<String> entity = restTemplate.getForEntity("http://eureka-service/hello?name={name}&sex={sex}", String.class,params);
String body = entity.getBody();
return body;
}
getForObject(String url,Class responseType,Object… urlVariables);
该方法与上面的getForEntity(String url,Class responseType,Map urlVariables)方法类似,不同点在于该方法可以理解上getForEntity的进一步封装,它可以直接对body中的内容进行转换,这样方法返回的就直接是包装好的对象。第二个参数用于指定需要包装成的对象。
getForObject(String url,Class responseType,Map urlVariables);
与上面的类似
post请求
postForEntity(String url,Object request,Class responseType,Object… urlVariables);
postForEntity(String url,Object request,Class responseType,Map urlVariables);
postForBody(String url,Object request,Class responseType,Object… urlVariables);
postForBody(String url,Object request,Class responseType,Map urlVariables);
post请求与get请求的不同点在于第二个参数request,该参数可以为任何类型,如果request对象不是一个HttpEntity,那么他将被转换成一个HttpEntity对象。
HttpEntity详解