当前位置: 首页 > news >正文

SpringBoot v2.7.x+ 整合Swagger3入坑记?

目录

一、依赖

二、集成Swagger Java Config

三、配置完毕

四、解决方案

彩蛋


想尝鲜,坑也多,一起入个坑~

一、依赖

SpringBoot版本:2.7.14

Swagger版本:3.0.0

<dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-spring-boot-starter</artifactId><version>3.0.3</version>
</dependency><dependency><groupId>io.springfox</groupId><artifactId>springfox-boot-starter</artifactId><version>3.0.0</version>
</dependency>

二、集成Swagger Java Config


@Value("${server.port:8080}")
private String port;@Value("${server.servlet.context-path:}")
private String rootPath;@Bean
Docket docket(SwaggerProperties properties) {Docket docket = new Docket(DocumentationType.OAS_30).apiInfo(apiInfo(properties)).groupName(properties.getGroupName()).select().apis(scanBasePackages(properties.getBasePackage())).paths(PathSelectors.any()).build().globalRequestParameters(globalRequestParameters(properties)).globalResponses(HttpMethod.POST, responses()).globalResponses(HttpMethod.GET, responses()).pathMapping("/");log.info("Swagger3 successfully started: http://{}:{}{}/doc.html", IPUtils.getLocalIP(), port, rootPath);return docket;
}@Bean
public ModelPropertyBuilderPlugin modelPropertyBuilderPlugin() {return new DictPropertyPlugin();
}/*** 构建响应状态码*/
private List<Response> responses() {List<Response> responses = new LinkedList<>();responses.add(new ResponseBuilder().code("S").description("响应成功").build());responses.add(new ResponseBuilder().code("E").description("非'S'即为响应失败").build());return responses;
}private ApiInfo apiInfo(SwaggerProperties properties) {return new ApiInfoBuilder().title(properties.getTitle()).description(properties.getDescription()).license(properties.getLicense()).licenseUrl(properties.getLicenseUrl()).termsOfServiceUrl(properties.getTermsOfServiceUrl()).contact(new Contact(properties.getContact().getName(), properties.getContact().getUrl(), properties.getContact().getEmail())).version(properties.getVersion()).build();
}/*** 自定义请求参数** @return - list*/
private List<RequestParameter> globalRequestParameters(SwaggerProperties properties) {List<RequestParameter> params = new ArrayList<>();properties.getParams().forEach(e -> {RequestParameter parameter = new RequestParameterBuilder().name(e.getName()).description(e.getDesc()).required(e.isRequired()).in(e.getParamType()).hidden(e.isHidden()).build();params.add(parameter);});return params;
}/*** 多包扫描支持,扫描的包生成{@linkplain Predicate < RequestHandler >}** @param basePackages - 扫描的包*/
private Predicate<RequestHandler> scanBasePackages(final String... basePackages) {if (basePackages == null || basePackages.length == 0) {throw new IllegalArgumentException("basePackages不能为空");}Predicate<RequestHandler> predicate = null;for (int i = basePackages.length - 1; i >= 0; i--) {String strBasePackage = basePackages[i];if (StrUtil.isNotBlank(strBasePackage)) {Predicate<RequestHandler> tempPredicate = RequestHandlerSelectors.basePackage(strBasePackage);predicate = predicate == null ? tempPredicate : predicate.or(tempPredicate);}}if (predicate == null) {throw new IllegalArgumentException("basePackage配置不正确");}return predicate;
}
/*** swagger3 自定义展示枚举类型信息*/
public class DictPropertyPlugin implements ModelPropertyBuilderPlugin {private final Logger log = LoggerFactory.getLogger(getClass());@Overridepublic void apply(ModelPropertyContext ctx) {Optional<BeanPropertyDefinition> opt = ctx.getBeanPropertyDefinition();opt.ifPresent(bean -> {Class<?> cls = bean.getRawPrimaryType();if (IDict.class.isAssignableFrom(cls) && Enum.class.isAssignableFrom(cls)) {if (cls.getEnumConstants() == null) {return;}try {Field f = PropertySpecificationBuilder.class.getDeclaredField("description");f.setAccessible(true);String prefix = cls.getSimpleName() + "(" + f.get(ctx.getSpecificationBuilder()) + ")【";StringJoiner join = new StringJoiner(",", prefix, "】");for (IDict<?, ?> d : (IDict<?, ?>[]) cls.getEnumConstants()) {join.add(d.getDesc() + "[" + d.getCode() + "]-" + ((Enum<?>) d).name());}ctx.getSpecificationBuilder().description(join.toString());} catch (Exception e) {log.error("字典值处理失败:{}", cls.getName(), e);}}});}@Overridepublic boolean supports(DocumentationType type) {return DocumentationType.OAS_30.equals(type);}
}

