记录一下Java程序卡住的问题(长时间无返回无响应)

一、jstack 查看运行情况,如图

jstack是java虚拟机自带的一种堆栈跟踪工具。
11666是java 进程 pid

jstack -l 11666

在这里插入图片描述

排查发现数据库连接池出现问题了

at com.alibaba.druid.pool.DruidDataSource.takeLast(DruidDataSource.java:1518)
	at com.alibaba.druid.pool.DruidDataSource.getConnectionInternal(DruidDataSource.java:1143)
	at com.alibaba.druid.pool.DruidDataSource.getConnectionDirect(DruidDataSource.java:1014)
	at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:994)
	at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:984)
	at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:103)

二、利用druid监控或者 jconsole 查看连接池的状态

需要配置application.yml或者写组件

1.druid监控配置

datasource:
    druid:
      stat-view-servlet:
        enabled: true
        loginUsername: admin
        loginPassword: 123456
        allow:
      web-stat-filter:
        enabled: true
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.Filter;


/**
 * @descriptions:
 * @author: LZH
 * @date: 2022/5/16/016 14:37
 * @version: 1.0
 */
@Configuration
public class DruidConfig {
    @ConfigurationProperties(prefix = "spring.datasource")
    @Bean
    public DataSource druid() {
        return new DruidDataSource();
    }

    /* 配置一个Druid监控
         1.配置一个druid的后台  管理servlet
         2.配置一个druid的filter
      */
//  1.配置一个druid的后台  管理servlet
    @Bean
    public ServletRegistrationBean statViewServlet() {
//        注意:请求是/druid/*
        ServletRegistrationBean<StatViewServlet> bean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");
        Map<String, String> initParam = new HashMap<>();
        initParam.put(StatViewServlet.PARAM_NAME_USERNAME, "admin");
        initParam.put(StatViewServlet.PARAM_NAME_PASSWORD, "123456");
//        如果不写,则默认所有ip都可以访问
        initParam.put(StatViewServlet.PARAM_NAME_ALLOW, "");
        initParam.put(StatViewServlet.PARAM_NAME_DENY, "192.168.10.11");
        bean.setInitParameters(initParam);
        return bean;
    }

    //     2.配置一个druid的filter
    @Bean
    public FilterRegistrationBean webStatFilter() {
        FilterRegistrationBean<Filter> bean = new FilterRegistrationBean<>();
        bean.setFilter(new WebStatFilter());

        Map<String, String> initParams = new HashMap<>();
        initParams.put(WebStatFilter.PARAM_NAME_EXCLUSIONS, "*.js,*.css,/druid/*");
        bean.setInitParameters(initParams);
//        设置拦截请求
        bean.setUrlPatterns(Arrays.asList("/*"));
        return bean;
    }

}

在这里插入图片描述

2.jconsole

找到java安装目录,如 C:\Program Files\Java\jdk-11.0.6\bin

在这里插入图片描述
打开后选择已经运行的java程序找到MBean

在这里插入图片描述

三、配置连接数大小以及等待时间

1. DataSourceAutoConfiguration

由于系统原因,已经默认有了 DataSourceExtraProperties相关的配置我们对此重新覆盖了一下


package xxx;

import com.alibaba.druid.pool.DruidDataSource;
import xxx.QueryInterceptor;
import xxx.SaveInterceptor;
import xxx.DynamicDataSource;
import com.github.pagehelper.PageInterceptor;
import org.apache.ibatis.mapping.DatabaseIdProvider;
import org.apache.ibatis.mapping.VendorDatabaseIdProvider;
import org.apache.ibatis.plugin.Interceptor;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.jdbc.core.JdbcTemplate;

import javax.sql.DataSource;
import java.io.IOException;
import java.util.*;

@EnableConfigurationProperties({DataSourceExtraProperties.class})
@Configuration
public class DataSourceAutoConfiguration {
    public DataSourceAutoConfiguration() {
    }

    @Primary
    @ConditionalOnClass({DruidDataSource.class})
    @ConfigurationProperties(prefix = "spring.datasource")
    @Bean(
            initMethod = "init",
            destroyMethod = "close"
    )
/*
    public DruidDataSource dataSourceDefault(DataSourceProperties dataSourceProperties) {
        return (DruidDataSource)dataSourceProperties.initializeDataSourceBuilder().type(DruidDataSource.class).build();
    }

*/
    public DruidDataSource dataSourceDefault() {
        return new DruidDataSource();
    }

    @Bean
    public DynamicDataSource dataSource(DataSourceExtraProperties dataSourceExtraProperties, DataSource dataSourceDefault) {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        Map<Object, Object> targetDataSources = new HashMap(1);
        targetDataSources.put("dataSourceDefault", dataSourceDefault);
        dynamicDataSource.setTargetDataSources(targetDataSources);
        dynamicDataSource.setDefaultDbtype(dataSourceExtraProperties.getDbType());
        return dynamicDataSource;
    }

    @Bean(
            name = {"jdbcTemplate"}
    )
    public JdbcTemplate jdbcTemplate(@Qualifier("dataSource") DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }

