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实现客户端负载均衡,请求列表维护在客户端,然后通过一定的规则,在列表中选择请求进行访问,最后达到负载均衡的效果。

java实现一个负载均衡算法 负载均衡springcloud_spring

2. Ribbon实例

本此实例的代码在上一篇文章中。
Springcloud服务治理EureKa。服务注册中心与客户端

  1. 首先启动一个服务注册中心
    application.yml
server:
  port: 8080
spring:
  application:
    name: eurekaApplication1
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8080/eureka/
    register-with-eureka: false

服务注册中心端口8080

  1. 启动两个服务提供者
    首先启动一个应用
    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;
    }
}
  1. 启动一个服务消费者
@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/
  1. 测试
    查看服务注册中心界面

    访问链接 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。

java实现一个负载均衡算法 负载均衡springcloud_ribbon_02

1.1 getForEntity方法

这个方法会返回ResponseEntity类型,这个类的源码可以知道使HttpEntity的扩展,包含HttpStatus与BodyBuilder的信息,说明可以对response进行处理。这个对象ResponseEntity是Spring对于Http请求响应response的封装。

HttpStatus是一个枚举类,包含了HTTP的请求状态,BodyBuilder封装请求体对象,父类HttpEntity包含了请求头信息对象HttpHeaders。

再来看这三个方法,主要是参数不同。

  1. 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";
}

运行结果

java实现一个负载均衡算法 负载均衡springcloud_spring_03

如果返回的响应体是一个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;
    }
}

上面的代码,会把占位符和第三个位置的数据进行替换,如果存在多个参数,按顺序书写就行。

  1. 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;
}
  1. 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这个代码。

  1. public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables)
  2. public <T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables)
  3. public <T> T getForObject(URI url, Class<T> responseType)

这三种方法的使用方式与getForEntity一样。

2. POST请求API

POST的API

java实现一个负载均衡算法 负载均衡springcloud_java实现一个负载均衡算法_04

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体和返回值。