Spring Cloud Ribbon是基于HTTP和TCP的客户端负载工具,在Spring Cloud微服务架构中使用客户端负载均衡调用只需要两步:

  1. 服务提供者只需启动多个实例并注册到一个注册中心或是多个相关联的服务注册中心
  2. 服务消费者直接通过调用被@LoadBalanced注解修饰过的RestTemplate来实现面向服务的调用接口

服务注册中心、服务注册提供者以及服务消费者的搭建,参考上一篇《Spring Cloud服务治理之Eureka》

RestTemplate主要有GET、POST、PUT、DELETE四种请求类型,具体用法如下:

GET请求

get请求一般通过getForEntity函数和getForObject函数进行调用实现

  • getForEntity函数

getForEntity(String url ,Class ResponseType ,Object … urlVariables)
其中url为请求地址,responseType为请求响应体body的包装类型,urlVariables为url中占位符对应的参数

getForEntity(String url ,Class ResponseType ,Map … urlVariables)
urlVariables的参数类型为Map类型

getForEntity(URI uri ,Class ResponseType )
URI队形替代了url和urlVariables参数,用于指定访问地址和参数的绑定

  • getForObject参数(与getForEntity类似)

getForObject(String url ,Class ResponseType ,Object … urlVariables)
url参数指定访问的地址,ResponseType 参数定义返回类型,urlVariables为url中占位符对应的参数

getForObject(String url ,Class ResponseType ,Map … urlVariables)
urlVariables的参数类型为Map类型

getForObject(URI uri ,Class ResponseType )
URI队形替代了url和urlVariables参数,用于指定访问地址和参数的绑定

POST请求

在restTemplate中,对POST请求有三种调用方式

• postForEntity函数
 postForEntity(String url ,Object request ,Class ResponseType ,Object… urlVariables)
 postForEntity(String url ,Object request ,Class ResponseType ,Map… urlVariables)
 postForEntity(URI uri ,Object request ,Class ResponseType )• postForObject函数
 postForObject(String url ,Object request ,Class ResponseType ,Object… urlVariables)
 postForObject(String url ,Object request ,Class ResponseType ,Map… urlVariables)
 postForObject(URI uri ,Object request ,Class ResponseType )• postForLocation函数
 该方法实现了以POST请求提交资源,并返回新资源的URI
 postForLocation(String url ,Object request ,Object… urlVariables)
 postForLocation(String url ,Object request ,Map… urlVariables)
 postForLocation(URI uri ,Object request )
PUT请求

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

put(String url, Object request, Object … urlVariables)
 put(String url, Object request, Map urlVariables)
 put(URI uri, Object request)
DELETE请求

delete函数三种不同的重载方法

delete(String url, Object … urlVariables)
 delete(String url, Map urlVariables)
 delete(URI uri)

服务消费者具体demo源码如下:
pom.xml具体如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.example</groupId>
	<artifactId>consumer</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.2.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>

	<name>consumer</name>
	<description>The project for Spring Cloud server consumer</description>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
		<spring-cloud.version>Dalston.SR5</spring-cloud.version>
	</properties>


	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<!--引入 netflix-ribbon -->
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-ribbon</artifactId>
		</dependency>

		<!--Eureka Client 依赖-->
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-eureka</artifactId>
		</dependency>

		<!--断路器 hystrix依赖-->
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-hystrix</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>

		<!-- log4j日志打印 -->
		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.17</version>
		</dependency>
	</dependencies>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

properties文件如下

spring.application.name = ribbon-consumer
server.port = 9000

eureka.client.serviceUrl.defaultZone = http://localhost:1111/eureka

具体演示代码:

package com.example.consumer.controller;

import com.example.consumer.service.HelloService;
import com.example.consumer.vo.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;

import java.net.URI;
import java.util.HashMap;
import java.util.Map;

@RestController
public class ConsumerController {
    @Autowired
    RestTemplate restTemplate;

    @Autowired
    HelloService helloService;

    static Logger logger = LoggerFactory.getLogger(ConsumerController.class);

    @RequestMapping(value="/ribbon-consumer",method = RequestMethod.GET)
    public String helloConsumer(){
        logger.info( restTemplate.getForEntity("http://HELLO-SERVICE/hello",String.class).getBody());
        logger.info(helloService.helloService());
        return  "ribbon-consumer for get";
    }

    @RequestMapping(value="/ribbon-consumer1",method = RequestMethod.GET)
    public String hello1Consumer(){

        logger.info("ribbon-consumer1 with String: " + restTemplate.getForEntity("http://HELLO-SERVICE/hello1?name={1}",String.class,"Tino").getBody());

        Map<String,Object> params = new HashMap<>();
        params.put("name","Tom");
        logger.info("ribbon-consumer1 with Map: " + restTemplate.getForEntity("http://HELLO-SERVICE/hello1?name={name}",String.class,params).getBody());

        UriComponents uriComponents = UriComponentsBuilder.fromUriString("http://HELLO-SERVICE/hello1?name={name}").build().expand("Lily").encode();
        logger.info("ribbon-consumer1 getForEntity with Uri : " + restTemplate.getForEntity(uriComponents.toUri(),String.class).getBody());

        UriComponents uriComponents1 = UriComponentsBuilder.fromUriString("http://HELLO-SERVICE/hello1?name={name}").build().expand("Tony").encode();
        logger.info("ribbon-consumer1 getForEntity with Uri: " + restTemplate.getForObject(uriComponents1.toUri(),String.class));

        return "ribbon-consumer1 for get";
    }