    public PageInterceptor pageInterceptor() {
        PageInterceptor pageInterceptor = new PageInterceptor();
        Properties properties = new Properties();
        properties.setProperty("autoRuntimeDialect", "true");
        properties.setProperty("rowBoundsWithCount", "true");
        pageInterceptor.setProperties(properties);
        return pageInterceptor;
    }

    @Bean
    public QueryInterceptor queryInterceptor() {
        return new QueryInterceptor();
    }

    @Bean
    public SaveInterceptor saveInterceptor() {
        return new SaveInterceptor();
    }

    @Bean(
            name = {"abSqlSessionFactory"}
    )
    public SqlSessionFactoryBean sqlSessionFactory(@Qualifier("dataSource") DataSource dataSource, SaveInterceptor saveInterceptor) {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        sqlSessionFactoryBean.setMapperLocations(this.resolveMapperLocations("classpath*:com/xx/*/mapping/*.xml", "classpath*:com/xx/*/*/mapping/*.xml"));
        sqlSessionFactoryBean.setPlugins(new Interceptor[]{this.pageInterceptor(), this.queryInterceptor(), saveInterceptor});
        DatabaseIdProvider databaseIdProvider = new VendorDatabaseIdProvider();
        Properties mysqlp = new Properties();
        mysqlp.setProperty("MySQL", "mysql");
        mysqlp.setProperty("Oracle", "oracle");
        databaseIdProvider.setProperties(mysqlp);
        sqlSessionFactoryBean.setDatabaseIdProvider(databaseIdProvider);
        return sqlSessionFactoryBean;
    }

    private Resource[] resolveMapperLocations(String... locations) {
        ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
        List<Resource> resources = new ArrayList();
        String[] var4 = locations;
        int var5 = locations.length;

        for(int var6 = 0; var6 < var5; ++var6) {
            String mapperLocation = var4[var6];

            try {
                Resource[] mappers = resourceResolver.getResources(mapperLocation);
                resources.addAll(Arrays.asList(mappers));
            } catch (IOException var9) {
            }
        }

        return (Resource[])resources.toArray(new Resource[resources.size()]);
    }

    @Bean({"abMapperScannerConfigurer"})
    public MapperScannerConfigurer mapperScannerConfigurer() {
        MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
        mapperScannerConfigurer.setSqlSessionFactoryBeanName("abSqlSessionFactory");
        mapperScannerConfigurer.setBasePackage("com.xx.**.dao");
        return mapperScannerConfigurer;
    }

    @Bean({"abSqlSessionTemplate"})
    public SqlSessionTemplate sqlSessionTemplate(@Qualifier("abSqlSessionFactory") SqlSessionFactoryBean sqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory.getObject());
    }
}

2. application.yml

spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: xxx
    username: xx
    password: xx
    dbType: mysql
    initial-size: 10
    min-idle: 10
    max-active: 50
    # 配置获取连接等待超时的时间
    max-wait: 6000
    # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
    timeBetweenEvictionRunsMillis: 60000
    # 配置一个连接在池中最小生存的时间,单位是毫秒
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    # 打开PSCache,并且指定每个连接上PSCache的大小
    poolPreparedStatements: true
    maxPoolPreparedStatementPerConnectionSize: 20

总结

1.我们先利用 jstack 排查问题找出是:
数据库连接池的连接长时间没有释放,导致后续的请求过来了但是查询数据库的连接还在等待导致系统卡死的问题

2.利用 vconsole 的监控去配置 datasource 的属性发现一直不生效,开始寻找解决方案,参考如下
springboot配置druid数据源不生效问题

3.由于出现上述问题多半是并发造成的,我们对此进行模拟并发请求测试是否真正解决问题

进入你的网页程序f12打开找到network找一个后台请求,最好是数据量大一点的,如图
在这里插入图片描述
linux中 vi a.sh把复制的东西粘贴

curl 'https://position.csdnimg.cn/oapi/get' \
  -H 'authority: position.csdnimg.cn' \
  -H 'pragma: no-cache' \
  -H 'cache-control: no-cache' \
  -H 'sec-ch-ua: "Chromium";v="94", "Google Chrome";v="94", ";Not A Brand";v="99"' \
  -H 'accept: application/json, text/javascript, */*; q=0.01' \
  -H 'content-type: application/json' \
  -H 'sec-ch-ua-mobile: ?0' \
  -H 'user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36' \
  -H 'sec-ch-ua-platform: "Windows"' \
  -H 'origin: https://blog.csdn.net' \
  -H 'sec-fetch-site: cross-site' \
  -H 'sec-fetch-mode: cors' \
  -H 'sec-fetch-dest: empty' \
  -H 'referer: https://blog.csdn.net/qq_35754976/article/details/120456967' \
  -H 'accept-language: zh-CN,zh;q=0.9,zh-TW;q=0.8,en-US;q=0.7,en;q=0.6' \
  --compressed

保存退出然后执行以下命令测试并发,windows可以找工具或者postman队列执行(postman参考该链接

# 并发测试接口实用性  a.sh里面是curl数据
for i in {1..100}
do
    sh a.sh >/dev/null &
done

下图代表成功,然后再次访问程序是否还会卡死的现象
在这里插入图片描述

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值