Spring Cloud Ribbon客户端负载均衡(一)

前言

Spring Cloud Ribbon是一个基于HTTPTCP客户端负载均衡工具,,它基于Netflix Ribbon实现,在本文中我们主要来介绍如何使用Ribbon来实现客户端的负载均衡。

客户端负载均衡

我们通常说的负载俊航一般是指服务端的负载均衡,包括硬件负载均衡软件负载均衡,其中硬件负载均衡主要是通过在服务器节点之间安装专门用来负载均衡的设备,比如F5负载均衡器,而软件负载均衡则是通过在服务器上安装一些具有负载均衡功能的软件来完成请求分发工作,比如Nginx,这两种均衡方式都类似于下面这种架构方式:
在这里插入图片描述

硬件负载均衡的设备或是软件负载均衡的软件模块都会维护一个可用的服务清单,通过心跳检测来剔除故障的服务端节点以保证清单中都是可以正常访问的服务端节点。当客户端发送请求到负载均衡设备的时候,该设备按某种算法(比如线性轮询、权重、流量等)从维护的可用服务端清单中取出一台服务端的地址,然后进行转发。而客户端负载均衡和服务端负载均衡最大的不同点在于上面所提到服务清单的存储位置。在客户端负载均衡中,所有客户端节点都维护着自己要访问的服务端清,单而这个服务端清单来自于服务注册中心

通过Spring Cloud Ribbon的封装,我们在微服务架构中使用客户端负载均衡调用需要完成如下两步:

  • 服务提供者只需要启动多个服务实例并注册到一个注册中心或是多个相关联的服务注册中心
  • 服务消费者直接通过被@LoadBalanced注解修饰过的RestTemplate来实现面向服务的接口调用
    详情可参照Eureka入门实例

RestTemplate详解

GET请求

在我们之前的实例中用到了getForEntity函数,该方法返回的是ResponseEntity,是Spring对HTTP请求响应的封装,其中主要存储了HTTP的几个重要元素,比如HTTP请求状态码的枚举对象HttpStatus(也就是我们常说的404、500这些错误码)、在它的父类HttpEntity中存储着HTTP请求的头信息对象HttpHeaders以及泛型类型的请求体对象。
来看下面这个实例,返回的是String类型和User对象类型

@RequestMapping(value="/ribbon-consumer",method=RequestMethod.GET)
	public  String helloConsumer() {
		ResponseEntity<String> responseEntity=restTemplate.getForEntity(requestUrl, String.class);
		return responseEntity.getBody();
	}
@RequestMapping(value="/ribbon-consumerV2",method=RequestMethod.GET)
	public  User helloConsumerV2() {
		return restTemplate.getForEntity(requestUrl, User.class).getBody();
	}

getForEntity函数

上面的例子是比较常用的方法,getForEntity函数实际上提供了以下三种不同的重载实现。

  • getForEntity(String url, Class responseType, Object… uriVariables)
    三个参数分别是请地址url,请求响应体body的包装类型responseType,url中绑定的参数uriVariables。GET请求的参数绑定通过使用url拼接的方式,如http://MY-SERVICE/user?name={1},然后使用getForEntity(“http://HELLO-SERVICE/user?name={1}",String.class,"Amy”)
public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables)
			throws RestClientException {

		RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
		ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
		return nonNull(execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables));
	}
  • getForEntity(String url, Class responseType, Map<String, ?> uriVariables)
    这个方法提供的参数中,只有uriVariables的参数类型与上面的方法不同,这里是使用了Map类型,所以使用该方法进行参数绑定时需要在占位符中指定Map中参数的key值。
public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables)
			throws RestClientException {

		RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
		ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
		return nonNull(execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables));
	}

在Map类型的uriVariables中,我们需要put一个key参数来绑定url中的占位符,比如:

RestTemplate restTemplate=new RestTemplate();
Map<String,String> params=new HashMap<>();
params.put("name","Amy");
ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://HELLO-SERVICE/user?name={name}",String.class.params);
  • getForEntity(URI url, Class responseType)
    该方法使用URI对象来替代之前的url和urlVariables参数来指定访问地址和参数绑定.
 public <T> ResponseEntity<T> getForEntity(URI url, Class<T> responseType) throws RestClientException {
		RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
		ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
		return nonNull(execute(url, HttpMethod.GET, requestCallback, responseExtractor));
	}

URI是JDK java.net包下的一个类,它表示一个统一资源标识符引用,例如下面这个例子:

RestTemplate restTemplate=new RestTemplate();
UriComponents uriComponents=UriComponentsBuilder.fromUrilString("http://HELLO_SERVICE/user?name={name}").build().expand("Amy").encode();
URI uri=uriComponents.toUri();
ResponseEntity<String> responseEntity=restTemplate.getForEntity(uri,String.class).getBody();


getForObject函数

