杂谈---第一次真正的高并发编程体验

发布时间:2013-11-18 01:23:00作者:左潇龙阅读(10433 )评论(84)

    引言

     

      由于最近LZ负责的业务系统频繁宕机,导致LZ疲于本命,上一个星期(因为现在是周一了,0.0)连续加了五天班,其中还包括周末,就是为了出一套应急方案。宕机的根本原因,现在已经渐渐真正的明晰了,最早的一次是因为消息发送与数据库操作的顺序错误导致的数据库宕机,目前已经通过顺序的调换解决了数据库的压力。然而数据库的问题解决之后,则开始是应用服务器出问题。最近频繁宕机的原因是连接数经常爆满,完全无法应付nginx的疯狂攻击。

      经过与公司领导与业务人员的交流,最终才知道,由于业务模式的变化,导致LZ负责的业务系统用户剧增。因为高并发的原因,LZ的系统现在已经频繁的不堪重负,数次被压垮,提升服务器的响应速度已经迫在眉睫。最终定制的解决方案是比较主流的解决方案,也就是将前端采用集群部署,而后台与其它业务系统的交互,则会采用独立的服务器处理。不过这个方案并不能一解燃眉之急,集群的部署和前端与后台的分割都需要不短的时间进行部署和测试,因此只能安排到这周一(其实就是今天,0.0)才开始推行。

      为了避免在系统向集群转换的过程中再出现问题,LZ与各位领导商量之后就制定了一个简单的应急方案,而LZ则担任了编写应急方案的职责。最近的几天,LZ就是在忙于编写这个应急方案(除此之外,还要边应付业务人员和其它同事对系统的不满,0.0),怎一个焦头烂额可以形容。

     

    并发的意识

     

      应急方案采用的方式是,增加用户数限制以及优先级的功能。用户数限制的功能类似于火车站之前的抢登陆,简单的说,就是这个系统只能若干个(比如1000个)用户登陆,后面来的用户将被拒绝登陆。优先级的功能,则是指在人数达到一定数目时,需要将某些角色的用户踢掉,让另外一部分角色的人优先使用系统(这是因为那些被踢的角色查询的数据量较大且对系统的依赖性不高,容易给服务器造成不必要的压力)。这个功能LZ一开始觉得挺简单的,但是当LZ真写起来的时候,才发现真的没有想象中的那么简单。

      最开始的难度在于用户数的精确统计,因为用户数与session数量并没有直接关系,要想统计精确的用户数,必须按照用户名去重。这一点在找到适合的监听器(LZ使用的监听器是存在于web服务器中的HttpSessionAttributeListener,但这不一定适用于所有项目)之后被解决,方案就是记录每个用户的session数量,为0则代表该用户已退出,否则代表正在使用系统(也就是会算作1个用户)。

      然而最难的地方却不是用户数的精确统计,而是并发所导致的难度。因为用户数是在高并发的情况下统计的,因此必须考虑并发的情况,在代码中添加适当的同步,即要保证统计数据的正确性,又得保证足够的性能。如果因为这个数量统计而影响性能,那就与这个功能的意义背道而驰了。

      这还是LZ第一次在高并发的情况下编程(以前其实也有,只是由于功能并不核心,所以从未考虑过性能,只是无脑的在方法上使用synchronized),每写一句代码,都要考虑如果有成百上千个线程同时运行会如何。这每一句代码似乎都成了美女,每一个都可能有成百上千个大爷翻牌,而且还是同时,因此到底如何同时伺候N(N>1)个大爷,自然就成了一个问题。这也算是LZ第一次在编程的过程中,真真切切的产生了并发的意识。

     

    书中自有黄金屋

     

      或许是老天开眼,也或许是LZ运气尚且说的过去,在这之前的几个星期,LZ刚买了一本关于并发编程的书籍,一直都没有看。这下可好了,刚好派上用场,因此拿起这本书不到两天,LZ就一口气读了将近一半。最终也算是临时抱佛脚,将这个应急方案给应付过去了。

      由于LZ只是为了应付当前的情况,所以并没有细读,尽管读的过程中都读懂了,也与作者有深深的共鸣,但过后其实印象并不深。不过这足以让LZ度过当前的难关了,在编写这段并发代码时,LZ主要采取了以下几种小技巧(基于书中的思想)。

      1、将原本存在于监听器和过滤器的属性全部提出,使得两者不再需要考虑线程安全的问题。

      2、提出的属性放在单独的两个类(session有效列表和session无效列表,无效列表其实就是可能要被踢的session)当中,这两个类都是单例,并确保这两个类是线程安全的。

      3、由于需要遍历一个装满session的集合实现踢人的功能,因此采用备份的方式。如此一来,在遍历session并使得session失效时,并不会锁住失效session的列表,这样可以极大的提高性能(前提是限制的用户数并不高)。

      4、在监听器与过滤器使用这两个单例类时,坚决杜绝各种竞态条件或者复合操作。

      5、重构现有的代码,让需要同步的地方集中在一起,减少性能的损耗。

      这算是LZ从书中紧急领略的几个方式,今天LZ的应急方案已经正式上线,是否能顶住高并发还有待考量。不过LZ是领略了书中的一部分真谛才下的手,而非一时臆测,因此还是有一定把握的。(希望明早一上班不会被业务部门炮轰,0.0)

     

    黄金屋的诱惑

     

      由于这次事件,LZ已经彻底爱上了这本并发编程(前几天还说爱上了深入理解那本书,有点花心啊)。这本书并不厚,才200多页,LZ用了一天半不到的时间看了80页,准备直接趁热打铁,等看完这本书之后,再继续深入理解和设计模式这两本书。因此最近的计算机系统原理系列可能要延迟一下了。

      本文写的十分匆忙(今晚其实刚加过班,0.0),因此难免漏洞百出,各位猿友如果遇到过类似高并发的问题,可以畅述己见,让LZ等一届屌丝得以一窥天机。时间已经不早了,各位猿友明早见分晓吧。


    版权声明:本文版权归作者(左潇龙)所有,欢迎转载。但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

    31
    精彩
    1
    感动
    0
    搞笑
    2
    开心
    1
    愤怒
    0
    无聊
    1
    灌水
    0
    惊讶
