Spring Cloud入门之Zuul
原文链接:http://www.dubby.cn/detail.html?id=9009
网关(Gateway)
首先需要考虑,为什么会有网关这个东西呢?他是个什么东西?
经过之前的学习,我们脑海中复现的整个Spring Cloud大概是有服务注册中心(Eureka Server),服务(Eureka Client,Ribbon,Feign,Hystrix等),其中服务之间互相调用,呈网状结构,大致如下图所示:
我们先考虑一下负载均衡这一层,一般这一层都会是一些硬件负载均衡或者软件负载均衡组成,常见的有F5、Nginx(或者Openresty,支持Lua扩展)等。这有一个缺点,就是路由需要手动配置,或者是写个扩展,提供一个管理后台来配置路由关系,其实本质是一样的。这个到后期往往比较难以维护(并不是不可维护,其实这种方式我所知道的很多公司都是这么做的)。
再考虑一个问题,如果你的系统需要用户登录才可以访问某些服务,那么用户的登录状态解析该由谁来负责呢?你的请求希望加密,这种逻辑该由谁来做呢?我们可以在每个服务里做,用户登录态解析,做请求加密。不过,缺点很明显,不统一以后逻辑改变,或者升级难度太大,需要每个服务来配合(这点笔者深有体会)。
为了解决上述问题,API网关应运而生。这是一个景点的Facade模式,是整个微服务系统的门面。他是怎么解决的这些问题的呢?
- 和Eureka结合,自动维护服务信息,减少手工配置
- 对于签名,安全校验等,可以使用Zuul提供的Filter机制实现
快速入门
搭建项目
前提是你已经在本地运行起来eureka-server,demo-service,ribbon-consumer或者feign-consumer。
名字就叫Zuul吧,依赖如下:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Brixton.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
在启动类上修饰:
@EnableZuulProxy
@SpringCloudApplication
public class Application {
public static void main(String[] args) {
new SpringApplicationBuilder(Application.class).web(true).run(args);
}
}
创建application.properties
:
spring.application.name=api-gateway
server.port=5555
zuul.routes.a.path=/a/**
zuul.routes.a.url=ribbon-consumer
zuul.routes.b.path=/b/**
zuul.routes.b.url=feign-consumer
eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/
然后启动。
面向服务的路由
你访问http:localhost:5555/a/hello的时候,这个URL符合/a/**的规则,所以被转发给ribbon-consumer,这个服务实例的信息会从eureka-server上获取,不需要自己配置服务信息。
通过这种方式,我们极大的减少配置信息,让维护变得简单。
请求过滤
@Component
public class AccessFilter extends ZuulFilter {
private static final Logger logger = LoggerFactory.getLogger(AccessFilter.class);
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return 0;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
logger.info("AccessFilter run");
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest httpServletRequest = requestContext.getRequest();
logger.info(httpServletRequest.getRequestURI());
Object accessToken = httpServletRequest.getParameter("accessToken");
if (accessToken == null) {
requestContext.setSendZuulResponse(false);
requestContext.setResponseStatusCode(401);
return null;
} else {
}
logger.info("access token ok");
return null;
}
}
简单解释一下上面的代码:
- filterType:过滤器的类型,pre代表请求在被路由之前执行
- filterOrder:过滤器的执行顺序,根据这个返回值来依次执行
- shouldFilter:判断是否需要被过滤
- run:过滤的具体逻辑,这里通过
requestContext.setSendZuulResponse(false);
来过滤请求,不对骑进行路由,通过requestContext.setResponseStatusCode(401);
设置返回状态码,也可以通过requestContext.setResponseBody(body);
来设置内容
这样的话,你需要访问http:localhost:5555/a/hello?token=abc才能正确返回,不然的话,只能得到一个401的错误码。
再谈服务路由
忽略服务
上文提过的,面向服务的路由可以这么配置:
zuul.routes.a.path=/a/**
zuul.routes.a.url=ribbon-consumer
zuul.routes.b.path=/b/**
zuul.routes.b.url=feign-consumer
但是,其实服务的serviceId,Zuul是可以通过eureka-server拿到的,服务信息也是可以拿到的,所以Zuul提供了另一种更为方便的路由规则,也就是path的前缀使用serviceId,假设serviceId为hello-service:
zuul.routes.hello-service.path=/hello-service/**
zuul.routes.hello-service.url=hello-service
对于这样的配置,其实就是Zuul默认的路由规则,也就是说我们根本不需要配置这一条,Zuul也可以帮我们实现。
这样会有个问题,有部分服务不希望对外开放,也就是不希望被路由到,那么我们可以选择忽略他,zuul.ignored-services=*
的意思是忽略所有的路由,也就是禁用了Zuul帮你自动路由,也可以配置serviceId,多个用逗号隔开。
服务路径匹配
上面用的一直都是zuul.routes.hello-service.path=/hello-service/**
这种形式的路径,那么这个代表什么意思呢?
通配符 | 说明 |
---|---|
? | 匹配任意单个字符 |
* | 匹配任意数量的字符 |
** | 匹配任意数量的字符,支持多及目录 |
忽略表达式
还可以设置一个被路由忽略的表达式,也就是不被路由,那就是zuul.ignored-patterns
。
如果你不希望/hello被路由,你可以这么设置:
zuul.ignored-patterns=/**/hello/**
这个配置是会对所有的路由生效的,所以不要忽略了不该忽略的URL。
推荐阅读
-
spring-cloud入门之eureka-server(服务发现)
-
spring-cloud入门之spring-cloud-config(配置中心)
-
spring-cloud入门之eureka-client(服务注册)
-
Spring Cloud Zuul的重试配置详解
-
SpringCloud之服务注册与发现Spring Cloud Eureka实例代码
-
SpringCloud之消息总线Spring Cloud Bus实例代码
-
spring cloud-zuul的Filter使用详解
-
Spring Cloud之服务监控turbine的示例
-
Spring Cloud zuul自定义统一异常处理实现方法
-
Spring Cloud Gateway入门解读