1.微服务网关简介
在微服务环境下,不同的服务有其不同的网络地址,若让客户端直接与各个微服务通信,客户端会多次请求不同微服务,存在跨域请求,处理相对复杂。此时我们就需要使用微服务网关。微服务网关介于服务端与客户端的中间层,所有外部服务请求都会先经过微服务网关,客户只能跟微服务网关进行交互,无需调用特定微服务接口,使得开发得到简化。
2.Zuul简介
Zuul 是开源的微服务网关,可与 Eureka、Ribbon、Hystrix 等组件配合使用,Zuul 它的核心是一系列过滤器,这些过滤器可完成下面功能:
- 身份认证与安全:识别每个资源的验证要求,并拒绝那些要求不符合的请求
- 审核与监控:在边缘位置追踪有意义的数据和统计结果,从而带来精确的生产视图
- 动态路由:动态的将请求路由到不同的后端集群
- 压力测试:逐渐增加指向集群的流量,以了解性能
- 负载分配:为每一种负载类型分配对应容量,并弃用超出限定值的请求
- 静态响应处理:在边缘位置直接建立部分响应,从而避免转发到内部集群
- 多区域弹性:跨越 AWS Region 进行请求路由,实现 ELB 使用多样化,让系统边缘更贴近使用者
3.配置
Maven
引入spring-cloud-starter-netflix-eureka-client
,spring-cloud-starter-netflix-zuul
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
添加注解
@SpringBootApplication
@EnableZuulProxy
public class ApiGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(ApiGatewayApplication.class, args);
}
}
最后记得将他注册到注册中心上,就可以直接运行了,是不是非常简单呢?
4.详细配置
- 在配置文件中加入
management.endpoints.web.exposure.include=*
可以通过访问actuator/routes
来查看路由情况。 - 配置需要路由的路径和忽略的路径。配置路由格式为【 服务名称:路径 】,假如要配置product服务:
routes:
product: /myProduct/**
ignored-patterns:
- /**/product/**
- 跨域
假如服务中单个接口需要跨域,只要在方法上添加@CrossOrigin(allowCredentials = "true")
注解。
假如需要对多个接口做跨域,可以在Zuul中做配置。
/**
* 跨域配置
*
* @author BaoZhou
* @date 2018/9/13
*/
@Configuration
public class CorsConfig {
@Bean
public CorsFilter corsFilter() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
final CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
//可以写域名,比如http://www.baidu.com
config.setAllowedOrigins(Arrays.asList("*"));
config.setAllowedHeaders(Arrays.asList("*"));
config.setAllowedMethods(Arrays.asList("GET","POST"));
config.setMaxAge(300L);
source.registerCorsConfiguration("/**", config);
return new CorsFilter();
}
}
- 默认经过路由不转发cookie等信息,我们可以在配置文件中加sensitiveHeaders来开启
zuul:
routes:
product: /myProduct/**
ignored-patterns:
- /**/product/**
sensitiveHeaders:
- 配合SpringBus动态配置Zuul。需要添加配置项
@ConfigurationProperties("zuul")
@RefreshScope
public ZuulProperties zuulProperties() {
return new ZuulProperties();
}
Zuul有四种过滤器:
- PRE:在请求被路由之前调用,可用于身份验证、集群中选择请求的微服务、记录调试信息等,可以用于限流,鉴权,参数校验调整,请求转发。
- ROUTING:请求路由到微服务时执行,用于构建发送给微服务的请求,使用 HttpClient 或 Ribbon 请求微服务。
- POST:在路由到微服务后执行,可用来为响应添加 Header,收集统计信息和指标、将相应从微服务发送给客户端等,可以用于统计,日志等。
- ERROR:在其他阶段发生错误时执行该过滤器。
限流 令牌桶
public class RateLimitFilter extends ZuulFilter {
private static final RateLimiter RATE_LIMITER =RateLimiter.create(100);
@Override
public String filterType() {
return PRE_TYPE;
}
@Override
public int filterOrder() {
return SERVLET_DETECTION_FILTER_ORDER - 1;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
if(!RATE_LIMITER.tryAcquire())
{
throw new RateLimitException();
}
return null;
}
}
添加请求头
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;
}
}
校验请求参数
public class TokenFilter extends ZuulFilter {
/**
* 指定过滤器类型
* 可以从常量中去选取
* public static final String ERROR_TYPE = "error";
* public static final String POST_TYPE = "post";
* public static final String PRE_TYPE = "pre";
* public static final String ROUTE_TYPE = "route";
*
* @return
*/
@Override
public String filterType() {
return PRE_TYPE;
}
/**
* 设置过滤器优先级,数字越小优先级越高
* public static final int DEBUG_FILTER_ORDER = 1;
* public static final int FORM_BODY_WRAPPER_FILTER_ORDER = -1;
* public static final int PRE_DECORATION_FILTER_ORDER = 5;
* public static final int RIBBON_ROUTING_FILTER_ORDER = 10;
* public static final int SEND_ERROR_FILTER_ORDER = 0;
* public static final int SEND_FORWARD_FILTER_ORDER = 500;
* public static final int SEND_RESPONSE_FILTER_ORDER = 1000;
* public static final int SIMPLE_HOST_ROUTING_FILTER_ORDER = 100;
* public static final int SERVLET_30_WRAPPER_FILTER_ORDER = -2;
* public static final int SERVLET_DETECTION_FILTER_ORDER = -3;
*
* @return
*/
@Override
public int filterOrder() {
return PRE_DECORATION_FILTER_ORDER - 1;
}
@Override
public boolean shouldFilter() {
return true;
}
/**
* 处理逻辑
* @return
* @throws ZuulException
*/
@Override
public Object run() throws ZuulException {
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
//从Url参数里获取token,也可以从cookie,header里获取
String token = request.getParameter("token");
if(StringUtils.isEmpty(token))
{
requestContext.setSendZuulResponse(false);
requestContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
}
return null;
}
}
一般在run里写逻辑,shouldFilter里写是否需要过滤来保证逻辑的清晰。
文中很多内容引用自https://www.jianshu.com/p/29e9c91e3f3e,特别感谢!
网友评论