#1楼     时间:2013-11-18 08:24:00      来源:de_shuai
楼主看书蛮多的。
#2楼     时间:2013-11-18 08:41:00      来源:vbfool
想想我平时总做的那种“用户不过5,并发不过2”的系统,估计真遇上和楼主一样的情况,不知道会乱成什么样。
#3楼     时间:2013-11-18 09:07:00      来源:伊贺双刀流
说了半天,是本什么书啊?
#4楼     时间:2013-11-18 09:18:00      来源:Navono
讲了那么久都没说是什么书。。。。
#5楼     时间:2013-11-18 09:30:00      来源:左潇龙
@ de_shuai
还好吧,施主过奖了。
#6楼     时间:2013-11-18 09:31:00      来源:左潇龙
@ vbfool
早晚会遇到的。施主。
#7楼     时间:2013-11-18 09:35:00      来源:左潇龙
@ Navono
@伊贺双刀流
抱歉,两位施主,书名叫(Java并发编程实战)。
#8楼     时间:2013-11-18 09:38:00      来源:夜の魔王
每一句代码似乎都成了美女,每一个都可能有成百上千个大爷翻牌。楼主是开那啥的,代码都是美女,用户都是大爷。不知我理解的对不对?
#9楼     时间:2013-11-18 09:42:00      来源:BangQ
多大的并发量可以透露下么
#10楼     时间:2013-11-18 09:50:00      来源:Alvin
楼主是买书的?
#11楼     时间:2013-11-18 10:02:00      来源:左潇龙
@ 夜の魔王
bingo了,施主。
#12楼     时间:2013-11-18 10:03:00      来源:左潇龙
@ BangQ
目前还不确定这个数量,下限是1000。
#13楼     时间:2013-11-18 10:04:00      来源:左潇龙
@ Alvin
施主是不是打错字了,贫僧没看懂啊。
#14楼     时间:2013-11-18 10:52:00      来源:Wuya
貌似写了这么多字,有用的内容却不多。
#15楼     时间:2013-11-18 11:37:00      来源:llllboy
求书名
#16楼     时间:2013-11-18 11:42:00      来源:laosong
“每写一句代码,都要考虑如果有成百上千个线程同时运行会如何”
==楼主这个真心碉堡了,你确信你见过这么多线程的程序,iis最多300多个线程就会挂,java虚拟机最多200个吧
#17楼     时间:2013-11-18 11:43:00      来源:浩然天地
求书名
#18楼     时间:2013-11-18 13:13:00      来源:深蓝医生
等楼主汇报结果
#19楼     时间:2013-11-18 13:22:00      来源:左潇龙
@ Wuya
呵呵,让施主见笑了。
#20楼     时间:2013-11-18 13:22:00      来源:左潇龙
@ llllboy
往上翻有书名,施主。
#21楼     时间:2013-11-18 13:27:00      来源:左潇龙
@ laosong
施主的理解可能有点失误,上次LZ的系统已经顶到2049个线程了,尽管最终报出uable to create new native thread的异常,但线程数达到2049是真实的。
另外,稍微想一下也能想到,JVM的线程数怎么会就200多个。其它不说了,tomcat的参数当中就有maxThreads参数,一般都在千级别。当然了,这也看项目的大小。但是绝不止200多个,这也太少了,稍微用户量多一点就死翘翘了。
一般情况下,我们认为线程数的上限是堆大小和XSS决定的。
#22楼     时间:2013-11-18 13:28:00      来源:左潇龙
@ 浩然天地
详情见7楼,施主。
#23楼     时间:2013-11-18 13:29:00      来源:左潇龙
@ 深蓝医生
结果目前来看是OK的,但是贫僧不敢掉以轻心。。。。
#24楼     时间:2013-11-18 13:36:00      来源:Launcher
@ 左潇龙
引用@laosong
施主的理解可能有点失误,上次LZ的系统已经顶到2049个线程了,尽管最终报出uable to create new native thread的异常,但线程数达到2049是真实的。
另外,稍微想一下也能想到,JVM的线程数怎么会就200多个。其它不说了,tomcat的参数当中就有maxThreads参数,一般都在千级别。当然了,这也看项目的大小。但是绝不止200多个,这也太少了,稍微用户量多一点就死翘翘了。
一般情况下,我们认为线程数的上限是堆大小和XSS决定的。


如果你真需要如此多的线程来完成你的任务,那你就需要重新设计你的应用程序架构.因为实际上并行运行的线程数不会改变,创建更多的线程,意味着更多的资源竞争.
#25楼     时间:2013-11-18 13:47:00      来源:小彬
话说真的是一个访问用户对应一个线程吗?
A用户登录后,就挂在那里,然后线程会一直为他保留吗?
#26楼     时间:2013-11-18 13:51:00      来源:左潇龙
@ Launcher
施主理解错误了,这里2049个线程并不是为了并行的完成某项任务。如果真是这样设计的话,岂不是要来一台2049个CPU的机子吗。。。。怎么可能呢。。。
这里的2049个线程,只是web服务器(web服务器会启动线程响应请求)响应不过来用户的请求,导致响应速度变慢,恶性循环之后,线程数就超了。这不是刻意设计的。施主理解错了,0.0。
#27楼     时间:2013-11-18 14:42:00      来源:laosong
@ 左潇龙
抱歉前面没说清楚,我指的不是jvm的线程,是指os级别的线程,你搞个os级别的2000个线程试试看,os调度都过不来。灵你看tomcat默认最大线程数是多少
#28楼     时间:2013-11-18 14:43:00      来源:十年半山
这本书我还看了不到30页 回头要好好看看
#29楼     时间:2013-11-18 14:46:00      来源:laosong
@ 左潇龙
引用@Launcher
施主理解错误了,这里2049个线程并不是为了并行的完成某项任务。如果真是这样设计的话,岂不是要来一台2049个CPU的机子吗。。。。怎么可能呢。。。
这里的2049个线程,只是web服务器(web服务器会启动线程响应请求)响应不过来用户的请求,导致响应速度变慢,恶性循环之后,线程数就超了。这不是刻意设计的。施主理解错了,0.0。

线程数跟cpu多少没有直接关系,windows默认启动后总线程数(含所有程序)应该在千这个级别。web服务器完全不需要多少个线程,想nginx这种主要已异步为主。
#30楼     时间:2013-11-18 14:59:00      来源:Launcher
@ 左潇龙
引用@Launcher
施主理解错误了,这里2049个线程并不是为了并行的完成某项任务。如果真是这样设计的话,岂不是要来一台2049个CPU的机子吗。。。。怎么可能呢。。。
这里的2049个线程,只是web服务器(web服务器会启动线程响应请求)响应不过来用户的请求,导致响应速度变慢,恶性循环之后,线程数就超了。这不是刻意设计的。施主理解错了,0.0。


