Spring Cloud Ribbon 是一个客户端负载均衡器,且并不是单独进行部署的。Ribbon实现负载均衡有三个要素,服务发现、服务选择规则、服务监听。

一、Ribbon使用

1. 客户端负载均衡

负载均衡主要的功能就是缓解网络压力,实现系统的高可用。
在系统中,一般分为服务端负载均衡和客户端负载均衡。

  • 服务端负载均衡,存在代理对服务端的列表进行选择,并返回一个请求给客户端。
  • 客户端负载均衡时,把列表全部给客户端,代理相当于在客户端,均衡的操作逻辑在客户端完成。

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

在这里插入图片描述

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

  2. 启动两个服务提供者
    首先启动一个应用
    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;
        }
    }
    
  3. 启动一个服务消费者

    @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/
    
  4. 测试
    查看服务注册中心界面
    在这里插入图片描述
    访问链接 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方法
GETgetForObject, getForEntity
POSTpostForObject, postForLocation
PUTPUT
DELETEDELETE
HEADheadForHeaders
OPTIONSoptionsForAllow
AnyExchange, excute

1. GET请求API

GET的API。
在这里插入图片描述

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

    运行结果
    在这里插入图片描述如果返回的响应体是一个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;
        }
    }
    

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

  2. 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;
    }
    
  3. 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
在这里插入图片描述

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

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