Zuul 是 Netflix 开源的微服务网关。自身还整合了 Ribbon、Hystrix 和 Actuator。
前言
- 本文中涉及到的 Spring Cloud 内容,可以查看我的相关博客
- 使用的 Zuul 版本为 2.0.0M8
- 服务端指 Eureka Server 所在微服务,客户端指提供数据的微服务,消费端指获取数据的微服务
1、写 Zuul 网关
创建新项目,导入 Zuul 和 Eureka 依赖
compile('org.springframework.cloud:spring-cloud-starter-netflix-eureka-client')
compile('org.springframework.cloud:spring-cloud-starter-netflix-zuul')
启动类添加以下注解
@EnableZuulProxy
写下配置文件 application.yml
server:
port: 8040
spring:
application:
name: Geteway_Zuul
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
酱,这个 Zuul 微服务网关就写好了。从配置可知,它可以注册到 Eureka Server 上。
启动服务端、客户端(服务名:Port),启动本 Zuul 网关,访问 http://localhost:8040/port/
(port 为服务的全小写拼写,不能有大写字母。其后为微服务的服务路径)。结果请自行测试。
默认 Zuul 会代理所有注册到 Eureka Server 上的微服务。路由规则为 http://ZUUL_HOST:ZUUL_PORT/
微服务 serviceId/**,具体如上所述。
2、负载均衡和 Hystrix 容错、监控
如开篇所说,Zuul 集成了 Ribbon 和 Hystrix 。可自行检测,此处跳过。
3、路由
据书上说,Zuul 会暴露管理端点 routes,博主测试没有。这不重要,跳过。
接下来,是路由的一些配置
我写的微服务名为 Port ,以下配置中 port 即为此服务名,特此提醒
i、指定微服务的访问路径,相当于给服务起了个昵称
zuul:
routes:
# serviceId: 路径
port: /p/** # 映射 port 微服务到路径 /p/
ii、另一种方式指定微服务访问路径
zuul:
routes:
user-route: # 这是路由名字,可以任意起
service-id: port # 这是服务名
port: /p/** # 映射 port 微服务到路径 /p/
iii、忽略微服务、路径以及指定微服务
zuul:
# 忽略 port 微服务
# ignored-services: port
ignored-services: '*' # 忽略所有微服务
ignored-patterns: /**/admin/** # 忽略所有包含 /admin/ 的路径
routes:
port: /p/** # 指定微服务
iv、正则表达式指定 Zuul 的路由配置规则
public PatternServiceRouteMapper serviceRouteMapper(){
return new PatternServiceRouteMapper("(?<name>^.+)-(?<version>v.+$)","${version}/${name}");
}
例如微服务名为 provider-v1 ,通过这段代码,可以映射到 /v1/provider/** 这个路径
v、前缀
- 示例 A
zuul:
prefix: /user # 前缀
strip-prefix: true
routes:
port:
path: /p/**
- 示例 B
zuul:
routes:
port: # <-- 服务名
path: /user/** # 此时 /user 就是前缀
strip-prifix: true
strip-prefix=false 时, /user/a/b/c —> /a/b/user/c
strip-prefix=true 时, /user/a/b/c —> /user/a/b/c,即原地址
vi、Zuul 的安全和 Header
- 敏感 Header 的设置
zuul:
routes:
port:
path: /user/**
senstive-headers: Cookie,Set-Cookie,Authorization
若 senstive-headers 在 zuul 配置子目录下,即为全局配置:
zuul:
senstive-headers: Cookie,Set-Cookie,Authorization # 这几项其实也是默认值
- 忽略Header
zuul:
ignored-headers: HeaderA,HeaderB
4、Zuul 过滤器
首先,来编写一个 Zuul 过滤器
public class PreRequestLogFilter extends ZuulFilter{
private static final Logger LOGGER= LoggerFactory.getLogger(PreRequestLogFilter.class);
@Override
public String filterType() {
//指定过滤器类型,有 pre、route、post、error 等几种取值
return "pre";
}
@Override
public int filterOrder() {
//指定过滤器执行顺序,数字越大,越早执行
return 0;
}
@Override
public boolean shouldFilter() {
//决定是否要执行此过滤器
return true;
}
@Override
public Object run() throws ZuulException {
RequestContext ctx=RequestContext.getCurrentContext();
HttpServletRequest request=ctx.getRequest();
PreRequestLogFilter.LOGGER.info(String.format("send %s request to %s",request.getMethod(),request.getRequestURL().toString()));
return null;
}
}
启动类中增加:
@Bean
public PreRequestLogFilter preRequestLogFilter(){
return new PreRequestLogFilter();
}
控制台输出类似以下信息即表示过滤器已经被执行了
[nio-8040-exec-3] c.i.zuul.filter.PreRequestLogFilter : send GET request to http://localhost:8040/user/p/user/1
如下配置可以禁用过滤器
zuul:
PreRequestLogFilter: # 类名
pre: # 类型
disable: true
5 、Zuul 容错和回退
Zuul 默认整合了 Hystrix,但和之前监控方法级别的粒度不同,Zuul 的 Hystrix 监控数据的粒度是微服务
所以回退方法也一定会有大不同,下面为 Zuul 添加回退
@Component
public class PortFallbackProvider implements FallbackProvider {
@Override
public String getRoute() {
// 指定为哪个微服务提供回退
return "port";
}
@Override
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
return new ClientHttpResponse() {
@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.OK;
}
@Override
public int getRawStatusCode() throws IOException {
return this.getStatusCode().value();
}
@Override
public String getStatusText() throws IOException {
return this.getStatusCode().getReasonPhrase();
}
@Override
public void close() {
}
@Override
public InputStream getBody() throws IOException {
return new ByteArrayInputStream("微服务不可用!".getBytes());
}
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers=new HttpHeaders();
MediaType mt=new MediaType("application","json", Charset.forName("UTF-8"));
headers.setContentType(mt);
return headers;
}
};
}
}
若 Port 无法响应。浏览器会接收并显示“微服务不可用”
6、Sidecar 整合非 JVM 微服务
使用的 Sidecar 版本为 2.0.0.M8
首先,写一个非 JVM 的微服务。如下是一个名为 node-service.js 的文件
var http=require('http');
var url=require('url');
var path=require('path');
// 创建 Server
var server=http.createServer(function(req,res){
// 获取路径
var pathname=url.parse(req.url).pathname;
res.writeHead(200,{'Content-Type':'application/json; charset=utf-8'});
// 首页
if(pathname==='/'){
res.end(JSON.stringify({'index':'Welcome to the welcome page'}));
}
// health页
else if(pathname==='/health.json'){
res.end(JSON.stringify({'status':'UP'}));
}
// 404页
else{
res.end('404')
}
});
// 监听并打印日志
server.listen(8060,function(){
console.log('listening on localhost:8060');
});
在 Node.js 命令行中进入文件所在目录,然后执行以下命令来执行这个服务
$ node node-service.js
分别访问 localhost:8060、localhost:8060/health.json 测试服务
然后,在 Zuul 项目中加入 Sidecar 依赖
compile('org.springframework.cloud:spring-cloud-netflix-sidecar')
启动类上添加以下注解
@EnableSidecar
// @EnableSidecar 整合了 @EnableCircuitBreaker 、 @EnableDiscoveryClient 、 @EnableZuulProxy
配置文件添加如下内容
server:
port: 8070
spring:
application:
name: Zuul_Sidecar
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka
instance:
prefer-ip-address: true
sidecar:
port: 8060 # Node.js 微服务的端口
health-uri: http://localhost:8060/health.json # Node.js 微服务的健康检查 URL
启动服务端、客户端、node-service,然后启动 Sidecar,访问http://localhost:8070/zuul_sidecar/
首页显示“{‘index’:’Welcome to the welcome page’}”,即表示已成功使用 Sidecar(摩托挎斗)带上了 node-service 服务
后记
以上的代码大多数经过我的测试
引用内容源自 《Spring Cloud与Docker微服务架构实战》/周立 著