routing zuul_一、zuul如何路由到上游服务器

所有文章

正文

zuul在分布式项目中充当着一个网关的角色,而它最主要的功能像nginx一样针对上游服务器做反向代理。我们可以将它理解为一个服务的门面,作为客户端来说不需要再面向各式各样的服务,只需要面向zuul即可,简化了客户端与服务端的交互关系。

既然,zuul成为了客户端与服务端的中间层,那么zuul显然可以进行拦截、记录、安全管理、路由...等等各种处理。本文,将从路由这个点切入,看看路由的过程。

ZuulServlet

首先,客户端和服务端的交互显然少不了的http,所以先找到zuul针对Servlet的实现

可以看到,ZuulServlet直接继承了HttpServlet。所以,ZuulServlet依然走的是http通信协议,我们跟进ZuulServlet的service方法。

@Overridepublic void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throwsServletException, IOException {try{

init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);//初始化一个上下文

RequestContext context =RequestContext.getCurrentContext();

context.setZuulEngineRan();//路由前置处理-------------------

try{//pre类型的ZuulFilter

preRoute();

}catch(ZuulException e) {//error类型的ZuulFilter

error(e);//post类型的ZuulFilter

postRoute();return;

}//路由处理-----------------------

try{//route类型的ZuulFilter

route();

}catch(ZuulException e) {//error类型的ZuulFilter

error(e);//post类型的ZuulFilter

postRoute();return;

}//路由后置处理--------------------

try{//post类型的ZuulFilter

postRoute();

}catch(ZuulException e) {//error类型的ZuulFilter

error(e);return;

}

}catch(Throwable e) {//...

} finally{

RequestContext.getCurrentContext().unset();

}

}

显然,service方法很清晰地描绘了一个这样的路由过程:

浏览器发起响应 -> preFilter -> routeFilter -> postFilter -> 浏览器接受响应

|---------|-----------|-------> errorFilter -> 浏览器接受响应

PreDecorationFilter

preFilter无非就是对Servlet的请求信息进行处理,为routeFilter做准备。默认的preFilter有这么5个

这里我们以PreDecorationFilter为例,看看它的处理过程。

publicObject run() {

RequestContext ctx=RequestContext.getCurrentContext();final String requestURI = this.urlPathHelper.getPathWithinApplication(ctx.getRequest());//获取路由信息

Route route = this.routeLocator.getMatchingRoute(requestURI);if (route != null) {//... 处理路由信息,添加到context当中

} else{//...

}return null;

}

PreDecorationFilter主要是做了一个路由准备。例如:http://localhost:8080/consumer/user/get?userId=1

这里的route信息将会是

经过PreDecorationFilter以后,我们已经知道了一个请求该路由到哪里去。

RibbonRoutingFilter

routeFilter默认有以下三种,这里以RibbonRoutingFilter为例

跟进RibbonRoutingFilter的run方法

@OverridepublicObject run() {

RequestContext context=RequestContext.getCurrentContext();this.helper.addIgnoredHeaders();try{

RibbonCommandContext commandContext=buildCommandContext(context);//转发请求

ClientHttpResponse response =forward(commandContext);//设置响应结果到上下文

setResponse(response);returnresponse;

}catch(ZuulException ex) {throw newZuulRuntimeException(ex);

}catch(Exception ex) {throw newZuulRuntimeException(ex);

}

}

run方法中做了一次请求转发,我们跟进forward看看

protected ClientHttpResponse forward(RibbonCommandContext context) throwsException {

Map info = this.helper.debug(context.getMethod(),

context.getUri(), context.getHeaders(), context.getParams(),

context.getRequestEntity());//构造RibbonCommand

RibbonCommand command = this.ribbonCommandFactory.create(context);try{//执行RibbonCommand

ClientHttpResponse response =command.execute();returnresponse;

}catch(HystrixRuntimeException ex) {returnhandleException(info, ex);

}

}

这里构造并执行了一个RibbonComand,具体的实例对象是HttpClientRibbonCommand,我们看看它的类图

HttpClientRibbonCommand主要是包含了三种实现

1、ClientRequest:实现了请求响应

2、RibbonCommand表示了一个负载均衡的实现

3、HystrixCommand表示了一个熔断的实现

到这里我们基本可以知道HttpClientRibbonCommand的请求过程

Hystrix熔断前置判断 -> Ribbon负载均衡处理 -> http请求到上游服务 -> 返回响应结果 -> 设置到上下文当中

SendResponseFilter

经过routeFilter以后,我们已经获得了上游服务器的response结果。然后就是postFilter,默认的postFilter只有一个SendResponseFilter,顾名思义其实就是发送响应结果返回到客户端。

打开SendResponseFilter的run方法

@OverridepublicObject run() {try{

addResponseHeaders();

writeResponse();

}catch(Exception ex) {

ReflectionUtils.rethrowRuntimeException(ex);

}return null;

}

只做了一件事,写入响应数据,跟进writeResponse方法

private void writeResponse() throwsException {

RequestContext context=RequestContext.getCurrentContext();//...

HttpServletResponse servletResponse =context.getResponse();//...

OutputStream outStream=servletResponse.getOutputStream();//获取输入流

InputStream is = null;try{if (context.getResponseBody() != null) {

String body=context.getResponseBody();//响应内容转化为字节流

is = newByteArrayInputStream(body.getBytes(servletResponse.getCharacterEncoding()));

}else{//...

}//...

if (is != null) {//写入响应流

writeResponse(is, outStream);

}

}finally{//清理...

}

}

这里生成了字节流并写入outStream,继续跟进writeResponse

private void writeResponse(InputStream zin, OutputStream out) throwsException {byte[] bytes =buffers.get();int bytesRead = -1;while ((bytesRead = zin.read(bytes)) != -1) {

out.write(bytes,0, bytesRead);

}

}

单纯地写入输出流

总结

Zuul作为网关,主要实现都包含在了ZuulFilter的实现当中。以一个ThreadLocal实现的RequestContext来传递节点数据。如果想做一些自定义的处理可以通过实现ZuulFilter。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值