相关文章:
Spring cloud - 概述
Spring cloud eureka - 注册中心
Spring cloud feign - RPC
Spring cloud ribbon - 负载均衡
Spring cloud hystrix - 断路器
Spring cloud hystrix dashboard - 断路器dashboard
Spring cloud zuul- 网关
关键字
路由、网关、负载
背景
服务注册发现巧秒的实现了各个微服务之间的松耦合调用,从而不考虑服务提供方实例节点的动态变化(增加或减少)。特别是配合配置中心、负载均衡、断路器等功能,完美的实现了微服务架构。所有这些微服务都是在一个安全的局域网内裸奔,不过也并无大碍,但是整个服务体系最终会面向客户,不可能完全闭关锁国自己嗨,一旦提供的服务需要暴露给外部使用就会带来一连串的问题:如:安全、路由、使用的复杂度、性能、新老业务的对接等。
什么是zuul
zuul是由netflix提供的用于内外网隔离和负载均衡以及路由的轻量级网关。
下图为netflix官网的使用方式
zuul作用
根据spring cloud官网解释,zuul具有以下功能:
- 认证
- 监控
- 压力测试
- 金丝雀测试
- 动态路由
- 服务迁移
- 负载剪裁
- 安全
- 静态资源处理
- 动态流量管理
集成
maven依赖
<!--eureka client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<!--actuator 用于打开默认的endpoints-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--zuul-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId> spring-cloud-starter-zuul</artifactId>
</dependency>
代码
main方法启动类
@SpringBootApplication
@EnableEurekaClient//eureka client
@EnableZuulProxy //zuul服务器
public class ZuulApp
{
public static void main( String[] args )
{
//启动项目
SpringApplication.run(ZuulApp.class,args);
}
}
配置
#应用名称
spring:
application:
name: zuul
#端口
server:
port: 9393
#管理接口配置
management:
context-path: /admin
port: ${server.port}
#eureka client配置:支持从eureka获取服务自动路由
eureka:
instance:
preferIpAddress: true
statusPageUrlPath: ${management.context-path}/info
healthCheckUrlPath: ${management.context-path}/health
instance-id: ${spring.cloud.client.ipAddress}:${server.port}
homePageUrl: https://${spring.cloud.client.ipAddress}:${server.port}
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
#自定义配置
zuul:
routes:
#服务
baidu:
#context-path配置:如http://api.xxx.com/baidu/user/1中的/baidu
path: /baidu/**
#context-path对应的实际提供方的服务地址
url: http://www.baidu.com
测试
浏览器中访问 http://localhost:9393/baidu , 正常情况下会跳转到baidu.com,即实现的简单的路由
集成服务注册发现
上面的例子简单演示了手功配置服务与服务节点的路由映射方式。实际业务中,服务会随着业务的变化越来越多,手工维护太多的映射会变得很困难也容易出错。服务注册发现是一个自动维护服务与各节点映射的中央服务器,如果服务提供方是微服务,就可完全重用服务注册发现的映射,从而实现路由配置的全自动化。
事实上zuul项目中,只需要加入eureka client相关依赖和简单配置(注册中心地址等)默认自动支持eureka路由映射。
上一节已经把eurka相关依赖和配置引入了项目,因此本节不需要任何修改,即支持eureka.
测试
假如注册到eureka中的微服务名为:stores,节点有多个; 接口:/product 返回内容中包含当前节点的ip, 那么在浏览器中访问:http://localhost:9393/stores/product ,不停刷新返回的ip会不断变化。
原理
以下图片为官网原理图
zuul通过大量的filter对请求进行安全、认证、路由进行控制。
PRE Filters 是在把请求路由到目标节点前执行。如:认证、加载目标服务节点、打印日志。
ROUTING Filters 把请求路由到目标服务的节点. 到目标的请求就在这些filter中被创建,并通过Apache HttpClient 或 Netflix Ribbon转发到目标节点。
POST Filters 是目标节点请示结束并返回到zuul后执行。可以把HTTP headers添加到返回给客户端的response中,并可以收集统计信息和健康信息,以及把目标节点的业务数据返回给客户。
ERROR Filters 任何一个步骤出错都会调用当前类型的filter.
ZuulServletFilter 核心代码如下:
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
try {
init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);
try {
preRouting();
} catch (ZuulException e) {
error(e);
postRouting();
return;
}
filterChain.doFilter(servletRequest, servletResponse);
try {
routing();
} catch (ZuulException e) {
error(e);
postRouting();
return;
}
try {
postRouting();
} catch (ZuulException e) {
error(e);
return;
}
} catch (Throwable e) {
error(new ZuulException(e, 500, "UNCAUGHT_EXCEPTION_FROM_FILTER_" + e.getClass().getName()));
} finally {
RequestContext.getCurrentContext().unset();
}
}
zuul通过RequestContext(theadLocal)保存了当前request/response,并在各filter中进行传递。
各种类型在spring cloud的实现,具体请查看地:spring-cloud-netflix-core-xxx.jar下org.springframework.cloud.netflix.zuul.filters包中的类。
自定义filter
zuul可以实现很多功能,但部分功能并没实现,有时为了达到定制化的目的,可以行实现对应的filter
原理:实现ZuulFilter接口;
代码:
@Component //注册为普通的bean
public class MyPreFilter extends ZuulFilter {
private static final Logger LOGGER = LoggerFactory.getLogger(MyPreFilter.class);
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return 1;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
LOGGER.debug("执行了MyPreFilter");
return null;
}
}
filterType:返回过滤器的类型。有pre、route、post、error类型。详细参考com.netflix.zuul.ZuulFilter.filterType() 。
filterOrder:返回一个int值来指定过滤器的执行顺序,不同的过滤器允许返回相同的数字。
shouldFilter:返回一个boolean值来判断该过滤器是否要执行,true表示执行,false表示不执行。
run:过滤器的具体逻辑。
问题
性能和高可用
做为zuul网关,所有请求如果都经过zuul,那么很可能网关会成为整个系统的瓶颈,目前为了提高性能和高可用主是通过横向扩展机器节点以及断路器实现。