Springwebflux源码学习-服务启动流程

学习目标 理解WebFlux核心组件HttpHandler、WebHandler的创建过程 理解DispatcherHandler如何映射请求url及参数的过程 理解Netty服务的主要启动节点 此次学习的Spring boot版本为2.6.6,对应的Spring webflux版本为5.3.18 入口 Springboot工程入口方法为 SpringApplication.run() ,从此方法开始进行分析。 public static void main(String[] args) { SpringApplication.run(Main.class, args); } 决定启动的web应用类型 整个服务的web应用类型是在构造 SpringApplication 类时进行确定的,具体代码如下: this.webApplicationType = WebApplicationType.deduceFromClasspath(); 判断应用类型的逻辑 org.springframework.web.reactive.DispatcherHandler存在 org.springframework.web.servlet.DispatcherServlet不存在 org.glassfish.jersey.servlet.ServletContainer不存在 即确认webApplicationType为 Reactive if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null) && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) { return WebApplicationType.REACTIVE; } 创建具体的ApplicationContext 根据webApplicationType(Reactive)确定创建的ApplicationContext为AnnotationConfigReactiveWebServerApplicationContext,此Context支持 @Component 和 JSR-330规范中 @Inject 形式的依赖注解 SpringAplication.createApplicationContext() switch (webApplicationType) { case SERVLET: return new AnnotationConfigServletWebServerApplicationContext(); case REACTIVE: return new AnnotationConfigReactiveWebServerApplicationContext(); default: return new AnnotationConfigApplicationContext(); } 最主要的ApplicationContext.refresh()方法 refresh的Refresh阶段 刷新操作是在 AbstractApplicationContext 类的refresh方法中进行,在调用invokeBeanFactoryPostProcessors方法时会有对controller注解的扫描与处理 ...

2022-05-24 · 2 min · 351 words · tomyli

开发中会遇到的Maven问题解惑

多模块系统版本控制问题 在开发想保持项目下的所有子模块版本一致,可以使用${rversion}变量 配置父pom <groupId>cn.imcompany</groupId> <artifactId>big-parent</artifactId> <version>${revision}</version> <packaging>pom</packaging> <modules> <module>big-web</module> </modules> <properties> <revision>1.0.0</revision> </properties> 子模块配置 <parent> <groupId>cn.imcompany</groupId> <artifactId>big-parent</artifactId> <version>${revision}</version> </parent> <artifactId>big-web</artifactId> 修改版本方式 直接修改revision变量的值 使用 mvn clean package -Drevision=1.1.0 进行配置 使用revision后Deploy操作不显示正确版本问题 需要配合 Flatten Maven Plugin 即可解决,父pom中需要增加以下配置: <build> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>flatten-maven-plugin</artifactId> <version>1.2.2</version> <configuration> <updatePomFile>true</updatePomFile> </configuration> <executions> <execution> <id>flatten</id> <phase>process-resources</phase> <goals> <goal>flatten</goal> </goals> </execution> <execution> <id>flatten.clean</id> <phase>clean</phase> <goals> <goal>clean</goal> </goals> </execution> </executions> </plugin> </plugins> </build> 配置指定项目不发布到远程仓库 在新版本的deploy插件已经支持配置,配置skip变量即可 <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> <version>3.0.0-M2</version> <configuration> <skip>true</skip> </configuration> </plugin> Reference Maven: POM files without a version in it? - SoftwareEntwicklung Beratung Schulung

2022-04-07 · 1 min · 85 words · tomyli

Linux代码结构学习