properties

package com.muchenx.common.swagger.config;import org.springframework.boot.context.properties.ConfigurationProperties;
import springfox.documentation.service.ParameterType;import java.util.ArrayList;
import java.util.List;@ConfigurationProperties(prefix = "swagger")
public class SwaggerProperties {/*** swagger会解析的包路径**/private String basePackage = "com.muchenx";/*** 分组名*/private String groupName = "default";/*** 标题**/private String title = "MuchenX";/*** 描述**/private String description = "MuchenX Cloud Project supports by Spring Cloud Alibaba";/*** 版本**/private String version = "v1.0";/*** 许可证**/private String license = "";/*** 许可证URL**/private String licenseUrl = "";/*** 服务条款URL**/private String termsOfServiceUrl = "";/*** host信息**/private String host = "";/*** 联系人信息*/private Contact contact = new Contact();/*** 自定义参数*/private List<Param> params = new ArrayList<>();public String getGroupName() {return groupName;}public void setGroupName(String groupName) {this.groupName = groupName;}public String getBasePackage() {return basePackage;}public void setBasePackage(String basePackage) {this.basePackage = basePackage;}public String getTitle() {return title;}public void setTitle(String title) {this.title = title;}public String getDescription() {return description;}public void setDescription(String description) {this.description = description;}public String getVersion() {return version;}public void setVersion(String version) {this.version = version;}public String getLicense() {return license;}public void setLicense(String license) {this.license = license;}public String getLicenseUrl() {return licenseUrl;}public void setLicenseUrl(String licenseUrl) {this.licenseUrl = licenseUrl;}public String getTermsOfServiceUrl() {return termsOfServiceUrl;}public void setTermsOfServiceUrl(String termsOfServiceUrl) {this.termsOfServiceUrl = termsOfServiceUrl;}public String getHost() {return host;}public void setHost(String host) {this.host = host;}public Contact getContact() {return contact;}public void setContact(Contact contact) {this.contact = contact;}public List<Param> getParams() {return params;}public void setParams(List<Param> params) {this.params = params;}public static class Contact {/*** 联系人**/private String name = "Ian Geng";/*** 联系人url**/private String url = "www.muchenx.com";/*** 联系人email**/private String email = "gzhygz@gmail.com";public String getName() {return name;}public void setName(String name) {this.name = name;}public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}public String getEmail() {return email;}public void setEmail(String email) {this.email = email;}}public static class Param {// 请求参数名private String name;// 请求参数描述private String desc;/*** 请求参数类型:QUERY("query"),HEADER("header"),PATH("path"),* COOKIE("cookie"),FORM("form"),FORMDATA("formData"),BODY("body");*/private ParameterType paramType = ParameterType.HEADER;// 请求参数默认值private String defaultValue = "";// 是否必填private boolean required = false;// 是否隐藏private boolean hidden = false;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getDesc() {return desc;}public void setDesc(String desc) {this.desc = desc;}public ParameterType getParamType() {return paramType;}public void setParamType(ParameterType paramType) {this.paramType = paramType;}public String getDefaultValue() {return defaultValue;}public void setDefaultValue(String defaultValue) {this.defaultValue = defaultValue;}public boolean isRequired() {return required;}public void setRequired(boolean required) {this.required = required;}public boolean isHidden() {return hidden;}public void setHidden(boolean hidden) {this.hidden = hidden;}}
}

三、配置完毕

在启动类增加注解开起swagger:@springfox.documentation.oas.annotations.EnableOpenApi

此时控制台报错

