记一次unable to create new native thread错误处理过程

早上运维说线上出错了,发了如下截图:

错误截图

unable to create new native thread,看到这里,首先想到的是让运维搞一份线上的线程堆栈(可能通过jstack命令搞定的)。发现线上的堆栈竟然有5M多大,打开文件后线程数量居然达到了8000多个。有大量的线程堆栈如下图所示:
线程堆栈
。看到有大量的线程是以scheduler-开头的,可以确定的是程序里有使用spring的定时任务功能,定时的处理一些任务。会不会是有长时间的定时任务造成线程没有关闭。于是翻看了下spring 的配置文件。代码如下:

    <task:executor id="executor" pool-size="5" />
    <task:scheduler id="scheduler" pool-size="10" />
    <task:annotation-driven executor="executor" scheduler="scheduler" />

可以肯定的是这里有配置线程池,但是线程池的大小也就10个啊,怎么会有8000多个线程。看到这里,我差点去翻spring的源码,想看看是不是spring的bug。检查看发现通过task:scheduler启动的线程是以scheduler-命名的,并不会有后面的SendThread与EventThread。上面的堆栈还能看出线程与zookeeper有关。于是还是回到自已的代码中,(必竟自已的代码出问题的可能性还是比框架代码出问题的概率要高出一个量级的)。
  服务是分布式部署的,各部署的机器能够提供对等的服务。对于有需要一台机器执行的任务便会用到分布式锁,而zookeeper是能够用来实现分布式锁的,代码如下:

/**
 * 基于zookeep实现的分布式锁
 */
public class ZookeeperLock implements DistributedLock,Watcher{
    private String basePath;
    private ZooKeeper zk ;
    private String host;
    
    Logger logger = LoggerFactory.getLogger(ZookeeperLock.class);
    private final static String realIP =  IpUtils.getRealIp();
    
    public ZookeeperLock(String host,String basePath) {
        this.basePath = basePath;
        this.host = host;
    }
    
    @Override
    public boolean getDistributedLock(String businessname) {
        if(StringUtils.isBlank(businessname)){
            throw new IllegalArgumentException("businessname can not be empty!!!");
        }
        try {
            zk = new ZooKeeper(host, 3000, this);
            logger.info("success get zk:"+zk +"and host is:" + host);
        } catch (IOException e) {
            logger.error("error is IOException:" + e.getMessage());
        }
        int retries=0;
        while(retries++ < 5){
            if(zk.getState() == States.CONNECTED ){
                String key =basePath + "/" + businessname + "_" +DateFormatUtils.format(new Date(), "yyMMdd") ;
                try {
                    if(zk.exists(key, false) == null){
                        zk.create(key, realIP.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
                        logger.info("get distributed lock success! ip is " + realIP);
                        return true;
                    }
                    
                } catch (KeeperException e) {
                    logger.error("error is KeeperException: " + e.getMessage());
                    releaseLock();
                } catch (InterruptedException e) {
                    logger.error("error is InterruptedException 1: " + e.getMessage());
                }
                logger.debug("fail to get lock , interrupt the loop!");
                break;
            }
            //仅在State状态不少Connected时才会循环多次等待zookeeper连接成功
            try {
                Thread.sleep(1000);
                logger.info("zookeeper client has not connected." + zk.getState());
            } catch (InterruptedException e) {
                logger.error("error is InterruptedException 2: " + e.getMessage());
            }
        }
        return false;
    }

    @Override
    public void releaseLock() {
        if(zk != null){
            try {
                zk.close();
                zk = null;
            } catch (InterruptedException e) {
            }
        }
    }

    public void process(WatchedEvent event) {
        logger.debug( " Zookeeper client connect success! The clent ip is " + realIP);
    }

}

分布式锁原理:
1):往指定的目录创建临时节点,创建成功的成获取锁。
2):业务执行完成后,需要删除相应的临时节点,相当于释放锁。
代码提供了加锁与解锁的逻辑,通过ZookeeperLock 对象,便能够实现分布式锁了。ZookeeperLock通过xml文件的配置来进行初使化。
为了方便对ZookeeperLock 的使用,提供了相应的模版类,代码如下:

@Component 
public class ZookeeperLockTemplate {
    private static final Logger logger = LoggerFactory.getLogger(ZookeeperLockTemplate.class);
    
    @Autowired  //容器会自动注入
    private ZookeeperLock distributedLock;
    
    public void execute(String businessname, ZookeeperLockCallBack callBack){
        try {
            if (distributedLock.getDistributedLock(businessname)) {
                callBack.call();
            }
        } catch (Exception e) {
            logger.error("ZookeeperLockTemplate error! ", e);
        } finally {
            distributedLock.releaseLock();
        }
    }
    
}

代码看上去并没有什么问题,但是问题就出在ZookeeperLockTemplate这个类上。通过componet注解注释的类在Spring 容器里是单例的,里面的属性即使scope类型是prototype,每次使用的时候也是用的同一个实例。上面的ZookeeperLockTemplate如果在多线程的情况下使用,便会有大的问题,一个线程可能关闭另一个线程开启的锁,从而造成另一个线程的锁无法关闭,导制zk = new ZooKeeper(host, 3000, this)这个对象的线程一直在内存中修改的方法也很简单,在execute方法里,每次都从spring IOC的容器里取一次就好了。
  上面的代码如果有问题,那在线上应该能够频繁的重现,而不至于上线好久了才发现这样的问题。这里主要是因为线上就三个定时任务,而且定时任务执行的并不频繁,但是他们的执行时间是有重叠的(也就是有可能有多线程的情况)。从日子累计到8000多条线程也可以看出只有运行了一定时间后,才可能出现问题。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,265评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,274评论 1 288
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,087评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,479评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,782评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,218评论 1 207
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,594评论 2 309
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,316评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,955评论 1 237
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,274评论 2 240
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,803评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,177评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,732评论 3 229
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,953评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,687评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,263评论 2 267
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,189评论 2 258

推荐阅读更多精彩内容