Linux代码结构 准备学习Linux,看源码是少不了的,先对Linux源码有个大体认识(基于linux4.13) 代码主要文件解析 Makefile 这是整个源码的Makefile,里面定义了一系列的变量和规则,比如默认的gcc编译flag。 Documentation/ 项目文档,里面包含了很多关于配置内核、与ramdisk运行等有用的信息。这里面没有不同配置项的帮助信息,这些信息可以在相关子目录的 Kconfig 文件中查看。 arch/ 体系结构相关代码都在这里,还有一部分在include/arch-generic中。每个体系结构在这个目录下都有一个子目录。比如基于x86体系的代码都在 arch/x86 下。另外,这个目录下还包含低级别的内存管理,中断处理,早期初始化,汇编例程等信息。 crypto/ 内核使用的加密API代码。 drivers/ 所有设备相关的信息都在这个目录下。包括视频设备,网卡设备,低级别的SCSI设备。比如网络设备一般是在 drivers/net 目录下。 fs/ 通用的文件系统与其它类型的文件系统代码都在这里。现在的操作系统的根文件系统一般为ext4格式,它的代码在 fs/ext4 下 include/ 所有.c文件中包含的头文件都定义在这个目录下。 init/ 这个目录包含了内核启动的入口文件 main.c ,版本信息文件 version.c 和早期用户空间相关的代码,早期空间代码提供内核出现时的功能,它不需要在内核中进行运行。 ipc/ 进程间通信相关代码,包含共享内存,信号量等信息。 kernel/ 内核级别代码,上层的系统调用代码在这里,还有关于 printk() 代码,调度,信号量处理的代码。 lib/ 内核所有使用的库都在这里。常见的字符串运算,调试库,命令行解析代码等。 mm/ 高级别的内存管理代码。虚拟内存是通过这些库与低级别的体系结构代码(在 arch//mm )一同实现的。这里完成了早期引导内存管理,涉及文件的内存映射,页缓存的管理,内存分配和RAM中页面的交换。 net/ 高级别的网络代码。与低级别的网络进行发包与收包交互。*net/core* 中包含其它网络协议使用的核心代码。net子目录就是特定的网络协议实现。 scripts/ 这里面包含构建内核的脚本文件。 security/ 不同Linux的安全模型代码。 sound/ 声卡及相关代码在这里。 usr/ 该目录包含构建根文件系统映射的cpio格式的文档代码,该文档用于早期的用户空间。

2022-04-06 · 1 min · 54 words · tomyli

记一次SpringWebflux框架下堆外OOM排查经历

症状 生产环境业务页面打不开,看错误日志是 OutOfDirectMemoryError (OOM)了,详情报错信息如下: 2022-03-21 06:00:00.541 [,] [tafprx-asyrecv_9] ERROR r.c.p.Operators - [error,314] - Operator called default onErrorDropped java.lang.OutOfMemoryError: Direct buffer memory at java.base/java.nio.Bits.reserveMemory(Bits.java:175) at java.base/java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:118) at java.base/java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:317) at io.netty.buffer.PoolArena$DirectArena.allocateDirect(PoolArena.java:632) at io.netty.buffer.PoolArena$DirectArena.newChunk(PoolArena.java:607) at io.netty.buffer.PoolArena.allocateNormal(PoolArena.java:202) at io.netty.buffer.PoolArena.tcacheAllocateSmall(PoolArena.java:172) at io.netty.buffer.PoolArena.allocate(PoolArena.java:134) at io.netty.buffer.PoolArena.allocate(PoolArena.java:126) at io.netty.buffer.PooledByteBufAllocator.newDirectBuffer(PooledByteBufAllocator.java:395) at io.netty.buffer.AbstractByteBufAllocator.directBuffer(AbstractByteBufAllocator.java:187) at io.netty.buffer.AbstractByteBufAllocator.directBuffer(AbstractByteBufAllocator.java:178) at io.netty.buffer.AbstractByteBufAllocator.buffer(AbstractByteBufAllocator.java:115) at org.springframework.core.io.buffer.NettyDataBufferFactory.allocateBuffer(NettyDataBufferFactory.java:71) at org.springframework.core.io.buffer.NettyDataBufferFactory.allocateBuffer(NettyDataBufferFactory.java:39) at org.springframework.http.codec.json.AbstractJackson2Encoder.encodeValue(AbstractJackson2Encoder.java:236) at org.springframework.http.codec.json.AbstractJackson2Encoder.lambda$encode$0(AbstractJackson2Encoder.java:150) at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:113) at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1815) at reactor.core.publisher.MonoCompletionStage.lambda$subscribe$0(MonoCompletionStage.java:82) at java.base/java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:859) at java.base/java.util.concurrent.CompletableFuture.uniWhenCompleteStage(CompletableFuture.java:883) at java.base/java.util.concurrent.CompletableFuture.whenComplete(CompletableFuture.java:2251) at java.base/java.util.concurrent.CompletableFuture.whenComplete(CompletableFuture.java:143) at reactor.core.publisher.MonoCompletionStage.subscribe(MonoCompletionStage.java:57) at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64) at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:157) at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1815) at reactor.core.publisher.MonoFlatMap$FlatMapInner.onNext(MonoFlatMap.java:249) at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79) at reactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:199) at reactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:199) at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.complete(MonoIgnoreThen.java:284) at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onNext(MonoIgnoreThen.java:187) at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1815) at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:151) 根据报错的堆栈,找到 AbstractJackson2Encoder:236 行,调试发现框架使用的buffer在最后都进行了释放,看着没问题。 ...