Caused by: java.lang.NullPointerException: Cannot invoke "org.springframework.web.servlet.mvc.condition.PatternsRequestCondition.getPatterns()" because "this.condition" is nullat springfox.documentation.spring.web.WebMvcPatternsRequestConditionWrapper.getPatterns(WebMvcPatternsRequestConditionWrapper.java:56) ~[springfox-spring-webmvc-3.0.0.jar:3.0.0]at springfox.documentation.RequestHandler.sortedPaths(RequestHandler.java:113) ~[springfox-core-3.0.0.jar:3.0.0]at springfox.documentation.spi.service.contexts.Orderings.lambda$byPatternsCondition$3(Orderings.java:89) ~[springfox-spi-3.0.0.jar:3.0.0]at java.base/java.util.Comparator.lambda$comparing$77a9974f$1(Comparator.java:473) ~[na:na]at java.base/java.util.TimSort.countRunAndMakeAscending(TimSort.java:355) ~[na:na]at java.base/java.util.TimSort.sort(TimSort.java:220) ~[na:na]at java.base/java.util.Arrays.sort(Arrays.java:1307) ~[na:na]at java.base/java.util.ArrayList.sort(ArrayList.java:1721) ~[na:na]at java.base/java.util.stream.SortedOps$RefSortingSink.end(SortedOps.java:392) ~[na:na]at java.base/java.util.stream.Sink$ChainedReference.end(Sink.java:258) ~[na:na]at java.base/java.util.stream.Sink$ChainedReference.end(Sink.java:258) ~[na:na]at java.base/java.util.stream.Sink$ChainedReference.end(Sink.java:258) ~[na:na]at java.base/java.util.stream.Sink$ChainedReference.end(Sink.java:258) ~[na:na]at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:510) ~[na:na]at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) ~[na:na]at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921) ~[na:na]at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:na]at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:682) ~[na:na]...........

原因是:主要出现在Spring Boot 2.6及以后,只要是Spring Boot 2.6引入的新PathPatternParser导致的。

四、解决方案

spring官方提及此issue:because "this.condition" is null · Issue #28794 · spring-projects/spring-boot · GitHub

但尚未解决,issue已关闭。

springfox社区活跃,已有大神解决此问题:Spring 5.3/Spring Boot 2.4 support · Issue #3462 · springfox/springfox · GitHub

适合的方案如下

 

1.Path匹配策略切换回ant_path_matcher(大多情况此方案可解决)

spring.mvc.pathmatch.matching-strategy=ant_path_matcher

2.若还是不能解决,添加如下配置

@Bean
ic WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping(WebEndpointsSupplier webEndpointsSupplier, ServletEndpointsSupplier servletEndpointsSupplier, ControllerEndpointsSupplier controllerEndpointsSupplier, EndpointMediaTypes endpointMediaTypes, CorsEndpointProperties corsProperties, WebEndpointProperties webEndpointProperties, Environment environment) {List<ExposableEndpoint<?>> allEndpoints = new ArrayList();Collection<ExposableWebEndpoint> webEndpoints = webEndpointsSupplier.getEndpoints();allEndpoints.addAll(webEndpoints);allEndpoints.addAll(servletEndpointsSupplier.getEndpoints());allEndpoints.addAll(controllerEndpointsSupplier.getEndpoints());String basePath = webEndpointProperties.getBasePath();EndpointMapping endpointMapping = new EndpointMapping(basePath);boolean shouldRegisterLinksMapping = this.shouldRegisterLinksMapping(webEndpointProperties, environment, basePath);return new WebMvcEndpointHandlerMapping(endpointMapping, webEndpoints, endpointMediaTypes, corsProperties.toCorsConfiguration(), new EndpointLinksResolver(allEndpoints, basePath), shouldRegisterLinksMapping, null);
}private boolean shouldRegisterLinksMapping(WebEndpointProperties webEndpointProperties, Environment environment, String basePath) {return webEndpointProperties.getDiscovery().isEnabled() && (StringUtils.hasText(basePath) || ManagementPortType.get(environment).equals(ManagementPortType.DIFFERENT));
}

涉及依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-actuator</artifactId><version>2.7.14</version><scope>compile</scope>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-actuator-autoconfigure</artifactId><version>2.7.14</version><scope>compile</scope>
</dependency>

配置完重启服务问题解决!

彩蛋

swagger3与springboot完整的集成方案已上架,欢迎查收:彩蛋~

相关文章:

SpringBoot v2.7.x+ 整合Swagger3入坑记?

目录 一、依赖 二、集成Swagger Java Config 三、配置完毕 四、解决方案 彩蛋 想尝鲜&#xff0c;坑也多&#xff0c;一起入个坑~ 一、依赖 SpringBoot版本&#xff1a;2.7.14 Swagger版本&#xff1a;3.0.0 <dependency><groupId>com.github.xiaoymin<…...

说说你了解的 CDC

