john's tech blog

hope is coming


  • 首页

  • 标签

  • 归档

mybatisplus版本引发的连接泄漏血案

发表于 2020-04-18

上周几次接到同事电话说线上环境用户登陆失败,我一查线上日志看到很多Connection timeout的错,

因为我们用到了HikariCP连接池,理论上来说不会出现连接泄漏的问题,我就加了HikariCP的leakDetectionThreshold连接泄漏检测配置。

今晚又被提示说登陆不上了,我一看日志,果真如提前预料的那样也是Connection timeout的错,但是这次竟然日志中没有出现Apparent connection leak detected类似的字样,甚是奇怪。

其实在去年年底的时候已经出现过几次,当时我是定位到mybatisplus中ActiveRecord用法(比如updateById等操作)会导致Connection timeout,当时也没去深入源码看是什么问题就草草略过,只是叫同事不要用ActiveRecord的操作(可惜同事还是继续用的乐此不疲),后来又遇到多数据源下Spring事务不生效的问题,是因为不是用同一个dynamicDatasource的Bean导致的,之后也想当然认为ActiveRecord引发的Connection timout是这个问题引起的,当时系统也没多少用户访问,所以问题没暴露的频繁。

这次几回出现这个异常看来是另有隐情, 我想从根本上解决问题,首先就把日志级别调为debug模式,然后我结合着用户日志行为分析(这几次是每到傍晚6点过后就会出现问题),模拟用户界面操作,果然过一会连接数飙升,在HikariCP经过Cleanup后也没移除,既有连接一直处于active状态,说明连接是存在泄漏。接着我发现了为什么leakDetectionThreshold没生效?竟然是因为我没在代码里加上相应配置,只是在配置文件中加上而已(因为我们是用的自定义数据源,所以配置也需要在HikariCP的配置Bean中手动加上)。

果然过了我配置的60s检测时间过后就抛出了一条Apparent connection leak detected的异常,异常定位到我们使用ActiveRecord的updateById那边,这边我也同时想到了 在我们写的类似代码里调用ActiveRecord的时候是不会被Spring管理的,因为我们操作的Entity是自己new出来然后去selectById的,不是经过注入的Service去调用的。 接着我下意识点开updateById的定义,发现了重要线索,里面竟然在update过后没有调用closeSession,我也看了insert方法是有close操作的。所以到这里我想应该是mybatisplus本身的bug,然后我一看我们项目引用的版本竟然是RC版,我瞬间感觉崩溃(当时竟然没去考虑这个版本问题)。为了证明这一点,我去github上的mybatisplus版本release记录中去看,发现他们在3.0.4中才把这个bug修复,加上了finally close的操作,而我们用的只是3.0.1-RC版本。

最后我把pom中mybatisplus版本升到了3.0.4把这个坑给填上了。

参考资料:

  1. 聊聊hikari连接池的leakDetectionThreshold
  2. 去除全局缓存sqlSession,增加Model,通用service层sqlSession释放.修改测试用例异常范围
  3. 使用mybatis plus的activerecorde怎么开启事务呢

spring中的annotation-config

发表于 2020-04-14

spring中ApplicationContextInitializer接口的使用

发表于 2020-04-09

ApplicationContextInitializer接口有哪些用处呢?

  1. 用于ConfigurableApplicationContext通过调用refresh函数来初始化Spring容器之前的回调函数;

  2. 通常在web应用中,设计在初始化Spring容器之前调用。例如依赖于容器ConfigurableApplicationContext中的Enviroment来记录一些配置信息或者使一些配置文件生效;

  3. 参考ContextLoader和FrameworkServlet中支持定义contextInitializerClasses作为context-param或定义init-param。

  4. 支持Order注解,表示执行顺序,越小越早执行.

Spring中自带的实现ApplicationContextInitializer接口的初始化器

  1. ContextIdApplicationContextInitializer
    ContextIdApplicationContextInitializer,上下文id初始化器。该初始化器的核心作用是给上下文起个名,一般为application,可以通过spring.application.name配置来进行修改。

  2. DelegatingApplicationContextInitializer
    DelegatingApplicationContextInitializer,委托应用上下文初始化器。该初始化器的能力和委托应用监听器是一样的,它使得我们可以通过配置文件的context.initializer.classes来配置初始化器,而不必使用spring.factories。

  3. ServerPortInfoApplicationContextInitializer