2022-03-31 · 3 min · 615 words · tomyli

那些提高效率MAC软件(全程免费)

NTFS 支持MAC电脑读取NTFS格式文件 eul 监控MAC运行状态的工具 uPic 文件与图片云端上传工具 Sequel-Ace MAC上数据库管理工具,支持Mysql与MariaDB CopyQ MAC上免费的剪切板工具 Bob MAC上翻译软件 LuLu MAC上强大的防火墙 hidden MAC菜单栏图标隐藏工具 Brooklyn MAC上炫酷的屏保程序 Gifski 把Video转换成Gif BackgroundMusic 音频工具,可以为每个app设定音量值 pixel-picker MAC取色软件 Itsycal MAC日历软件 SwitchHosts 快速切换机器Host,开发必备 hammerspoon 使用Lua脚本来控制电脑 squirrel 鼠鬚管,无敌的输入法 iTerm2 MAC上的终端模拟器 keycastr MAC上按键显示工具 TODO pot-app/pot-desktop: 🌈一个跨平台的划词翻译和OCR软件 | A cross-platform software for text translation and recognition. TODO jordanbaird/Ice: Powerful menu bar manager for macOS TODO qarmin/czkawka: Multi functional app to find duplicates, empty folders, similar images etc. TODO FelixKratz/SketchyBar: A highly customizable macOS status bar replacement TODO nikitabobko/AeroSpace: AeroSpace is an i3-like tiling window manager for macOS TODO Alex313031/thorium: Chromium fork named after radioactive element No. 90. Windows and MacOS/Raspi/Android/Special builds are in different repositories, links are towards the top of the README.md. TODO odlp/bluesnooze: Sleeping Mac = Bluetooth off TODO nonwill/GoldenDict-OCR: GoldenDict++:内置大量的官方版本问题的修正;先期添加了一个简单的插件机制,并基于该机制接入了多个 OCR 划词 和 音频播放 引擎;后期在增强易用性的基础上为提高查询效率、减少运行时 CPU 及 内存 占用、降低代码维护难度,完全重构了所有的实现;将来的目标是将功能扩展和词典格式处理抽象为完整的插件实现,以进一步增强应用的扩展性和可维护性。 TODO Homebrew/homebrew-bundle: 📦 Bundler for non-Ruby dependencies from Homebrew, Homebrew Cask and the Mac App Store. ungoogled-software/ungoogled-chromium: Google Chromium, sans integration with Google 生活中去google化 ...

2021-10-25 · 1 min · 180 words · tomyli

让好代码生生不息-更好的使用String

