Springcloud负载均衡Ribbon
- 一、Ribbon使用
- 1. 客户端负载均衡
- 2. Ribbon实例
- 二、RestTemplate使用
- 1. GET请求API
- 1.1 getForEntity方法
- 1.2 getForObject方法
- 2. POST请求API
- 2.1 postForLocation方法
- 2.2 postForObject方法
- 2.3 postForEntity方法
- 3. PUT请求API
- 4. DELETE请求API
Spring Cloud Ribbon 是一个客户端负载均衡器,且并不是单独进行部署的。Ribbon实现负载均衡有三个要素,服务发现、服务选择规则、服务监听。
一、Ribbon使用
1. 客户端负载均衡
负载均衡主要的功能就是缓解网络压力,实现系统的高可用。
在系统中,一般分为服务端负载均衡和客户端负载均衡。
- 服务端负载均衡,存在代理对服务端的列表进行选择,并返回一个请求给客户端。
- 客户端负载均衡时,把列表全部给客户端,代理相当于在客户端,均衡的操作逻辑在客户端完成。
Ribbon实现客户端负载均衡,请求列表维护在客户端,然后通过一定的规则,在列表中选择请求进行访问,最后达到负载均衡的效果。
2. Ribbon实例
本此实例的代码在上一篇文章中。
Springcloud服务治理EureKa。服务注册中心与客户端
- 首先启动一个服务注册中心
application.yml
server:
port: 8080
spring:
application:
name: eurekaApplication1
eureka:
client:
service-url:
defaultZone: http://localhost:8080/eureka/
register-with-eureka: false
服务注册中心端口8080
- 启动两个服务提供者
首先启动一个应用
application.yml
server:
port: 8083
spring:
application:
name: providerService
eureka:
client:
service-url:
defaultZone: http://localhost:8080/eureka/
修改端口,重新启动一个
application.yml
server:
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体和返回值。