getForObject函数可以理解为对getForEntity的进一步封装,它通过HttpMessageConverterExtractor对HTTP的请求响应体body内容进行对象转换,实现请求直接返回包装好的对象内容,比如:

RestTemplate restTemplate=new RestTemplate();
String result =restTemplate.getForObject(uri,String.class);

当body是一个User对象时,可以直接这样实现:

RestTemplate restTemplate=new Resttemplate();
User result=restRemplat.getForObject(uri,User.class);

当不需要关注请求响应除body外的其他内容时,可以少一个从Response中获取body的步骤。它与getForEntity函数类似,也提供了三个不同的重载实现。

@Override
	@Nullable
	public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException {
		RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
		HttpMessageConverterExtractor<T> responseExtractor =
				new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
		return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
	}

	@Override
	@Nullable
	public <T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException {
		RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
		HttpMessageConverterExtractor<T> responseExtractor =
				new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
		return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
	}

	@Override
	@Nullable
	public <T> T getForObject(URI url, Class<T> responseType) throws RestClientException {
		RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
		HttpMessageConverterExtractor<T> responseExtractor =
				new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
		return execute(url, HttpMethod.GET, requestCallback, responseExtractor);
	}

POST请求

postForEntity函数

该方法同为GET请求中的getForEntity类似,会在调用后返回ResponseEntity对象,其中T为请求响应的body类型。比如下面这个例子,使用postForEntity提交POST请求到HELLO-SERVICE服务的/user接口,提交的body内容为user对象,请求响应返回的body类型为String。

RestTemplate restTemplate=new RestTemplate();
User user=new User("Amy",18);
ResponseEntity<String> responseEntity=restTemplate.postForEntity("http://HELLO-SERVICE/user",user,String.class);
String body=responseEntity.getBody();

postForEntity函数也实现了三种不同的重载方法

@Override
	public <T> ResponseEntity<T> postForEntity(String url, @Nullable Object request,
			Class<T> responseType, Object... uriVariables) throws RestClientException {

		RequestCallback requestCallback = httpEntityCallback(request, responseType);
		ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
		return nonNull(execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables));
	}

	@Override
	public <T> ResponseEntity<T> postForEntity(String url, @Nullable Object request,
			Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException {

		RequestCallback requestCallback = httpEntityCallback(request, responseType);
		ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
		return nonNull(execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables));
	}

	@Override
	public <T> ResponseEntity<T> postForEntity(URI url, @Nullable Object request, Class<T> responseType)
			throws RestClientException {

		RequestCallback requestCallback = httpEntityCallback(request, responseType);
		ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
		return nonNull(execute(url, HttpMethod.POST, requestCallback, responseExtractor));
	}

PUT请求

在RestTemplate中,对PUT请求可以通过put方式进行调用实现,比如:

RestTemplate restTemplate =new RestTemplate();
Long id=10001L;
User user=new User("Andy",10);
restTemplate.put("http://HELLO-SERVICE/user/{1}",user,id);

put函数也实现了三种不同的重载方法:

@Override
	public void put(String url, @Nullable Object request, Object... uriVariables)
			throws RestClientException {

		RequestCallback requestCallback = httpEntityCallback(request);
		execute(url, HttpMethod.PUT, requestCallback, null, uriVariables);
	}

	@Override
	public void put(String url, @Nullable Object request, Map<String, ?> uriVariables)
			throws RestClientException {

		RequestCallback requestCallback = httpEntityCallback(request);
		execute(url, HttpMethod.PUT, requestCallback, null, uriVariables);
	}

	@Override
	public void put(URI url, @Nullable Object request) throws RestClientException {
		RequestCallback requestCallback = httpEntityCallback(request);
		execute(url, HttpMethod.PUT, requestCallback, null);
	}

put函数为void类型,没有返回内容,也就没有其他函数定义的responseType参数,除此之外的其他传入参数定义与用法跟postForObject基本一致。

DELETE请求

在RestTemplate中,对DELETE请求可以通过delete方法进行调用实现,比如:

RestTemplate restTemplate=new RestTemplate();
Long id=10001L;
restTemplate.delete("http://HELLO-SERVICE/user/{1}",id);

delete函数也实现了三种不同的重载方法:

@Override
	public void delete(String url, Object... uriVariables) throws RestClientException {
		execute(url, HttpMethod.DELETE, null, null, uriVariables);
	}

	@Override
	public void delete(String url, Map<String, ?> uriVariables) throws RestClientException {
		execute(url, HttpMethod.DELETE, null, null, uriVariables);
	}

	@Override
	public void delete(URI url) throws RestClientException {
		execute(url, HttpMethod.DELETE, null, null);
	}

由于我们在进行REST请求时,通常都将DELETE请求的唯一标识拼接在url中,所以DELETE请求也不需要request的body信息,url指定DELETE请求的位置,uriVaribales绑定url中的参数即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值