Spring Cloud 学习 | - 03 - 服务消费(Rest + Ribbon实现负载均衡)
文章目录
Spring Cloud 学习 | - 03 - 服务消费(Rest + Ribbon实现负载均衡)
学习
Spring Cloud
之路,文中Spring Boot
版本为2.1.3.RELEASE
,Spring Cloud
版本为Greenwich.SR1
。因能力有限,难免会有不足或者错误之处,还望不吝指正,谢!
接上篇 Spring Cloud 学习 | - 02 - Eureka集群实现高可用,我们把服务提供者注入到Eureka注册中心,注册完了,我们最终是要消费服务的。本篇将开始进行服务消费的过程。
1. 修改服务提供者并注册到Eureka中
1.1 Maven依赖:
主要的依赖如下:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
1.2 编写Service
新增接口 UserService
,并添加一个方法 findAll()
:UserService.java
:
public interface UserService {
/**
* 查询所有用户
* @return
*/
List findAll();
}
并实现该接口 UserServiceImpl
。我们要使用 Spring
的自动注入功能,加上 @Service
注解:UserServiceImpl.java
:
@Service
public class UserServiceImpl implements UserService {
/**
* 查询所有用户
* @return
*/
@Override
public List findAll() {
List list = Arrays.asList("Cindy","Pony","Marry");
return list;
}
}
1.3 编写Controller
新增 UserController
,对外暴露一个 list
接口。UserController.java
:
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/list")
public List list(){
return userService.findAll();
}
}
@RestController
表示这是一个Rest风格的接口,返回的是JSON
,这个注解等效于@Controller
+@ResponseBody
;
1.4 配置
server:
port: 8090
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka
instance:
prefer-ip-address: true # 返回ip而不是host名称
# ip-address: 127.0.0.1 # 指定自己的ip信息
lease-expiration-duration-in-seconds: 15 # 服务失效时间,默认值90秒
lease-renewal-interval-in-seconds: 5 # 服务续约(renew)的间隔,默认为30秒
spring:
application:
name: user-provider
1.5 主启动类
@SpringBootApplication
@EnableDiscoveryClient
public class UserProviderApplication {
public static void main(String[] args) {
SpringApplication.run(UserProviderApplication.class, args);
}
}
1.6 启动并测试
依次启动Eureka
服务和user-provider
,访问http://localhost:8761/
:
我们测试一下接口是否正常:
接口正常取到数据。
至此,服务提供者成功注册到eureka注册中心。接下来,开始服务消费者。
2. 创建服务消费者
2.1 新建消费者项目
利用Spring Initializr
快速创建一个Spring Boot
项目,名称为:user-consumer
,主要依赖如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2.2 编写配置
编写application.yml
配置文件,将服务注册到eureka
注册中心,完整的.yml
配置如下:
server:
port: 8080
spring:
application:
name: user-consumer
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka
instance:
prefer-ip-address: true
2.3 在启动类添加注解,并注入Bean
在XxxApplication启动类上添加@EnableDiscoveryClient
启用客户端发现功能,并向IoC容器中注入一个Bean:RestTemplate
,Rest API调用需要用到。
@SpringBootApplication
@EnableDiscoveryClient
public class UserConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(UserConsumerApplication.class, args);
}
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
2.4 编写Controller
@RestController
public class UserController {
@Autowired(required = false)
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/list")
public Object findAll(){
List<ServiceInstance> instanceList = discoveryClient.getInstances("user-provider");
// 我们这里用第一个实例
ServiceInstance instance = instanceList.get(0);
StringBuffer buffer = new StringBuffer("http://");
buffer.append(instance.getHost()).append(":").append(instance.getPort()).append("/user/list");
return restTemplate.getForObject(buffer.toString(), Object.class);
}
}
2.5 启动测试
我们启动user-consumer
服务,查看eureka
注册中心,已经有了user-consumer
服务:
我们测试一下接口是否正常:
成功拿到结果。
2.6 问题反思?
这里我们用的是实例列表里取第一个实例,如果是要实现负载均衡,应该是顺序选择或者随机一个实例或者其它什么的。其实我们修改取到实例的方法即可。但是我们是快速开发,不是造轮子,SpringCloud已经帮我们弄好了解决方法,Rest + Ribbon 实现注解式的负载均衡。下边,我们用Rest + Ribbon的方式实现负载均衡的功能。
3. Rest + Ribbon 实现负载均衡
3.1 启动注册2个服务提供者
- 我们修改以下消费提供者的
UserController.java
的list()
方法,返回参数多一个port端口:
@GetMapping("/list")
public Map list(){
Map<String,Object> data = new HashMap<>(16);
data.put("port", port);
data.put("rows", userService.findAll());
return data;
}
- 我们复制一份
user-provider
的配置启动类,取名:UserProviderApplication-2
,确定OK - 修改
user-provider
的配置端口,改为8091:
server:
port: 8091
- 保存并启动,启动完成后,三个启动项目如下:
- 查看注册中心:
3.2 修改服务消费者
- 在
RestTemplate
这个Bean上加注解@LoadBalanced
,完整的代码如下:
@SpringBootApplication
@EnableDiscoveryClient
public class UserConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(UserConsumerApplication.class, args);
}
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
- 修改
UserController
,调用方式采用Rest+Ribbon的方式,完整的代码如下:
@RestController
public class UserController {
@Autowired(required = false)
private RestTemplate restTemplate;
@GetMapping("/list")
public Object findAll(){
// List<ServiceInstance> instanceList = discoveryClient.getInstances("user-provider");
// // 我们这里用第一个实例
// ServiceInstance instance = instanceList.get(0);
// StringBuffer buffer = new StringBuffer("http://");
// buffer.append(instance.getHost()).append(":").append(instance.getPort()).append("/user/list");
StringBuffer buffer = new StringBuffer("http://").append("user-provider").append("/user/list");
return restTemplate.getForObject(buffer.toString(), Object.class);
}
}
3.3 启动消费者并测试
启动user-consumer
进行API接口测试:
结果是对的,并且我们发现返回的数据里port交替出现。已经作了负载均衡,之所以交替出现,是因为默认的负载均衡策略规则是轮询策略,就是一个一个按顺序来。