引入
这两周老大安排我做了年会系统的后端开发工作,这里面有消息的推送websocket相关技术,也有关于易信开发的相关技术,由于我之前没有应用过,在这里做个总结。
需求
这里面需求映射为技术的话无非就对数据库的增删查改的操作,只是比普通的需求点多了这两个特殊的需求。
需求一,当总监在台上点击pad上的“开始抽奖”按钮时,页面左部份出现奖品的滚动操作,右部分每一秒中出现一个获奖者,并同时滚动。此刻,中奖名单也每秒一个的速度推送至台下的员工,从而模拟奖品的分发,当然中奖是随机的。
需求二,用户通过关注易信号,获取进入员工绑定页面的图文消息(这里面有个操作就是易信号的openid与员工信息的绑定),同时通过回复关键词获取自己的序号,。不仅如此,员工余下的操作都是在易信打开的页面完成的。
上述就是主要的两个需求点。
关于websocket
摘自 维基百科
基本介绍
WebSocket一种在单个TCP连接上进行全双工通讯的协议。
WebSocket通信协议于2011年被IETF定为标准RFC 6455,并被RFC7936所补充规范。WebSocket API也被W3C定为标准。
WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
1 | 客户端请求 |
与及其他技术的比较
现在,很多网站为了实现推送技术,所用的技术都是轮询。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。
而比较新的技术去做轮询的效果是Comet。这种技术虽然可以双向通信,但依然需要反复发出请求。而且在Comet中,普遍采用的长链接,也会消耗服务器资源。
Websocket使用ws或wss的统一资源标志符,类似于HTTPS,其中wss表示在TLS之上的Websocket。
优点:
较少的控制开销。在连接创建后,服务器和客户端之间交换数据时,用于协议控制的数据包头部相对较小。在不包含扩展的情况下,对于服务器到客户端的内容,此头部大小只有2至10字节(和数据包长度有关);对于客户端到服务器的内容,此头部还需要加上额外的4字节的掩码。相对于HTTP请求每次都要携带完整的头部,此项开销显著减少了。
更强的实时性。由于协议是全双工的,所以服务器可以随时主动给客户端下发数据。相对于HTTP请求需要等待客户端发起请求服务端才能响应,延迟明显更少;即使是和Comet等类似的长轮询比较,其也能在短时间内更多次地传递数据。
保持连接状态。于HTTP不同的是,Websocket需要先创建连接,这就使得其成为一种有状态的协议,之后通信时可以省略部分状态信息。而HTTP请求可能需要在每个请求都携带状态信息(如身份认证等)。
更好的二进制支持。Websocket定义了二进制帧,相对HTTP,可以更轻松地处理二进制内容。
可以支持扩展。Websocket定义了扩展,用户可以扩展协议、实现部分自定义的子协议。如部分浏览器支持压缩等。
更好的压缩效果。相对于HTTP压缩,Websocket在适当的扩展支持下,可以沿用之前内容的上下文,在传递类似的数据时,可以显著地提高压缩率。
关于一些坑和事故
问题一:在本地进行性能测试的时候,chrome连接到250左右个websocket的时候出现连接错误。
原因是:谷歌浏览器 最多支持250个websocket连接。
问题二:建立大量socket连接时,出现connection refused。
原因是:tomcat的配置文件server.xml里有两个参数。
1 | <Connector port="8080" protocol="HTTP/1.1" |
其中最后两个参数意义如下:
maxThreads:tomcat起动的最大线程数,即同时处理的任务个数,默认值为200
acceptCount:当tomcat起动的线程数达到最大时,接受排队的请求个数,默认值为100
后来我设置这两个参数我都设置成1500。然后本地测试 建立连接后, 服务器 cpu 大概在60%-70%
问题三:在最后实际上线的时候,650+同时建立websocket连接,服务器cpu到达300%。无法进行访问。
这个问题我没有办法。后来谷歌了一些资料,同时也请教了一些同学。知道自己要改进的地方还有很多:
下面贴一下Tomcat 的三种(bio,nio.apr) 高级 Connector 运行模式,来自于网络。
1)bio
默认的模式,性能非常低下,没有经过任何优化处理和支持.
2)nio
利用java的异步io护理技术,no blocking IO技术.
想运行在该模式下,直接修改server.xml里的Connector节点
启动后,就可以生效。
3)apr
修改protocol为org.apache.coyote.http11.Http11AprProtocol。
需要安装,从操作系统级别来解决异步的IO问题,大幅度的提高性能。
如果不用APR,一个线程同一时间只能处理一个用户,势必会造成阻塞。所以生产环境下 用apr是非常必要的。
1 | 安装APR |
==WARNING:==
生产使用
虽然WebSocket实现可以与任何HTTP连接器一起工作,但是不推荐WebSocket和BIO HTTP连接器配合使用。因为WebSocket的典型使用并不适合HTTP BIO连接器,HTTP BIO 连接器要求为每个连接分配一个线程,而不管是否有空闲的连接。
据报告(56304),Linux会花费大量时间来报告丢掉的连接。当以BIO HTTP连接器使用WebSocket时,这可能会导致该时段内写入出现阻塞。这似乎是不可取的。通过使用内核网络参数/proc/sys/net/ipv4/tcp_retries2,可以减少因连接丢失所花费的报告时间。此外,他们使用了非阻塞IO,可以让Tomcat实现自己的超时机制从而处理这些情况,你也可以使用其他的HTTP连接器。
同时,有同学指出:
tomcat 里有相关的websocket的配置参数,我查了一下 ,确实如此:
Java WebSocket 1.0 规范要求在一个不同的线程上执行到发起写入的线程的异步写入回调。
由于容器线程池不是通过Servlet API被暴露,因此WebSocket实现必须提供自己的线程池。该线程池可以通过下列servlet context初始化参数被控制:
org.apache.tomcat.websocket.executorCoreSize
executor线程池的核心大小。如果不设置,则默认为0。
org.apache.tomcat.websocket.executorMaxSize
executor线程池所允许的最大值。如果不设置,则默认为200。
org.apache.tomcat.websocket.executorKeepAliveTimeSeconds
executor线程池中空闲进程所保留的最大时间。如果未指定,则默认为60秒。
那么设置方法如何呢?
web.xml中
1 | <!--websocket executor 线程池的核心容量大小 --> |
关于Linux的优化网络,修改/etc/sysctl.cnf文件
1 | 1. 修改/etc/sysctl.cnf文件,在最后追加如下内容: |
其它
后来SA建议我说对于高并发,一定得测试,然后调整参数。
本篇博客 还会再做修正。同时简单介绍下易信/微信的接口开发。
致谢
- tomcat优化-有改protocol 和 缓存 集群方案
- Apache Tomcat 8 WebSocket How-To 中文翻译版
- websocket维基百科
- Tomcat 7.x/8.x 优化