SpringCloud之Zuul源码解析

Zuul 是在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架。Zuul 相当于是设备和 Netflix 流应用的 Web 网站后端所有请求的前门。Zuul 可以适当的对多个 Amazon Auto Scaling Groups 进行路由请求。
其架构如下图所示:

Zuul提供了一个框架,可以对过滤器进行动态的加载,编译,运行。过滤器之间没有直接的相互通信。他们是通过一个RequestContext的静态类来进行数据传递的。RequestContext类中有ThreadLocal变量来记录每个Request所需要传递的数据。
过滤器是由Groovy写成。这些过滤器文件被放在Zuul Server上的特定目录下面。Zuul会定期轮询这些目录。修改过的过滤器会动态的加载到Zuul Server中以便于request使用。

客户定制:比如我们可以定制一种STATIC类型的过滤器,用来模拟生成返回给客户的response。
过滤器的生命周期如下所示:

就像上图中所描述的一样,Zuul 提供了四种过滤器的 API,分别为前置(Pre)、后置(Post)、路由(Route)和错误(Error)四种处理方式。
一个请求会先按顺序通过所有的前置过滤器,之后在路由过滤器中转发给后端应用,得到响应后又会通过所有的后置过滤器,最后响应给客户端。在整个流程中如果发生了异常则会跳转到错误过滤器中。
一般来说,如果需要在请求到达后端应用前就进行处理的话,会选择前置过滤器,例如鉴权、请求转发、增加请求参数等行为。在请求完成后需要处理的操作放在后置过滤器中完成,例如统计返回值和调用时间、记录日志、增加跨域头等行为。路由过滤器一般只需要选择 Zuul 中内置的即可,错误过滤器一般只需要一个,这样可以在 Gateway 遇到错误逻辑时直接抛出异常中断流程,并直接统一处理返回结果。
Zuul可以通过加载动态过滤机制,从而实现以下各项功能:
验证与安全保障: 识别面向各类资源的验证要求并拒绝那些与要求不符的请求。
审查与监控: 在边缘位置追踪有意义数据及统计结果,从而为我们带来准确的生产状态结论。
动态路由: 以动态方式根据需要将请求路由至不同后端集群处。
压力测试: 逐渐增加指向集群的负载流量,从而计算性能水平。
负载分配: 为每一种负载类型分配对应容量,并弃用超出限定值的请求。
静态响应处理: 在边缘位置直接建立部分响应,从而避免其流入内部集群。
多区域弹性: 跨越AWS区域进行请求路由,旨在实现ELB使用多样化并保证边缘位置与使用者尽可能接近。
除此之外,Netflix公司还利用Zuul的功能通过金丝雀版本实现精确路由与压力测试。

Ribbon客户端默认选择静态类HttpClientRibbonConfiguration创建的HttpClientRibbonCommandFactory工厂类创建,HTTP Client客户端选择HttpClientConfiguration中静态类ApacheHttpClientConfiguration创建Apache相关客户端。

候选类ZuulProxyAutoConfiguration初始化RestClientRibbonConfiguration、OkHttpRibbonConfiguration、HttpClientRibbonConfiguration以及RibbonRoutingFilterSimpleHostRoutingFilter

RibbonCommandFactoryConfiguration:设置Ribbon相关属性。

ZuulServerAutoConfiguration:初始化ZuulControllerZuulHandlerMapping、CompositeRouteLocator、SimpleRouteLocator。

Zuul提供了两种形式的网关服务:微服务【服务治理】方式 & http协议方式。


1.http协议方式提供网关功能

public class ZuulProxyAutoConfiguration extends ZuulServerAutoConfiguration {@Autowiredprivate DiscoveryClient discovery;@Bean@ConditionalOnMissingBean(DiscoveryClientRouteLocator.class)public DiscoveryClientRouteLocator discoveryRouteLocator() {String prefix = this.server.getServlet().getServletPrefix()return new DiscoveryClientRouteLocator(prefix, this.discovery, this.zuulProperties,this.serviceRouteMapper, this.registration);}}public class ZuulServerAutoConfiguration {@Autowiredprotected ZuulProperties zuulProperties;@Bean@Primarypublic CompositeRouteLocator primaryRouteLocator(Collection<RouteLocator> routeLocators) {//routeLocators:DiscoveryClientRouteLocatorreturn new CompositeRouteLocator(routeLocators);}
}