分析&回答 什么是 CDC CDC,Change Data Capture,变更数据获取的简称&#xff0c;使用CDC我们可以从数据库中获取已提交的更改并将这些更改发送到下游&#xff0c;供下游使用。这些变更可以包括INSERT,DELETE,UPDATE等。用户可以在以下的场景下使用CDC&#xff1a; 使用f…...

SpingMvc入门

SpingMvc入门 1.MVC Spring的工作流程&#xff1a;2.sping mvc入门3.静态资源处理 前言 Spring MVC是一种基于Java的web应用开发框架&#xff0c;它采用了MVC&#xff08;Model-View-Controller&#xff09;设计模式来帮助开发者组织和管理应用程序的各个组件。 1.MVC Spring的…...

JVM的故事——类文件结构

类文件结构 文章目录 类文件结构一、概述二、无关性基石三、Class类文件的结构 一、概述 计算机是只认由0、1组成的二进制码的&#xff0c;不过随着发展&#xff0c;我们编写的程序可以被编译成与指令集无关、平台中立的一种格式。 二、无关性基石 对于不同平台和不同平台的…...

springboot自定义表格(动态合并单元格)

一、需求展示&#xff08;一个订单多个商品&#xff0c;商品数量不限订单行合并&#xff09; 二、技术选型&#xff08;jxls自定义模板&#xff09; <!-- 版本具体看官网Release&#xff0c;这里我们使用 2.13.0 --><dependency><groupId>org.jxls</group…...

C++零碎记录(二)

3. 调用其他类 3.1 类中有其他的类 #include <iostream> using namespace std;//点和圆关系案例//点类 class Point { public://设置xvoid setX(int x){m_X x;}//获取xint getX(){return m_X;}//设置yvoid setY(int y){m_Y y;}//获取yint getY(){return m_Y;}private…...

数学建模:回归分析

&#x1f506; 文章首发于我的个人博客&#xff1a;欢迎大佬们来逛逛 数学建模&#xff1a;回归分析 文章目录 数学建模&#xff1a;回归分析回归分析多元线性回归案例 多项式回归一元多项式回归多元二项式回归 非线性回归逐步回归 回归分析 多元线性回归 案例 首先进行回归分…...

数据库(一)

数据库 1.为什么要使用数据库 如果要存储数据&#xff0c;我们是可以使用文件来存储数据的&#xff0c;但是使用文件管理数据有很多缺点&#xff0c;比如&#xff1a; 不安全&#xff0c;不利于管理&#xff0c;查询&#xff0c;如果要存储大量的数据&#xff0c;使用文件管理…...

【算法与数据结构】106、LeetCode从中序与后序遍历序列构造二叉树

文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析&#xff1a;首先我们要知道后序遍历数组的最后一个元素必然是根节点&#xff0c;然后根据根节点在中序遍历数组中的…...

kali 安装cpolar内网穿透实现 ssh 远程连接

文章目录 1. 启动kali ssh 服务2. kali 安装cpolar 内网穿透3. 配置kali ssh公网地址4. 远程连接5. 固定连接SSH公网地址6. SSH固定地址连接测试 简单几步通过cpolar 内网穿透软件实现ssh 远程连接kali! 1. 启动kali ssh 服务 默认新安装的kali系统会关闭ssh 连接服务,我们通…...

算法训练 第一周

一、合并两个有序数组 本题给出了两个整数数组nums1和nums2&#xff0c;这两个数组均是非递减排列&#xff0c;要求我们将这两个数组合并成一个非递减排列的数组。题目中还要求我们把合并完的数组存储在nums1中&#xff0c;并且为了存储两个数组中全部的数据&#xff0c;nums1中…...

软件评测师之码制

目录 一、机器数二、码制三、数的表示范围 一、机器数 机器数就是一个数在计算机中的二进制表示&#xff0c;计算机中机器数的最高位是符号位&#xff0c;正数符号位为0&#xff0c;负数符号位为1&#xff0c;机器数包含原码、反码和补码三种表示形式。 二、码制 表现形式数…...

ubuntu18安装cmake27的方法

背景是ubuntu18默认的cmake是3.10 $ apt search cmake Sorting... Done Full Text Search... Done bear/bionic,bionic 2.3.11-1 allgenerate compilation database for Clang toolingcatkin/bionic,bionic 0.7.8-1 allLow-level build system macros and infrastructure for …...

通讯编程006——NodeJS OPC UA Client开发简单教程