我觉得是你理解错误了,问题的症结就在这里,你不理解I/O模型,不能区分I/O线程和Worker线程,不知道在读取/写入 I/O 时使用的线程是非常少的,而大量创建的线程被你的应用程序逻辑代码占据.最明显的例子是收到请求后以同步的方式访问数据库或其它I/O设备.这就造成调用线程不得不等待一个I/O操作完成,而没法立即被复用.正确的做法是仍然以异步I/O的模式访问数据库或其它I/O设置,调用线程就可以理解返回用于处理其它请求.

如果你收到请求后执行的是CPU密集型任务,那么你更应该减少创建的线程数,而通过任务队列的形式,使用尽可能少的线程来执行队列中的任务.甚至你可以向另一台物理机器发起一个异步I/O请求,将计算密集型任务投递给它,它在完成任务后会唤醒调用机器的线程来告知处理结果.

总的原则就是,尽可能让创建的线程都在工作,而不是创建大量的线程后,让它们都在等待.
#31楼     时间:2013-11-18 15:18:00      来源:左潇龙
@ laosong
施主不是说JVM的线程吗。。。。怎么又成OS的了?
贫僧说的在千级别,是指的设置的值不是指默认值。。。
#32楼     时间:2013-11-18 15:19:00      来源:左潇龙
@ laosong
。。。施主还是不理解贫僧说的话。。。。贫僧说的是并不是主动开的这么多线程,而是服务器启动的线程而已。
另外,并行的时候和CPU怎么没有关系?并行的时候如果超过CPU数目,会降低性能。
#33楼     时间:2013-11-18 15:23:00      来源:左潇龙
@ Launcher
施主还是理解失误了,施主刚开始说并行。但是这里很明显不是并行,而是并发。
现在又说并发,施主到底是想讨论并行还是并发。。。。而且这里和I/O模型,I/O瓶颈,数据库等并没有多大关系,因为数据库并没有达到瓶颈,数据库一直表现很稳定,主要还是并发量大,导致系统响应不急时的问题。其实根本原因是tomcat是开源的非商业的web服务器,本身支持的并发量就不高,所以用户数一剧增就扛不住了。

“如果你真需要如此多的线程来完成你的任务,那你就需要重新设计你的应用程序架构.因为实际上并行运行的线程数不会改变,创建更多的线程,意味着更多的资源竞争.”

而且施主这里说的,并行的线程数,其实是不对的。这里是并发的线程数,希望施主搞清楚并行和并发的区别。
#34楼     时间:2013-11-18 15:33:00      来源:左潇龙
@ laosong
@Launcher
两位施主,以下是贫僧粗浅的理解,简单的描述出来是这样的。
并行:多个线程,每个线程都有单独的CPU(一般情况下,线程数会少于CPU数或等于CPU个数),一起完成同一项任务。
并发:多个线程,一共只有一个CPU(也可能是多个,但一般远远少于线程数,总之就是并发的时候存在CPU资源的竞争),一起完成多项任务。
这里是并发,而非并行,其实并行这个词压根就不在本文的讨论范围内。
#35楼     时间:2013-11-18 15:57:00      来源:newjoin
#36楼     时间:2013-11-18 15:58:00      来源:newjoin
孰是孰非,请专家为我们讲解
#37楼     时间:2013-11-18 16:00:00      来源:Launcher
@ 左潇龙
引用@Launcher
施主还是理解失误了,施主刚开始说并行。但是这里很明显不是并行,而是并发。
现在又说并发,施主到底是想讨论并行还是并发。。。。而且这里和I/O模型,I/O瓶颈,数据库等并没有多大关系,因为数据库并没有达到瓶颈,数据库一直表现很稳定,主要还是并发量大,导致系统响应不急时的问题。其实根本原因是tomcat是开源的非商业的web服务器,本身支持的并发量就不高,所以用户数一剧增就扛不住了。

“如果你真需要如此多的线程来完成你的任务,那你就需要重新设计你的应用程序架构.因为实际上并行运行的线程数不会改变,创建更多的线程,意味着更多的资源竞争.”

而且施主这里说的,并行的线程数,其实...


你解释这个没用的,因为我告诉你的是<并发>的线程数是有上限的,你不管这个上限,而创建了很多<并发>的线程,那么比<并行>多出来的<并发>线程就会处于等待,造成资源浪费,拉底系统性能.

简单来说,就是你提到的"2049线程",90%都处于等待状态,它们占用了内存,寄存器等系统资源,但是却不做事.如果你能很好的设计你的系统,你可以让那10%的线程始终处于忙碌状态.需要调度的线程越少,调度的性能就越高.
#38楼     时间:2013-11-18 16:02:00      来源:Launcher
@ 左潇龙
引用@laosong
。。。施主还是不理解贫僧说的话。。。。贫僧说的是并不是主动开的这么多线程,而是服务器启动的线程而已。
另外,并行的时候和CPU怎么没有关系?并行的时候如果超过CPU数目,会降低性能。


服务器启不启动线程,在于你有没有把服务器启动的线程给独占了.也就是我前面说的,你在收到请求后同步访问I/O(或者使用了诸如event,mutext等同步数据结构),独占了调用线程.为了处理下一个请求,服务器不得不再创建一个线程来工作.
#39楼     时间:2013-11-18 16:32:00      来源:L'nemo
如果排除了硬件问题,数据库无瓶颈。
那么再排除web服务器的问题;tomcat连接线程数是可以设置的。
我想博主的问题应该是出在web服务器上。这个跟底层的JVM线程似乎没有关系,
其次 "在编写这段并发代码时,LZ主要采取了以下几种小技巧(基于书中的思想)" 可以看出博主的项目设计有问题。
最后, 这个2049 是并发时的连接数?还是2049个线程?如果是并发,那数据量就相当大了。如果是2049个线程,我想应该是项目设计问题。
#40楼     时间:2013-11-18 17:06:00      来源:涛褪荒芜
@ vbfool
我去年就做了一个这样的系统,用户不过5并发不过2.话说过程挺和谐的
#41楼     时间:2013-11-18 17:06:00      来源:王芳兵
这篇文章没白看,至少博主介绍了本书;也认识了个新东西:nginx。