开发中常用的String操作 开发中经常用到的操作有: 格式化/日志格式化 字符串判断 字符串处理,常见的如处理字符串的第一个/最后一个字符 String.format String.format()是一个格式化的方法,使用%为前缀来表示要格式化的内容占位,一般的使用方法为: String.format("test result is %s", "success"); 但是当要格式化的占位符与参数个数不同时会发生什么呢? 占位符个数与参数个数相同 如上代码,可以正常进行输出: test result is success 占位符个数少于参数个数 String.format("test result is %s", "success", "other"); 可以正常打印,输出结果为: test result is success 占位符个数多于参数个数 String.format("test result is %s %s %s", "success", "other"); 抛出了异常java.util.MissingFormatArgumentException: Format specifier ‘%s’ 问题总结 为什么占位符数少于参数个数可以正常执行,而多于参数个数却报了异常?看JAVADOC发现,如果占位符个数少于参数个数,则多余的参数被忽略,如果多于参数个数,则在进行解析时会判断待处理占位符位置与参数个数,主要代码逻辑如下: if (args != null && lasto > args.length - 1) throw new MissingFormatArgumentException(fs.toString()); 所以在使用String.format方法 切记 要参数个数多于待解析占位符的个数,这种问题常因为Copy操作后,处理了部分参数而忘记对应处理占位符 替换方案 目前看到很多的老代码打印日志时大都使用String.format(),可以改成slf4j的{} 还可以使用Apache common utils中的StringUtils.joinWith()或者Google guava的Joiner连接器 Apache commons utils-StringUtils 在StringUtils中有一个isNumeric方法,用来判断字符串是不是数字,那下面的代码会返回什么呢? ...

2021-08-23 · 2 min · 221 words · tomyli

让好代码生生不息-更好的对待NULL

什么样的代码是好代码 易读的代码,行云流水的代码 JAVA中的NULL处理 众所周知,在Java代码中总是少不了一堆的判null逻辑,在代码bug中,NullPointerException出镜率也是非常高的,尤其是现在的主流分布式架构,一个web接口要调用后端的N个RPC服务来实现功能,基于防御编程的思想,判null就更重要了 在处理NULL时怎么做更好 IDE 既然无法避免null,IDE就给程序员增加了可以快速判断空的功能,比如在IDEA中,可以使用Postfix功能,在对象上o使用.nn来快速输入if(o != null) {} JAVA基础类库 近期的JDK版本也针对null判断增加了一些实用的方法 Objects类 JAVA8在Objects工具提供了isNull()和nonNull()方法,JAVA9又增加了requireNonNullElse()和requireNonNullElseGet()来给待处理对象增加设置默认值的方法 知名三方库 Apache common utils中提供了非常多的判空工具类,如针对String操作的StringUtils类和针对集合操作的CollectionUtils、MapUtils、ListUtils、SetUtils,这几个类都有一些典型的方法来进行判空处理,通用的方法如下: StringUtils isEmpty()/isNotEmpty() 判断String是否为空/不空 isBlank()/isNoneBlank() 判断String是否为空/不空,这类方法还会判断实际值是否为空串 defaultString() 此方法允许在String为null时为返回空串 defaultIfBlank()/defaultIfEmpty() 此方法允许在String为空串或null时设置一个默认值 集合类Utils isEmpty()/isNotEmpty() 判断集合是否为空/不空 emptyIfNull() 这是一个很有用的方法,当传入对象为空时则返回设置的默认对象,可以很好的根治if处理null的问题,下面两段代码演示一下 正常情况下有if判断 List<Object> values = rpcClient.getFromRpcList(); if(CollectionUtils.isNotEmpty(values)) { return values .forEach(System.out::println); } 使用emptyIfNull方法 return CollectionUtils.emptyIfNull(rpcClient.getFromRpcList()) .forEach(System.out::println); 这样保证了代码编写的连贯性,爽! 此方法在ListUtis、MapUtils、SetUtils中也有提供 defaultIfNull() 此方法允许在集合为null时设置一个默认值,也是可以链式操作 优秀的Coder 在编写通用方法时,对于有null返回的情况可以使用返回默认值来代替null 不返回空,以空对象来代替 比如返回一个new Object() ...

