上一篇文章《SpringCloud : 创建高可用的服务注册中心(Eureka)(一)》只是讲解了如果创建多个服务注册中心实例,并将客户端注册到服务中,并没有展示服务之间如何通信的功能。
下面通过生产者与消费者模式讲解两者之间如何通过注册中心通信。
一、创建生产者项目
1) 将上篇中的客户端项目 “12-eureka-client” 拷贝一份,并改名为“14-eureka-provider”。
2) 将配置文件“application.properties” 中的“spring.application.name=eureka-client” 改为 “spring.application.name=eureka-provider”
3) 创建model
Users.java
public class Users implements Serializable{
private static final long serialVersionUID = 5488580019044807879L;
private String id;
private String name;
private String sex;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Users() {
super();
}
public Users(String id, String name, String sex) {
super();
this.id = id;
this.name = name;
this.sex = sex;
}
}
4) 创建Controller
UserController.java
@RestController
@RequestMapping("user")
public class UserController {
@RequestMapping("index")
public Users index(){
return new Users("1", "张三", "男");
}
@RequestMapping("list")
public List<Users> list(){
List<Users> list = new ArrayList<>();
list.add(new Users("1", "张三", "男"));
list.add(new Users("2", "李四", "女"));
list.add(new Users("3", "王五", "男"));
return list;
}
}
5) 启动项目,分别访问 index \ list
http://localhost:8080/user/index
http://localhost:8080/user/list
二、创建消费者
拷贝生产者项目,改名为“15-eureka-consumer”
1)修改项目名称、端口号如下图
2) 新增Service层
通过Service层获取生产者中的controller的数据,
创建接口UserService.java ,第三个方法暂时不用管。
public interface UserService {
public Users loadUsers();
public List<Users> findUsers();
//public List<Users> findUsers2();
}
创建实现 UserServiceImpl .java ,第三个方法暂时不用管。
@Service
public class UserServiceImpl implements UserService {
@Autowired
private LoadBalancerClient client;
@Override
public Users loadUsers() {
//选择调用服务的名称
//此对象封装了服务的基本信息,如IP和端口
ServiceInstance si = this.client.choose("eureka-provider");
//访问的服务的URL
StringBuilder sBuilder = new StringBuilder();
sBuilder.append("http://").append(si.getHost()).append(":").append(si.getPort()).append("/user/index");
RestTemplate rTemplate = new RestTemplate();
ParameterizedTypeReference<Users> typeReference=new ParameterizedTypeReference<Users>() {
};
ResponseEntity<Users> entity = rTemplate.exchange(sBuilder.toString(), HttpMethod.GET, null, typeReference);
return entity.getBody();
}
@Override
public List<Users> findUsers() {
//选择调用服务的名称
//此对象封装了服务的基本信息,如IP和端口
ServiceInstance si = this.client.choose("eureka-provider");
//访问的服务的URL
StringBuilder sBuilder = new StringBuilder();
sBuilder.append("http://").append(si.getHost()).append(":").append(si.getPort()).append("/user/list");
RestTemplate rTemplate = new RestTemplate();
ParameterizedTypeReference<List<Users>> typeReference=new ParameterizedTypeReference<List<Users>>() {
};
ResponseEntity<List<Users>> entity = rTemplate.exchange(sBuilder.toString(), HttpMethod.GET, null, typeReference);
return entity.getBody();
}
// @Override
// public List<Users> findUsers2(){
// return this.findModel("eureka-provider", "/user/list");
// }
}
使用ribbon 负载均衡器通过生产者项目的名称获取生产者的项目地址的URL来获取的数据。
3) 修改UserController.java ,第三个方法暂时不用管。
@RestController
@RequestMapping("user")
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("index")
public Users index(){
return userService.loadUsers();
}
@RequestMapping("list")
public List<Users> list(){
return userService.findUsers();
}
//@RequestMapping("list2")
//public List<Users> list2(){
//return userService.findUsers2();
//}
}
4)启动项目后,访问消费者项目 index、list (注意端口号)
访问之前要先启动注册中心,如何启动看上一篇文章《SpringCloud : 创建高可用的服务注册中心(Eureka)(一)》
http://localhost:8081/user/index
http://localhost:8081/user/list
三、优化Service层
上面的service层代码通过ribbon负载均衡器跟生产者通信,而在现实环境中,这种通信可能会比较频繁,那么每个方法
都这么写,是非常不友好的,我们通过封装来优化下。
1) 创建一个抽象类AbstractHmService.java
public abstract class AbstractHmService<E> implements IHmService<E>{
@Autowired
private LoadBalancerClient client;
@Override
public E loadModel(String appName,String path) {
//选择调用服务的名称
//此对象封装了服务的基本信息,如IP和端口
ServiceInstance si = this.client.choose(appName);
//访问的服务的URL
StringBuilder sBuilder = new StringBuilder();
sBuilder.append("http://").append(si.getHost()).append(":").append(si.getPort()).append(path);
RestTemplate rTemplate = new RestTemplate();
ParameterizedTypeReference<E> typeReference=new ParameterizedTypeReference<E>() {};
ResponseEntity<E> entity= rTemplate.exchange(sBuilder.toString(), HttpMethod.GET, null, typeReference);
return entity.getBody();
}
@Override
public List<E> findModel(String appName,String path) {
//选择调用服务的名称
//此对象封装了服务的基本信息,如IP和端口
ServiceInstance si = this.client.choose(appName);
//访问的服务的URL
StringBuilder sBuilder = new StringBuilder();
sBuilder.append("http://").append(si.getHost()).append(":").append(si.getPort()).append(path);
RestTemplate rTemplate = new RestTemplate();
ParameterizedTypeReference<List<E>> typeReference=new ParameterizedTypeReference<List<E>>() {};
ResponseEntity<List<E>> entity= rTemplate.exchange(sBuilder.toString(), HttpMethod.GET, null, typeReference);
return entity.getBody();
}
}
2. Service层的实现类继承这个抽象类,并将上面所有类中隐藏的第三个方法开启
@Service
public class UserServiceImpl extends AbstractHmService<Users> implements UserService {
@Autowired
private LoadBalancerClient client;
@Override
public Users loadUsers() {
return this.loadModel("eureka-provider", "/user/index");
}
@Override
public List<Users> findUsers() {
//选择调用服务的名称
//此对象封装了服务的基本信息,如IP和端口
ServiceInstance si = this.client.choose("eureka-provider");
//访问的服务的URL
StringBuilder sBuilder = new StringBuilder();
sBuilder.append("http://").append(si.getHost()).append(":").append(si.getPort()).append("/user/list");
RestTemplate rTemplate = new RestTemplate();
ParameterizedTypeReference<List<Users>> typeReference=new ParameterizedTypeReference<List<Users>>() {
};
ResponseEntity<List<Users>> entity = rTemplate.exchange(sBuilder.toString(), HttpMethod.GET, null, typeReference);
return entity.getBody();
}
@Override
public List<Users> findUsers2(){
return this.findModel("eureka-provider", "/user/list");
}
}
3)重启消费者项目,访问 http://localhost:8081/user/index 和 http://localhost:8081/user/list2
这时候你会发现,第一个页面报错,第二个页面能正常显示数据(^*__*^)。
这是因为获取单个元素是,entity.getBody()把结果当作是json字符串了,无法做javabean转换,可以将loadModel方法改成
如下,并将UserServiceImpl.java中的loadUsers方法调用修改后的方法。
@Override
public E loadModel(String appName,String path, Class<E> clz) {
//选择调用服务的名称
//此对象封装了服务的基本信息,如IP和端口
ServiceInstance si = this.client.choose(appName);
//访问的服务的URL
StringBuilder sBuilder = new StringBuilder();
sBuilder.append("http://").append(si.getHost()).append(":").append(si.getPort()).append(path);
RestTemplate rTemplate = new RestTemplate();
return rTemplate.getForObject(sBuilder.toString(), clz);
}
这样重启项目后,就能正常访问了。