纵观30多楼观客的回复,大部分的中心思想可以归纳为两点:a)博主,你的项目设计有问题;b)博主,我比你厉害。
我只能呵呵。
#42楼     时间:2013-11-18 17:10:00      来源:徐少侠
@ 左潇龙
Web服务器来不及响应的请求,不是并发请求。是队列内的请求
所以2049个线程是队列最大数量吧?

如果数据库一直稳定,CPU也没有爆,那么必然是业务处理中的同步等待太厉害了。基于楼主的几个优化方法,其中有部分能降低线程等待,因此的确能改善性能问题。
#43楼     时间:2013-11-18 17:13:00      来源:Teresa.luo
路过,标记下
#44楼     时间:2013-11-18 17:17:00      来源:左潇龙
@ Launcher
@L'nemo
贫僧再次强调一下,那次2049个线程是因为有线程等待资源导致的,是不正常的状态。贫僧说这个的意思,只是想表达一下,JVM的线程数并不是只有200多个,针对上面那位施主(laosong)的问题。(再次PS:这个结论是上面的施主提出的,贫僧可没这么说过,这里本身也与JVM的线程数没什么关系)

贫僧的tomcat设置到底有没有问题不是贫僧说了算的,因为贫僧其实只是项目中的一个程序员而已,连高级都不是。而且就算是有问题,现在也不重要了,因为说到底tomcat已经扛不住了,这就像你让一个国际上的拳击选手去打一个小朋友,小朋友的任何招数都是无用的,因为在绝对的实力面前,一切挣扎都是徒劳的。其实现在看来,这些参数设置高设置低都没什么意义了(这项目贫僧来之前就已经运行三年多了,换句话说,这些参数已经用了三年多),应急方案已经有了,今天也安然度过,接下来只需要等着集群的部署就OK了。

贫僧就解释到这里吧。
#45楼     时间:2013-11-18 17:29:00      来源:Launcher
@ 左潇龙
引用@Launcher
@L'nemo
贫僧再次强调一下,那次2049个线程是因为有线程等待资源导致的,是不正常的状态。贫僧说这个的意思,只是想表达一下,JVM的线程数并不是只有200多个,针对上面那位施主(laosong)的问题。(再次PS:这个结论是上面的施主提出的,贫僧可没这么说过,这里本身也与JVM的线程数没什么关系)

贫僧的tomcat设置到底有没有问题不是贫僧说了算的,因为贫僧其实只是项目中的一个程序员而已,连高级都不是。而且就算是有问题,现在也不重要了,因为说到底tomcat已经扛不住了,这就像你让一个国际上的拳击选手去打一个小朋友,小朋友的任何招数都是无用的,因为在绝对的实力面前...


"贫僧再次强调一下,那次2049个线程是因为有线程等待资源导致的",你说的这个意思不就是我前面一再强调的吗?

"等待资源"?等待什么资源?谁在"等待资源"?找到触发"等待"的代码了吗?有分析过如何优化吗?

我前面也说过了,如果是等待I/O的话,你可以使用异步I/O来解决;如果是等待同步数据结构,诸如critical section,event,lock,semaphore等,那么你应该修改你的业务逻辑,减少同步原语,或者抛弃抢先式计划模型,改用协作式计划模型(比如 Concurrency Runtime).
#46楼     时间:2013-11-18 17:34:00      来源:左潇龙
@ Launcher
贫僧已无语。。。。好吧,施主就当贫僧不懂并发吧。
贫僧只想说,懂点信号量,事件,锁机制真的没什么意义,解决实际问题才是正道,而且对于I/O,已经说了,不是I/O瓶颈,施主一再强调I/O有什么意思呢。
况且,扛不住高并发不一定就是业务逻辑,同步的问题,刚才都已经说了。比如给你个单核256M内存的机子,你能抗住100个并发吗?再次强调,现在的主要问题是硬件和软件已经跟不上用户带来的压力了,提升硬件,或者做集群才是正道,只靠点花拳绣腿,累死累活改代码,是解决不了根本问题的。施主何必在这一直秀自己的基本功呢。。。哎。
贫僧很好奇施主是做Java的吗?难道施主的Java项目当中充满了
“critical section,event,lock,semaphore等,那么你应该修改你的业务逻辑,减少同步原语,或者抛弃抢先式计划模型,改用协作式计划模型(比如 Concurrency Runtime).”
这些东西吗?
况且,一个维护了三年多的项目,老代码一大堆,请问施主有这个兴趣去改吗?
#47楼     时间:2013-11-18 17:50:00      来源:Launcher
@ 左潇龙
引用@Launcher
贫僧已无语。。。。好吧,贫僧不懂并发。


你懂不懂"并发"没关系,哪怕你再精通"并发",我只从"2049个线程","uable to create new native thread"字面来看,就知道你的程序结构有问题,除此之外就只有内存泄露了.
#48楼     时间:2013-11-18 17:52:00      来源:Launcher
@ 左潇龙
引用@Launcher
贫僧已无语。。。。好吧,施主就当贫僧不懂并发吧。
贫僧只想说,懂点信号量,事件,锁机制真的没什么意义,解决实际问题才是正道,而且对于I/O,已经说了,不是I/O瓶颈,施主一再强调I/O有什么意思呢。
况且,扛不住高并发不一定就是业务逻辑,同步的问题,刚才都已经说了。比如给你个单核256M内存的机子,你能抗住100个并发吗?再次强调,现在的主要问题是硬件和软件已经跟不上用户带来的压力了,提升硬件,或者做集群才是正道,只靠点花拳绣腿,累死累活改代码,是解决不了根本问题的。施主何必在这一直秀自己的基本功呢。。。哎。
贫僧很好奇施主是做Java的吗?难道施主的Java项目当中充满了...


请你退回去看我最开始的回帖,一直针对的是"2049个线程","uable to create new native thread",表明你的程序结构有问题,后面引出的I/O什么的,是告诉你引起此问题的最常见的解决办法.
#49楼     时间:2013-11-18 17:52:00      来源:左潇龙
@ Launcher
施主,你再看下回复。另外,施主是做Java的吗?再另外,不能创建本地线程的原因施主能否说一下?
#50楼     时间:2013-11-18 17:55:00      来源:Launcher
@ 左潇龙
引用@Launcher
施主,你再看下回复。另外,施主是做Java的吗?再另外,不能创建本地线程的原因施主能否说一下?


