一个老项目的高并发改造,遇到的redis连接不释放问题。

问题来由

一个老系统使用频率很低,但是一旦用,就是很多人一起用。每次这个时候,服务都会挂掉。

原因是使用mysql数据库做复杂计算。没有使用缓存。

着手解决

框架版本

struts 2.0

spring 3.2

集成redis

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:cache="http://www.springframework.org/schema/cache"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                        http://www.springframework.org/schema/context
                        http://www.springframework.org/schema/context/spring-context-3.0.xsd
                        http://www.springframework.org/schema/mvc
                        http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
                        http://www.springframework.org/schema/cache
                        http://www.springframework.org/schema/cache/spring-cache-3.0.xsd">

    <!-- 加载配置文件 -->
    <!-- <context:property-placeholder location="classpath:redis.properties"
        /> -->
    <!-- redis连接池配置 -->
    <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <!--最大空闲数 -->
        <property name="maxIdle" value="${redis.maxIdle}" />
        <!--连接池的最大数据库连接数 -->
        <property name="maxTotal" value="${redis.maxTotal}" />
        <!--最大建立连接等待时间 -->
        <property name="maxWaitMillis" value="${redis.maxWaitMillis}" />
        <!--逐出连接的最小空闲时间 默认1800000毫秒(30分钟) -->
        <property name="minEvictableIdleTimeMillis" value="${redis.minEvictableIdleTimeMillis}" />
        <!--每次逐出检查时 逐出的最大数目 如果为负数就是 : 1/abs(n), 默认3 -->
        <property name="numTestsPerEvictionRun" value="${redis.numTestsPerEvictionRun}" />
        <!--逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1 -->
        <property name="timeBetweenEvictionRunsMillis" value="${redis.timeBetweenEvictionRunsMillis}" />
        <!--是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个 -->
        <property name="testOnBorrow" value="${redis.testOnBorrow}" />
        <property name="testOnReturn" value="${redis.testOnReturn}" />
        <!--在空闲时检查有效性, 默认false -->
        <property name="testWhileIdle" value="${redis.testWhileIdle}" />

    </bean>

    <!-- redis集群配置 哨兵模式 -->
    <bean id="sentinelConfiguration"
        class="org.springframework.data.redis.connection.RedisSentinelConfiguration">
        <property name="master">
            <bean class="org.springframework.data.redis.connection.RedisNode">
                <!-- 这个值要和Sentinel中指定的master的值一致,不然启动时找不到Sentinel会报错的 -->
                <property name="name" value="mymaster"></property>

                <!-- 配置注master节点 <constructor-arg name="host" value="${redis.hostName}"/>
                    <constructor-arg name="port" value="${redis.port}"/> -->
            </bean>
        </property>
        <!-- 记住了,这里是指定Sentinel的IP和端口,不是Master和Slave的 -->
        <property name="sentinels">
            <set>
                <!-- 我这里就配置一个哨兵 -->
                <bean class="org.springframework.data.redis.connection.RedisNode">
                    <constructor-arg name="host" value="${redis.sentinel.host1}"></constructor-arg>
                    <constructor-arg name="port" value="${redis.sentinel.port1}"></constructor-arg>
                </bean>
                <bean class="org.springframework.data.redis.connection.RedisNode">
                    <constructor-arg name="host" value="${redis.sentinel.host2}"></constructor-arg>
                    <constructor-arg name="port" value="${redis.sentinel.port2}"></constructor-arg>
                </bean>
                <bean class="org.springframework.data.redis.connection.RedisNode">
                    <constructor-arg name="host" value="${redis.sentinel.host3}"></constructor-arg>
                    <constructor-arg name="port" value="${redis.sentinel.port3}"></constructor-arg>
                </bean>
            </set>
        </property>
    </bean>
    <bean id="jedisConnectionFactory"
        class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
        p:password="${redis.password}">
        <constructor-arg name="sentinelConfig" ref="sentinelConfiguration"></constructor-arg>
        <constructor-arg name="poolConfig" ref="jedisPoolConfig"></constructor-arg>
    </bean>
    <!--redis操作模版,使用该对象可以操作redis -->
    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
        <property name="connectionFactory" ref="jedisConnectionFactory" />
        <!--如果不配置Serializer,那么存储的时候缺省使用String,如果用User类型存储,那么会提示错误User can‘t cast
            to String!! -->
        <property name="keySerializer">
            <bean
                class="org.springframework.data.redis.serializer.StringRedisSerializer" />
        </property>
        <property name="valueSerializer">
            <bean
                class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer" />
        </property>
        <property name="hashKeySerializer">
            <bean
                class="org.springframework.data.redis.serializer.StringRedisSerializer" />
        </property>
        <property name="hashValueSerializer">
            <bean
                class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer" />
        </property>
        <!--开启事务 -->
        <property name="enableTransactionSupport" value="false"></property>
    </bean>

    <!-- 配置RedisCacheManager -->
    <bean id="cacheManager" class="org.springframework.data.redis.cache.RedisCacheManager">
        <constructor-arg index="0" ref="redisTemplate"></constructor-arg>
    </bean>

    <!--自定义redis工具类,在需要缓存的地方注入此类 -->
    <bean id="redisUtil" class="com.redoor.comm.utils.RedisUtil">
        <property name="redisTemplate" ref="redisTemplate" />
    </bean>
