一、zuul如何路由到上游服务器
所有文章
https://www.cnblogs.com/lay2017/p/11908715.html
正文
zuul在分布式项目中充当着一个网关的角色,而它最主要的功能像nginx一样针对上游服务器做反向代理。我们可以将它理解为一个服务的门面,作为客户端来说不需要再面向各式各样的服务,只需要面向zuul即可,简化了客户端与服务端的交互关系。
既然,zuul成为了客户端与服务端的中间层,那么zuul显然可以进行拦截、记录、安全管理、路由...等等各种处理。本文,将从路由这个点切入,看看路由的过程。
ZuulServlet
首先,客户端和服务端的交互显然少不了的http,所以先找到zuul针对Servlet的实现
可以看到,ZuulServlet直接继承了HttpServlet。所以,ZuulServlet依然走的是http通信协议,我们跟进ZuulServlet的service方法。
@Override
public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws ServletException, 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为例,看看它的处理过程。
public Object 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方法
@Override
public Object run() {
RequestContext context = RequestContext.getCurrentContext();
this.helper.addIgnoredHeaders();
try {
RibbonCommandContext commandContext = buildCommandContext(context);
// 转发请求
ClientHttpResponse response = forward(commandContext);
// 设置响应结果到上下文
setResponse(response);
return response;
}
catch (ZuulException ex) {
throw new ZuulRuntimeException(ex);
}
catch (Exception ex) {
throw new ZuulRuntimeException(ex);
}
}
run方法中做了一次请求转发,我们跟进forward看看
protected ClientHttpResponse forward(RibbonCommandContext context) throws Exception {
Map<String, Object> 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(); return response;
}
catch (HystrixRuntimeException ex) {
return handleException(info, ex);
} }
这里构造并执行了一个RibbonComand,具体的实例对象是HttpClientRibbonCommand,我们看看它的类图
HttpClientRibbonCommand主要是包含了三种实现
1、ClientRequest:实现了请求响应
2、RibbonCommand表示了一个负载均衡的实现
3、HystrixCommand表示了一个熔断的实现
到这里我们基本可以知道HttpClientRibbonCommand的请求过程
Hystrix熔断前置判断 -> Ribbon负载均衡处理 -> http请求到上游服务 -> 返回响应结果 -> 设置到上下文当中
SendResponseFilter
经过routeFilter以后,我们已经获得了上游服务器的response结果。然后就是postFilter,默认的postFilter只有一个SendResponseFilter,顾名思义其实就是发送响应结果返回到客户端。
打开SendResponseFilter的run方法
@Override
public Object run() {
try {
addResponseHeaders();
writeResponse();
}
catch (Exception ex) {
ReflectionUtils.rethrowRuntimeException(ex);
}
return null;
}
只做了一件事,写入响应数据,跟进writeResponse方法
private void writeResponse() throws Exception {
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 = new ByteArrayInputStream(body.getBytes(servletResponse.getCharacterEncoding()));
} else {
// ...
} // ... if (is != null) {
// 写入响应流
writeResponse(is, outStream);
}
} finally {
// 清理...
}
}
这里生成了字节流并写入outStream,继续跟进writeResponse
private void writeResponse(InputStream zin, OutputStream out) throws Exception {
byte[] bytes = buffers.get();
int bytesRead = -1;
while ((bytesRead = zin.read(bytes)) != -1) {
out.write(bytes, 0, bytesRead);
}
}
单纯地写入输出流
总结
Zuul作为网关,主要实现都包含在了ZuulFilter的实现当中。以一个ThreadLocal实现的RequestContext来传递节点数据。如果想做一些自定义的处理可以通过实现ZuulFilter。
一、zuul如何路由到上游服务器的更多相关文章
- springcloud Zuul中路由配置细节
上篇文章我们介绍了API网关的基本构建方式以及请求过滤,小伙伴们对Zuul的作用应该已经有了一个基本的认识,但是对于路由的配置我们只是做了一个简单的介绍,本文我们就来看看路由配置的其他一些细节. 首先 ...
- Spring Cloud(Dalston.SR5)--Zuul 网关-路由配置
Spring Cloud 在 Zuul 的 routing 阶段实现了几个过滤器,这些过滤器决定如何进行路由工作. 简单路由(SimpleHostRoutingFilter) 该过滤器运行后,会将 H ...
- springCloud学习4(Zuul服务路由)
镇博图 springcloud 总集:https://www.tapme.top/blog/detail/2019-02-28-11-33 本篇中 Zuul 版本为 1.x,目前最新的是 2.x,二者 ...
- nginx系列10:通过upstream模块选择上游服务器和负载均衡策略round-robin
upstream模块的使用方法 1,使用upstream和server指令来选择上游服务器 这两个指令的语法如下图: 示例: 2,对上游服务使用keepalive长连接 负载均衡策略round-rob ...
- SpringCloud系列——Zuul 动态路由
前言 Zuul 是在Spring Cloud Netflix平台上提供动态路由,监控,弹性,安全等边缘服务的框架,是Netflix基于jvm的路由器和服务器端负载均衡器,相当于是设备和 Netflix ...
- Nginx 针对上游服务器缓存
L:99 nginx缓存 : 定义存放缓存的载体 proxy_cache 指令 Syntax: proxy_cache zone | off; Default: proxy_cache off; Co ...
- Nginx 当上游服务器返回失败时的处理办法
陶辉95课 Syntax: proxy_next_upstream error | timeout | invalid_header | http_500 | http_502 | http_503 ...
- Nginx 反向代理如何连接上游服务器
L:92 想上游服务器先建立TCP连接 如三次握手 下面指令可以控制握手时间 proxy_next_upstream 指令当出现502可以换个上游服务器 Tcp keepalive 一般都是由进程在 ...
- Zuul 网关路由
Zuul 网关路由 路由是微服务架构中不可或缺的一部分,例如:/api/user映射到user服务,/api/shop映射到shop服务. Zuul是一个基于JVM的路由和服务端的负载均衡器.Zuul ...
随机推荐
- 【转载】 Bill Gates和Elon Musk推荐,人工智能必读的三本书 -《终极算法》,《超级智能》和《终极发明》
原文地址: https://blog.csdn.net/ztf312/article/details/80761917 ---------------------------------------- ...
- pip 安装指定版本
pip在安装包的时候可以不需要从网上下载,以windows的scipy为例 pip install scipy==0.15.1 以上表示安装0.15.1版本的scipy,这里用”==”接版本,如果权限 ...
- Egret中的对象池Pool
为了可以让对象复用,防止大量重复创建对象,导致资源浪费,使用对象池来管理. 一 对象池A 二 对象池B 一 对象池A 1. 支持传入构造函数 2. 支持预先创建对象 3. 支持统一执行函数 /** * ...
- 增强篇6 CMOD增强删除
CMOD实施了一个增强,但是不需要了,怎么删除呢? 要删除PPCO0005 这个增强,进入编辑状态,发现没有删除按钮, 把PPCO0005 这项删除掉 敲回车,厉害了,他又回来了,所以在这里是没法删 ...
- MySQL普通索引性能试验
首先使用如下node.js脚本创建两张表,并为这两张表各自生成10000条数据: var fs = require('fs'); var nameS = "赵钱孙李周吴郑王冯陈褚卫蒋沈韩杨朱 ...
- AWS 数据库(七)
数据库概念 关系型数据库 关系数据库提供了一个通用接口,使用户可以使用使用 编写的命令或查询从数据库读取和写入数据. 关系数据库由一个或多个表格组成,表格由与电子表格相似的列和行组成. 以行列形式存储 ...
- centos7之zabbix监控DELL磁盘阵列
本篇我们介绍戴尔服务器R730.R720.R710等服务器下挂在的MD1200磁盘阵列柜监控方式 一.使用场景 在生产环境中存储肯定是离不开的,服务器自带的硬盘卡槽有限,所以一般需要存储的量大的话,都 ...
- 【计算机视觉】目标检测之ECCV2016 - SSD Single Shot MultiBox Detector
本文转载自: http://www.cnblogs.com/lillylin/p/6207292.html SSD论文阅读(Wei Liu--[ECCV2016]SSD Single Shot Mul ...
- android基础---->Parcelable的使用
android中Parcelable序列化的使用,简单的记录一下. 目录导航: Serializable在android中的使用 Parcelable在android中的使用 Serializabl ...
- 仔细看参数--NGINX之tcp_nodelay
一.知识准备 ● 在nginx优化中有个经常需要设置的参数,tcp_nodelay ● 该参数最核心的功能,就是把小包组成成大包,提高带宽利用率也就是著名的nagle算法 ● tcp协议中,有一个现象 ...