Spring Cloud Ribbon 消费服务 实现客户端负载均衡
一,eureka客户端集群
在上一篇文章spring cloud 服务注册中心eureka高可用集群搭建 中已经搭建了eureka集群,服务提供者也可以实现简单的集群,可以新建几个eureka client model,更简单的做法就是启动 eureka client后修改server.port,再次启动,多启动几个,就是一个简单的集群,这里注意要把热加载插件注释掉。效果如下:
二,服务消费者
2.1创建消费者model
新建一个model,取名 springboot-service-ribbon
pom:
<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>
<parent>
<groupId>com.baosight</groupId>
<artifactId>springboot-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>springboot-service-ribbon</artifactId>
<description>Demo project for Spring Boot service-ribbon 服务消费者</description>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</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-devtools</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.properties配置
#注册中心集群
eureka.client.service-url.defaultZone=http://10.25.25.92:8080/eureka/,http://10.25.25.24:8080/eureka/,http://10.25.25.39:8080/eureka/
server.port=8764
spring.application.name=service-ribbon
主启动类:
@EnableDiscoveryClient表明是eureka客户端,@Bean将一个restTemplate实例注入容器,@LoadBalanced开启客户端负载均衡
@SpringBootApplication
@EnableDiscoveryClient
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
}
调用微服务service
服务提供方的contorller:
@RestController
public class Contorller {
@Autowired
private TSUserService tSUserService;
@Autowired
private QywxMenuService qywxMenuService;
@Value("${server.port}")
private String serverPort;
@RequestMapping("/list")
public List home(){
Assist assist = new Assist();
assist.setStartRow(0);
assist.setRowSize(2);
return qywxMenuService.selectQywxMenu(assist);
}
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public List getInfo(@PathVariable String id) {
Assist assist = new Assist();
//根据id查询
assist.setRequires(Assist.andEq("qywx_menu.id", id));
assist.setStartRow(0);
assist.setRowSize(10);
return qywxMenuService.selectQywxMenu(assist);
//return tSUserService.selectTSUser(assist);
}
@RequestMapping("/ribbon")
public String ribbon(){
Assist assist = new Assist();
assist.setStartRow(0);
assist.setRowSize(2);
return "第一个ribbon"+serverPort;
}
}
服务消费者调用提供者的service
根据业务需求要消费提供方的请求,与之对应的消费者调用微服务service代码如下:
@Service
public class HelloService {
@Autowired
RestTemplate restTemplate;
public String hiService() {
return restTemplate.getForObject("http://springboot-eureka-clent/ribbon",String.class);
}
public List getInfo( String id) {
//{1}是占位符,代表第一个参数,实际传输中会被id替换掉
return restTemplate.getForObject("http://springboot-eureka-clent/{1}", List.class, id);
}
public List home(){
return restTemplate.getForObject("http://springboot-eureka-clent/list", List.class);
}
}
getForObject方法简单介绍
第一个参数自然就是要调用的微服务的路径,格式是 http://服务提供者application name/请求路径,第二个参数就是getForObject方法的返回值类型,例如。返回是list,值就是List.class。方法getForObject还有另一个常用的重载方法,先看看RestTemplate类里面是怎么定义的
public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables)
/* */ throws RestClientException
/* */ {
/* 292 */ RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
/* */
/* 294 */ HttpMessageConverterExtractor<T> responseExtractor = new HttpMessageConverterExtractor(responseType, getMessageConverters(), this.logger);
/* 295 */ return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
/* */ }
/* */
/* */ public <T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException
/* */ {
/* 300 */ RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
/* */
/* 302 */ HttpMessageConverterExtractor<T> responseExtractor = new HttpMessageConverterExtractor(responseType, getMessageConverters(), this.logger);
/* 303 */ return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
/* */ }
第一个方法里面第三个参数可以传入多对象,这种方式适合用占位符,例如:
public List getInfo(String name, String id) {
return restTemplate.getForObject("http://springboot-eureka-clent/ribbon?name={1}&id={2}", List.class, name,id);
}
第二个重载方法第三个参数是map,可以这样使用
public List getInfo(String name, String id) {
Map map=new HashMap();
map.put("name",name);
map.put("id",id);
//{name}也是占位,用map的key占位
return restTemplate.getForObject("http://springboot-eureka-clent/ribbon?name={name}&id={id}", List.class, map);
服务消费者控制层
@RestController
public class Contorller {
@Autowired
HelloService helloService;
@RequestMapping("/ribbon")
public String ribbon(){
return helloService.hiService();
}
@RequestMapping("/list")
public List home(){
return helloService.home();
}
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public List getInfo(@PathVariable String id) {
return helloService.getInfo(id);
}
三,启动消费者
分别启动eureka注册中心,服务提供者者,起两个,端口号分别为8001和8002,启动消费者。浏览器访问:http://localhost:8764/ribbon ,并不断刷新,结果是“第一个ribbon8001”和“第一个ribbon8002”交替出现,说明服务调用成功,且实现了负载均衡,默认负载均衡算法是轮询。