</beans>

代码优化

添加缓存。

重新方法。减少不要的数据库查询。

异步入库。

并发测试

遇到的问题

高并发测试下,总是报“unexpected end of stream”

找了很多资料,发现高并发下会产生大量的time_wait的socket连接。

最后查看redisTemplater源码,发现是redis配置了enableTransactionSupport开启,这种情况下,redis不会归还连接池的连接。如果不需要使用redis事务,关闭即可。

可参考:

SpringDataRedis事务 专题

Sping Data Redis 使用事务时,不关闭连接的问题

过程中经历

开始根据报错,以为是参数配置的问题,做了一下几件事 。最后终于找到问题。

tomcat服务器性能调优

redis服务器调优

redis参数调优

原文地址:https://www.cnblogs.com/morninglight/p/10348879.html

时间: 2024-10-08 12:56:55

一个老项目的高并发改造,遇到的redis连接不释放问题。的相关文章

高并发架构系列:Redis为什么是单线程、及高并发快的3大原因详解

Redis的高并发和快速原因 1.redis是基于内存的,内存的读写速度非常快: 2.redis是单线程的,省去了很多上下文切换线程的时间: 3.redis使用多路复用技术,可以处理并发的连接.非阻塞IO 内部实现采用epoll,采用了epoll+自己实现的简单的事件框架.epoll中的读.写.关闭.连接都转化成了事件,然后利用epoll的多路复用特性,绝不在io上浪费一点时间. 下面重点介绍单线程设计和IO多路复用核心设计快的原因. 为什么Redis是单线程的 1.官方答案 因为Redis是基

高并发架构系列:Redis并发竞争key的解决方案详解

需求由来 1.Redis高并发的问题 Redis缓存的高性能有目共睹,应用的场景也是非常广泛,但是在高并发的场景下,也会出现问题:缓存击穿.缓存雪崩.缓存和数据一致性,以及今天要谈到的缓存并发竞争. 这里的并发指的是多个redis的client同时set key引起的并发问题. 2.出现并发设置Key的原因 Redis是一种单线程机制的nosql数据库,基于key-value,数据可持久化落盘.由于单线程所以Redis本身并没有锁的概念,多个客户端连接并不存在竞争关系,但是利用jedis等客户端

Java架构师,微服务架构设计,并发编程,java8新特性,P2P金融项目,高并发,分布式

微服务架构设计 微服务 软件架构是一个包含各种组织的系统组织,这些组件包括 Web服务器, 应用服务器, 数据库,存储, 通讯层), 它们彼此或和环境存在关系.系统架构的目标是解决利益相关者的关注点. Conway's law: Organizations which design systems[...] are constrained to produce designs which are copies of the communication structures of these or

思考如何做一个普通网站和高并发的网站

写写总结一下,分别用C#和Java如何构建一个普通网站和一个高并发.安全的网站. 构建一个普通网站的checklist:.net mvc:mvc+EF+mysqljava:主流仍然使用ssh2?纯前端如node.js的优势和缺点如何? 注意事项:基本架构,前端框架,数据库事务 脚本构建一个高并发的网站:基本架构上的调整:缓存的加入(如memcached等),多线程的使用,需不需要做成分布式结构(web service等),图片等静态资源的CDN网络:数据库表之间字段关联的设计,字段冗余的合理使用

企业级JAVA大型分布式电商项目实战高并发集群分布式系统架构

并发,在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,但任一个时刻点上只有一个程序在处理机上运行. "高可用性"(High Availability)通常来描述一个系统经过专门的设计,从而减少停工时间,而保持其服务的高度可用性. 一. 设计理念 空间换时间 多级缓存,静态化 客户端页面缓存(http header中包含Expires/Cache of Control,last modified(304,server不返回bo

高并发架构系列:Redis缓存和MySQL数据一致性方案详解

一.需求起因在高并发的业务场景下,数据库大多数情况都是用户并发访问最薄弱的环节.所以,就需要使用redis做一个缓冲操作,让请求先访问到redis,而不是直接访问MySQL等数据库.这个业务场景,主要是解决读数据从Redis缓存,一般都是按照下图的流程来进行业务操作.读取缓存步骤一般没有什么问题,但是一旦涉及到数据更新:数据库和缓存更新,就容易出现缓存(Redis)和数据库(MySQL)间的数据一致性问题. 不管是先写MySQL数据库,再删除Redis缓存:还是先删除缓存,再写库,都有可能出现数

【高并发简单解决方案】redis队列缓存 + mysql 批量入库 + php离线整合

原文地址 :https://segmentfault.com/a/1190000004136250需求背景:有个调用统计日志存储和统计需求,要求存储到mysql中:存储数据高峰能达到日均千万,瓶颈在于直接入库并发太高,可能会把mysql干垮. 问题分析 思考:应用网站架构的衍化过程中,应用最新的框架和工具技术固然是最优选择:但是,如果能在现有的框架的基础上提出简单可依赖的解决方案,未尝不是一种提升自我的尝试. 解决: 问题一:要求日志最好入库:但是,直接入库mysql确实扛不住,批量入库没有问题

java700多个G架构师项目实战,高并发集群分布式,大数据高可用,视频教程获取方式

学习思路: 1.先学习第一套或者第二套架构师课程,帮助没有基础或基础学得不好的同学建立架构师思维,整套需要全部学习,很重要!! 2.根据工作需要,分别去学习第二套.第三套里的实战课程里的知识点,不需要全部全学习,那么多资料你没有那么多精力全部学完!! 对比内容: 本套课包含像Dubbo,Netty,Nio,Mina,Mecached,Nosql,MongoDB, Nginx, ActiveMQ等课程更全,我这里就不一一列举,亲们可以对比大小 本教程优势: 1.六套 架构师课程,基本包含了淘宝卖的

架构师项目实战高并发高性能

架构以及我理解中架构的本质 在开始谈我对架构本质的理解之前,先谈谈对今天技术沙龙主题的个人见解,千万级规模的网站感觉数量级是非常大的,对这个数量级我们战略上 要重 视 它 , 战术上又 要 藐 视 它.先举个例子感受一下千万级到底是什么数量级?现在很流行的优步(Uber),从媒体公布的信息看,它每天接单量平均在百万左右, 假如每天有10个小时的服务时间,平均QPS只有30左右.对于一个后台服务器,单机的平均QPS可以到达800-1000,单独看写的业务量很简单 .为什么我们又不能说轻视它?第一,