springcloud使用zuul聚合微服务

前言

1.springcloud编写用户微服务
2.springcloud编写电影微服务
3.springcloud集成网关ZUUL
依上面教程,我已经实现了用户,电影微服务以及zuul网关,微服务的设计难点之一在于对原有业务的拆分,
在我看来每个微服务职责要尽可能单一,但是这样同样也带来了一个问题,那就是微服务之间不可避免的一
些交集.例如终端需要查询用户信息和电影信息,这里有两种做法
1.让终端查询用户信息后在查询电影信息
2.网关层查询用户信息和电影信息,聚合后返回给终端
后一种方式显然更好一些,因为他节省了带宽,相较于终端两次请求网关,显然网关两次请求微服务的网络情况更好

目的

利用RXJAVA聚合微服务,这里面其实很多东西可以讨论,关于分布式协议和分布式事务,这次先简单的说明
一下查询聚合,因为查询是幂等操作,不需要事务

正文

工程初始化

可以参见这篇博客**springcloud集成网关ZUUL**
验证:
http://localhost:1051/api/user/user/getAll
http://localhost:1051/api/movie/movie/findOneById?id=2

集成feign,添加用户和电影的消费端

可以参见这篇博客**springcloud集成feign**
仿照user创建movie的feign客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@FeignClient(name = "consul-movie")
public interface MovieFeignClient {

@RequestMapping(value = "/movie/findOneById",method = RequestMethod.GET)
Movie findOneById(@RequestParam("id") Long id);
}

错误的写法
@FeignClient(name = "consul-movie")
public interface MovieFeignClient {

@RequestMapping(value = "/movie/findOneById",method = RequestMethod.GET)
Movie findOneById(Long id);
}

get多参数写法

1
2
直接写Long id或者直接是User user这种对象,feign依然会用post方式调用,所以会报错接口不支持
需要用@RequestParam("id")注解,或者@RequestParam Map<String,Object> map

扩展
20181031更新:zuul上的service中添加hystrix回退并没有执行,如下图,想着service中利用的是
feign实现的,所以给feign加上回退,需要在配置上加上feign.hystrix.enabled=true

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
@FeignClient(name = "consul-movie",fallback = MovieFeignClient.MovieFeignClientFallBack.class)
public interface MovieFeignClient {

@RequestMapping(value = "/movie/findOneById",method = RequestMethod.GET)
Movie findOneById(@RequestParam("id") Long id);

@Component
class MovieFeignClientFallBack implements MovieFeignClient{

@Override
public Movie findOneById(Long id) {
Movie movie = new Movie();
movie.setId(-1L);
return movie;
}
}
}
//加上日志的版本
@FeignClient(name = "consul-movie",fallbackFactory = MovieFeignClient.MovieFeignClientFallBackFactory.class)
public interface MovieFeignClient {

@RequestMapping(value = "/movie/findOneById",method = RequestMethod.GET)
Movie findOneById(@RequestParam("id") Long id);

@Component
class MovieFeignClientFallBackFactory implements FallbackFactory<MovieFeignClient>{

private static final Logger LOGGER = LoggerFactory.getLogger(MovieFeignClientFallBackFactory.class);
@Override
public MovieFeignClient create(Throwable throwable) {
return new MovieFeignClient() {
@Override
public Movie findOneById(Long id) {
LOGGER.error("MovieFeignClient findOneById fallback;reason was:[{}]",throwable);
Movie movie = new Movie();
movie.setId(-1L);
return movie;
}
};
}
}
}

集成ribbon

添加一个配置类java

1
2
3
4
5
6
7
8
9
@Component
public class RibbonConf {

@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}

添加ribbon调用服务类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@Service
public class MovieRibbonService {

@Autowired
private RestTemplate restTemplate;

@HystrixCommand(fallbackMethod = "fallback")
public Observable<Movie> findOneById(Long id) {
return Observable.create(observer -> {
Movie movie = restTemplate.getForObject("http://consul-movie/movie/findOneById", Movie.class, id);
observer.onNext(movie);
observer.onComplete();
});
}

public Observable<Movie> fallback(Long id) {
return Observable.create(observer -> {
Movie movie = new Movie();
movie.setId(-1L);
observer.onNext(movie);
observer.onComplete();
});
}

@HystrixCommand(fallbackMethod = "fallback1")
public Movie findOneById1(Long id) {
return restTemplate.getForObject("http://consul-movie/movie/findOneById", Movie.class, id);
}

public Movie fallback1(Long id,Throwable throwable) {
System.out.println("consul-movie /movie/findOneById 报错:"+throwable);
Movie movie = new Movie();
movie.setId(-1L);
return movie;
}
}

新建聚合controller和服务(RXjava)