不做 java.我只是对OS和多核时代的并发编程感兴趣而已.

"uable to create new native thread"

http://xiaohuabiao.blog.163.com/blog/static/138482182011102633657724/
#51楼     时间:2013-11-18 17:56:00      来源:左潇龙
@ Launcher
施主的意思是说,“uable to create new native thread”是因为I/O引起的?
#52楼     时间:2013-11-18 18:09:00      来源:Launcher
@ 左潇龙
引用@Launcher
施主的意思是说,“uable to create new native thread”是因为I/O引起的?

我可不是这么说的,从字面看,直接原因是已创建且存活的线程数量达到了系统或JVM的上限。

这篇博文中也有些描述:http://xiaohuabiao.blog.163.com/blog/static/138482182011102633657724/

而我指出的问题是,如果你已经有 2049 个线程了,而你还要再创建新线程来执行请求,这就说明你的程序结构有问题,或者内存泄露了。

而通常这种 web 应用,线程数高的主要原因是当前的线程都在等待I/O完成,或者等待同步对象释放。如果是前者,我说了,可以通过异步I/O机制来释放调用线程,这样你就不需要新建线程来响应请求。

多核时代,并发编程,需要你自己计划线程的调度,你可以看下OpenMP,AMP等,这里已经屏蔽了线程,只有任务,运行时会通过适配硬件的线程数量来执行这些任务。并发,但是还要协调。
#53楼     时间:2013-11-18 18:29:00      来源:左潇龙
@ Launcher
怪不得交流起来有点累呢。。总感觉贫僧看不懂施主的意思,施主也看不懂贫僧的意思。原来施主不是做Java的,抽象层次不一样,讨论起来其实意义不大。
至于施主发的这篇文章,贫僧还是更相信书中的内容,就不看了吧。多谢施主的推荐了。
不管怎么说,非常感谢施主的热心回复。
#54楼     时间:2013-11-18 19:15:00      来源:deerchao
楼主把书看完再和 @Launcher 讨论吧。
如果看完还觉得自己比对方有道理,就再多看几本书吧。
#55楼     时间:2013-11-18 19:23:00      来源:左潇龙
#56楼     时间:2013-11-18 20:47:00      来源:Sword-Breaker
看完评论,楼主沾沾自喜的写了篇东西,结果被人一说就开始到处胡扯...估计再过几年也还是这个水平...别人好心给你讲了这么多重要的知识,换来的只有楼主的“你做过Java吗” “我只是一个普通程序员” “三年多的系统你去改?” “硬件跟不上,软件垃圾”之类的废话...果然嘴炮废话贴最吸引仇恨啊...
#57楼     时间:2013-11-18 21:17:00      来源:左潇龙
@ Sword-Breaker
施主真会断章取义,贫僧只能呵呵了。园子里也有这种无脑喷。。。施主自便吧。
#58楼     时间:2013-11-18 21:34:00      来源:互联网Fans
大侠 求书名
求共鸣
#59楼     时间:2013-11-18 21:36:00      来源:_刘宏伟_
不知道LZ的线程是为啥要创建那么多,我也做过高并发的处理。
我是创建了一个固定的线程,需要同步的业务由这个线程做独立处理。
仅供参考。
#60楼     时间:2013-11-18 21:47:00      来源:左潇龙
@ 互联网Fans
《JAVA并发编程实战》,能共鸣吗,0.0。
#61楼     时间:2013-11-18 22:06:00      来源:左潇龙
@ _刘宏伟_
线程数太高是因为设置的比较高,就贫僧平时的观察来看,正常的线程数大约在400左右,最低的时候200多。那次2000多个线程是因为资源等待引起的,或者是锁之间的等待引起的。不过当时比较匆忙,这只是猜测,还没仔细观察就给restart了,之后也没再出现,面对如山的代码,接连不断的需求,没有时间去一一排查代码,因此这个问题就只能暂且放下了。
不过这次做集群的时候要做压力测试,希望能找出一点蛛丝马迹,只是项目时间已经比较久了,诞生于最早技术部成立的时候,公司也正在着手建立ESB总线,一旦建立好,这个项目也就到死期了,最大的可能就是被兼并到其它的综合系统当中了。
其实贫僧就是在照顾着一个即将死去的老人。
#62楼     时间:2013-11-19 01:26:00      来源:左潇龙
@ Launcher
晚上再仔细看看施主的回复,才发现我们讨论的根本不是一个问题。白天工作比较忙,都是瞄一眼就匆匆忙忙回复了,没时间认真的回复,这点抱歉。(贫僧的确有点夜猫子,0.0)

施主至始至终大部分都是一直在强调那次2049个线程的问题,其实这一点只是为了告诉之前那位施主,JVM的线程数根本不是200多,是为了澄清那位施主的误解。
之后施主就一直揪着这个问题不放了,简单来说,施主就是一直在分析这2049个线程是因为什么引起的,比如等待I/O资源等等。说的倒也算是头头是道吧,但也都是一些博文当中就能搜索到的概念或者是原理解释,但是说真的,没有什么实际的作用。

问题的关键点在于,2049这个问题早就过去了(再次强调,现在系统的问题并不是线程数超额,而是连接数,或者更准确的说是TCP连接,提到这个2049只是为了回答那位施主的疑问),公司不会给你时间去大量翻代码找一个若即若离的问题(再次强调,这个项目已经三年多,贫僧进入公司一年整,大部分的代码都是之前产生的,换句话说,贫僧是在维护一个老项目),因为总会有人打断你,也会有需求缠着你,还有一些运维的事找你,业务人员也找你,还有其它系统的人找你,也会有新人问你问题等等。

因此施主一直想表达这样的意思,你去代码里找问题啊,你去好好看看你的代码啊,你去好好设计你的项目啊。贫僧真的很无语,0.0。