    @RequestMapping(value="/ribbon-consumer2",method = RequestMethod.GET)
    public User hello2Consumer(){
        ResponseEntity<User> responseEntity = restTemplate.getForEntity("http://HELLO-SERVICE/hello2?name={1}",User.class,"Jerry");
        User user = responseEntity.getBody();
        logger.info(user.toString());
        return user;
    }

    @RequestMapping(value="/ribbon-consumer3",method = RequestMethod.POST)
    public User hello3Consumer(){
        User user = new User("Ada",12L);
        ResponseEntity<User> responseEntity = restTemplate.postForEntity("http://HELLO-SERVICE/hello3",user,User.class);
        logger.info("ribbon-consumer3 for postForEntity with object :"+ responseEntity.getBody().toString());

        User user1 = new User("Ada",12L);
        HttpHeaders headers = new HttpHeaders();
        headers.add("nickName","Herry");
        HttpEntity<User> httpEntity = new HttpEntity<>(user1,headers);
        ResponseEntity<User> responseEntity1 = restTemplate.postForEntity("http://HELLO-SERVICE/hello3", httpEntity, User.class);
        logger.info("ribbon-consumer3 for postForEntity with alert http header :"+ responseEntity1.getBody().toString());

        User user2 = new User("Ada",12L);
        User userRes = restTemplate.postForObject("http://HELLO-SERVICE/hello3",user2,User.class);
        logger.info("ribbon-consumer3 for postForObject with object :"+ userRes.toString());

        return userRes;

    }

    @RequestMapping(value="/ribbon-consumer4",method = RequestMethod.PUT)
    public void hello4Consumer(){
        User user = new User("Jay",12L);
        restTemplate.put("http://HELLO-SERVICE/hello4",user,User.class);
    }


    @RequestMapping(value="/ribbon-consumer5",method = RequestMethod.DELETE)
    public void hello5Consumer(){
        restTemplate.delete("http://HELLO-SERVICE/hello6/{1}",7L);
    }
}

服务消费者直接通过调用被@LoadBalanced注解修饰过的RestTemplate来实现面向服务的调用接口,其启动类的代码如下:

package com.example.consumer;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@EnableCircuitBreaker
@EnableDiscoveryClient
@SpringBootApplication
public class ConsumerApplication {
	static Logger logger = LoggerFactory.getLogger(ConsumerApplication.class);
	@Bean
	@LoadBalanced
	RestTemplate restTemplate(){
		return new RestTemplate();
	}

	public static void main(String[] args) {
		SpringApplication.run(ConsumerApplication.class, args);
		logger.info("consumer-service 启动完成");
	}

}

服务注册提供者代码,pom和properties的代码见上一篇《Spring Cloud服务治理之Eureka》 相应部分

package com.example.hello.contrller;

import com.example.hello.vo.User;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.List;


@RestController
public class HelloController {

    private final Logger logger = Logger.getLogger(getClass());

    @Autowired
    @SuppressWarnings("all")
    private DiscoveryClient client; // 服务发现客户端

    @Autowired
    private Registration registration;

    @RequestMapping(value = "/hello",method =  RequestMethod.GET)
    public String index(HttpServletRequest requet){
        //获取可以的服务实例清单
        List<ServiceInstance> instanceList = client.getInstances(registration.getServiceId());
        try {
            for (ServiceInstance instance:instanceList) {
                //遍历循环查找当前机器的服务实例
                if(requet.getLocalPort()==instance.getPort()){
                    String result = "/testBalance, host:port=" + instance.getUri()  + ", " + "service_id:" + instance.getServiceId();
                    System.err.println(result);
                    break;
                }
            }
        }catch (Exception ex){
            logger.info("没有找到对应的instance");
        }
        return "Hello World";
    }

    @GetMapping(value = "/hello1")
    public String hello1(@RequestParam String name){
        ServiceInstance instance = client.getLocalServiceInstance();
        logger.info("/user,host:" + instance.getHost() + ", service_id:" + instance.getServiceId());
        return "Hello " + name;
    }

    @GetMapping(value = "/hello2")
    public User hello2(@RequestParam String name){
        ServiceInstance instance = client.getLocalServiceInstance();
        logger.info("/user,host:" + instance.getHost() + ", service_id:" + instance.getServiceId());
        return  new User(name,0L);
    }

    @PostMapping(value = "/hello3")
    public User hello3(@RequestBody User user,HttpServletRequest request){
        ServiceInstance instance = client.getLocalServiceInstance();
        logger.info("/user,host:" + instance.getHost() + ", service_id:" + instance.getServiceId());
        String name = request.getHeader("nickName") == null?user.getName():request.getHeader("nickName");
        user.setName(name);
        user.setAge(user.getAge()+5);
        return user;
    }

    @PutMapping(value = "/hello4")
    public User hello4(@RequestBody User user,HttpServletRequest request){
        ServiceInstance instance = client.getLocalServiceInstance();
        logger.info("/user,host:" + instance.getHost() + ", service_id:" + instance.getServiceId());
        user.setAge(user.getAge()+8);
        logger.info("put method result:" + user.toString());
        return user;
    }

    @DeleteMapping(value = "/hello5/{age}")
    public User hello5(@PathVariable Long age,HttpServletRequest request){
        ServiceInstance instance = client.getLocalServiceInstance();
        logger.info("/user,host:" + instance.getHost() + ", service_id:" + instance.getServiceId());
        User user = new User("Jessica",29L);
        user.setAge(user.getAge()-age);
        logger.info("delete method result:" + user.toString());
        return user;
    }
}