前言
以下类容来自书籍《Spring Cloud与Docker微服务架构实战(第2版)》
简介
上图中,一个功能客户端可能需要调用好几次下级服务,每个下级服务需要独立的认证,当某个服务部署在内网时,访问也较为困难。
微服务网关是介于客户端和服务端之间的中间层,所有外部请求会先经过网关。这样各服务提供者在后期因需求变动时,客户端无需关心,简化了开发。监控和认证也可在网关中进行。
NetFlix的服务网关是Zuul,可以和Eureka、Ribbon、Hystrix配合使用。Zuul的过滤器可完成一下功能:身份认证与安全、监控、动态路由、压力测试、负载均衡、静态响应处理(边缘位置建立部分响应,从而避免其转发到内部集群。)、多区域弹性(不懂)。
Zuul网关搭建
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
server:
port: 8040
spring:
application:
name: microservice-gateway-zuul
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true
management:
security:
enabled: false
@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
}
将网关服务注册到Eureka上,启动Eureka、服务提供者、服务消费者、Zuul网关服务。
此时访问http://localhost:8040/microservice-consumer-movie/user/1,服务网关会根据serviceId将请求转发到对应的服务,转发到http://localhost:8081/user/1。当然,在转发的同时也支持负载均衡。
Zuul的过滤器
过滤器是是Zuul的核心组件,大部分功能就是通过过滤器来实现的。
过滤器类型
- PRE:请求被路由之前被调用,可实现身份验证,集群情况下选择微服务,记录调试信息。
- ROUTING:将请求路由到微服务,用于构建发送给微服务的请求,并使用Apache HttpClient或Netflix Ribbon请求微服务。
- POST:在路由到微服务以后执行,用来响应添加标准的HTTP Header、收集统计信息和指标,将响应从微服务发送给客户端。
- Error:在其他阶段发生错误时执行
编写过滤器
PRE过滤器
public class PreRequestLogFilter extends ZuulFilter {
private static final Logger LOGGER = LoggerFactory.getLogger(PreRequestLogFilter.class);
@Override
public String filterType() {
//PRE类型的过滤器
return FilterConstants.PRE_TYPE;
}
@Override
public int filterOrder() {
//在org.springframework.cloud.netflix.zuul.filters.pre.PreDecorationFilter之前执行
return FilterConstants.PRE_DECORATION_FILTER_ORDER - 1;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
PreRequestLogFilter.LOGGER.info(String.format("send %s request to %s ctx.getResponseBody() %s", request.getMethod(), request.getRequestURL().toString(),ctx.getResponseBody()));
return null;
}
}
@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
@Bean
public PreRequestLogFilter preRequestLogFilter() {
return new PreRequestLogFilter();
}
}
访问http://localhost:8040/microservice-provider-user/1,可看到PRE过滤器生效
POST过滤器
public class ResponseFilter extends ZuulFilter {
private static final Logger LOGGER = LoggerFactory.getLogger(ResponseFilter.class);
@Override
public Object run() {
RequestContext context = RequestContext.getCurrentContext();
try {
// 获取返回值内容,加以处理
InputStream stream = context.getResponseDataStream();
String body = StreamUtils.copyToString(stream, Charset.forName("UTF-8"));
ResponseFilter.LOGGER.info(String.format("ResponsebBody %s", body));
String returnStr = "哎呀,我被改掉了!";
//你的处理逻辑,加密,添加新的返回值等等.....
// 内容重新写入
context.setResponseBody(returnStr);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
public boolean shouldFilter() {
RequestContext ctx = RequestContext.getCurrentContext();
String requestURI = String.valueOf(ctx.get("requestURI"));
if (requestURI.contains("test")) {
//不需要处理的URL请求,返回false
return false;
}
return true;
}
@Override
public int filterOrder() {
return FilterConstants.SEND_RESPONSE_FILTER_ORDER - 3;
}
@Override
public String filterType() {
return FilterConstants.POST_TYPE;// 在请求被处理之后,会进入该过滤器
}
}
@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
@Bean
public PreRequestLogFilter preRequestLogFilter() {
return new PreRequestLogFilter();
}
@Bean
public ResponseFilter responseFilter() {
return new ResponseFilter();
}
}
请求后可看到,我们可以获取并改变返回值