 1.1.ZuulProperties

ZuulProperties:负责加载zuul相关的路由属性。

zuul.routes.blog.path=/blog/**
zuul.routes.blog.serviceId=http://mp-admin-blog.csdn.net
@ConfigurationProperties("zuul")
public class ZuulProperties {private String prefix = "";// 集合routes中key表示 服务名 或者 域名private Map<String, ZuulRoute> routes = new LinkedHashMap<>();private Set<String> ignoredServices = new LinkedHashSet<>();private Set<String> ignoredPatterns = new LinkedHashSet<>();private Host host = new Host();//设置http连接相关属性,如超时相关属性public static class ZuulRoute {//zuul.routes.blog.path=/blog/** ,其中id 即为blogprivate String id;private String path;private String serviceId;private String url;private boolean stripPrefix = true;private Boolean retryable;private Set<String> sensitiveHeaders = new LinkedHashSet<>();private boolean customSensitiveHeaders = false;}
}


1.2.ZuulHandlerMapping 的初始化

ZuulHandlerMapping通过Order控制其所有接口HandlerMapping实现类中优先级最高。

public class ZuulHandlerMapping extends AbstractUrlHandlerMapping {private final RouteLocator routeLocator;private final ZuulController zuul;public ZuulHandlerMapping(RouteLocator routeLocator, ZuulController zuul) {this.routeLocator = routeLocator;//CompositeRouteLocatorthis.zuul = zuul;setOrder(-200);}
}

截此为止服务启动过程中完成 ZuulHandlerMapping 持有路由相关属性。


1.3.ZuulHandlerMapping执行路由匹配请求

当SpringMVC处理请求时,由于在众多HandlerMapping实现类中ZuulHandlerMapping优先级是最高的,所以任何请求ZuulHandlerMapping优先处理。如果ZuulHandlerMapping通过request uri可以得到目标handler,即ZuulController,则后续请求流程由当前ZuulHandlerMapping触发完成,否则RequestMappingHandlerMapping完成。那ZuulHandlerMapping是如何匹配到ZuulController呢?

public class ZuulHandlerMapping extends AbstractUrlHandlerMapping {private final RouteLocator routeLocator;private final ZuulController zuul;private volatile boolean dirty = true;@Overrideprotected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {if (this.errorController != null && urlPath.equals(this.errorController.getErrorPath())) {return null;}// 集合 IgnoredPaths 是否包含 当前路径 urlPathif (isIgnoredPath(urlPath, this.routeLocator.getIgnoredPaths())) return null;RequestContext ctx = RequestContext.getCurrentContext();if (ctx.containsKey("forward.to")) {return null;}if (this.dirty) {synchronized (this) {if (this.dirty) {// 将 ZuulProperties 配置的所有路由path信息 与 ZuulController建立绑定关系, 添加至 HandlerMapping抽象类registerHandlers();this.dirty = false;}}}// SpringMVC 正常相关逻辑return super.lookupHandler(urlPath, request);}private void registerHandlers() {Collection<Route> routes = this.routeLocator.getRoutes();if (routes.isEmpty()) {this.logger.warn("No routes found from RouteLocator");}else {for (Route route : routes) {// 最后将fullPath & ZuulController对应关系添加到抽象类AbstractUrlHandlerMapping属性handlerMap中registerHandler(route.getFullPath(), this.zuul);}}}
}

urlPath【当前请求对应的uri】如果命中集合 IgnoredPaths 中元素,表明当前请求是被ZuulFilter所忽略,返回null即意味着当前请求继续被RequestMappingHandlerMapping处理。

lookupHandler:利用urlPath正则匹配【Ant模式】抽象类AbstractUrlHandlerMapping属性handlerMap中元素,如果匹配通过则返回handler之ZuulController。

属性dirty的重要性:提前建立ZuulRoute 与 ZuulController之间的对应关系。


1.3.1.dirty属性

抽象类AbstractUrlHandlerMapping存在Map类型的属性之handlerMap。属性元素key为fullPath,value为ZuulController。当务之急就是将类ZuulRoute中属性转化为类Route相关属性。

public Route(String id, String path, String location, String prefix,Boolean retryable, Set<String> ignoredHeaders, boolean prefixStripped) {this(id, path, location, prefix, retryable, ignoredHeaders);this.prefixStripped = prefixStripped;
}
  • path:请求下游服务真实URI路径。
  • location: 优先获取ZuulRoute中URL属性,否则选择path属性。
  • prefix:ZuulProperties中prefix属性。
  • prefixStripped:ZuulRoute中属性stripPrefix,默认为true。
  • fullPath:prefix + path。
  • ZuulRoute & ZuulProperties均存在属性stripPrefix,表示是否对path进行截取,最终目的是得到path。
public class CompositeRouteLocator{private final Collection<? extends RouteLocator> routeLocators;@Overridepublic List<Route> getRoutes() {List<Route> route = new ArrayList<>();// 通常情况下 集合routeLocators 中元素只有 DiscoveryClientRouteLocatorfor (RouteLocator locator : routeLocators) {route.addAll(locator.getRoutes());}return route;}
}public class SimpleRouteLocator{@Overridepublic List<Route> getRoutes() {List<Route> values = new ArrayList<>();// ZuulRoute元素其实即为ZuulProperties映射的配置属性for (Entry<String, ZuulRoute> entry : getRoutesMap().entrySet()) {ZuulRoute route = entry.getValue();String path = route.getPath();values.add(getRoute(route, path));}return values;}protected Route getRoute(ZuulRoute route, String path) {if (route == null) {return null;}String targetPath = path;//首先判断ZuulProperties中属性 zuul.prefix 是否存在值String prefix = this.properties.getPrefix();if(prefix.endsWith("/")) {//去掉前缀值中存在的后斜杠prefix = prefix.substring(0, prefix.length() - 1);}if (path.startsWith(prefix + "/") && this.properties.isStripPrefix()) {targetPath = path.substring(prefix.length());//将path中prefix截掉}if (route.isStripPrefix()) {// 获取首个字符 * 的索引indexint index = route.getPath().indexOf("*") - 1;if (index > 0) {// 获取path中首个字符 * 之前的全部字符String routePrefix = route.getPath().substring(0, index);//将 routePrefix 字符串 全部替换为 空字符串targetPath = targetPath.replaceFirst(routePrefix, "");// 重新在 routePrefix 之前拼接 prefix。// 如果 prefix = admin, path = admin/like/**, 则此时 prefix 为 admin/admin/likeprefix = prefix + routePrefix;}}Boolean retryable = this.properties.getRetryable();if (route.getRetryable() != null) {retryable = route.getRetryable();}return new Route(route.getId(), targetPath, route.getLocation(), prefix,retryable,route.isCustomSensitiveHeaders() ? route.getSensitiveHeaders() : null, route.isStripPrefix());}
}

通过dirty属性提前在handlerMap建立ZuulRoute 与 ZuulController之间的对应关系,下一步就需要通过请求URL从handlerMap获取对应的ZuulController。

以上完成请求地址的路由匹配。


1.4.ZuulController执行过滤器ZuulFilter

  1. 通过ZuulServlet触发过滤器流程的开始。毕竟过滤器属于Servlet的范畴。
  2. 由ZuulRunner引申出FilterProcessor,FilterProcessor控制pre、route、post三类过滤器先后执行顺序。
  3. FilterProcessor加载出不同类型的全部过滤器,并依次执行全部的过滤器。
  4. 过滤器真正执行逻辑是由抽象类ZuulFilter定义的。
public class SimpleControllerHandlerAdapter implements HandlerAdapter {@Overridepublic boolean supports(Object handler) {return (handler instanceof Controller);}@Override@Nullablepublic ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler){return ((Controller) handler).handleRequest(request, response);}

 ZuulController匹配的adapter为SimpleControllerHandlerAdapter。

public class ZuulController extends ServletWrappingController {@Overridepublic ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {return super.handleRequestInternal(request, response);}
}
public class ServletWrappingController extends AbstractController{private Servlet servletInstance;//ZuulServletprotected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response){this.servletInstance.service(request, response);return null;}
}

 由ZuulServlet真正触发过滤器的执行流程。

public class ZuulServlet extends HttpServlet {public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws ServletException, IOException {init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);RequestContext context = RequestContext.getCurrentContext();context.setZuulEngineRan();try {preRoute();//前置路由route();// 路由postRoute();// 后置路由} catch (ZuulException e) {postRoute();return;}}
}
public class ZuulServlet extends HttpServlet {private ZuulRunner zuulRunner;public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws ServletException, IOException {init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);RequestContext context = RequestContext.getCurrentContext();context.setZuulEngineRan();try {preRoute();//前置路由route();// 路由postRoute();// 后置路由} catch (ZuulException e) {postRoute();return;}}void postRoute() throws ZuulException {zuulRunner.postRoute();}void route() throws ZuulException {zuulRunner.route();}void preRoute() throws ZuulException {zuulRunner.preRoute();}
}

public class ZuulRunner {public void postRoute() throws ZuulException {FilterProcessor.getInstance().postRoute();}public void route() throws ZuulException {//FilterProcessor#FilterProcessor.getInstance().route();}public void preRoute() throws ZuulException {FilterProcessor.getInstance().preRoute();}
}
public class FilterProcessor {public void postRoute() throws ZuulException {runFilters("post");}public void route() throws ZuulException {runFilters("route");}public void preRoute() throws ZuulException {runFilters("pre");}public Object runFilters(String sType) {// 所有网关涉及的全部过滤器必经路径boolean bResult = false;//获取当前类型【pre 、post、route】的全部过滤器List<ZuulFilter> list = FilterLoader.getInstance().getFiltersByType(sType);if (list != null) {for (int i = 0; i < list.size(); i++) {//依次执行每个过滤器ZuulFilter zuulFilter = list.get(i);Object result = processZuulFilter(zuulFilter);if (result != null && result instanceof Boolean) {bResult |= ((Boolean) result);}}}//pre 、post、route返回值没有任何意义return bResult;}public Object processZuulFilter(ZuulFilter filter) throws ZuulException {RequestContext ctx = RequestContext.getCurrentContext();String filterName = "";RequestContext copy = null;Object o = null;Throwable t = null;// 执行全部过滤器的父类过滤器ZuulFilterResult result = filter.runFilter();ExecutionStatus s = result.getStatus();switch (s) {case FAILED:// 处理核心逻辑异常信息t = result.getException();ctx.addFilterExecutionSummary(filterName, ExecutionStatus.FAILED.name(), execTime);break;case SUCCESS:// 核心方法任何返回值都按成功处理o = result.getResult();ctx.addFilterExecutionSummary(filterName, ExecutionStatus.SUCCESS.name(), execTime);break;default:break;}if (t != null) throw t;usageNotifier.notify(filter, s);return o;}
}

前置过滤器包含:ServletDetectionFilter、Servlet30WrapperFilter、FormBodyWrapperFilter、DebugFilter、PreDecorationFilter。

PreDecorationFilter主要是设置一些代理东西,不常用

对于route类型的过滤器存在三种默认的过滤器: RibbonRoutingFilterSimpleHostRoutingFilter、SendForwardFilter【重定向相关拦截处理】。

后置过滤器:SendResponseFilter。

1.4.1.ZuulFilter

以下是每个类型的过滤器都会执行的必经逻辑:

  1. 首先执行shoulderFilter。
  2. 其次执行真正的核心流程run。

核心方法run任何返回值类型都按success处理;只有出现异常则按失败处理,即过滤器直接抛出异常。 

public abstract class ZuulFilter{public ZuulFilterResult runFilter() {ZuulFilterResult zr = new ZuulFilterResult();if (!isFilterDisabled()) {if (shouldFilter()) {// 首先判断当前过滤器是否允许执行核心逻辑Tracer t = TracerFactory.instance().startMicroTracer("ZUUL::" + this.getClass().getSimpleName());try {Object res = run();// 过滤器的核心逻辑 ~ 对于核心方法的返回值没有任何意义zr = new ZuulFilterResult(res, ExecutionStatus.SUCCESS);} catch (Throwable e) {// 只有核心方法run 出现异常,才会终止请求流程t.setName("ZUUL::" + this.getClass().getSimpleName() + " failed");zr = new ZuulFilterResult(ExecutionStatus.FAILED);zr.setException(e);} finally {t.stopAndLog();}} else {//跳过当前过滤器zr = new ZuulFilterResult(ExecutionStatus.SKIPPED);}}return zr;}
}

1.5.调用下游downstream服务

对于route类型的过滤器,自定义的过滤器的优先级远高于自带三种过滤器。如果需要触发下游服务继续调用,目前只有RibbonRoutingFilter、SimpleHostRoutingFilter两种过滤器支持。其中,RibbonRoutingFilter是通过微服务(服务治理)方式实现,SimpleHostRoutingFilter则是通过http方式调用下游服务。

RibbonRoutingFilter、SimpleHostRoutingFilter、SendResponseFilter过滤器生效的共同条件是:RequestContext之sendZuulResponse属性必须为true。意味着在自定义过滤器内部如果允许下游服务继续访问,必须显式设置sendZuulResponse的属性值。

1.5.1.RibbonRoutingFilter

public class RibbonRoutingFilter extends ZuulFilter {@Overridepublic boolean shouldFilter() {RequestContext ctx = RequestContext.getCurrentContext();return (ctx.getRouteHost() == null && ctx.get("serviceId") != null&& ctx.sendZuulResponse());}
}

1.5.2.SimpleHostRoutingFilter

public class SimpleHostRoutingFilter extends ZuulFilter {@Overridepublic boolean shouldFilter() {return RequestContext.getCurrentContext().getRouteHost() != null && RequestContext.getCurrentContext().sendZuulResponse();}public Object run() {RequestContext context = RequestContext.getCurrentContext();...String uri = this.helper.buildZuulRequestURI(request);this.helper.addIgnoredHeaders();// 通过http方式调用下游服务CloseableHttpResponse response = forward(this.httpClient, verb, uri, request,headers, params, requestEntity);setResponse(response);return null;}private void setResponse(HttpResponse response) throws IOException {// 将下游服务的响应封装在 上下文 属性zuulResponse中RequestContext.getCurrentContext().set("zuulResponse", response);// 将下游服务的响应头、响应实体等信息 单独添加至 上下文中this.helper.setResponse(response.getStatusLine().getStatusCode(),response.getEntity() == null ? null : response.getEntity().getContent(),revertHeaders(response.getAllHeaders()));}
}

触发下游服务 & 将下游服务响应的相关属性跟上下文RequestContext绑定。 

1.5.3.SendResponseFilter

通过以下得知,该过滤器整合下游服务响应内容的前提是RibbonRoutingFilter or SimpleHostRoutingFilter 至少有一个生效。

public class SendResponseFilter extends ZuulFilter {@Overridepublic boolean shouldFilter() {RequestContext context = RequestContext.getCurrentContext();return context.getThrowable() == null&& (!context.getZuulResponseHeaders().isEmpty()// 下游服务的响应头必须被添加至上下文ZuulResponseHeaders属性中// 此处是指下游服务的响应不能为空|| context.getResponseDataStream() != null|| context.getResponseBody() != null);}@Overridepublic Object run() {addResponseHeaders();writeResponse();return null;}private void writeResponse() throws Exception {RequestContext context = RequestContext.getCurrentContext();if (context.getResponseBody() == null && context.getResponseDataStream() == null) {return;}HttpServletResponse servletResponse = context.getResponse();if (servletResponse.getCharacterEncoding() == null) { // only set if not setservletResponse.setCharacterEncoding("UTF-8");}//此处是指在自定义过滤器中添加的响应信息OutputStream outStream = servletResponse.getOutputStream();InputStream is = null;if (context.getResponseBody() != null) {// 如果在自定义过滤器中添加了ResponseBody,则下游服务的响应不会被添加到最终响应值中String body = context.getResponseBody();is = new ByteArrayInputStream(body.getBytes(servletResponse.getCharacterEncoding()));}else {is = context.getResponseDataStream();// 获取下游服务的响应值if (is!=null && context.getResponseGZipped()) {// 下游服务是否需要压缩if (isGzipRequested(context)) {servletResponse.setHeader(ZuulHeaders.CONTENT_ENCODING, "gzip");}else {is = handleGzipStream(is);}}}if (is!=null) {// 将下游服务的响应is写到 outStream中,即为最终响应内容writeResponse(is, outStream);}...}
}

自定义过滤器内部如果存在通过字符流Writer写入数据,则在SendResponseFilter内部抛出异常:getWriter() has already been called for this response,但是不会打印出堆栈信息,该异常非常隐蔽。自定义过滤器内部可以选择字节流方式写入数据。

记 SpringBoot 拦截器报错 getWriter() has already been called for this response

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/350179.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

高速公路智能管理系统:构建安全畅通的数字大动脉

随着城市化进程的加速和交通需求的增长&#xff0c;高速公路系统作为城市交通的重要组成部分&#xff0c;正承担着越来越多的交通运输任务。为了提升高速公路的安全性、便捷性和智能化管理水平&#xff0c;高速公路智能管理系统应运而生。本文将深入探讨高速公路智能管理系统的…

Linux shell编程学习笔记58:cat /proc/mem 获取系统内存信息

0 前言 在开展系统安全检查的过程中&#xff0c;除了收集cpu信息&#xff0c;我们还需要收集内存信息。在Linux中&#xff0c;获取内存信息的命令很多&#xff0c;这里我们着重研究 cat /proc/mem命令。 1 cat /proc/mem命令 /proc/meminfo 文件提供了有关系统内存的使用情况…

能耗分析与远程抄表是什么?

一、引言 在21世纪的数字化时代&#xff0c;能耗分析和远程抄表已成为现代能源管理的重要组成部分。这两项技术不仅提高了能源效率&#xff0c;还为企业和个人提供了更精细的能源使用数据&#xff0c;从而实现更科学的节能减排。 二、能耗分析的深度洞察 能耗分析是通过收集…

[Java基本语法] 逻辑控制与方法

&#x1f338;个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 &#x1f3f5;️热门专栏:&#x1f355; Collection与数据结构 (92平均质量分)https://blog.csdn.net/2301_80050796/category_12621348.html?spm1001.2014.3001.5482 &#x1f9c0;线程与…

【C++进阶】模板进阶与仿函数:C++编程中的泛型与函数式编程思想

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ ⏩收录专栏⏪&#xff1a;C “ 登神长阶 ” &#x1f921;往期回顾&#x1f921;&#xff1a;栈和队列相关知识 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀模板进阶 &#x1f9e9;<&…

【C语言】13.数组指针与函数指针及其应用

一、数组指针 顾名思义&#xff0c;数组指针就是指向数组的指针。形如&#xff1a;int (*p)[10]; 注意&#xff1a;[]的优先级要高于*号的&#xff0c;所以必须加上&#xff08;&#xff09;来保证p先和*结合。 数组指针的使用 int arr[10] {0}; int (*parr)[10] &arr;…

探索服务器硬件:理解基础组件及其重要性

在现代IT基础设施中&#xff0c;服务器扮演着至关重要的角色。无论是托管网站、管理数据、运行应用程序还是提供各种在线服务&#xff0c;服务器硬件的性能和稳定性都是确保这些任务顺利进行的关键。本文将介绍服务器硬件的基本组件及其功能&#xff0c;以帮助读者更好地理解和…

程序优化 --- arthas trace命令使用

最近在做优化,通过arthas的trace命令去观察方法内的耗时情况以便对程序进行修改. 1.启动arthas之后选择需要监测的程序 2.找到需要监测的接口,一般都是直接找service例子如下: trace 类地址.类名 方法名 (中间有空格)

数据可视化后起之秀——pyecharts

题目一&#xff1a;绘制折线图&#xff0c;展示商家A与商家B各类饮品的销售额 题目描述&#xff1a; 编写程序。根据第9.3.1&#xff0c;绘制折线图&#xff0c;展示商家A与商家B各类饮品的销售额。 运行代码&#xff1a; #绘制折线图&#xff0c;展示商家A与商家B各类饮品的…

一键安全体检!亚信安全携手鼎捷软件推出企业安全体检活动 正式上线

亚信安全联合鼎捷软件股份有限公司&#xff08;以下简称“鼎捷软件”&#xff09;正式推出“一键安全体检”服务。亚信安全网络安全专家将携手鼎捷软件数据安全专家&#xff0c;围绕企业的数智安全状况&#xff0c;进行问题探索与治愈、新问题预测与预警&#xff0c;在全面筛查…

【git使用一】windows下git下载、安装和卸载

目录 &#xff08;1&#xff09;下载安装包 &#xff08;2&#xff09;安装git &#xff08;3&#xff09;安装验证 &#xff08;4&#xff09;卸载git &#xff08;1&#xff09;下载安装包 官网下载地址&#xff1a;Git 国内镜像下载地址&#xff1a;CNPM Binaries Mir…

docker安装rabbitmq和延迟插件(不废话版)

1.下载镜像 docker pull rabbitmq:3.8-management 2.启动 docker run -e RABBITMQ_DEFAULT_USERlicoos -e RABBITMQ_DEFAULT_PASSlicoosrabbitmq -v mq-plugins:/plugins --name mq --hostname mq -p 15672:15672 -p 5672:5672 -d rabbitmq:3.8-management 3.下载对…

基于matlab的MTCNN(多任务卷积神经网络)人脸检测算法

关键词&#xff1a;Matlab&#xff1b;深度学习&#xff1b;多任务卷积神经网络&#xff1b;人脸检测&#xff1b; 背景 在不受约束的环境中&#xff0c;由于个体姿势的多样性、光照条件的变化以及潜在的遮挡问题&#xff0c;人脸检测和对齐任务面临诸多挑战。近期的研究表明…

Python也能“零延迟“通信吗?ZeroMQ带你开启高速模式!

目录 1、零基础入门ZeroMQ 🚀 1.1 ZeroMQ简介与安装 1.2 基础概念:Socket类型详解 1.3 实战演练:Hello World示例 2、深入浅出消息模式 🔌 2.1 请求-应答模式( REQ/REP ) 2.2 发布-订阅模式( PUB/SUB ) 2.3 推送-拉取模式( PUSH/PULL ) 3、Python实战ZeroM…

redis+lua实现分布式限流

redislua实现分布式限流 文章目录 redislua实现分布式限流为什么使用redislua实现分布式限流使用ZSET也可以实现限流&#xff0c;为什么选择lua的方式实现依赖lua脚本yaml代码实现 Jmeter压测 为什么使用redislua实现分布式限流 原子性&#xff1a;通过Lua脚本执行限流逻辑&am…

socket收发数据的处理

1. TCP 协议是一种基于数据流的协议 Socket的Receive方法只是把接收缓冲区的数据提取出来,当系统的接收缓冲区为空,Receive方法会被阻塞,直到里面有数据。 Socket的Send方法只是把数据写入到发送缓冲区里,具体的发送过程由操作系统负责。当操作系统的发送缓冲区满了,Send方法会…

计算机网络 —— 网络层 (路由协议)

计算机网络 —— 网络层 &#xff08;路由协议&#xff09; 什么是路由协议内部网关协议RIP关键特性 OSPF主要特点 外部网关协议BGP关键特性 我们今天来看路由协议&#xff1a; 什么是路由协议 路由协议是网络设备&#xff08;主要是路由器&#xff09;用来决定数据包在网络中…

CinemachineFreelook相机鬼畜抖动的解决方法

又是培训机构的出品。。。。。。 2020.3.27 发现跟随时抖动的原因了&#xff0c;其实应该按照传统相机的跟随逻辑放在late update里执行&#xff0c;但是由于开发cinemachine的开发组很坑地把cinemachine freelook里的三个轨道Damping值默认给了3&#xff0c;于是相机会以每0.7…

Python 全栈系列253 再梳理flask-celery的搭建

说明 最近做了几个实验&#xff0c;将结论梳理一下&#xff0c;方便以后翻看。 1 flask-celery 主要用于数据流的同步任务&#xff0c;其执行由flask-aps发起&#xff0c;基于IO并发的方法&#xff0c;达到资源的高效利用&#xff0c;满足业务上的需求。2 目前部署环境有算网…

Java内存模型,堆、栈和方法区的区别

Java内存管理是Java虚拟机&#xff08;JVM&#xff09;技术的核心之一。了解Java内存管理对于提高程序性能、解决内存泄漏和优化资源利用至关重要。 一、Java内存模型&#xff08;Java Memory Model, JMM&#xff09; Java内存模型描述了Java程序中变量&#xff08;包括实例字…