- 博客(76)
- 收藏
- 关注
原创 一个优秀的日志应该是什么样的
在上一篇"一个合格的日志应该是什么样的",我们打印的日志已经基本可用了。在线上接口行为和预期的不一致时,INFO日志又不够详细,看不出问题。
2025-03-30 16:16:12
850
原创 一个合格的日志应该是什么样的
参数校验失败用WARN,通过后打印参数用DEBUG级别,核心方法用INFO,调用量大时对INFO日志采样或降级为DEBUG...如果是重要接口,可以用INFO打印入参...当然为了代码美观,你可以将入参的打印逻辑用AOP封装,将参数校验的逻辑提取到专门的校验方法,自定业务异常及Assert工具返回响应。
2025-03-30 11:25:40
436
原创 忽略Git文件的修改,让它不被提交
有的时候文件是由git托管的,但是我们希望只在本地修改,而且修改不被提交到服务端,这时候可以使用。使用Git托管的工程中,经常有这样的需求,希望文件只是本地修改,不提交到服务端。如果仅仅是本地存在的文件,我们可以通过.gitignore配置避免文件被提交。
2025-02-24 22:46:49
355
原创 Spring Cache的使用
Spring提供了EhCache和Caffeine的默认实现,如果使用没有默认实现的Cache,可以通过自定义CacheManager来实现@Bean。
2025-01-15 21:25:14
1026
原创 文件上传超时问题定位: 背景知识
文件上传时,浏览器和服务端建立连接,读取并发送文件的内容,Servlet容器接收数据后写入本地文件,封装为对象传递给Servlet实现类。如果是Spring Cloud Gateway,可以通过如下设置,配置Gateway和后端服务器的超时时间。如果使用的是Tomcat,可以设置连接超时时间和读入超时时间。
2025-01-07 21:43:29
582
原创 怎么确定性能优化是真的有效?
当我们做的软件即将迎接挑战的时候,我们经常会做的事情是压测,并尝试做性能优化,不过性能优化的效果有时候并不是那么的明显。假设我们对一个接口做了优化,优化前后接口的耗时分别如下图有2次是接口性能明显提供(迭代1、3),有1次明显变差,感觉是优化成功了,而这差异可能是环境造成的,无法做一个定量的明确回答。
2024-10-31 11:16:32
449
原创 基于Redis实现的延迟队列
我们需要做的是在程序中定时的执行这段Lua脚本,并且实现类似DelayQueue的逻辑,支持阻塞的take()操作,以及消费失败时的错误处理,显然要处理的错误细节并不少。通过[[#3. 数据消费]]的操作,redisson已经将到期的延迟任务写入到delayBlockingQueue了,剩下要做的就是用delayBlockingQueue实现阻塞队列了,核心代码在 RedissonBlockingQueue,其实实现很简单,我们来看下代码,take()方法实际只是执行了一个redis命令。
2024-10-11 18:05:05
623
原创 ShardingSphere导致的NPE
1. 项目背景工程内使用shardingsphere支持分库分表,上层使用的MybatisPlus,有一张表的操作总是报NullPointException。2. 异常堆栈### SQL: INSERT INTO t_tg_message ( update_id, from_id, from_user_name, chat_id, type, data, text, create_time, deleted ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ? )### C
2024-08-23 10:08:12
513
原创 Git仓库拆分和Merge
我们原先有一个项目叫open-api,后来想要做租户独立发展,每个租户独立成一个项目,比如租户akc独立部署一个akc-open-api,租户yhd独立部署一个yhd-open-api,其中大部分代码是相同的,少量租户定制代码。上面我们从open-api项目复制了一个新的akc-open-api项目,公共的代码修我们不想重复劳动,通过修改open-api,再通过跨项目合并到不同的租户下。这一节的目的是从open-api复制一个新项目称为akc-open-api,操作步骤如下。关联open-api的仓库。
2024-07-20 11:36:55
745
原创 Maven报错 NullPointerException as JavaCompiler.readSourceFile
我这的问题的原因是在Maven使用的Java和工程使用的Java版本问题,全局环境变量设置的Java版本为Java 11,而IDEA设置的是Java8,Maven配置是。修改项目的JDK版本,修改环境变量中的Java版本就能解决该问题。
2024-07-11 22:32:13
633
1
原创 GRPC使用之ProtoBuf
Protocol Buffers提供一种跨语言的结构化数据的序列化能力,类似于JSON,不过更小、更快,除此以外它还能用用接口定义(IDL interface define language),通protoc编译Protocol Buffer定义文件,生成结构化类,以及服务调用的客户端和服务端。除此以外,proto允许用户自己通过message、enum定义自己的类型,比如之前提到的Person,我们再看一下示例。
2024-07-07 17:00:41
1068
原创 GRPC使用之HelloWorld
先通过mvn archetype:generate从quickstart生成一个极简的Maven项目引入grpc的Maven依赖以及打包插件,插件略复杂,主要的目的是为了将grpc生成的代码直接打包进当前工程的jar,暂时忽略具体含义即可。
2024-07-05 11:15:40
722
原创 SpringBoot: Eureka入门
服务提供者在启动的时候会将自己注册到服务注册中心(zookeeper、consul等实现),服务消费者从注册中心拿到服务提供者的IP,在客户端做负载均衡,直接连接服务提供者的IP,相较于反向代理的方案好处是服务A和服务B是直接调用,避免了一次中间转发。最简单的方法是让服务A配置服务B的所有节点的IP,在服务A内部做负载均衡调用服务B的不同节点。我们先来看看Eureka的使用。我们需要将服务B的节点配置为upstream,定义nginx的server,服务A通过nginx调用服务B,我们看看下面的核心配置。
2024-06-29 20:00:18
1581
原创 Java核心: JarIndex的使用
在讲解的时候,我们发现URLClassLoader加载类或资源时通过访问ClassPath下的每一个路径,来确定类是否存在的,假设我们执行的命令是这样的如果我们代码里使用了org.springframework.boot.loader.launch.Archive,我们的类加载器会依次尝试在每个ClassPath下每个元素一个复杂的大型项目依赖几十个上百个第三方jar是很常见的,总是这么查找显然是极其低效的。
2024-06-10 13:42:56
1504
原创 SpringBoot: 启动流程和类装载
前面我们学过Spring定制了自己的可执行jar,将真正执行时需要的类和依赖放到BOOT-INF/classes、BOOT-INF/lib来,为了能够识别这些为止的源文件,Spring定制了自己类加载器,本节我们来讲解这个类加载器。
2024-06-09 22:26:46
1805
1
原创 SpringBoot: 可执行jar的特殊逻辑
这一篇我们来看看Java代码怎么操作zip文件(jar文件),然后SpringBoot的特殊处理,文章分为2部分。
2024-06-08 16:12:44
1351
1
原创 Java核心: 为图片生成水印
今天干了一件特别不务正业的事,做了一个小程序用来给图片添加水印。事情的起因是有人管我要身份证照片,手边并没有一个趁手的工具来生成图片水印。很多APP提供了水印的功能,但会把我的图片上传到他们的服务器,身份证太敏感了,显然我并不想让别人有机会保留照片。我把图片处理做了一个抽象,入参是BufferedImage,对图片添加水印、盲印、隐式写入后返回新的BufferedImage作为结果。
2024-06-06 21:45:38
672
原创 SpringBoot: 读取项目的Git版本号
在开发项目的时候,我们经常会想要拿到线上运行的程序版本,以确定程序是否正确发布。Spring Boot提供了这样的能力支持。
2024-06-05 20:58:34
1368
原创 SpringBoot: 使用GraalVM编译native应用
曾今Go语言里让我最艳羡的两个特性,一个是Goroutine,一个是native编译。Java 21的虚线程实现了类似Goroutine的能力。Spring Boot 3.x开始提供了GraalVM的支持,现在Spring Boot也能打包成native文件了。这一篇文章的目标是用一个案例讲解如何将Spring Boot应用打包成native文件。
2024-06-05 12:23:42
2371
1
原创 Maven实战: 从工程创建自定义archetype
使用mvn archetype:generate命令,利用上面生成的模板,创建工程,使用如下命令创建新项目。
2024-06-04 21:15:23
590
原创 Maven实战: 创建自定义archetype
在DiveInSpringArchetype根目录下已经生成了pom.xml文件,我们修改一下pom.xml的内容,只保留GAV信息,添加archetype-packaging扩展接着来定义archetype-metadata.xml,这个文件存放在src/main/resources/META-INF/maven/archetype-metadata.xml,重点关注下面3个点。
2024-06-04 19:47:50
2203
原创 SpringBoot:手动创建应用
Spring提供了在线的Spring Initialzr在线创建Spring Boot项目,为了更好的理解Spring Boot项目,这里我们选择手动创建。
2024-06-02 17:28:02
1315
1
原创 Java核心: 使用instrumentation
接下来我们要做的是定义一个最简单的instrumentation实现,仅仅是打印-javaagent后带的参数,以及执行过程加载的所有的类、它所属的ClassLoader。首先要做的是创建工程,后续用于打包成agent的jar,这里我们命名为AsmToString.jar。这里使用mvn archetype:generate创建工程为了让-javaagent能使用这个jar,需要在MANIFEST.MF里添加Premain-Class的定义。
2024-05-29 10:10:18
1301
原创 Java核心: 使用asm操作字节码
在上一篇中我们提到,通过实现AbstractProcessor,并调用javac -processor能够生成代码来实现特殊逻辑。这一篇我们讲解ASM的目的就是解决问题1,它不但能建新类,还能修改已有类,比如为POJO类生成toString方法,为Service类的业务方法提供类似AOP的增强。本文的讲解思路如下。
2024-05-28 12:48:39
1442
原创 Java核心:注解处理器
首先我们需要定义一个注解,用来标注后续要生成toString方法的类。@ToString的逻辑很简单,这里我们只把它定义为一个标记注解。定义@ToString注解之后,我们就可以把它用在想要自动生成toString方法的类上,比如我们有一个Point类的定义,我们希望为Point类生成toString方法,可以在Point上添加@ToString注解@ToStringreturn x;return y;
2024-05-26 21:43:12
1114
原创 Java核心: 脚本引擎和动态编译
静态语言和动态语言的在相互吸收对方的优秀特性,取人之长补己之短。脚本引擎和动态编译就是其中一个关键特性,扩展了Java的能力边界。
2024-05-26 13:56:30
1678
原创 Java核心: Stream流的实现原理
Java 8之后我们对Stream的操作已经习以为常了,它帮助我们从怎么做的细节里脱身,只需要告诉它做什么即可。这一篇文章我们主要讲讲Java Stream的实现原理,自己手写一个Stream框架,然后再来讲解Java Stream的核心类,做到知其然知其所以然。我们先看一个Stream使用案例,假设用一堆字符串创建流(of),取前5个(limit)、操作每个字符串取首字母(map),最后用终结操作符收集所有的首字母到List中(collect)。
2024-05-22 18:21:32
1597
原创 Java并发: 我所经历过的坑
我们用下面这段代码模拟了这种场景,首先我们定义了一个LongWait类,目的是让它占用线程池的线程,好让线程池没有线程可用,走RejectedExecutionHandler的逻辑。可以看到Broken运行之前,有一个timer-thread-线程,但是Broken运行后,这个线程就丢失了,连带着Timer运行异常,HeartBeat不再打印。下面是调度代码,通过日志能看到的是future.get()一直没有返回,最后的一个代码无法输出,具体看下面的线程堆栈信息。===持续更新,也欢迎提供案例===
2024-05-21 17:54:22
607
原创 Java并发: 锁和同步
前面我们通过LockSupport实现了一个简单的独占锁FIFOMutex,但是功能比较简易。Java内部通过了一个类似的实现,只需要覆写少数方法就能创建一个功能强大的锁,AbstractQueueSynchronizer类似于FIFOMutex,AQS也维护了一个内部状态state,将等待锁的线程通过一个CLH队列保存,额外提供ConditionObject对象,支持基于条件的等待还唤醒,同时它还支持共享锁。
2024-05-21 10:38:01
1705
原创 Java并发: 基于Unsafe的CAS实现无锁数据结构
为了方便测试和对比,我们事先定义了ID接口,里边只有一个方法incrementAndGet: 自增并返回int值。
2024-05-19 11:55:26
1137
原创 Java并发: 面临的挑战
关于ABA问题,我们举个例子来看,时间点1,线程1和线程3都读取到变量v的值1;编译成字节码后并没有看起来这么简单,对应的字节码如下(删除了Code里部分字节码),它会被拆成4步,加载id的值(Code:2),加载常量1(Code:6),将id和1相加(Code:7),写入到id字段(Code:8)想想我们在数据库使用乐观锁时SQL是怎么写的,是不是感觉有异曲同工之妙呢,oldStock就是expected,newStock就是x,version一般都是单调递增的,提供了更强的保护,避免ABA问题。
2024-05-19 08:32:06
967
原创 Java并发: 线程线程池和回调
被绑定的平台线程有个名字叫carrier。我们通过操作系统执行一个可执行程序时,比如Shell下执行echo,Shell会通过系统条用fork()创建一个新的进程,再通过exec()系统调用读取可执行文件ELF,用其中的数据布置现场,将其中的指令初始化栈和PC计数器,分时调度到当前进程的时候,就可以执行当前进程对应的CPU指令集了。在基于平台线程的时候,这种方法确实是奏效的,因为平台线程的个数是有限的,而且往往会使用线程池,因此经过特定次数的初始化后,就不再需要创建新的SimpleDateFormat了。
2024-05-17 22:53:19
1456
原创 Java泛型,这一篇就够了
之前的案例里我们已经看到泛型类( RawArray$Pair)的定义了,泛型类的类型参数不能用到静态方法上。我们来看一下泛型方法的定义,类型参数放到修饰词之后,返回值之前,看示例的of方法。通常调用泛型方法时我们不需要明确地知道类型参数,编译器自己能通过入参/返回值接收对象的引用推断出类型参数。r.key = k;return r;通过这么使用我们定义的泛型方法继承自Throwable的类不能是泛型类型,catch的括号里不能用类型变量。
2024-05-13 08:00:56
1352
原创 Java Web这一路走来
大部分Java应用都是Web或网络应用,MVC框架在Java框架中有着举足轻重的地位,一开始的Web应用并不现在这样子的,一步一步走来,每一步都经历了无数的血和泪的教训,以史为镜可以知兴替。
2024-04-07 11:47:02
957
原创 Cassandra入门试用
不确定系统瓶颈的情况下,不要随意调优,Connection和新建Connection的Threshold调整可能不但不提升性能,反而降低。单连接、3节点、普通HDD、单JVM分配8G内存的情况下,并发30~40基本到最高性能, 35K QPS。配置文件修改, $CASSANDRA_HOME/conf/cassandra.yaml。案例: 配置了3节点集群。
2023-12-25 20:13:34
1214
1
原创 bash命令: logrotate的使用
Linux服务器上运行的程序都会通过日志来告知外部自己的运行过程、结果以及遇到的问题,随着日志的不断累计,它会变的越来越大,所以常见的方案是根据时间或者日志文件的大小,对日志文件进行轮转,比如Java程序里使用Log4j按天创建日志文件,不过很多Linux程序并没有提供这样的能力,logrotate就是问了解决这个问题而诞生的。
2023-12-13 20:20:26
1528
原创 OkHttp: 拦截器和事件监听器
假设我们访问的是http://www.publicobject.com/helloworld.txt,它实际上会有一个302跳转到https://publicobject.com/helloworld.txt, OkHttp会自动完成跳转。拦截器是一种强大的机制,可以用来监测、重写、重试调用。下面是一个简单的例子,用来打印请求的输入和输出。输出只会有一个Request,一个Response,内部的跳转过程没有感知。重写响应可以重写响应的HTTP头、响应内容等,一般来说是不推荐的,可能会违反直觉。
2023-12-12 21:21:30
1521
原创 OkHttp: 使用入门
当你需要为一个请求修改设置的时候,调用OkHttpClient.builder()方法,返回的builder和原始的OkHttpClient共享连接池、Dispatcher和配置信息,只需要修改特有的配置即可。使用流做为请求体,请求体内容会在发送请求的时候输出,这个例子里使用的是Okio的BufferedSink,你也可以通过BufferedSkin.outputStream获取输出流。当端不可触达的时候,客户端连接问题、服务端不可用、或者两者之间的问题,可以通过设置超时。Call可以被其他线程取消。
2023-12-10 18:32:52
2961
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人