京东资深工程师分享:微服务架构之网关层Zuul剖析( 二 )

过滤器对应DB的字段如下filter_id,revision,create_time,is_active,is_canary,filter_code,filter_type,filter_name,disable_property_name,filter_order,Application_name
我们再回到主流程看ZuulServlet,每当一个客户请求一个HttpServlet对象,该对象的service()方法就要被调用,而且传递给这个方法一个”请求”(ServletRequest)对象和一个”响应”(ServletResponse)对象作为参数
public class ZuulServlet extends HttpServlet {private ZuulRunner zuulRunner = new ZuulRunner();@Overridepublic void service(JAVAx.servlet.ServletRequest req, javax.servlet.ServletResponse res) throws javax.servlet.ServletException, java.io.IOException { try { init((HttpServletRequest) req, (HttpServletResponse) res);RequestContext.getCurrentContext().setZuulEngineRan(); try { preRoute(); } catch (ZuulException e) { error(e); postRoute(); return; } try { route(); } catch (ZuulException e) { error(e); postRoute(); return; } try { postRoute(); } catch (ZuulException e) { error(e); return; } } catch (Throwable e) { } finally { RequestContext.getCurrentContext().unset(); }}运行时主要从filterRegistry根据type取出过滤器依次执行
六、Zuul2.x版本解读
Zuul2.x的核心功能特性
服务器协议
HTTP/2——完整的入站(inbound)HTTP/2连接服务器支持
双向TLS(Mutual TLS)——支持在更安全的场景下运行
弹性特性
自适应重试——Netflix用于增强弹性和可用性的核心重试逻辑
源并发保护——可配置的并发限制 , 避免源过载 , 隔离Zuul背后的各个源
运营特性
请求Passport——跟踪每个请求的所有生命周期事件 , 这对调试异步请求非常有用
状态分类——请求成功和失败的可能状态枚举 , 比HTTP状态码更精细
请求尝试——跟踪每个代理的尝试和状态 , 对调试重试和路由特别有用
实际上Zuul2.x是将ZuulFilter变换成Netty Handler,在Netty中 , 一系列的Handler会聚合在一起并使用Pipline执行 , 拿Netty的Sample来说明下
//EventLoopGroup线程组 , 包含一组NIO线程 //bossGroupworkerGroup,一个用于连接管理 , 另外一个进行SocketChannel的网络读写 EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); ServerBootstrap bootstrap = new ServerBootstrap();bootstrap.group(bossGroup,workerGroup).channel(NIOServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(10240, 0, 2, 0, 2)).addLast(new StringDecoder(UTF_8)).addLast(new LengthFieldPrepender(2)).addLast(new StringEncoder(UTF_8)).addLast(new ServerHandler());}}).childOption(ChannelOption.TCP_NODELAY, true);ChannelFuture future = bootstrap.bind(18080).sync();在Zuul2.x中默认注册了这些Handler
@Overrideprotected void initChannel(Channel ch) throws Exception{ // Configure our pipeline of ChannelHandlerS. ChannelPipeline pipeline = ch.pipeline(); storeChannel(ch); addTimeoutHandlers(pipeline); addPassportHandler(pipeline); addTcpRelatedHandlers(pipeline); addHttp1Handlers(pipeline); addHttpRelatedHandlers(pipeline); addZuulHandlers(pipeline);}我们在上面的pipeline中注册了一个ServerHandler,这个handler就是用来处理Client端实际发送的数据的
public class ServerHandler extends SimpleChannelInboundHandler<String> { @Override public void channelRead0(ChannelHandlerContext ctx, String message) throws Exception {System.out.println("from client:" + message);JSONObject json = JSONObject.fromObject(message);String source = json.getString("source");String md5 = DigestUtils.md5Hex(source);json.put("md5Hex",md5);ctx.writeAndFlush(json.toString());//write bytes to socket,and flush(clear) the buffer cache. } @Override public void channelReadComplete(ChannelHandlerContext ctx) {ctx.flush(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {cause.printStackTrace();ctx.close(); } }Zuul2.x相比1.x最大的变化就是异步化 , 最大的功臣莫过于Netty,上面涉及到的很重要的就是ChannelPipleline和ChannelFuture
ChannelPipleline实际上是一个双向链表 , 提供了
addBeforeaddAfteraddFirstaddLastremove等方法 , 链表操作会影响Handler的调用关系 。ChannelFuture是为了解决如何获取异步结果的问题而声音设计的接口 , 有未完成和完成这两种状态 , 不过通过CannelFuture的get()方法获取结果可能导致线程长时间被阻塞 , 一般使用非阻塞的GenericFutureListener
@Override public void channelRead(ChannelHandlerContext ctx, Object msg) { ChannelFuture future = ctx.channel().close(); future.addListener(new ChannelFutureListener() { public void operationComplete(ChannelFuture future) { } }); }


推荐阅读