前言
我们接着上一节。在许多场景下,外部请求需要查询Zuul后端的多个微服务。比如一个电影售票手机APP,在购票订单页上,既需要查询“电影微服务”获得电影相关信息,又需要查询“用户微服务”获得当前用户信息。如果让手机端直接请求各个微服务(即使使用Zuul进行转发),那么网络开销,流量耗费,耗费时长可能都无法令人满意。那么对于这种场景,可使用Zuul聚合微服务请求——手机APP发送一次请求给Zuul,由Zuul请求用户微服务以及电影微服务,并组织好数据给手机APP。
使用这种方式,在手机端只需发送一次请求即可,简化了客户端侧的开发;不仅如此,由于Zuul,用户微服务,电影微服务一般都在同一个局域网中,因此速度会非常快,效率会非常高。
编码
1.复制项目microservice-gateway-zuul,将ArtifactId修改为microservice-gateway-zuul-aggregation.
2.修改启动类。
@SpringBootApplication @EnableZuulProxy public class ZuulApplication { public static void main(String[] args) { SpringApplication.run(ZuulApplication.class, args); } @Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); } }
3.创建实体类。
public class User { private Long id; private String username; private String name; private Integer age; private BigDecimal balance; // getters and setters... }
4.创建java类。
@Service public class AggregationService { @Autowired private RestTemplate restTemplate; @HystrixCommand(fallbackMethod = "fallback") public Observable<User> getUserById(Long id){ // 创建一个被发现者 return Observable.create(observer -> { User user = restTemplate.getForObject( "http://microservice-provider-user/{id}",User.class,id); observer.onNext(user); observer.onCompleted(); }); } @HystrixCommand(fallbackMethod = "fallback") public Observable<User> getMovieUserByUserId(Long id){ return Observable.create(observer -> { User movieUser = restTemplate.getForObject( "http://microservice-consumer-movie/user/{id}",User.class,id); observer.onNext(movieUser); observer.onCompleted(); }); } public User fallback(Long id) { User user = new User(); user.setId(-1L); return user; } }
5.创建Controller,在Controller中聚合多个微服务请求。
@RestController public class AggregationController { public static final Logger LOGGER = LoggerFactory.getLogger(ZuulApplication.class); @Autowired private AggregationService aggregationService; @GetMapping("/aggregate/{id}") public DeferredResult<HashMap<String,User>> aggregate(@PathVariable Long id){ Observable<HashMap<String,User>> result = this.aggregateObservable(id); return this.toDeferredResult(result); } public Observable<HashMap<String,User>> aggregateObservable(Long id){ // 合并两个或者多个Observeables发射出的数据项,根据指定的函数变换它们 return Observable.zip( this.aggregationService.getUserById(id), this.aggregationService.getMovieUserByUserId(id), (user,movieUser) -> { HashMap<String,User> map = Maps.newHashMap(); map.put("user",user); map.put("movieUser",movieUser); return map; } ); } public DeferredResult<HashMap<String,User>> toDeferredResult(Observable<HashMap<String,User>> details){ DeferredResult<HashMap<String,User>> result = new DeferredResult<>(); // 订阅 details.subscribe(new Observer<HashMap<String,User>>(){ @Override public void onCompleted() { LOGGER.info("完成..."); } @Override public void onError(Throwable throwable) { LOGGER.error("发生错误...",throwable); } @Override public void onNext(HashMap<String,User> movieDetails) { result.setResult(movieDetails); } }); return result; } }
这样,代码就编写完了。当然,这里是用RxJava写的。也可以不用这种方式。只要实现调用多个微服务请求,然后将结果数据组织好返回出去就行。
测试1
启动项目microservice-discovery-eureka.
启动项目microservice-provider-user.
启动项目microservice-consumer-movie.
启动项目microservice-gateway-zuul-aggregation.
访问http://localhost:8040/aggregate/1,获得结果。
<HashMap> <movieUser> <id>1</id> <username>account1</username> <name>张三</name> <age>20</age> <balance>98.23</balance> </movieUser> <user> <id>1</id> <username>account1</username> <name>张三</name> <age>20</age> <balance>98.23</balance> </user> </HashMap>
说明已成功用Zuul聚合了用户微服务以及电影微服务的RESTful API.
测试2
1.在测试1基础上停止项目microservice-provider-user以及microservice-consumer-movie.
2.访问http://localhost:8040/aggregate/1
<HashMap> <movieUser> <id>-1</id> <username/> <name/> <age/> <balance/> </movieUser> <user> <id>-1</id> <username/> <name/> <age/> <balance/> </user> </HashMap>
我们看到,Zuul聚合微服务也实现了容错机制。
代码下载地址:https://gitee.com/fengyuduke/my_open_resources/blob/master/microservice-gateway-zuul-aggregation.zip