自定义博客皮肤VIP专享

*博客头图:

格式为PNG、JPG,宽度*高度大于1920*100像素,不超过2MB,主视觉建议放在右侧,请参照线上博客头图

请上传大于1920*100像素的图片!

博客底图:

图片格式为PNG、JPG,不超过1MB,可上下左右平铺至整个背景

栏目图:

图片格式为PNG、JPG,图片宽度*高度为300*38像素,不超过0.5MB

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+
  • 博客(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

原创 Java核心: 类加载器

这一节我们来学习Java的类加载器,以及常用的类加载器实现URLClassLoader。

2024-06-09 22:52:34 1110

原创 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

原创 Window子系统Ubutun密码重置

Window里装了Ubutun子系统,但是太久没使用导致忘了密码以及root账号的密码,这时候该怎么办?

2024-06-04 13:31:32 780

原创 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关注的人

提示
确定要删除当前文章?
取消 删除