第一是,Java的代码(专指企业级使用现有J2EE框架的开发)当中很少有主动的线程创建,或者是主动的同步操作(主动的是指程序猿写的),所以代码里不小心多建了线程或者是同步写错了这种情况是不太可能出现的,也正因如此,贫僧才难得写一次需要同步的程序。当然了,不知道施主的项目中是不是都是自己创建的,这点贫僧就不清楚了,贫僧也不知道施主做什么语言,不了解没发言权,0.0。第二,这些代码大部分其实并不是贫僧写的,而且代码量确实很大。第三,贫僧也想看,无数次想重构一下代码,但真的没时间,所以施主给贫僧的感觉是有点站着说话不腰疼,0.0。第四,贫僧倒是想设计我们的项目,可惜那是四年前设计的,当时贫僧还没进公司,0.0。第五,这个项目已经快被抛弃了,这个公司已经明确表示了,将来会有新的综合系统(综合就是指将多个系统集成到总线上)替代,其实简单点说,这个项目已经快功成身退了,到时贫僧也会转到新项目当中,没有人会花费这么大的精力去完善一个即将被抛弃的项目。

这就是工作,平时你可以有自己的兴趣,你可以去死钻研那些代码(不过贫僧的选择一般是看优秀框架或者JDK的源码或者看书,不会是看那些老代码,0.0),但是工作讲究的是效率,讲究的是解决问题。只要能解决问题,都是好办法,而不是花费大量的时间去做一件不一定能有收成的事,比如死找代码。

另外,贫僧为了保护一定的公司机密,所以没敢透漏任何具体的项目内容,难道施主就这么自信,就凭2049个线程就一下断定了问题?贫僧项目组包括领导上下也有几十个人参与过这件事,难道不敌施主一个基于2049的猜测或者推理?事实情况是,其实都能大概猜出来是因为什么(无非就是数据库有锁,也就是施主说的I/O,或者是线程之间有锁呗),可是真的找出来岂是那么好找的?

如果施主真的想帮贫僧,贫僧自然感激不尽,不过施主只是一直在争论一个与本文关系不大的问题。

如果施主真有意帮忙的话,可以讨论下本文的正题,比如最大的正题--连接数爆满,nginx不关闭连接。或者是Java中如何合理的编写并发程序(就像贫僧列出的那五条一样)。又或者在集群方面有什么好的建议?又或者对于nginx有何高见?

其实听到施主不是做Java的,就觉得讨论这些没什么意思,因为贫僧曾经就想当然的YY过其它语言的代码,结果发现完全不是这么回事。所以贫僧也希望施主莫要YY非自己熟悉的领域的代码结构,其实有时候是有很大差别的。

话就至此吧,大半夜了,看会书休息了。
#63楼     时间:2013-11-19 09:59:00      来源:L'nemo
@ 左潇龙
引用@Launcher
@L'nemo
贫僧再次强调一下,那次2049个线程是因为有线程等待资源导致的,是不正常的状态。贫僧说这个的意思,只是想表达一下,JVM的线程数并不是只有200多个,针对上面那位施主(laosong)的问题。(再次PS:这个结论是上面的施主提出的,贫僧可没这么说过,这里本身也与JVM的线程数没什么关系)

贫僧的tomcat设置到底有没有问题不是贫僧说了算的,因为贫僧其实只是项目中的一个程序员而已,连高级都不是。而且就算是有问题,现在也不重要了,因为说到底tomcat已经扛不住了,这就像你让一个国际上的拳击选手去打一个小朋友,小朋友的任何招数都是无用的,因为在绝对的实力面前...


从博主3个月写了九十多篇文章,可以看出博主是个积极向上的人,也是一个乐于分享的人。
其次,多线程,我想只要是面向客户的web程序,都应该把多线程问题作为程序设计的必然考虑的因素之一,而且多线程本身就比较繁琐且很难预测,所以一直都是大家比较关注的。所以我回帖是希望能从博主这里获取一些实际经验。
我想从文章中知道的是,博主遇到的问题背景是什么,怎么产生的。其次博主是如何解决的,解决的细节是什么?这两个问题我个人认为博主可以详细写一写。
#64楼     时间:2013-11-19 12:16:00      来源:laosong
@ 左潇龙
引用@_刘宏伟_
线程数太高是因为设置的比较高,就贫僧平时的观察来看,正常的线程数大约在400左右,最低的时候200多。那次2000多个线程是因为资源等待引起的,或者是锁之间的等待引起的。不过当时比较匆忙,这只是猜测,还没仔细观察就给restart了,之后也没再出现,面对如山的代码,接连不断的需求,没有时间去一一排查代码,因此这个问题就只能暂且放下了。
不过这次做集群的时候要做压力测试,希望能找出一点蛛丝马迹,只是项目时间已经比较久了,诞生于最早技术部成立的时候,公司也正在着手建立ESB总线,一旦建立好,这个项目也就到死期了,最大的可能就是被兼并到其它的综合系统当中了。
其实贫僧就是在照顾着一个即...

鄙人也好久没搞过java了,但一些基本概念还是有的,并发本质上线程多少没有任何关系,只有有多线程,只要有共享资源,写代码的时候脑子中就得蹦一根弦。博主的系统并发其实完全依赖tomcat这种app engine,自己的代码也基本跟线程和共享资源没啥关系,请求来了,tomcat发现线程池中没有可用线程时创建一个新线程来满足请求,但由于博主的系统一个请求处理时间很长,所以线程池经常不得空,tomcat老是得创建新线程,所以才会到2000的上限(然后就悲剧了),正常情况tomcat的200个线程完全可以满足大负荷请求,创建更多的线程虽然可以让请求得到处理,但请求处理效率反而下降。
其实我也明白博主的意思,但说实话博主此篇文章技术含量不大,关于sessionlist这种共享资源,谁都知道要尽快使用,不要锁太长时间。其实如果对数据精度要求不高,完全可以把在使用这个list之前把需要的特定信息复制过来(新list),然后可以慢慢使用(一些无锁的并发算法就是这么干的)
#65楼     时间:2013-11-19 12:21:00      来源:laosong
@ _刘宏伟_
引用不知道LZ的线程是为啥要创建那么多,我也做过高并发的处理。
我是创建了一个固定的线程,需要同步的业务由这个线程做独立处理。
仅供参考。