ServerPortInfoApplicationContextInitializer,服务端口信息初始化器,该初始化器同时也是一个监听器。在ServerPortInfoApplicationContextInitializer中,在初始化时,将自己放到了应用的监听器列表中。

此后,ServerPortInfoApplicationContextInitializer会监听web服务初始化事件,该事件发生时,会将服务的端口号这到环境的属性源中。

参考资料:

  1. Spring中,Bean定义加载器的核心功能
  2. ApplicationContextInitializer使用以及加载的原理

  3. SpringBoot之ApplicationContextInitializer的理解和使用

spring中的MutablePropertySources类设计

发表于 2020-04-08

刚看到MutablePropertySources这个类中有一段示例感觉蛮有意思的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
* Customize the set of {@link PropertySource} objects to be searched by this
* {@code Environment} during calls to {@link #getProperty(String)} and related
* methods.
*
* <p>Subclasses that override this method are encouraged(鼓励) to add property
* sources using {@link MutablePropertySources#addLast(PropertySource)} such that
* further subclasses may call {@code super.customizePropertySources()} with
* predictable(可预测的) results. For example:
* public class Level1Environment extends AbstractEnvironment {
* @Override
* protected void customizePropertySources(MutablePropertySources propertySources) {
//没有来自基类的操作 no-op from base class
* super.customizePropertySources(propertySources);
* propertySources.addLast(new PropertySourceA(...));
* propertySources.addLast(new PropertySourceB(...));
* }
* }

* public class Level2Environment extends Level1Environment {
* @Override
* protected void customizePropertySources(MutablePropertySources propertySources) {
//从超类中全部 添加 add all from superclass
* super.customizePropertySources(propertySources);
* propertySources.addLast(new PropertySourceC(...));
* propertySources.addLast(new PropertySourceD(...));
* }
* }
* </pre>
* In this arrangement, properties will be resolved against sources A, B, C, D in that
* order. That is to say that property source "A" has precedence over property source
* "D". If the {@code Level2Environment} subclass wished to give property sources C
* and D higher precedence than A and B, it could simply call
* {@code super.customizePropertySources} after, rather than before adding its own:
* <pre class="code">
* public class Level2Environment extends Level1Environment {
* @Override
* protected void customizePropertySources(MutablePropertySources propertySources) {
* propertySources.addLast(new PropertySourceC(...));
* propertySources.addLast(new PropertySourceD(...));
* super.customizePropertySources(propertySources);
// add all from superclass
* }
* }

也就是继承AbstractEnvironment类最主要目的是要通过customizePropertySources添加属性资源,而且在子类中调用积累的customizePropertySources将决定属性的优先级关系。

除了这些操作,我们还可以像之前的文章中说得那样可以通过ApplicationContextInitializer中的initialize方法中去获取MutableProperties然后再调用addLast或其他方法。

spring中的ApplicationContextInitializer揭秘

发表于 2020-04-07 | 更新于 2020-04-09

看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
在它上面有这么几行注释:

Note that clients of any {@link ConfigurableEnvironment} may further customize property sources via the {@link #getPropertySources()} accessor, typically within an {@link org.springframework.context.ApplicationContextInitializer ApplicationContextInitializer}.


意思就是实现ConfigurableEnvironment的应用都有可能通过getPropertySources访问器来自定义property sources,比如通过ApplicationContextInitializer。

那么我们来看看ApplicationContextInitializer这个接口是怎样的


```Java
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {

/**
* Initialize the given application context.
* @param applicationContext the application to configure
*/
void initialize(C applicationContext);

}

里面就一个initialize函数,但是传入了一个重要的参数applicationContext,而且这个参数继承自ConfigurableApplicationContext,这个接口就如之前提到的实现了EnvironmentCapable接口,有了getEnvironment方法,就可以通过类似如下例子来自定义:

1
2
ConfigurableEnvironment env = applicationContext.getEnvironment();
env.getPropertySources().addLast(new PropertySourceX(...));

那有哪些实现了ApplicationContextInitializer接口的类呢,有什么作用呢?下回再解!

1…456…47

John

232 日志
43 标签
GitHub Twitter
欢迎关注我的公众号:沉迷Spring
© 2023 johnwonder
由 Hexo 强力驱动 v3.2.0
|
主题 – NexT.Pisces v7.1.1
|
0%