2021-07-17 · 1 min · 60 words · tomyli

JAVA中获取调用栈帧的最优方式

前言 近期在做日志优化,需要记录业务日志,日志内容包括业务的请求、响应信息。当然还应该有具体的调用方法位置、日志的所在的方法等通用信息。 解决方案 一般情况下,在程序有异常信息时可以打印出整个调用堆栈信息,但是现在需要的是正常调用下来获取到当前方法的调用帧。 JAVA9之前 Thread.dumpStack() 这个可以打印出当前的堆栈错误信息,本质上是new一个Exception,调用了printStackTrace()方法,但是这个方法只建议用于调试。 public class StackTraceExample1 { public static void main(String[] args) { one(); } public static void one() { two(); } private static void two() { three(); } private static void three() { Thread.dumpStack(); } } 执行后输出如下: java.lang.Exception: Stack trace at java.base/java.lang.Thread.dumpStack(Thread.java:1379) at cn.imcompany.stack.StackTraceExample1.three(StackTraceExample1.java:25) at cn.imcompany.stack.StackTraceExample1.two(StackTraceExample1.java:21) at cn.imcompany.stack.StackTraceExample1.one(StackTraceExample1.java:17) at cn.imcompany.stack.StackTraceExample1.main(StackTraceExample1.java:13) 直接以异常的方式显示出来了。 ...

2021-04-20 · 1 min · 154 words · tomyli

2020-ARTS-打卡第十四天

Algorithm 题目 题目描述 98.验证二叉搜索树,给定一个二叉树,判断其是否是一个有效的二叉搜索树。假设一个二叉搜索树具有如下特征: 节点的左子树只包含小于当前节点的树 节点的右子树只包含大于当前节点的树 所有左子树及右子树自身也必须是二叉搜索树 题目解答 import java.util.Stack; public class ValidBST { public boolean isValidBST (TreeNode root) { Stack<TreeNode> stack = new Stack<>(); double inorder = -Double.MAX_VALUE; while (!stack.isEmpty() || root != null) { while (root != null) { stack.push(root); root = root.left; } root = stack.pop(); if (inorder >= root.val) { return false; } inorder = root.val; root = root.right; } return true; } } 使用中序遍历(即左->根->右)来实现,从左至右进行比对,每次与上次保存的值进行对比,如果当前节点值比上次保存的值小,则可以判定此树不是二叉搜索树。时间复杂度为O(n),空间复杂度为O(n) ...

2020-04-20 · 1 min · 126 words · tomyli

2020-ARTS-打卡第十三天

Algorithm 题目 题目描述 给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。答案中不可以包含重复的三元组。 题目解答 import java.util.ArrayList; import java.util.Arrays; import java.util.List; class Solution { public List<List<Integer>> threeSum(int[] nums) { List<List<Integer>> result = new ArrayList<>(); if(null == nums || nums.length < 3) { return result; } Arrays.sort(nums); for (int i = 0; i < nums.length; i ++) { if(nums[i] > 0) { break; } if(i > 0 && nums[i] == nums[i - 1]) { continue; } int L = i + 1; int R = nums.length - 1; while (L < R) { int target = nums[i] + nums[L] + nums[R]; if (target == 0) { result.add(Arrays.asList(nums[i], nums[L], nums[R])); while (L < R && nums[L] == nums[L + 1]) L++; while (L < R && nums[R] == nums[R - 1]) R--; L++; R--; } else if (target < 0) { L++; } else { R--; } } } return result; } 一共有三种解法,一是三层for循环,时间复杂度为O(n3);二是二层for循环外加Hash表,时间复杂度为O(n2),增加了空间复杂度O(n);第三种就是上面的解法,排序后从两侧开始匹配,时间复杂度为O(n2),空间复杂度为O(1) ...

2020-04-08 · 2 min · 229 words · tomyli