本文介绍如何在NodeJS环境下开发OPC UA Client&#xff0c;通过本文可以对OPC UA的基本概念有所了解&#xff0c;掌握OPC UA的本质。相关软件请登录网信智汇(wangxinzhihui.com)。 开发步骤如下&#xff1a; 1&#xff09;首先需要安装nodejs&#xff0c;要求版本至少是12。 …...

「高等数学」雅可比矩阵和黑塞矩阵的异同

「高等数学」雅可比矩阵和黑塞矩阵的异同 雅可比矩阵&#xff0c;Jacobi matrix 或者 Jacobian&#xff0c;是向量值函数&#xff08; f : R n → R m f:\mathbb{R}^n \to \mathbb{R}^m f:Rn→Rm&#xff09;的一阶偏导数按行排列所得的矩阵。 黑塞矩阵&#xff0c;又叫海森矩…...

继承(个人学习笔记黑马学习)

1、基本语法 #include <iostream> using namespace std; #include <string>//普通实现页面//Java页面 //class Java { //public: // void header() { // cout << "首页、公开课、登录、注册...(公共头部)" << endl; // } // void footer() …...

ToBeWritten之ATTCK 测评方案

也许每个人出生的时候都以为这世界都是为他一个人而存在的&#xff0c;当他发现自己错的时候&#xff0c;他便开始长大 少走了弯路&#xff0c;也就错过了风景&#xff0c;无论如何&#xff0c;感谢经历 转移发布平台通知&#xff1a;将不再在CSDN博客发布新文章&#xff0c;敬…...

JSONUtil详解

JSONUtil是一个通用的JSON工具类&#xff0c;用于在Java中操作JSON数据。虽然之前提到的示例中没有直接提及JSONUtil&#xff0c;但可以解释一下可能存在的一些常见JSON操作方法&#xff0c;这些方法通常可以在不同的JSON工具类中找到。 JSONUtil中的一些常见方法包括&#xf…...

ArcGIS Maps SDK for JS(一):概述与使用

文章目录 1 概述2 如何使用ArcGIS Maps SDK for JavaScript2.1 AMD 模块与 ES 模块2.2 AMD 模块和 ES 模块比较 3 几种安装方式3.1 通过 ArcGIS CDN 获取 AMD 模块3.2 通过 NPM 运行 ES 模块3.3 通过 CDN 获取 ES 模块3.4 本地构建 ES3.5 本地构建 AMD 3 VSCode下载与安装2.1 下…...

【STM32】FSMC接口的复用和非复用

问题背景 在阅读《零死角玩转STM32—F103指南者》&#xff0c;以及《STM32F10x-中文参考手册》关于FSMC一章节的时候&#xff0c;对于在控制NOR/SRAM的时候使用到的引脚,在提到NOR器件的时候提到了地址复用和非复用接口&#xff0c;一时间没明白是什么东西。 结论 非复用模式…...

网络六边形受到攻击

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 抽象 现代智能交通系统 &#xff08;ITS&#xff09; 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 &#xff08;…...

树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法

树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源&#xff1a; http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作&#xff0c;无需更改相机配置。但是&#xff0c;一…...

DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径

目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...

SciencePlots——绘制论文中的图片

文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了&#xff1a;一行…...

基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容

基于 ​UniApp + WebSocket​实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配​微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...

五年级数学知识边界总结思考-下册

目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解&#xff1a;由来、作用与意义**一、知识点核心内容****二、知识点的由来&#xff1a;从生活实践到数学抽象****三、知识的作用&#xff1a;解决实际问题的工具****四、学习的意义&#xff1a;培养核心素养…...

Java多线程实现之Callable接口深度解析

Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...

WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成

厌倦手动写WordPress文章&#xff1f;AI自动生成&#xff0c;效率提升10倍&#xff01; 支持多语言、自动配图、定时发布&#xff0c;让内容创作更轻松&#xff01; AI内容生成 → 不想每天写文章&#xff1f;AI一键生成高质量内容&#xff01;多语言支持 → 跨境电商必备&am…...

(转)什么是DockerCompose?它有什么作用?

一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用&#xff0c;而无需手动一个个创建和运行容器。 Compose文件是一个文本文件&#xff0c;通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...

Redis数据倾斜问题解决

Redis 数据倾斜问题解析与解决方案 什么是 Redis 数据倾斜 Redis 数据倾斜指的是在 Redis 集群中&#xff0c;部分节点存储的数据量或访问量远高于其他节点&#xff0c;导致这些节点负载过高&#xff0c;影响整体性能。 数据倾斜的主要表现 部分节点内存使用率远高于其他节…...