工作线程一般情况下创建线程池就够了,线程池里面线程数量基本相当于系统cpu核心数量即可
#66楼     时间:2013-11-19 13:37:00      来源:左潇龙
@ laosong
做过Java还是不一样,知道线程是由web服务器控制的。
此外施主说的线程数太高这个问题,确实引起了贫僧的好奇心,因此贫僧今天还真专门问了问项目经理,到底为什么设这么高。
项目经理的意思是,之前其实也设的比较低,约莫3、4百左右。但是后来达到accpetCount时(当时accept具体是多少,这个没提),就会给用户显示connection refused。因为这边可用的线程满了,挂起来的数目(也就是accpetCount)也达到上限了,就直接拒绝服务了。
后来业务人员就暴动了,他们可不管你什么参数优化,配置优化,他们只觉得,我要用系统,你凭什么拒绝我连接?
所以后面项目经理就给调高了,也就再没出现过问题。以上这些事是发生在贫僧来之前的。

所以说,有些东西,其实得看需求,不能太死板,不一定就得按照文档上的建议去做就是对的。施主说的也没错,找一个合适的线程数,会让响应稍微快一点,但是同时可以接受的请求数变少了,用户会被拒绝,这就会导致业务那边(其实也就是用户)体验不好。

总的来说,现在是拿时间换了点人数。就是说,设置的低点,可以同时处理200个,但是速度比较快。设置的高点,同时处理的数目增加,但是速度比较慢了。(但是要记住,速度慢点,业务人员是没感觉的,但是你要是拒绝服务,你试试?)

比如就按照设置成200和300来说,假设一下来了300个请求,开300个线程去处理,可能总体时间确实没200个快,但是用户的请求都被接受了,他们只是感觉慢点,还是可以接受的,甚至不怎么能感觉出来。但是你要是只接受200个,哪怕你再快,这100个也得等一会,假设acceptCount设置为50的话,那会有50个用户就直接显示connection refused了,这样他们可就暴走了。

于是当时应该就是秉承着宁可慢点,也不能让业务人员被拒绝这种思想,就给调高了,为了彻底消除connection refused。因为慢点他们不投诉,但是拒绝他们可就投诉了,你说你怎么设置吧?

至于施主说本文技术含量不高,这个贫僧没什么好说的,只是贫僧有说本文技术含量很高吗?好像没有吧。。。。或者施主要是有何高招,给个技术含量高的方案也行。贫僧拜读。
#67楼     时间:2013-11-19 14:24:00      来源:laosong
@ 左潇龙
这种情况建议博主还是用多个tomcat负载均衡吧,反正前端有nginx,你现在这种情况用户体验很糟糕
#68楼     时间:2013-11-19 15:37:00      来源:左潇龙
@ laosong
这个已经在做了,文中已经说了。。0.0。应急方案目前是抗住了,只是慢了点。
#69楼     时间:2013-11-19 17:42:00      来源:laosong
@ 左潇龙
引用@laosong
这个已经在做了,文中已经说了。。0.0。应急方案目前是抗住了,只是慢了点。

我怎么感觉做个tomcat负载均衡比你的应急方案简单多了
#70楼     时间:2013-11-19 18:57:00      来源:左潇龙
@ laosong
应急方案只是代码上的更改,因此不需要重新搭建测试环境,只需要贫僧一个人花一天多写完代码,测试一下就可以上线了。总共也就用了两天,就搞定了。

而负载均衡的方案不只是负载均衡,还有前后端的分离。负载均衡和前后端的分离是需要重新搭建一套测试环境的,再加上搭建完以后测试的时间,说不定有什么问题还需要改一下(比如session和缓存的问题以及一些意外的问题),而且最主要的是,部门间的协作一定会延长这个时间,因为需要测试部配合,大公司流程多(如果施主进过的话,你懂的),难免耽误不少时间。

不过施主说的也没错,要说简单是很简单,改下配置,多启动几个应用,完事。但是这需要严格的测试,公司有公司的规定,必须按流程走。

什么叫应急方案?应急的意思就是说实施起来会很快。施主貌似不是很明白应急的意义所在,这和难度没关系,就是时间上快而已。

不知道施主的公司是不是做集群和前后端分离的时候,就是直接改下配置,启动俩应用做负载均衡,再启动一个应用做后端服务,然后就扔上去了,一天搞定。。。呵呵。
#71楼     时间:2013-11-20 09:23:00      来源:laosong
@ 左潇龙
引用@laosong
应急方案只是代码上的更改,因此不需要重新搭建测试环境,只需要贫僧一个人花一天多写完代码,测试一下就可以上线了。总共也就用了两天,就搞定了。

而负载均衡的方案不只是负载均衡,还有前后端的分离。负载均衡和前后端的分离是需要重新搭建一套测试环境的,再加上搭建完以后测试的时间,说不定有什么问题还需要改一下(比如session和缓存的问题以及一些意外的问题),而且最主要的是,部门间的协作一定会延长这个时间,因为需要测试部配合,大公司流程多(如果施主进过的话,你懂的),难免耽误不少时间。

不过施主说的也没错,要说简单是很简单,改下配置,多启动几个应用,完事。但是这需要严格的测试,公司...


一天,怎么可能要那么多时间,如果系统成熟,分分钟的事,另这些事本质上跟程序员没关系,应该是运维的事。当然如果你们的系统是个烂架构我就不说了
#72楼     时间:2013-11-20 12:28:00      来源:左潇龙
@ laosong
贫僧有说跟程序猿有关系?说了是测试部需要测试,这是公司的规定,任何改动,哪怕什么都不改,只是重启也得通知业务部门。分分钟,施主还真好意思说,出任何问题你都吃不了兜着走。施主以为运维的人傻?不测试就随便改了出了问题可是自己的事。
而且施主不要忘了还有前后端的分离,这个和业务系统关系很大,不测试你就分分钟换掉,贫僧只能说施主的认识还停留在理论上。
目测施主的公司不超50人,权限松动,之前贫僧也是在小公司,项目想怎么整就怎么整,完全不需要测试,自己点点就算测试了。
分分钟换集群,呵呵,贫僧是不是应该觉得施主很牛X?哈哈。。
#73楼     时间:2013-11-20 15:33:00      来源:laosong
@ 左潇龙
引用@laosong
贫僧有说跟程序猿有关系?说了是测试部需要测试,这是公司的规定,任何改动,哪怕什么都不改,只是重启也得通知业务部门。分分钟,施主还真好意思说,出任何问题你都吃不了兜着走。施主以为运维的人傻?不测试就随便改了出了问题可是自己的事。
而且施主不要忘了还有前后端的分离,这个和业务系统关系很大,不测试你就分分钟换掉,贫僧只能说施主的认识还停留在理论上。
目测施主的公司不超50人,权限松动,之前贫僧也是在小公司,项目想怎么整就怎么整,完全不需要测试,自己点点就算测试了。
分分钟换集群,呵呵,贫僧是不是应该觉得施主很牛X?哈哈。。

