第7章 服务网关
服务网关和Zuul
为什么需要网关服务?

服务网关的要素:
- 稳定性、高可用
- 性能、并发性
- 安全性
- 扩展性
- (很多非业务功能在这里完成)
常用的网关方案:
- Nginx + Lua
- Kong:(商业软件)
- Tyk:开源,Go语言开发
- Spring Cloud Zuul:以java技术栈为主构建微服务 适合使用zuul 快速上手 但性能比Nginx差
Zuul的特点
Zuul的四种过滤器API
- 前置(Pre)
- 路由(Route)
- 后置(Post)
- 错误(Error)
fillter之间通过RequestContext进行交互

zuul http请求生命周期

pre:参数校验..
routing:转发,重写http请求…
post:对结果进行加工
error:发生异常时会到达,做统一异常处理
customer:自定义过滤器
Zuul:路由转发&自定义&排除
0、新建项目:api-gateway
pom
1 2 3
| config-client eureka-discovery zuul
|
1、路由转发
启动类:@EnableZuulProxy
通过访问gateway,路由到product/list
1
| http://localhost:9000/product/product/list 项目名/路径
|
2、自定义路由
1 2 3
| zuul: routes: config: /myconfig/**
|
访问:
1 2 3
| 都可以 http://localhost:9000/myconfig/order-dev.yml http://localhost:9000/config/order-dev.yml
|
3、禁止路由
1 2 3
| zuul: ignored-patterns: - /**/product/list
|
Zuul:Cookie&动态路由
1、Cookie
使用zuul后,默认Cookie是不传递的,配置使Cookie不被过滤掉
1 2 3 4 5 6 7
| zuul: routes: config: path: /myconfig/** serviceId: config sensitiveHeaders:
|
2、动态路由
改路由配置后动态生效,不用重启gateway
1 2 3
| 思路 1、只需要使用统一配置中心,把zuul节点的配置放在git端就可以动态更新yml了 2、yml动态更新了,配置类也需要动态注入,使用@RefreshScope(一旦yml变化了,新配置就重新注入类)
|
在启动类添加:
1 2 3 4 5
| @ConfigurationProperties("zuul") @RefreshScope public ZuulProperties zuulProperties(){ return new ZuulProperties(); }
|
Zuul:路由和高可用小结
典型应用场景
pre:限流、鉴权、参数校验、请求转发…
post:统计、日志
Zuul高可用
- 多个节点注册到Eureka Server 实现高可用
- 内部:a调用b 可以变成 a > zuul服务 > b
- 外部:Nginx+Zuul “混搭”,取长补短
下章节探讨zuul过滤器相关
第8章 Zuul综合使用
Zuul:Pre和Post过滤器
客户端 > Nginx(负载均衡) > Zuul > …
1、Pre过滤器
应用场景:统一在Zuul做权限校验
场景细节:希望所有经过zuul的请求 带 token参数 且 不为null(实际业务中需要结合数据库进行更多校验);如果不带参,则校验不通过,返回401(权限不足)。
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
| package com.mxx.apigateway.filter;
import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import com.netflix.zuul.exception.ZuulException; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import javax.servlet.http.HttpServletRequest; import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_DECORATION_FILTER_ORDER; import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;
@Component public class TokenFilter extends ZuulFilter{
@Override public String filterType() { return PRE_TYPE; }
@Override public int filterOrder() { return PRE_DECORATION_FILTER_ORDER - 1; }
@Override public boolean shouldFilter() { return true; }
@Override public Object run() throws ZuulException { RequestContext requestContext = RequestContext.getCurrentContext(); HttpServletRequest request = requestContext.getRequest(); String token = request.getParameter("token");
if(StringUtils.isEmpty(token)){ requestContext.setSendZuulResponse(false); requestContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value()); } return null; } }
|
测试
1 2 3
| http://localhost:9000/myconfig/order-dev.yml?token=123 # 可以 http://localhost:9000/myconfig/order-dev.yml?token= # 不行 http://localhost:9000/myconfig/order-dev.yml # 不行
|
2、Post过滤器
应用场景:统一请求结束后做处理
场景细节:加个header参数
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
| package com.mxx.apigateway.filter;
import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import com.netflix.zuul.exception.ZuulException; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletResponse; import java.util.UUID; import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.POST_TYPE; import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.SEND_RESPONSE_FILTER_ORDER;
@Component public class AddResponseHeaderFilter extends ZuulFilter{ @Override public String filterType() { return POST_TYPE; }
@Override public int filterOrder() { return SEND_RESPONSE_FILTER_ORDER -1; }
@Override public boolean shouldFilter() { return true; }
@Override public Object run() throws ZuulException { RequestContext requestContext = RequestContext.getCurrentContext(); HttpServletResponse response = requestContext.getResponse(); response.setHeader("X-Foo", UUID.randomUUID().toString());
return null; } }
|
测试:浏览器控制台 > …
Zuul:限流
1、限流
Zuul:鉴权&添加用户服务
业务需求:
- order/create 只能买家访问
- order/finish 只能卖家访问
- product/list 都可以访问
所以过滤器要区分买家/卖家…如何区分?… header cokkie …先完成登录才会看到信息 … 先把用户服务建起来
1、添加用户服务
1 2 3 4
| 数据库 新建项目:user 业务:模拟买家卖家登录功能实现 业务:完结订单接口开发
|
2、权限校验
1 2 3 4
| 业务:完成权限校验 优化场景:硬编码到filer不适合 权限特别多/经常变动的场合 优化方案:分开写买家卖家的权限类,在是否需要拦截的方法里判断用户类型,决定是否放行,以后有新角色只需要加类就行 注意:api-gateway不要连数据库或者直接api调用服务,建议都利用redis做中间
|
方案:
Zuul:跨域
具体代码: