TIME_WAIT状态的连接过多导致系统端口资源耗尽问题(1)

  • 问题表现:
    • 从某一天开始,主服务器上逐步出现了一些报错,比如:
      • 各种连接失败:mysql连接失败、redis连接失败,memcache插入数据失败
      • 某些时候,redis的llen命令返回值还异常,正常情况下应该是返回一个整数,但有时候会返回string,string的内容是“ok”。(后续证明,这个问题是多线程使用redis不当导致)
    • 出现连接失败的频率变得越来越高。
  • 问题排查:
    • 首先排除网络原因,因为这些连接不是在本地,就是在内网。
    • 再次,排除连接数超过上限原因:监控能看到mysql/redis连接数并不多
    • 最后查了一下端口资源:sudo netstat -anp | wc -l,  你妹,2w多个, 其中绝大部分的连接状态是TIME_WAIT。好了,问题基本可以确定,就是socket连接过多,导致端口资源耗尽。
  • 问题解决

初步确定问题后,要找到问题的源头才好解决。所以要先查这些TIME_WAIT从哪里来的。确定基本都是mysql连接

netstat -anp | grep TIME_WAIT | awk '{print $5}' | sort | uniq -c | sort -nr | less

    刚开始怀疑是网站前端php搞的,因为php的mysql连接都是短连接,短连接关闭以后,对应的socket连接状态就会变成TIME_WAIT,持续3s左右之后才会释放这个资源。如果前端请求较多,建立连接的速度超过释放速度,就会导致端口资源耗尽。但php涉及mysql地方较多,一时半会儿也优化不了,所以临时先想个方案处理一下,调整一下系统参数,设置socket连接可以重用(一般情况下不推荐):

echo 1 >> /proc/sys/net/ipv4/tcp_tw_reuse

    调整系统参数后,安定了一个星期左右,然后又开始零星出现之前的连接失败问题。还得继续找到源头处理。用了一个很笨的办法,就是不断的查mysql的连接情况, 人工看有哪些连接经常出现,看是否能找到产生这些连接的程序

mysql -uuser -ppwd  -hdb_host -e "show full processlist"

    结果还真发现,有一类sql请求异常的多次出现,异常的原因是因为每次出现的连接端口号都不同,这意味着这个请求在不断的申请和释放端口资源。而这个sql请求的程序理论上应该是一个长连接才对

    查代码,发现程序是用的一个python的mysql库SQLAlchemy,代码如下:

# -*- coding: utf-8 -*-
import sqlalchemy
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from common.config import mysql

class MysqlSession(object):
    mysql_engine = None
    Session = None
    def init(self):
        mysqlStr = 'mysql://{0}:{1}@{2}:{3}/{4}?charset=utf8'  \
            .format(mysql['username'], mysql['password'], mysql['host'], mysql['port'], mysql['dbname'])

        MysqlSession.mysql_engine = sqlalchemy.create_engine(
            mysqlStr,
            encoding = "utf-8",
            pool_size=20,
            pool_recycle=10,
            echo = False)
        MysqlSession.Session = sessionmaker(bind=MysqlSession.mysql_engine)

    def getSession(self):
        session = MysqlSession.Session()
        return session

Base = declarative_base()

    程序里在初始化的地方调用了上面的init,然后在一个循环里,不断的调用getSession,然后close session。特地去实验了一下,每次getSession确实都会从一个新的端口去连接mysql server。于是改掉此处,在循环外调用getSession和关闭 session,TIME_WAIT连接数很快就下降到几百!

 

看起来问题解决,后续继续观察。

posted @ 2014-08-27 17:50  公孙青阳  阅读(5248)  评论(0编辑  收藏  举报