Springcloud负载均衡Ribbon
Spring Cloud Ribbon 是一个客户端负载均衡器,且并不是单独进行部署的。Ribbon实现负载均衡有三个要素,服务发现、服务选择规则、服务监听。
一、Ribbon使用
1. 客户端负载均衡
负载均衡主要的功能就是缓解网络压力,实现系统的高可用。
在系统中,一般分为服务端负载均衡和客户端负载均衡。
- 服务端负载均衡,存在代理对服务端的列表进行选择,并返回一个请求给客户端。
- 客户端负载均衡时,把列表全部给客户端,代理相当于在客户端,均衡的操作逻辑在客户端完成。
Ribbon实现客户端负载均衡,请求列表维护在客户端,然后通过一定的规则,在列表中选择请求进行访问,最后达到负载均衡的效果。
2. Ribbon实例
本此实例的代码在上一篇文章中。
Springcloud服务治理EureKa。服务注册中心与客户端
-
首先启动一个服务注册中心
application.ymlserver: port: 8080 spring: application: name: eurekaApplication1 eureka: client: service-url: defaultZone: http://localhost:8080/eureka/ register-with-eureka: false
服务注册中心端口8080
-
启动两个服务提供者
首先启动一个应用
application.ymlserver: port: 8083 spring: application: name: providerService eureka: client: service-url: defaultZone: http://localhost:8080/eureka/
修改端口,重新启动一个
application.ymlserver: port: 8084 spring: application: name: providerService eureka: client: service-url: defaultZone: http://localhost:8080/eureka/
@RestController public class MyController { @Autowired private DiscoveryClient client; @Qualifier("eurekaRegistration") @Autowired private Registration registration; //服务注册 private final Logger logger = LoggerFactory.getLogger(MyController.class); @GetMapping("/hello") public String hello() { ServiceInstance instance = serviceInstance(); String result = "host:port=" + instance.getUri() + ", service_id" + instance.getServiceId(); logger.info(result); return "hello eureka"; } private ServiceInstance serviceInstance() { List<ServiceInstance> list = client.getInstances(registration.getServiceId()); if (list != null && list.size() > 0) { for (ServiceInstance itm : list) { if (itm.getPort() == 8083) { return itm; } } } return null; } }
-
启动一个服务消费者
@RestController public class ConsumerController { @Autowired RestTemplate restTemplate; /** * restTemplate 调用 * @return */ @GetMapping("/consumer") public String consumer(){ return restTemplate.getForEntity("http://PROVIDERSERVICE/hello",String.class).getBody(); } }
application.yml
server: port: 8090 spring: application: name: consumerService eureka: client: service-url: defaultZone: http://localhost:8080/eureka/
-
测试
查看服务注册中心界面
访问链接 http://localhost:8090/consumer
发现8084的控制台输出
再访问一次该链接
发现8083的控制台输出
本例需要注意使用@LoadBalanced注解过的RestTemplate对象。@SpringBootApplication @EnableEurekaClient public class Eurekaconsumer2Application { public static void main(String[] args) { SpringApplication.run(Eurekaconsumer2Application.class, args); } @Bean @LoadBalanced //负载均衡,否则restTemplate无法识别微服务名称 RestTemplate restTemplate(){ return new RestTemplate(); } }
二、RestTemplate使用
RestTemplate 是由 Spring提供的一个客户端,主要用于对Rest服务进行访问。这个类提高了开发效率,使Http服务的通信得到简化,简化了提交表单的难度,还自带Json自动转换功能。
只要加上注解@LoadBalanced,就可以使用Ribbon的客户端负载均衡。
Http方法 | RestTemplate方法 |
---|---|
GET | getForObject, getForEntity |
POST | postForObject, postForLocation |
PUT | PUT |
DELETE | DELETE |
HEAD | headForHeaders |
OPTIONS | optionsForAllow |
Any | Exchange, excute |
1. GET请求API
GET的API。
1.1 getForEntity方法
这个方法会返回ResponseEntity类型,这个类的源码可以知道使HttpEntity的扩展,包含HttpStatus与BodyBuilder的信息,说明可以对response进行处理。这个对象ResponseEntity是Spring对于Http请求响应response的封装。
HttpStatus是一个枚举类,包含了HTTP的请求状态,BodyBuilder封装请求体对象,父类HttpEntity包含了请求头信息对象HttpHeaders。
再来看这三个方法,主要是参数不同。
-
public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables)
第一个参数为url,表示请求地址;第二个参数为responseType,表示请求响应体的封装类型;第三个为uriVariables,表示不定参数。
如果返回的是基本类型String//消费者代码 @RestController public class RibbonController { @Autowired private RestTemplate restTemplate; @GetMapping("/get1") public String getForEntity1(){ ResponseEntity<String> forEntity = restTemplate.getForEntity("http://PROVIDERSERVICE/get1?id={1}", String.class, "12"); String body = forEntity.getBody(); System.out.println(body); return body; } }
//生产者代码 @GetMapping("/get1") public String getForEntity1(@RequestParam("id") Integer id) { System.out.println("id: "+id); ServiceInstance instance = serviceInstance(); String result = "host:port=" + instance.getUri() + ", service_id" + instance.getServiceId(); logger.info(result); return "hello eureka"; }
运行结果
如果返回的响应体是一个User对象,
则代码应为//消费者代码 @RestController public class RibbonController { @Autowired private RestTemplate restTemplate; @GetMapping("/get1") public String getForEntity1(){ ResponseEntity<String> forEntity = restTemplate.getForEntity("http://PROVIDERSERVICE/get1?id={1}", User.class, "12"); User body = forEntity.getBody(); System.out.println(body); return body; } }
上面的代码,会把占位符和第三个位置的数据进行替换,如果存在多个参数,按顺序书写就行。
-
public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables)
第三个参数使用map进行封装,在url中,占位符需要使用map中的key作为参数@GetMapping("/get2") public String getForEntity2(){ Map<String, Object> map = new HashMap<>(); map.put("id","12"); ResponseEntity<String> forEntity = restTemplate.getForEntity("http://PROVIDERSERVICE/get1?id={id}", String.class, map); String body = forEntity.getBody(); return body; }
-
public <T> ResponseEntity<T> getForEntity(URI url, Class<T> responseType)
这个方法使用URI对url与 uriVariavles进行封装。@GetMapping("/get3") public String getForEntity3(){ UriComponents uriComponents = UriComponentsBuilder.fromUriString("http://PROVIDERSERVICE/get1?id={id}").build().expand("12").encode(); URI uri = uriComponents.toUri(); String body = restTemplate.getForEntity(uri, String.class).getBody(); return body; }
1.2 getForObject方法
getForObject 方式包含了HTTP转换为pojo的功能,通过HttpMessageConverter进行转换,将响应体Body的内容转换成对象。
User user = restTemplate.getForObject("http://PROVIDERSERVICE/get1?id={1}", User.class, "12");
返回的时候直接获取到pojo,不需要从HttpEntity中的getBody这个代码。
public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables)
public <T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables)
public <T> T getForObject(URI url, Class<T> responseType)
这三种方法的使用方式与getForEntity一样。
2. POST请求API
POST的API
2.1 postForLocation方法
@Override
@Nullable
public URI postForLocation(String url, @Nullable Object request, Object... uriVariables)
throws RestClientException {
RequestCallback requestCallback = httpEntityCallback(request);
HttpHeaders headers = execute(url, HttpMethod.POST, requestCallback, headersExtractor(), uriVariables);
return (headers != null ? headers.getLocation() : null);
}
@Override
@Nullable
public URI postForLocation(String url, @Nullable Object request, Map<String, ?> uriVariables)
throws RestClientException {
RequestCallback requestCallback = httpEntityCallback(request);
HttpHeaders headers = execute(url, HttpMethod.POST, requestCallback, headersExtractor(), uriVariables);
return (headers != null ? headers.getLocation() : null);
}
@Override
@Nullable
public URI postForLocation(URI url, @Nullable Object request) throws RestClientException {
RequestCallback requestCallback = httpEntityCallback(request);
HttpHeaders headers = execute(url, HttpMethod.POST, requestCallback, headersExtractor());
return (headers != null ? headers.getLocation() : null);
}
这三种方法都是返回资源定位的URI。
三种方法操作基本一样,首先根据request返回一个RequestCallback, 然后执行execute返回headers,最后从headers获取location。
RequestCallback 允许将操作请求头写入请求体中,使用execute方法,对给定的URL执行HTTP方法,模板将总是关闭请求并处理任何错误。
这三种方法都是对给定的数据发送POST创建资源,返回HTTP头。
这三种方法的参数如同前面getForEntity一样。
public URI postForLocation(String url, @Nullable Object request, Object... uriVariables)
需要注意的是request可以是一个HttpEntity,可以当作一个完整的HTTP请求进行处理,里面包含了请求头和请求体。request也可以是一个普通对象,restTemplate 会把请求对象转换成HttpEntity进行处理,request的内容会被当成一个消息体进行处理。
2.2 postForObject方法
@Override
@Nullable
public <T> T postForObject(String url, @Nullable Object request, Class<T> responseType,
Object... uriVariables) throws RestClientException {
RequestCallback requestCallback = httpEntityCallback(request, responseType);
HttpMessageConverterExtractor<T> responseExtractor =
new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);
}
@Override
@Nullable
public <T> T postForObject(String url, @Nullable Object request, Class<T> responseType,
Map<String, ?> uriVariables) throws RestClientException {
RequestCallback requestCallback = httpEntityCallback(request, responseType);
HttpMessageConverterExtractor<T> responseExtractor =
new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);
}
@Override
@Nullable
public <T> T postForObject(URI url, @Nullable Object request, Class<T> responseType)
throws RestClientException {
RequestCallback requestCallback = httpEntityCallback(request, responseType);
HttpMessageConverterExtractor<T> responseExtractor =
new HttpMessageConverterExtractor<>(responseType, getMessageConverters());
return execute(url, HttpMethod.POST, requestCallback, responseExtractor);
}
使用同之前方法一样,返回封装对象。返回类型需要自己指定。
2.3 postForEntity方法
返回ResponseEntity。
3. PUT请求API
@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的请求方式对资源进行创建或者更新,没有返回值。
使用方式同之前的方法。
4. DELETE请求API
@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);
}
使用delete删除操作,使用的都是唯一标识删除数据,不需要body体和返回值。