博主不要激动,前面没说鄙人都做过什么就是怕博主说鄙人在装x,鄙人03年的时候就负责一个alex全球一千名的网站的开发工作,至少那时候我们都已经是集群了(博主现在的系统还非集群想也非多大系统),当然是weblogic而非tomcat。既然博主现在的系统也不是博主开发,所以我说它是个烂架构也并非在打击博主,至于我说分分钟就能上线当然是建立在架构已经很好支持的情况下,只需要运维加机器,部署环境就可以,这些是运维的事,开发已经完全从这些琐事中解脱出来了。
#74楼     时间:2013-11-20 21:05:00      来源:左潇龙
@ laosong
呵呵,施主想多了。贫僧一直很淡定。。
至于施主说自己做过什么全球前一千的网站,还是03年。个人还是觉得年数与水平关系不大,尤其在天朝。至于施主是否真的很牛X其实与贫僧没多大关系。
其实施主说贫僧项目的架构烂,贫僧是很欢迎的,因为这说明贫僧表现的时候到了。但是关键就是施主既然说了烂,那最好就要给个可行的方式,如何能有效地改善,并且确实可行。只是施主只是一味的说烂,确没任何建议。唯一的建议也是文中已经提过的集群,这也就算了,施主又开始说你们太慢,我分分钟搞定。而且贫僧多次强调还有前后端的分离,施主却只字不提。
其实施主说的分分钟搞定,就是指你可以分分钟把nginx设置好,然后启动俩应用,说实话这没什么可炫耀的,这本身就没什么难度。问题就是你弄好以后,这个项目能否正常工作。所以施主就加了个前提,项目好的前提下可以分分钟搞定。言外之意就是说,项目好,我设置完就肯定没错,所以分分钟搞定。换句话说,施主的分分钟依赖于项目,与施主关系不大。
这就像现在要去玩,有三十个人,但是只有一个轿车。轿车一次拉不了三十个,所以只能多拉几次,有点慢。此时,施主就开始发话了,你们真慢,给我个公交车我分分钟就把你们一次性拉过去。但问题这不是没有公交车吗,有的话谁都能做到。要真厉害,施主说个办法,哪怕是小轿车,施主也能一次性拉走,这样的话,说别人慢才有意义吧?
不知道施主能看懂这个比喻不?如果看懂了,施主自己觉得有意思吗?
#75楼     时间:2013-11-21 01:33:00      来源:浅夏晴雨
@laosong 所说的就是水平扩展性。这个确实是架构设计问题。
不过从楼主的后面的回复来看,似乎也并不打算优化这一点,或者说认为这一点做不到了。
#76楼     时间:2013-11-23 01:37:00      来源:左潇龙
@ 浅夏晴雨
施主错了,大错特错啊。阿弥陀佛。
“似乎也并不打算优化这一点”这句显然错了,真实的情况是贫僧非常想优化(只要是个程序猿,只要他在维护一个比较古老的项目,试问有人不想把自己维护的项目优化的维护性和扩展性更强一些?)。
“认为这一点做不到了”这句就更错了,贫僧当然可以做到,前面都说了,只是集群(也就是施主所谓的水平扩展性)需要测试一下,大约需要一个多星期的时间。
只是那位施主一直在强调自己可以分分钟搞定,因此贫僧才说“搞不定”。但是这里的“搞不定”,只是在说,对于贫僧个人的项目以及公司制度来说,无法“分分钟搞定”而已,而不是“搞不定”。
而对于那位施主的分分钟,贫僧只能表示呵呵,详见74楼,不再重复了。
#77楼     时间:2013-11-25 12:49:00      来源:徐少侠
@ 左潇龙
@L'nemo
@Launcher

因为曾经有过几百个的限制导致连接拒绝,然后调高后症状消除
但是并行程序处理本质上Web服务器的请求队列长度无关
大家吵吵的那个2049,可以大致看作是请求队列,但不是并行线程

很多时候,单线程的性能高于多线程
极端的,如果web永远单线程,反而能提高整体服务器的吞吐量,呵呵
#78楼     时间:2015-07-27 19:20:00      来源:Mis'World
请问博主是哪本书?可以推荐一下吗
#79楼     时间:2015-11-09 20:04:00      来源:asefesfefe
@ Sword-Breaker
跟你同感,之前看过LZ的博客的一些文章,有些简直在胡扯,不过佩服LZ的分享精神。
#80楼     时间:2016-08-09 15:14:40      来源:上海市网友

sdsddfdffdfdfddffdffdddfdfdsdfsdfdsfdsfdfdffffffd

  • dsfdsfdsfsfdfdsfdfsdfsdfddfdfdfdsfdsfsdfdsfdsfsdfd
  • jyghgggggggggggggggggggggggggggj
  • ghjughjhgggggjhgjhgjhgj
  • gggggggg
#81楼     时间:2017-03-18 20:09:00      来源:郑州的文武
文章看的不大明白,直到我把评论看完。
#82楼     时间:2017-07-31 10:37:01      来源:杭州市网友

激烈的讨论才是健康的讨论

#83楼     时间:2017-10-08 22:11:00      来源:wxyjuly
@ Launcher
引用
@左潇龙
引用引用@Launcher
施主理解错误了,这里2049个线程并不是为了并行的完成某项任务。如果真是这样设计的话,岂不是要来一台2049个CPU的机子吗。。。。怎么可能呢。。。
这里的2049个线程,只是web服务器(web服务器会启动线程响应请求)响应不过来用户的请求,导致响应速度变慢,恶性循环之后,线程数就超了。这不是刻意设计的。施主理解错了,0.0。


我觉得是你理解错误了,问题的症结就在这里,你不理解I/O模型,不能区分I/O线程和Worker线程,不知道在读取/写入 I/O 时使用的线程是非常少的,而大量创建的线程被你的应用程序逻辑代码占据.最明显...

对的...
#84楼     时间:2019-12-25 16:44:03      来源:长沙市网友
@vbfool
想想我平时总做的那种“用户不过5,并发不过2”的系统,估计真遇上和楼主一样的情况,不知道会乱成什么样。

不用担心,哈哈哈,在多几个请求放服务器还是顶得住的

发表评论

站内搜索