针对feign实现的service,之前在上面截图中可以发现我在上面加了HystrixCommand没有用,所以去掉了,不知道是不是因为里面是feign实现的,所以下面会有ribbon实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/**
* creambing.com Inc.
* Copyright (c) 2016-2017 All Rights Reserved.
*/


package com.springcloud.consulzuulum.service.rxjava;

import com.springcloud.consulzuulum.feign.movie.Movie;
import com.springcloud.consulzuulum.feign.movie.MovieFeignClient;
import io.reactivex.Observable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
* Class Name:MovieService
* Description:movie rxjava 服务类
*
* @author Bing
* @version v1.0
* @create 2018-10-30 22:01
*/
@Service
public class MovieService {

@Autowired
MovieFeignClient movieFeignClient;

public Observable<Movie> findOneById(Long id) {
return Observable.create(observer -> {
Movie movie = movieFeignClient.findOneById(id);
observer.onNext(movie);
observer.onComplete();
});
}
}

控制类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
/**
* creambing.com Inc.
* Copyright (c) 2016-2017 All Rights Reserved.
*/


package com.springcloud.consulzuulum.controller;

import com.google.common.collect.Maps;
import com.springcloud.consulzuulum.feign.movie.Movie;
import com.springcloud.consulzuulum.feign.movie.MovieFeignClient;
import com.springcloud.consulzuulum.feign.user.User;
import com.springcloud.consulzuulum.feign.user.UserFeignClient;
import com.springcloud.consulzuulum.service.rxjava.MovieService;
import com.springcloud.consulzuulum.service.rxjava.UserService;
import io.reactivex.Observable;
import io.reactivex.Observer;
import io.reactivex.disposables.Disposable;
import org.springframework.beans.factory.annotation.Autowired;
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.context.request.async.DeferredResult;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* Class Name:UserAndMovieController
* Description:用户和电影聚合控制类
*
* @author Bing
* @version v1.0
* @create 2018-10-30 17:11
*/
@RestController
@RequestMapping("/um")
public class UserAndMovieController {

@Autowired
UserFeignClient userFeignClient;

@Autowired
MovieFeignClient movieFeignClient;

@Autowired
UserService userService;

@Autowired
MovieService movieService;

@RequestMapping(value = "/getUserAndMovie", method = RequestMethod.GET)
Map<String, Object> getUserAndMovie(Long id) {
Long startTime = System.currentTimeMillis();
Map<String, Object> result = new HashMap<>();
//同步调用
List<User> users = userFeignClient.getAllUser();
Movie movie = movieFeignClient.findOneById(id);
result.put("users", users);
result.put("movie", movie);
Long endTime = System.currentTimeMillis();
System.out.println("getUserAndMovie同步调用花费时间:"+(endTime-startTime));
return result;
}

@RequestMapping(value = "/getUserAndMovieUseRx", method = RequestMethod.GET)
DeferredResult<HashMap<String,Object>> getUserAndMovieUseRx(Long id) {
Long startTime = System.currentTimeMillis();
//rx异步调用
Observable<HashMap<String,Object>> observable = aggregateObservable(id);
DeferredResult<HashMap<String,Object>> result = toDefer(observable);
Long endTime = System.currentTimeMillis();
System.out.println("getUserAndMovieUseRx异步调用花费时间:"+(endTime-startTime));
return result;
}

public Observable<HashMap<String,Object>> aggregateObservable(Long id){
return Observable.zip(
userService.getAllUsers(),
movieService.findOneById(id),
(users,movie) -> {
HashMap<String,Object> map = Maps.newHashMap();
map.put("users",users);
map.put("movie",movie);
return map;
}
);
}

public DeferredResult<HashMap<String,Object>> toDefer(Observable<HashMap<String,Object>> details){
DeferredResult<HashMap<String, Object>> result = new DeferredResult<>();
details.subscribe(new Observer<HashMap<String, Object>>() {
@Override
public void onSubscribe(Disposable disposable) {
System.out.println("");
}

@Override
public void onNext(HashMap<String, Object> stringObjectHashMap) {
result.setResult(stringObjectHashMap);
}

@Override
public void onError(Throwable throwable) {
System.out.println("发生错误:"+throwable);
}

@Override
public void onComplete() {
System.out.println("完成");
}
});
return result;
}

}

访问
feign http://localhost:1051/um/getUserAndMovieUseRx?id=1 hystrix有效
rxjava+ribbon http://localhost:1051/um/getUserAndMovieUseRibbon?id=1 hystrix无效
ribbon http://localhost:1051/um/getAllUserRibbon hystrix有效
ribbon http://localhost:1051/um/getUserAndMovieUseRibbon1?id=1 hystrix有效

Cream Bing wechat
subscribe to my blog by scanning my public wechat account