MySQL数据库命名规范及约定

一、【操作规范】
1. 如无备注,则表中的第一个id字段一定是主键且为自动增长;
2. 如无备注,则数值类型的字段请使用UNSIGNED属性;
3. 如无备注,排序字段order_id在程序中默认使用降序排列;
4. 如无备注,所有字段都设置NOT NULL,并设置默认值;
5. 如无备注,所有的布尔值字段,如is_hot、is_deleted,都必须设置一个默认值,并设为0;
6. 所有的数字类型字段,都必须设置一个默认值,并设为0;
7. 针对varchar类型字段的程序处理,请验证用户输入,不要超出其预设的长度;
8. 建表时将数据字典中的字段中文名和属性备注写入数据表的备注中(“PK、自动增长”不用写);
9. 如无说明,建表时一律采用innodb引擎;

二、【常用表名约定】
0. 说明:表前缀用项目名称首字母缩写;所以表名都小写,单词之间用下划线分开,单词都用单数形式
1. user – 用户
2. category – 分类
3. goods – 商品、产品等一切可交易网站的物品都用此命名
4. good_gallery – 物品的相册
5. good_cate – 物品的分类,除了单独作为表名,其他地方分类单词一律用缩写cate
4. attr – 属性
5. article – 文章、新闻、帮助中心等以文章形式出现的,一般都用此命名
6. cart – 购物车
7. feedback – 用户反馈
8. order – 订单
9. site_nav – 包括页头和页尾导航
10. site_config – 系统配置表
11. admin – 后台用户 【RBAC标准表】
12. role – 后台用户角色【RBAC标准表】
13. access – 后台操作权限,相当于action【RBAC标准表】
14. role_admin – 后台用户对应的角色【RBAC标准表】
15. access_role – 后台角色对应的权限【RBAC标准表】
16. 待续

三、【常用列名约定】
1. 表名_id – 通常用作外键命名
2. cid –
特殊的编号,带有元数据,方便关联查询,你可以把它理解成类别(层次)编号。举个例子,产品在分类时,往往需要将其归类到子分类下,相应的字段中也一般只
记录子分类的id,这时若需要知道该产品属于哪个主分类,就需要通过子分类信息再查询到主分类信息,这是比较麻烦的,cid字段就是要解决这个问题。一般
的站点几十个分类肯定是够用了,所以这里假设某一主分类的cid为11,则子分类的cid从1101开始编号,处理时只需截取前两位数值便可知道该产品属
于哪一个主分类了。
3. add_time – 添加时间、上架时间等
4. last_time – 最后操作时间,如登录、修改记录
5. expire_time – 过期时间
6. name – 商品名称、商家名称等,不要跟title混用,title只用于文章标题、职称等
7. price – 价格
8. thumb – 只要是列表页面中的窗口图,一律用此命名
9. image_src – 相册中的图片地址一律用此命名,不要出现各种img,image,img_url,thumb_url等
10. head_thumb – 用户头像, 虽然有点长,一定要遵守。不要出现上述情况
11. image_alt – 相册中图片的alt属性
12. desc – 描述、简介,比如goods_desc,不要出现goods_txt这种
13. details – 详情、文章内容等
14. order_id – 排序
15. telephone – 座机号码
16. mobile – 手机号码
17. phone – 当不区分手机和座机时,请用phone命名
18. address – 地址,单独出现不要用addr缩写,组合出现时需用缩写,比如mac地址,mac_addr
19. zipcode – 邮编
20. region – 地区,大的区域,比如记录杭州市、温州市等
21. area – 区域,小的,比如上城区,江干区等
22. avg_cost – 人均消费
23. 待续

四、【数据表字段设计范例】

分类表(t_category

字段名

列名

类型

属性备注

说明

流水号 id int(10) PK、自动增长
特殊编号 cid varchar(4) 第一个主分类为11、第一个子分类为1101,类推,仅支持二级分类
名称 name varchar(10) 页面中需注明输入不超过10个字
父分类 pid int(10)
统计量 count int(10)
是否热门 is_hot tinyint(1)
首页显示 is_index tinyint(1)
排序 order_id int(10)

发表在 db | 标签为 | MySQL数据库命名规范及约定已关闭评论

LISTENING,ESTABLISHED,TIME_WAIT,CLOSE_WAIT

TCP协议规定,对于已经建立的连接,网络双方要进行四次握手才能成功断开连接,如果缺少了其中某个步骤,将会使连接处于假死状态,连接本身占用的资源不
会被释放。网络服务器程序要同时管理大量连接,所以很有必要保证无用连接完全断开,否则大量僵死的连接会浪费许多服务器资源。在众多TCP状态中,最值得
注意的状态有两个:CLOSE_WAIT和TIME_WAIT。 
 
1、LISTENING状态
  FTP服务启动后首先处于侦听(LISTENING)状态。

2、ESTABLISHED状态
  ESTABLISHED的意思是建立连接。表示两台机器正在通信。

3、CLOSE_WAIT

    对方主动关闭连接或者网络异常导致连接中断,这时我方的状态会变成CLOSE_WAIT 此时我方要调用close()来使得连接正确关闭

4、TIME_WAIT

   
我方主动调用close()断开连接,收到对方确认后状态变为TIME_WAIT。TCP协议规定TIME_WAIT状态会一直持续2MSL(即两倍的分
段最大生存期),以此来确保旧的连接状态不会对新连接产生影响。处于TIME_WAIT状态的连接占用的资源不会被内核释放,所以作为服务器,在可能的情
况下,尽量不要主动断开连接,以减少TIME_WAIT状态造成的资源浪费。

    目前有一种避免TIME_WAIT资源浪费的方法,就是关闭socket的LINGER选项。但这种做法是TCP协议不推荐使用的,在某些情况下这个操作可能会带来错误。

 

来源:http://www.cppblog.com/prayer/archive/2009/04/01/78592.html

 

 

常用的三个状态是:ESTABLISHED 表示正在通信,TIME_WAIT 表示主动关闭,CLOSE_WAIT 表示被动关闭。

TCP协议规定,对于已经建立的连接,
网络双方要进行四次握手才能成功断开连接,如果缺少了其中某个步骤,将会使连接处于假死状态,连接本身占用的资源不会被释放。网络服务器程序要同时管理大
量连接,所以很有必要保证无用连接完全断开,否则大量僵死的连接会浪费许多服务器资源。在众多TCP状态中,最值得注意的状态有两
个:CLOSE_WAIT和TIME_WAIT。  

TIME_WAIT 

TIME_WAIT 是主动关闭链接时形成的,等待2MSL时间,约4分钟。主要是防止最后一个ACK丢失。  由于TIME_WAIT 的时间会非常长,因此server端应尽量减少主动关闭连接

CLOSE_WAIT
CLOSE_WAIT是被动关闭连接是形成的。根据TCP状态机,服务器端收到客户端发送的FIN,则按照TCP实现发送ACK,因此进入
CLOSE_WAIT状态。但如果服务器端不执行close(),就不能由CLOSE_WAIT迁移到LAST_ACK,则系统中会存在很多
CLOSE_WAIT状态的连接。此时,可能是系统忙于处理读、写操作,而未将已收到FIN的连接,进行close。此时,recv/read已收到
FIN的连接socket,会返回0。

为什么需要 TIME_WAIT 状态?
假设最终的ACK丢失,server将重发FIN,client必须维护TCP状态信息以便可以重发最终的ACK,否则会发送RST,结果server认
为发生错误。TCP实现必须可靠地终止连接的两个方向(全双工关闭),client必须进入 TIME_WAIT
状态,因为client可能面 临重发最终ACK的情形。

为什么 TIME_WAIT 状态需要保持 2MSL 这么长的时间?
如果 TIME_WAIT
状态保持时间不足够长(比如小于2MSL),第一个连接就正常终止了。第二个拥有相同相关五元组的连接出现,而第一个连接的重复报文到达,干扰了第二个连
接。TCP实现必须防止某个连接的重复报文在连接终止后出现,所以让TIME_WAIT状态保持时间足够长(2MSL),连接相应方向上的TCP报文要么
完全响应完毕,要么被 丢弃。建立第二个连接的时候,不会混淆。

 TIME_WAIT 和CLOSE_WAIT状态socket过多

如果服务器出了异常,百分之八九十都是下面两种情况:

1.服务器保持了大量TIME_WAIT状态

2.服务器保持了大量CLOSE_WAIT状态,简单来说CLOSE_WAIT数目过大是由于被动关闭连接处理不当导致的。

因为linux分配给一个用户的文件句柄是有限的,而TIME_WAIT和CLOSE_WAIT两种状态如果一直被保持,那么意味着对应数目的通道就一直
被占着,而且是“占着茅坑不使劲”,一旦达到句柄数上限,新的请求就无法被处理了,接着就是大量Too Many Open Files异常

 

来源:http://blog.csdn.net/kobejayandy/article/details/17655739

发表在 network | LISTENING,ESTABLISHED,TIME_WAIT,CLOSE_WAIT已关闭评论

ParameterInfo properties, attributes and out ref parameters of the methods

引用: http://stackoverflow.com/questions/15636969/parameterinfo-properties-attributes-and-out-ref-parameters-of-the-methods

Well, I'm confused by the properties of the ParameterInfo class.
Unfortunately documentation is not very clear: examples show how to build methods but don't show how these methods look in C#.

Cane somebody tell more about these properties:

  • DefaultValue
  • HasDefaultValue
  • IsIn
  • IsLcid
  • IsOptional
  • IsOut
  • IsRetval

And which combination leads to what method params.
I made a simple program which gives the following output:

Method name M1 void M1(object param)
IL signature: .method public hidebysig instance void M1(object param) cil managed
Method parameter description:
Is passed by reference False
HasDefaultValue=False
IsIn=False
IsLcid=False
IsOptional=False
IsOut=False
IsRetVal=False

Method name M2 void M2(object param = null)
IL signature .method public hidebysig instance void M2([opt] object param) cil managed
Method parameter description:
Is passed by reference False
HasDefaultValue=True
DefaultValue=null
IsIn=False
IsLcid=False
IsOptional=True
IsOut=False
IsRetVal=False

Method name M3 void M3(out object param)
IL signature .method public hidebysig instance void M3([out] object& param) cil managed
Method parameter description:
Is passed by reference True
HasDefaultValue=False
IsIn=False
IsLcid=False
IsOptional=False
IsOut=True
IsRetVal=False

Method name M4 void M4(ref object param)
IL signature .method public hidebysig instance void M4(object& param) cil managed
Method parameter description:
Is passed by reference True
HasDefaultValue=False
IsIn=False
IsLcid=False
IsOptional=False
IsOut=False
IsRetVal=False

Method name M5 void M5([In] object param)
IL signature .method public hidebysig instance void M5([in] object param) cil managed
Method parameter description:
Is passed by reference False
HasDefaultValue=False
IsIn=True
IsLcid=False
IsOptional=False
IsOut=False
IsRetVal=False

Method name M6 void M6([Out] object param)
IL signature .method public hidebysig instance void M6([out] object param) cil managed
Method parameter description:
Is passed by reference False
HasDefaultValue=False
IsIn=False
IsLcid=False
IsOptional=False
IsOut=True
IsRetVal=False

Method name M7 void M7([Out] out object param)
IL signature .method public hidebysig instance void M7([out] object& param) cil managed
Method parameter description:
Is passed by reference True
HasDefaultValue=False
IsIn=False
IsLcid=False
IsOptional=False
IsOut=True
IsRetVal=False

Method name M8 void M8([DefaultValue(null)] object param)
IL signature .method public hidebysig instance void M8(object param) cil managed
Method parameter description:
Is passed by reference False
HasDefaultValue=False
IsIn=False
IsLcid=False
IsOptional=False
IsOut=False
IsRetVal=False

Method name M9 void M9([DefaultValue(-10)] int param = 10)
IL signature .method public hidebysig instance void M9([opt] int32 param) cil managed
Method parameter description:
Parameter name param
Is passed by reference False
HasDefaultValue=True
DefaultValue=10
IsIn=False
IsLcid=False
IsOptional=True
IsOut=False
IsRetVal=False

Method name M10 void M10([Optional] int param)
IL signature .method public hidebysig instance void M10([opt] int32 param) cil managed
Method parameter description:
Is passed by reference False
HasDefaultValue=False
IsIn=False
IsLcid=False
IsOptional=True
IsOut=False
IsRetVal=False

I guess In, Out and Optional attributes relate to COM as they are located in System.Runtime.InteropServices namesapce.
But again documentation is quite poor. 🙁

And what is RetVal and where it is used?

发表在 .net | ParameterInfo properties, attributes and out ref parameters of the methods已关闭评论

jmap

查看进程内容情况

 

jmap -heap 53195

 

53195 = pid

发表在 java | 标签为 | jmap已关闭评论

TCP状态转换图

Linux内核协议栈为一个tcp连接管理使用两个队列,一个是半链接队列(用来保存处于SYN_SENT和SYN_RECV状态的请求),一个是accpetd队列(用来保存处于established状态,但是应用层没有调用accept取走的请求)。

第一个队列的长度是/proc/sys/net/ipv4/tcp_max_syn_backlog,默认是1024。如果开启了syncookies,那么基本上没有限制。

第二个队列的长度是/proc/sys/net/core/somaxconn,默认是128,表示最多有129个established链接等待accept。

 

windows :
TCPView
https://technet.microsoft.com/en-us/sysinternals/bb897437.aspx

Linux 各状态查看命令:

netstat -ant|fgrep ":"|cut -b 77-90|sort |uniq -c

状态转换图中状态的描述:
CLOSED:无连接是活动的或正在进行
LISTEN:服务器在等待进入呼叫
SYN_RECV:一个连接请求已经到达,等待确认
SYN_SENT:应用已经开始,打开一个连接
ESTABLISHED:正常数据传输状态
FIN_WAIT1:应用说它已经完成
FIN_WAIT2:另一边已同意释放
ITMED_WAIT:等待所有分组死掉
CLOSING:两边同时尝试关闭
TIME_WAIT:另一边已初始化一个释放
LAST_ACK:等待所有分组死掉
TCP/IP状态转换图的分析
其中,EATABLISHED是数据的传送状态。我们主要分析主动关闭这个框内的转换状态。
当客户端应用程序主动请求关闭时,调用close或shutdown关闭连接,这时应用程序发送FIN,然后进入FIN_WAIT_1状态,等待服务器端发送确认包ACK,接受到服务器端的ACK以后,然后客户端进入FIN_WAIT_2状态,等待服务器端调用close,并发送FIN,当客户端接受到FIN后,发送ACK,进入最终的TIME_WAIT状态,这结合着TCP关闭连接时的分组交换连接图可以更加的明白。需要注意的是,执行主动关闭的那一端进入TIME_WAIT状态。留在TIME_WAIT的持续的时间是MSL(最长分节生命周期 maximum segment lifttime)时间的两倍,也就是2MSL. MSL一般情况下是30秒到2分种,所以TIME_WAIT的时间一般为1-4分种。

 

点击查看原图

 

点击查看原图

点击查看原图

下面是(《Unix 网络编程第一卷》关于TIME_WAIT状态存在的理由)
存在TIME_WAIT状态有两个理由:
1.  实现终止TCP全双工连接的可靠性
假设最终的ACK丢失,服务器将重发最终的FIN,因此客户必须维护状态信息以允许它重发最终的ACK,如果不维护状态信息,它将响应以RST,而服务器则把该分节解释成一个错误,如果TCP打算执行所有必要的工作以彻底终止某个连接上两个方向的数据流,那么它必须,能够处理连接终止序列四个分节中任何一个分节的丢失的情况,主动关闭的那一端必须进入TIME_WAIT状态,因为它可能不得不重发最终的ACK。
2.  允许老的重复分节的网络中消失。

我们假设206.62.226.33端口1500和198.162.10.2端口21之间有一个TCP连接,我们关闭这个连接后,在以后某个时候又重新建立起相同的IP地址和端口之间的TCP连接。后一个连接称为前一个连接的化身,因为它们的IP地坛和端口号是相同的,TCP必须防止来自某个连接的老重复分组在连接终止后再现,从而被误解成属于同一个连接的化身。要实现这种功能TCP不能给处于TIME_WAIT状态的启动新的化身,既然TIME_WAIT状态的待续时间是2MLS,这就足够让某个方向上的分组最多存活MSL秒即被丢弃,另一个方向上的应答最多存活MSL秒也被丢弃,通过实施这个规则,我们就能保证当成功建立一个TCP连接时,来自该连接先前的化身的老重复分组都已在网络中消逝了

参考图2:

点击查看原图

点击查看原图

 

参考:http://www.cnxct.com/coping-with-the-tcp-time_wait-state-on-busy-linux-servers-in-chinese-and-dont-enable-tcp_tw_recycle/

其它TCP状态转换图一:
点击查看原图

 

发表在 network | 标签为 | TCP状态转换图已关闭评论

CLOSE_WAIT SYN_RECV Netty 分析过程

Netty出现大量的CLOSE_WAIT SYN_RECV通过下面的配置解决

net.ipv4.ip_forward = 0
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.default.accept_source_route = 0
kernel.sysrq = 0
kernel.core_uses_pid = 1
net.ipv4.tcp_syncookies = 1
kernel.msgmnb = 65536
kernel.msgmax = 65536
kernel.shmmax = 68719476736
kernel.shmall = 4294967296
net.ipv4.tcp_keepalive_time = 1800
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 3
net.ipv4.netfilter.ip_conntrack_tcp_timeout_last_ack = 10
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 30
net.ipv4.netfilter.ip_conntrack_tcp_timeout_established = 180

用 netstat 看发现有大量来自国外 IP 的 LAST_ACK 状态的连接。

net.ipv4.tcp_syncookies = 1 表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭;
net.ipv4.tcp_tw_reuse = 1 表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;
net.ipv4.tcp_tw_recycle = 1 表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。
net.ipv4.tcp_fin_timeout 修改系統默认的 TIMEOUT 时间

现象:在netstat的时候发现大量处于LAST_ACK状态的TCP连接,达到在ESTABLISHED状态的90%以上
[root@ccsafe ~]# netstat -ant|fgrep ":"|cut -b 77-90|sort |uniq -c                
       6 CLOSE_WAIT
       7 CLOSING     
    6838 ESTABLISHED
    1037 FIN_WAIT1  
     357 FIN_WAIT2  
    5830 LAST_ACK    
       2 LISTEN     
     276 SYN_RECV    
      71 TIME_WAIT  
[root@ccsafe ~]#
看看系统状态,性能都花在系统中断和上下文切换
[root@ccsafe ~]# vmstat 2
procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu------
r b    swpd    free    buff cache    si    so     bi     bo    in    cs us sy id wa st
1 0       0 3091812 363032 284132     0     0      0      0     1     1 0 0 100 0 0
0 0       0 3091812 363032 284132     0     0      0      0 13750 3174 0 5 94 0 0
0 0       0 3091936 363032 284132     0     0      0      0 13666 3057 1 5 94 0 0
0 0       0 3092060 363032 284132     0     0      0     16 13749 3030 0 5 95 0 0
0 0       0 3092060 363032 284132     0     0      0      0 13822 3144 0 5 95 0 0
0 0       0 3092060 363032 284132     0     0      0      0 13390 2961 0 5 95 0 0
0 0       0 3092060 363032 284132     0     0      0      0 13541 3182 0 6 94 0 0

查看socket队列信息
[root@ccsafe ~]# sar -n SOCK 5
Linux 2.6.18-53.1.13.el5PAE (ccsafe)      10/21/2008
06:31:43 PM     totsck     tcpsck     udpsck     rawsck    ip-frag     tcp-tw
06:31:48 PM       6951      13868          1          0          0        430
Average:          6951      13868          1          0          0        430
根据TCP状态的变化过程来分析,LAST_ACK属于被动关闭连接过程中的状态
ESTABLISHED->CLOSE_WAIT->(发送ACK)->LAST_ACK->(发送FIN+接收ACK)->CLOSED
现在状态都堆积到LAST_ACK,初步判断问题从上下两个状态着手
调节一下LAST_ACK时间...
[root@ccsafe ~]# sysctl -a |grep last_ack
net.ipv4.netfilter.ip_conntrack_tcp_timeout_last_ack = 30
[root@ccsafe ~]# sysctl -w net.ipv4.netfilter.ip_conntrack_tcp_timeout_last_ack=10
net.ipv4.netfilter.ip_conntrack_tcp_timeout_last_ack = 10
[root@ccsafe ~]# sysctl -p
[root@ccsafe ~]# watch -n 10 "netstat -ant|fgrep ":"|cut -b 77-90|sort |uniq -c"
Every 5.0s: netstat -ant|fgrep :|cut -b 77-90|sort |uniq -c                    
       6 CLOSE_WAIT
       9 CLOSING
    6420 ESTABLISHED
     693 FIN_WAIT1
     391 FIN_WAIT2
    5081 LAST_ACK
       2 LISTEN
     203 SYN_RECV
      66 TIME_WAIT
检查一下LAST_ACK所对应的应用
[root@ccsafe ~]# netstat -ant|fgrep "LAST_ACK"|cut -b 49-75|cut -d ":" -f1|sort |uniq -c|sort -nr --key=1,7|head -5
     101 220.160.210.6
      46 222.75.65.69
      31 221.0.91.118
      24 222.210.8.160
      22 60.161.81.28
[root@ccsafe ~]#
[root@ccsafe ~]# netstat -an|grep "220.160.210.6"
tcp         0 17280 10.1.1.145:80            220.160.210.6:52787          ESTABLISHED
tcp         1 14401 10.1.1.145:80            220.160.210.6:52513          LAST_ACK    
tcp         1 14401 10.1.1.145:80            220.160.210.6:52769          LAST_ACK    
tcp         1 14401 10.1.1.145:80            220.160.210.6:52768          LAST_ACK    
tcp         0    8184 10.1.1.145:80            220.160.210.6:52515          LAST_ACK    
tcp         1 14401 10.1.1.145:80            220.160.210.6:52514          LAST_ACK    
tcp         0    8184 10.1.1.145:80            220.160.210.6:52781          LAST_ACK    

是TCP80端口的应用,调节一下nginx的keepalive时间...
[root@ccsafe ~]# /usr/local/nginx/sbin/nginx -t -c /usr/local/nginx/conf/nginx.conf
2008/10/21 19:15:31 [info] 21352#0: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
2008/10/21 19:15:31 [info] 21352#0: the configuration file /usr/local/nginx/conf/nginx.conf was tested successfully
[root@ccsafe ~]# ps aux|egrep '(PID|nginx)'
USER        PID %CPU %MEM     VSZ    RSS TTY       STAT START    TIME COMMAND
root       8290 0.0 0.0    7572 1124 ?         Ss    Oct04    0:00 nginx: master process /usr/local/nginx/sbin/nginx
nobody     8291 0.2 0.3 19704 13776 ?         S     Oct04 71:35 nginx: worker process     
nobody     8292 0.3 0.2 17604 11680 ?         S     Oct04 77:26 nginx: worker process     
nobody     8293 0.2 0.4 22528 16636 ?         S     Oct04 58:13 nginx: worker process     
nobody     8294 0.3 0.4 24944 19020 ?         S     Oct04 94:07 nginx: worker process     
nobody     8295 0.3 0.5 27496 21508 ?         S     Oct04 84:41 nginx: worker process     
nobody     8296 0.3 0.1 13388 7496 ?         S     Oct04 84:14 nginx: worker process     
nobody     8297 0.2 0.0    9196 3268 ?         S     Oct04 58:21 nginx: worker process     
nobody     8298 0.3 0.2 15392 9504 ?         S     Oct04 75:16 nginx: worker process     
root      21354 0.0 0.0    3896    720 pts/0     S+    19:15    0:00 egrep (PID|nginx)
(动态加载新配置)
[root@ccsafe ~]# kill -HUP 8290
[root@ccsafe ~]#
Every 10.0s: netstat -ant|fgrep :|cut -b 77-90 |sort |uniq -c                                               
       1 CLOSE_WAIT
    1138 CLOSING
    7161 ESTABLISHED
    1427 FIN_WAIT1
     396 FIN_WAIT2
    5740 LAST_ACK
       2 LISTEN
     350 SYN_RECV
     148 TIME_WAIT
...
[root@ccsafe ~]# netstat -ant|fgrep ":"|cut -b 77-90 |sort |uniq -c
    1151 CLOSING     
    8506 ESTABLISHED
    1452 FIN_WAIT1  
     666 FIN_WAIT2  
    6568 LAST_ACK    
       2 LISTEN     
     429 SYN_RECV    
      92 TIME_WAIT  
...

LAST_ACK不下,而且CLOSING 和FIN_WAIT突增
着重看看可影响主动断开TCP连接时几个参数
tcp_keepalive_intvl:探测消息发送的频率
tcp_keepalive_probes:TCP发送keepalive探测以确定该连接已经断开的次数
tcp_keepalive_time:当keepalive打开的情况下,TCP发送keepalive消息的频率
[root@ccsafe ~]# sysctl -a|grep tcp_keepalive
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 2
net.ipv4.tcp_keepalive_time = 160
tcp_retries2:在丢弃激活(已建立通讯状况)的TCP连接之前?需要进行多少次重试
[root@ccsafe ~]# sysctl -a |grep tcp_retries
net.ipv4.tcp_retries2 = 15
net.ipv4.tcp_retries1 = 3
加速处理那些等待ACK的LAST_ACK,减少等待ACK的LAST_ACK的重试次数
[root@ccsafe ~]# sysctl -w net.ipv4.tcp_retries2=5
net.ipv4.tcp_retries2 = 5
减少keepalive发送的频率
[root@ccsafe ~]# sysctl -w net.ipv4.tcp_keepalive_intvl=15
net.ipv4.tcp_keepalive_intvl = 15
[root@ccsafe ~]# sysctl -p
排除syncookies的影响
[root@ccsafe ~]# !ec
echo "0" >/proc/sys/net/ipv4/tcp_syncookies
[root@ccsafe ~]# echo "1" >/proc/sys/net/ipv4/tcp_syncookies
[root@ccsafe ~]# sysctl -a|grep tcp_keepalive              
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 2
net.ipv4.tcp_keepalive_time = 160
[root@ccsafe ~]# sysctl -a|grep syncookies
net.ipv4.tcp_syncookies = 1
延长keepalive检测周期,保留ESTABLISHED数量
[root@ccsafe ~]# echo "1800" >/proc/sys/net/ipv4/tcp_keepalive_time
[root@ccsafe ~]# echo "5" >/proc/sys/net/ipv4/tcp_keepalive_probes
[root@ccsafe ~]# echo "15" >/proc/sys/net/ipv4/tcp_keepalive_intvl
[root@ccsafe ~]# sysctl -a|grep tcp_keepalive                      
net.ipv4.tcp_keepalive_intvl = 15
net.ipv4.tcp_keepalive_probes = 5
net.ipv4.tcp_keepalive_time = 1800
[root@ccsafe ~]# !wat
watch -n 10 "netstat -ant|fgrep ":"|cut -b 77-90|sort |uniq -c"
Every 10.0s: netstat -ant|fgrep :|cut -b 77-90|sort |uniq -c                         
       1 CLOSE_WAIT
     363 CLOSING
    5145 ESTABLISHED
    1073 FIN_WAIT1
     174 FIN_WAIT2
    6042 LAST_ACK
       2 LISTEN
     301 SYN_RECV
      85 TIME_WAIT

LAST_ACK不下,但是CLOSING有所回落
tcp_orphan_retries:在近端丢弃TCP连接之前?要进行多少次重试。
[root@ccsafe ~]# sysctl -a|grep tcp_orphan
net.ipv4.tcp_orphan_retries = 0
关键,丢TCP太频繁了,以至于后勤都跟不上。设置丢弃之前的重试次数
[root@ccsafe ~]# echo "3" >/proc/sys/net/ipv4/tcp_orphan_retries
[root@ccsafe ~]# !wat
watch -n 10 "netstat -ant|fgrep ":"|cut -b 77-90|sort |uniq -c"
Every 10.0s: netstat -ant|fgrep :|cut -b 77-90|sort |uniq -c                         
       1 CLOSE_WAIT
      24 CLOSING
    5422 ESTABLISHED
     279 FIN_WAIT1
     214 FIN_WAIT2
    1966 LAST_ACK
       2 LISTEN
     269 SYN_RECV
      74 TIME_WAIT
上下调节该值,找个合适的临界点
[root@ccsafe ~]# echo "7" >/proc/sys/net/ipv4/tcp_orphan_retries                 
[root@ccsafe ~]# !wat
watch -n 10 "netstat -ant|fgrep ":"|cut -b 77-90|sort |uniq -c"
Every 10.0s: netstat -ant|fgrep :|cut -b 77-90|sort |uniq -c                         
       1 CLOSE_WAIT
     175 CLOSING
    5373 ESTABLISHED
     436 FIN_WAIT1
     209 FIN_WAIT2
    3184 LAST_ACK
       2 LISTEN
     283 SYN_RECV
     110 TIME_WAIT
恢复,同时FIN_WAIT1的值过高。考虑减少tcp_fin_timeout时间
[root@ccsafe ~]# echo "2" >/proc/sys/net/ipv4/tcp_orphan_retries                 
[root@ccsafe ~]# sysctl -a|grep tcp_fin
net.ipv4.tcp_fin_timeout = 10
[root@ccsafe ~]# echo "5" >/proc/sys/net/ipv4/tcp_fin_timeout
[root@ccsafe ~]# !wat
watch -n 10 "netstat -ant|fgrep ":"|cut -b 77-90|sort |uniq -c"
Every 10.0s: netstat -ant|fgrep :|cut -b 77-90|sort |uniq -c                         
       2 CLOSE_WAIT
      17 CLOSING
    5665 ESTABLISHED
     145 FIN_WAIT1
     141 FIN_WAIT2
    1068 LAST_ACK
       2 LISTEN
     287 SYN_RECV
      68 TIME_WAIT
相比FIN_WAIT,SYN_RECV的值偏高。加大发送synack的质量
[root@ccsafe ~]# sysctl -a|grep synack
net.ipv4.tcp_synack_retries = 1
[root@ccsafe ~]# echo "2" >/proc/sys/net/ipv4/tcp_synack_retries
[root@ccsafe ~]# !wat
watch -n 10 "netstat -ant|fgrep ":"|cut -b 77-90|sort |uniq -c"
Every 10.0s: netstat -ant|fgrep :|cut -b 77-90|sort |uniq -c                         
       3 CLOSE_WAIT
      16 CLOSING
    5317 ESTABLISHED
     200 FIN_WAIT1
     158 FIN_WAIT2
    1001 LAST_ACK
       2 LISTEN
     303 SYN_RECV
      78 TIME_WAIT
[root@ccsafe ~]# sysctl -a|grep keepalive
net.ipv4.tcp_keepalive_intvl = 15
net.ipv4.tcp_keepalive_probes = 5
net.ipv4.tcp_keepalive_time = 1800
[root@ccsafe ~]# watch -n 10 "netstat -ant|fgrep ":"|cut -b 77-90|sort |uniq -c"
Every 10.0s: netstat -ant|fgrep :|cut -b 77-90|sort |uniq -c                         
       1 CLOSE_WAIT
       7 CLOSING
    5356 ESTABLISHED
     175 FIN_WAIT1
     136 FIN_WAIT2
    1045 LAST_ACK
       2 LISTEN
     345 SYN_RECV
      64 TIME_WAIT
减少keepalive的检测周期,LAST_ACK上升
[root@ccsafe ~]# echo "10" >/proc/sys/net/ipv4/tcp_keepalive_intvl
[root@ccsafe ~]# echo "1" >/proc/sys/net/ipv4/tcp_synack_retries                 
[root@ccsafe ~]# !wat
watch -n 10 "netstat -ant|fgrep ":"|cut -b 77-90|sort |uniq -c"
Every 10.0s: netstat -ant|fgrep :|cut -b 77-90|sort |uniq -c                                               
       1 CLOSE_WAIT
      13 CLOSING
    5605 ESTABLISHED
     212 FIN_WAIT1
     131 FIN_WAIT2
    1143 LAST_ACK
       2 LISTEN
     252 SYN_RECV
      79 TIME_WAIT
恢复
[root@ccsafe ~]# echo "15" >/proc/sys/net/ipv4/tcp_keepalive_intvl              
[root@ccsafe ~]# watch -n 10 "netstat -ant|fgrep ":"|cut -b 77-90|sort |uniq -c"
Every 10.0s: netstat -ant|fgrep :|cut -b 77-90|sort |uniq -c                       
       3 CLOSE_WAIT
      14 CLOSING
    5862 ESTABLISHED
     230 FIN_WAIT1
     205 FIN_WAIT2
    1064 LAST_ACK
       2 LISTEN
     244 SYN_RECV
      59 TIME_WAIT

[root@ccsafe ~]# watch -n 10 "netstat -ant|fgrep ":"|cut -b 77-90|sort |uniq -c"
Every 10.0s: netstat -ant|fgrep :|cut -b 77-90|sort |uniq -c                       
       3 CLOSE_WAIT
      26 CLOSING
    6712 ESTABLISHED
     270 FIN_WAIT1
     230 FIN_WAIT2
     994 LAST_ACK
       2 LISTEN
     254 SYN_RECV
      73 TIME_WAIT

[root@ccsafe ~]#
目前LAST_ACK占ESTABLISHED的量在15%左右

发表在 network | 标签为 , | CLOSE_WAIT SYN_RECV Netty 分析过程已关闭评论

Netty 移动开发注意事项

1. 最大句柄数修改

百万长连接接入,首先需要优化的就是Linux内核参数,其中Linux最大文件句柄数是最重要的调优参数之一,默认单进程打开的最大句柄数是1024,通过ulimit -a可以查看相关参数,示例如下:

[root@lilinfeng ~]# ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 256324
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024

......后续输出省略

当单个推送服务接收到的链接超过上限后,就会报“too many open files”,所有新的客户端接入将失败。

通过vi /etc/security/limits.conf 添加如下配置参数:修改之后保存,注销当前用户,重新登录,通过ulimit -a 查看修改的状态是否生效。

*  soft  nofile  1000000
*  hard  nofile  1000000

需要指出的是,尽管我们可以将单个进程打开的最大句柄数修改的非常大,但是当句柄数达到一定数量级之后,处理效率将出现明显下降,因此,需要根据服务器的硬件配置和处理能力进行合理设置。如果单个服务器性能不行也可以通过集群的方式实现。


2. 当心CLOSE_WAIT

从事移动推送服务开发的同学可能都有体会,移动无线网络可靠性非常差,经常存在客户端重置连接,网络闪断等。

在百万长连接的推送系统中,服务端需要能够正确处理这些网络异常,设计要点如下:

    客户端的重连间隔需要合理设置,防止连接过于频繁导致的连接失败(例如端口还没有被释放);
    客户端重复登陆拒绝机制;
    服务端正确处理I/O异常和解码异常等,防止句柄泄露。

最后特别需要注意的一点就是close_wait 过多问题,由于网络不稳定经常会导致客户端断连,如果服务端没有能够及时关闭socket,就会导致处于close_wait状态的链路过多。close_wait状态的链路并不释放句柄和内存等资源,如果积压过多可能会导致系统句柄耗尽,发生“Too many open files”异常,新的客户端无法接入,涉及创建或者打开句柄的操作都将失败。

下面对close_wait状态进行下简单介绍,被动关闭TCP连接状态迁移图如下所示:
点击查看原图
图3-1 被动关闭TCP连接状态迁移图

close_wait是被动关闭连接是形成的,根据TCP状态机,服务器端收到客户端发送的FIN,TCP协议栈会自动发送ACK,链接进入close_wait状态。但如果服务器端不执行socket的close()操作,状态就不能由close_wait迁移到last_ack,则系统中会存在很多close_wait状态的连接。通常来说,一个close_wait会维持至少2个小时的时间(系统默认超时时间的是7200秒,也就是2小时)。如果服务端程序因某个原因导致系统造成一堆close_wait消耗资源,那么通常是等不到释放那一刻,系统就已崩溃。

导致close_wait过多的可能原因如下:

    程序处理Bug,导致接收到对方的fin之后没有及时关闭socket,这可能是Netty的Bug,也可能是业务层Bug,需要具体问题具体分析;
    关闭socket不及时:例如I/O线程被意外阻塞,或者I/O线程执行的用户自定义Task比例过高,导致I/O操作处理不及时,链路不能被及时释放。

下面我们结合Netty的原理,对潜在的故障点进行分析。

设计要点1:不要在Netty的I/O线程上处理业务(心跳发送和检测除外)。Why? 对于Java进程,线程不能无限增长,这就意味着Netty的Reactor线程数必须收敛。Netty的默认值是CPU核数 * 2,通常情况下,I/O密集型应用建议线程数尽量设置大些,但这主要是针对传统同步I/O而言,对于非阻塞I/O,线程数并不建议设置太大,尽管没有最优值,但是I/O线程数经验值是[CPU核数 + 1,CPU核数*2 ]之间。

假如单个服务器支撑100万个长连接,服务器内核数为32,则单个I/O线程处理的链接数L = 100/(32 * 2) = 15625。 假如每5S有一次消息交互(新消息推送、心跳消息和其它管理消息),则平均CAPS = 15625 / 5 = 3125条/秒。这个数值相比于Netty的处理性能而言压力并不大,但是在实际业务处理中,经常会有一些额外的复杂逻辑处理,例如性能统计、记录接口日志等,这些业务操作性能开销也比较大,如果在I/O线程上直接做业务逻辑处理,可能会阻塞I/O线程,影响对其它链路的读写操作,这就会导致被动关闭的链路不能及时关闭,造成close_wait堆积。

设计要点2:在I/O线程上执行自定义Task要当心。Netty的I/O处理线程NioEventLoop支持两种自定义Task的执行:

    普通的Runnable: 通过调用NioEventLoop的execute(Runnable task)方法执行;
    定时任务ScheduledFutureTask:通过调用NioEventLoop的schedule(Runnable command, long delay, TimeUnit unit)系列接口执行。

为什么NioEventLoop要支持用户自定义Runnable和ScheduledFutureTask的执行,并不是本文要讨论的重点,后续会有专题文章进行介绍。本文重点对它们的影响进行分析。

在NioEventLoop中执行Runnable和ScheduledFutureTask,意味着允许用户在NioEventLoop中执行非I/O操作类的业务逻辑,这些业务逻辑通常用消息报文的处理和协议管理相关。它们的执行会抢占NioEventLoop I/O读写的CPU时间,如果用户自定义Task过多,或者单个Task执行周期过长,会导致I/O读写操作被阻塞,这样也间接导致close_wait堆积。

所以,如果用户在代码中使用到了Runnable和ScheduledFutureTask,请合理设置ioRatio的比例,通过NioEventLoop的setIoRatio(int ioRatio)方法可以设置该值,默认值为50,即I/O操作和用户自定义任务的执行时间比为1:1。

我的建议是当服务端处理海量客户端长连接的时候,不要在NioEventLoop中执行自定义Task,或者非心跳类的定时任务。

设计要点3:IdleStateHandler使用要当心。很多用户会使用IdleStateHandler做心跳发送和检测,这种用法值得提倡。相比于自己启定时任务发送心跳,这种方式更高效。但是在实际开发中需要注意的是,在心跳的业务逻辑处理中,无论是正常还是异常场景,处理时延要可控,防止时延不可控导致的NioEventLoop被意外阻塞。例如,心跳超时或者发生I/O异常时,业务调用Email发送接口告警,由于Email服务端处理超时,导致邮件发送客户端被阻塞,级联引起IdleStateHandler的AllIdleTimeoutTask任务被阻塞,最终NioEventLoop多路复用器上其它的链路读写被阻塞。

对于ReadTimeoutHandler和WriteTimeoutHandler,约束同样存在

发表在 network | 标签为 | Netty 移动开发注意事项已关闭评论

[转]Android微信智能心跳方案

来源:http://mp.weixin.qq.com/s?__biz=MzAwNDY1ODY2OQ==&mid=207243549&idx=1&sn=4ebe4beb8123f1b5ab58810ac8bc5994&scene=0#rd

 

前言:在1311月中旬时,因为基础组件组人手紧张,Leo安排我和春哥去广州轮岗支援。刚到广州的时候,Ray让我和春哥对LineWhatsApp的心跳机制进行分析。我和春哥抓包测试了差不多两个多礼拜,在我们基本上摸清了LineWhatsApp的心跳机制后,Ray才告诉我们真正的任务——对微信的固定心跳进行优化,并告诉我们这不是一件容易的事情。于是我和春哥开始构思第一个方案,我们开始想用统计的方法来解决问题,当我们拿着第一个方案和Ray讨论时,发现不能优雅应对Ray的所有提问:1、测试环境的准确性,失败到底是因为网络的特性导致还是因为用户当前的环境变化导致的暂时失败。2、临界值界定,如果方案选中的心跳值是临界值,我们该怎么办。Ray和组件组同事在网络方面有极其丰富的经验,虽然他没有给我们指出明确的方向,但提出的问题帮助我们更快的补齐需要面对的核心问题。这两个问题让我和春哥意识到如果能很好的解决,就可以给出一个比较好的心跳方案。第一个问题我和春哥开始就意识到,第二个问题我们确实在一开始时疏忽了。但直接解决这两个问题确实不容易,这着实让我和春哥迷茫了几天,有两三天在纺园我都没怎么睡着,因为想不到更好的方法。直到有一天思路发生了一些转变,既然最优解比较复杂,为什么不绕过去,使用有损服务理念找次优解呢。让复杂的事情简单化,好了,想到这里突然有一种拨开云雾的感觉。

思路对了,方案就可以做到简单并且可靠,大家可以看到最终的方案是比较简单的,并且效果还挺好的。在方案描述之前大概讲一下减低问题复杂度的方法:

a)延迟心跳测试法:这是测试结果准确的前提保障,我们认为长连接建立后连续三次成功的短心跳就可以很大程度的保证下一次心跳环境是正常的。

b)成功一次认定,失败连续累积认定:成功是绝对的,连续失败多次才可能是失败。

c)临界值避免:我们使用比计算出的心跳稍微小一点的值做为稳定心跳避免临界值。

d)动态调整:即使在一次完整的智能心跳计算过程中,我们没有找到最好的值,我们还有机会来进行校正。

当我和春哥想出第二个简单易行的方案后,我们心里就很有底了,去找Ray讨论,Ray听完后一次通过,然后Ray约了Harvey,给Harvey讲完后,Harvey说听起来可以,可以试试。

然后就开始动手,分析竞品加确定方案花了差不多两个月。写心跳的主要代码,只花了一天时间,我记得那天是年会后的一天。回过头来再看这个方案花费的时间还是值得的,后来灰度的统计数据显示,70%用户都可以达到我们的心跳上限。

搞完智能心跳后一段时间在广州没事干,我就跟Ray商量,Ray让我去测试下WebView的性能瓶颈。然后我跟周斯基一起来做这件事,搞完了安卓客户端WebView性能瓶颈测试后,因为怀孕的老婆一个人在深圳,领导就安排我先回深圳了。春哥坚守着把GCM部分完成后才回深圳。

等我们的心跳版本正式发布后,一年前我在公司km上分享了智能心跳方案,吸引不少做push的同事加入了讨论,感觉这方面的交流还是很有必要的。

好了,废话了很多,下面分享一下微信的智能心跳方案细节。由于字数比较多,建议大家使用PC版微信查看。

1.主要目标

本方案的主要目标是,在尽量不影响用户收消息及时性的前提下,根据网络类型自适应的找出保活信令TCP连接的尽可能大的心跳间隔,从而达到减少安卓微信因心跳引起的空中信道资源消耗,减少心跳Server的负载,以及减少部分因心跳引起的耗电。

主要方法是参考WhatsAppLine中有价值的做法,结合影响TCP连接寿命的因素,实现Android微信后台自适应心跳算法,同时使用GCM作为辅助通道增加新消息通知的可靠性。

2. WhatsAppLine、微信的Push策略分析

2.1 WhatsApp

在不支持GCM的设备上,采用和微信类似的长连接+心跳策略,WIFI和手机网络下的心跳间隔都为445秒,心跳5次后,主动断开连接再重连。

在支持GCM的设备上,主要靠GCM来激活WhatsAppWhatsApp启动后,会建立一个与服务器的长连接,直接通过此长连接发送Push消息,这个长连接10分钟无消息就会主动断掉,且这十分钟内不做心跳,断掉后WhatsApp客户端和它的服务器不再有连接。当有消息时候,服务器发现没有长连接会发送GCM消息,手机收到GCM消息后,会重新建立长连接来收取消息,10分钟无消息会再断开,如此循环。

2.2 Line

从测试中发现Line在国内、台湾、美国使用了不同的策略。

1、美国(使用GCM):

启动时,会保持7分钟心跳(CDMA2000网络)维持长连接半小时,之后主动断开长连接。当有消息时,服务器会发送GCM消息,Line客户端接收到GCM消息后,重新建立长连接,并再次用心跳维持半个小时。

2、国内(不使用GCM):

在国内,同样帐号在相同网络,不同的手机上测出了两种策略:

长连接+心跳策略(在Galaxy S3上使用),心跳间隔WIFI下是320秒,手机网络是7分钟。

轮询策略(在红米和Nexus S上使用),如图2-1所示。与心跳策略的主要区别用红色标出,客户端在长连接建立后也会定时发送请求,Server会回复并且同时关闭长连接。客户端等待轮询间隔T1后再次建立TCP连接。Line会根据手机的活跃状态动态调整T1,调整范围是从最小1分到最大到2小时半。而长连接存活时间T2比较固定,在WIFI4分钟,手机网络7分钟。如果在T2时收到新消息会延长T2的时间。

点击查看原图

2-1 Line在国内的轮询策略

3、台湾(不使用GCM):

IBG同事winguang提供的测试数据中看到,台湾使用的策略跟国内的轮询策略类似。

2.3 微信

微信没有使用GCM,自己维护TCP长连接,使用固定心跳。

2.4心跳典型值

WhatsApp

Line

GCM

WIFI

445

320

15分钟

手机网络

445

7分钟

28分钟

2.5LineWhatsApp、微信Push策略的优点

a)微信:当前心跳间隔比竞品短,所以微信在新消息提醒上会最及时。

b)使用GCMLineWhatsApp使用GCM策略的最大优点就是省电,以及减轻系统负荷(减少后台应用数目)。

cLineLine的轮询策略,优点是当Line处于活跃状态时,及时收消息。当Line处于不活跃状态时,省电。

2.6LineWhatsApp微信Push策略的不足

a)微信当前心跳频率相对竞品较大,在耗电、耗流量,占用信令通道等方面有所影响。

bLine的轮询策略,导致的问题是消息可能会延迟接收,测试发现最大延迟间隔到2.5小时。

cWhatsAppLine使用Push拉起一个定时长连接策略,缺点是要依赖GooglePush服务,如果GooglePush服务不稳定,消息也会延迟接收。

d)在国内的移动和联通2G网络下,由于运营商的策略,GCM长连接频繁断连,WhatsAppPush消息很不及时,体验非常差。

3. GCM研究

3.1 GCM特点

aAndroid2.2以下的手机不支持GCM2.23.0需要安装Google Store并设置Google帐号,4.04及以上版本不需要设置帐号也能支持。

bGCM只传递数据(可以传递小于4kb的数据),对这些数据的处理可以全部由开发者控制。

cAndroid应用不需要运行就可以接收消息(通过Android广播)

dGCM不保证发送的消息的顺序,也不保证消息一定能够推送到手机。

3.2 GCM心跳策略以及存在的问题

a用心跳保活长连接,心跳间隔为WIFI15分钟,数据网络下28分钟。

bGoogle可以改变所有Android设备的心跳间隔值(目前还未改变过)。

cGCM由于心跳间隔固定,并且较长,所以在NAT aging-time设置较小的网络(如联通2G,或有些WIFI环境下)会导致TCP长连接在下一次心跳前被网关释放。造成Push延迟接收。

3.3 GCM的可用性及稳定性

目前测试发现GCM在国内可用性不高,原因有:

a) Android很多被手机厂商定制化,厂商可能会去掉GCM服务。

b) Android2.23.0之间需要安装Google Store设置Google帐号。

c)由于国内2G和移动3GNAT超时时间都小于GCM心跳时间(28分钟)TCP长连接必然无法保活,每次都要等28分钟心跳失败重连后才能收到Push

d)某些运营商可能限制了5228端口,移动3G/2G下,发现几乎无法连接上GCM服务器,也就无法获得GCM通知,WhatsApp放后台10分钟后,经常很长时间都收不到Push消息。

在美国3G网络下抓包的24小时,GCM的连接极其稳定,24小时内GCM长连接未曾断过,在台湾3G网络下抓包14个小时,GCM连接也只断过一次。WhatsApp用户在此类地区网络下客户端可以获得很及时的Push通知。

在中国电信3G下抓包,大部分时间GCM连接都比较稳定,只会因为偶尔的DHCP造成断连现象,由于频率很低(平均数小时才发生一次),对Push体验的影响不大。

3.4 GCM Server类型

GCM提供两种Server模型:

aHTTP Server : 使用同步接口发送HTTP请求,一次请求可以发给最多1000个设备。

bXMPP Server :使用异步接口发送请求,只支持对单个设备(或同一个用户的多个关联设备发送),发送请求并发数须小于1000,支持设备到云端Server发送数据。需要Google将我们的发送Server加入白名单。

4.微信可能的改进点探讨

微信Push的优化主要有几个优化点:

a) 公共Push通道

b) 使用GCM Push作为辅助通道

c)自适应心跳间隔优化

4.1 公共Push通道

由于GCM在国内的可靠性很低,现在国内Android上的Push基本上是各自为政,很多软件都自己实现Push。导致手机被经常性的唤醒,耗电耗流量严重。

市面上已经有很多第三方的公共推送服务,大家可以选择一个适合自己应用的推送服务。腾讯也有信鸽和维纳斯组件,大家在选择方案的时候可以对比下。

最终因为我们国内外使用一套方案,并且是辅助公道,所以我们选择使用GCM

4.2 使用GCM Push作为辅助通道

当前使用GCM的成本不大,可以使用GCM作为辅助通道来增加新消息的及时性。

使用GCM作为辅助通道,在支持GCM的设备上微信上传自己的注册GCM ID给微信Server

微信Server在发现长连接失效的情况下,可以使用GCM 作为辅助通道通知客户端有新消息,客户端收到push通知后做一次sync

只利用GCM来激活微信,不传递消息的具体数据,要控制给同一设备发送GCM通知的时间间隔(如五分钟)

4.3 自适应心跳间隔优化

4.3.1影响TCP连接寿命的因素

Android下,不管是GCM,还是微信,都是通过TCP长连接来进行Push消息的,TCP长连接存活,消息Push就及时,所以要对影响TCP连接寿命的因素进行研究。

1NAT超时

大部分移动无线网络运营商都在链路一段时间没有数据通讯时,会淘汰 NAT 表中的对应项,造成链路中断(NAT超时的更多描述见附录6.1)。NAT超时是影响TCP连接寿命的一个重要因素(尤其是国内),所以客户端自动测算NAT超时时间,来动态调整心跳间隔,是一个重要的优化点。

2DHCP的租期(lease time

目前测试发现安卓系统对DHCP的处理有BugDHCP租期到了不会主动续约并且会继续使用过期IP,这个问题会造成TCP长连接偶然的断连。(租期问题的具体描述见附录6.2)。

3、网络状态变化

手机网络和WIFI网络切换、网络断开和连上等情况有网络状态的变化,也会使长连接变为无效连接,需要监听响应的网络状态变化事件,重新建立Push长连接。

4.3.2 心跳范围选择

1前后台区分处理

为了保证微信收消息及时性的体验,当微信处于前台活跃状态时,使用固定心跳。

微信进入后台(或者前台关屏)时,先用几次最小心跳维持长链接。然后进入后台自适应心跳计算。这样做的目的是尽量选择用户不活跃的时间段,来减少心跳计算可能产生的消息不及时收取影响。

2后台自适应心跳选择区间

可根据自身产品的特点选择合适的心跳范围。

4.3.3 状态转换图

点击查看原图

4.3.4自适应心跳算法描述

1按网络类型区分计算:

因为每个网络的NAT时间可能不一致。所以需要区分计算,数据网络按subType做关键字,WIFIWIFI名做关键字。

对稳定的网络,因为NAT老化时间的存在,在自适应计算态的时候,暂设计以下步骤在当前心跳区间逼近出最大可用的心跳。

a) 变量说明:

[MinHeartMaxHeart]——心跳可选区间。

successHeart——当前成功心跳,初始为MinHeart

curHeart——当前心跳初始值为successHeart

heartStep——心跳增加步长

successStep——稳定期后的探测步长

b) 最大值探测步骤:

点击查看原图

4-1 自适应心跳计算流程

自适应心跳计算流程如图4-1所示,经过该流程,会找到必然使心跳失败的curHeart(或者MaxHeart),为了保险起见,我们选择比前一个成功值稍微小一点的值作为后台稳定期的心跳间隔。

影响手机网络测试的因素太多,为了尽量保证测试结果的可靠性,我们使用延迟心跳测试法。在我们重新建立TCP连接后,先使用 短心跳连续成功三次,我们才认为网络相对稳定,可以使用curHeart进行一次心跳测试。图4-2显示了一次有效心跳测试过程。图4-3显示了在没有达到稳定网络环境时,我们会一直使用固定短心跳直到满足三次连续短心跳成功。

使用延迟心跳测试的好处是,可以剔除偶然失败,和网络变化较大的情况(如地铁),使测试结果相对可靠(五次延迟测试确定结论)。同时在网络波动较大的情况,使用短心跳,保证收取消息相对及时。

c) 运行时的动态调整策略(已经按测算心跳稳定值后)

NAT超时值算出来后,在维持心跳的过程中的策略

ü 无网络、网络时好时坏、偶然失败、NAT超时变小:在后台稳定期发生心跳发生失败后,我们使用延迟心跳测试法测试五次。如果有一次成功,则保持当前心跳值不变;如果五次测试全失败,重新计算合理心跳值。该过程如图4-4所示,有一点需要注意,每个新建的长连接需要先用短心跳成功维持3次后才用successHeart进行心跳。

点击查看原图

4-2 后台稳定态动态调整心跳策略

ü NAT超时变大:以周为周期,每周三将后台稳定态调至自适应计算态,使用心跳延迟法往后探测心跳间隔。

ü successHeartNAT超时临界值:因为我们现在选择的是一个比successHeart稍小的值作为稳定值,所以在计算过程中可以避开临界值。当运营商在我们后台稳定期将NAT超时调整为我们当前计算值,那么由于我们每周会去向下探索,所以下一周探测时也可以及时调整正确。

4.3.5 冗余Sync和心跳

在用户的一些主动操作以及联网状态改变时,增加冗余Sync和心跳,确保及时收到消息。

1、当用户点亮屏幕的时候,做一次心跳。

2、当微信切换到前台时,做一次Sync

3、联网时重建信令TCP,做一次Sync

5. 可能存在的风险及预防措施

5.1 DHCP租期因素

1问题:根据目前的测试结果显示,安卓不续约到期的IP Bug,会导致TCP连接在不确定的时间点失效,从而会导致一次心跳失败。

2预防:统计后台稳定期的心跳成功率,上报给后台。后台可以按地区分网络监控这个指标的波动,并且后台可以根据不同的波动,动态调整某区域特定网络下可选的心跳区间。

5.2 其他影响TCP寿命的因素

是否有遗漏的因素?欢迎各位联系我反馈。

6 附录

6.1 附录A——NAT超时介绍

因为 IP v4 IP 量有限,运营商分配给手机终端的 IP 是运营商内网的 IP,手机要连接 Internet,就需要通过运营商的网关做一个网络地址转换(Network Address TranslationNAT)。简单的说运营商的网关需要维护一个外网 IP、端口到内网 IP、端口的对应关系,以确保内网的手机可以跟 Internet 的服务器通讯。

点击查看原图

NAT 功能由图中的 GGSN 模块实现

大部分移动无线网络运营商都在链路一段时间没有数据通讯时,会淘汰 NAT 表中的对应项,造成链路中断。下表列出一些已测试过的网络的NAT超时时间(更多数据由于测试条件所限没有测到)

地区/网络

NAT超时时间

中国移动3G2G

5分钟

中国联通2G

5分钟

中国电信3G

大于28分钟

美国3G

大于28分钟

台湾3G

大于28分钟

长连接心跳间隔必须要小于NAT超时时间(aging-time),如果超过aging-time不做心跳,TCP长连接链路就会中断,Server就无法发送Push给手机,只能等到客户端下次心跳失败后,重建连接才能取到消息

6.2 附录B——安卓DHCP的租期(lease time)问题

目前测试发现安卓系统对DHCP的处理有Bug

1、 DHCP租期到了不会主动续约并且会继续使用过期IP,详细描述见http://www.net.princeton.edu/android/android-stops-renewing-lease-keeps-using-IP-address-11236.html这个问题导致的问题表象是,在超过租期的某个时间点(没有规律)会导致IP过期,老的TCP连接不能正常收发数据。并且系统没有网络变化事件,只有等应用判断主动建立新的TCP连接才引起安卓设备重新向DHCP Server申请IP租用。

2、 未到租期的一半时间,安卓设备重新向DHCP Server申请IP租用。从目前测试结果来看,这种现象恢复的比较快。

3、 移动2G/3G,联通2G没有抓到DHCP

4、 美国3G下抓取24小时,没有抓到DHCP

发表在 article | [转]Android微信智能心跳方案已关闭评论

ntp

1. yum install ntp -y

2. http://www.pool.ntp.org是NTP的官方网站,在这上面我们可以找到离我们城市最近的NTP Server. NTP建议我们为了保障时间的准确性,最少找两个个NTP Server
那么比如在英国的话就可以选择下面两个服务器
0.uk.pool.ntp.org
1.uk.pool.ntp.org

Asia/亚洲 — asia.pool.ntp.org

To use this specific pool zone, add the following to your ntp.conf file:

server 0.asia.pool.ntp.org
server 1.asia.pool.ntp.org
server 2.asia.pool.ntp.org
server 3.asia.pool.ntp.org

China/中国 — cn.pool.ntp.org

To use this specific pool zone, add the following to your ntp.conf file:

server 0.cn.pool.ntp.org
server 1.cn.pool.ntp.org
server 2.cn.pool.ntp.org
server 3.cn.pool.ntp.org

它的一般格式都是number.country.pool.ntp.org

第二步要做的就是在打开NTP服务器之前先和这些服务器做一个同步,使得我们机器的时间尽量接近标准时间.
这里我们可以用ntpdate命令手动更新时间
代码:
# ntpdate 0.uk.pool.ntp.org
6 Jul 01:21:49 ntpdate[4528]: step time server 213.222.193.35 offset -38908.575181 sec
# ntpdate 0.pool.ntp.org
6 Jul 01:21:56 ntpdate[4530]: adjust time server 213.222.193.35 offset -0.000065 sec

假如你的时间差的很离谱的话第一次会看到调整的幅度比较大,所以保险起见可以运行两次

3. 配置和运行NTP Server

现在我们就来创建NTP的配置文件了, 它就是/etc/ntp.conf. 我们只需要加入上面的NTP Server和一个driftfile就可以了
代码:
# vi /etc/ntp.conf
server 0.uk.pool.ntp.org
server 1.uk.pool.ntp.org

fudge 127.127.1.0 stratum 0  stratum
 
这行是时间服务器的层次。设为0则为顶级,如果要向别的NTP服务器更新时间,请不要把它设为0

driftfile /var/lib/ntp/ntp.drift  非常的简单. 接下来我们就启动NTP Server,并且设置其在开机后自动运行
代码:
# /etc/init.d/ntpd start

# chkconfig --level 35 ntpd on

 

------------------------------------

ntp tools:

#ntpstat
synchronised to NTP server (18.18.220.186) at stratum 3
time correct to within 183 ms
polling server every 128 s

已连接同步服务器

# ntpstat
unsynchronised
time server re-starting
polling server every 64 s

未连接同步服务器

# ntpq -p
remote refid st t when poll reach delay offset jitter
==============================================================================
192.168.7.49 192.168.7.50 5 u 13 64 3 5.853 1137178 2.696

remote - 本机和上层ntp的ip或主机名,“+”表示优先,“*”表示次优先
refid - 参考上一层ntp主机地址
st - stratum阶层
when - 多少秒前曾经同步过时间
poll - 下次更新在多少秒后
reach - 已经向上层ntp服务器要求更新的次数
delay - 网络延迟
offset - 时间补偿
jitter - 系统时间与bios时间差

 

 

----------------------------------------

 

4. chronyd

一些linux版本,默认可能安装 chronyd 做为NTP 时钟同步服务。

 

参考来源:

http://blog.csdn.net/iloli/article/details/6431757

发表在 linux | ntp已关闭评论

Sublime Text 3 安装Package Control

一、简单的安装方法

使用Ctrl+`快捷键或者通过View->Show Console菜单打开命令行,粘贴如下代码:

 

import urllib.request,os; pf = 'Package Control.sublime-package'; ipp = sublime.installed_packages_path(); urllib.request.install_opener( urllib.request.build_opener( urllib.request.ProxyHandler()) ); open(os.path.join(ipp, pf), 'wb').write(urllib.request.urlopen( 'http://sublime.wbond.net/' + pf.replace(' ','%20')).read())

 

如果顺利的话,此时就可以在Preferences菜单下看到Package Settings和Package Control两个菜单了。

顺便贴下Sublime Text2 的代码:

 

import urllib2,os; pf='Package Control.sublime-package'; ipp = sublime.installed_packages_path(); os.makedirs( ipp ) if not os.path.exists(ipp) else None; urllib2.install_opener( urllib2.build_opener( urllib2.ProxyHandler( ))); open( os.path.join( ipp, pf), 'wb' ).write( urllib2.urlopen( 'http://sublime.wbond.net/' +pf.replace( ' ','%20' )).read()); print( 'Please restart Sublime Text to finish installation')

二、手动安装

可能由于各种原因,无法使用代码安装,那可以通过以下步骤手动安装Package Control:

1.点击Preferences > Browse Packages菜单

2.进入打开的目录的上层目录,然后再进入Installed Packages/目录

3.下载Package Control.sublime-package并复制到Installed Packages/目录

4.重启Sublime Text。

 

参考:

https://sublime.wbond.net/installation#st3


通过 Package Control 安装Sublime插件

使用 Ctrl + Shift + P 调出面板,然后输入 pci ,选中“Package Control: Install Package”并回车,然后通过输入插件的名字找到插件并回车安装即可。

 

常用插件:

1. Emmet

Emmet的前身是大名鼎鼎的Zen coding,如果你从事Web前端开发的话,对该插件一定不会陌生。它使用仿CSS选择器的语法来生成代码,大大提高了HTML/CSS代码编写的速度。

2. AutoFileName

一款在 Sublime Text 中可以自动补全文件路径及名称的插件

3. jQuery

一款自动补全 jQuery 函数的插件,带有语法高亮,并且包含几乎所有的 jQuery 方法

4. DocBlockr

DocBlockr 是一款 Sublime Text 2 & 3 都可以使用的代码快注释插件。支持的语言有:JavaScript (including ES6), PHP, ActionScript, Haxe,CoffeeScript, TypeScript, Java, Groovy, Objective C, C, C++ and Rust.

 

 

 

 



发表在 technologys | Sublime Text 3 安装Package Control已关闭评论

C#生成PDF总结

见:http://www.cnblogs.com/Joetao/articles/2933941.html

发表在 .net | C#生成PDF总结已关闭评论

密码保护:把Bit转化为Byte流 解说

此内容受密码保护。如需查阅,请在下列字段中输入您的密码。

发表在 technologys | 密码保护:把Bit转化为Byte流 解说已关闭评论

Quoted-Printable

Quoted-Printable也是MIME邮件中常用的编码方式之一。同Base64一样,它也将输入的字符串或数据编码成全是ASCII码的可打印字符串。
Quoted-Printable编码的基本方法是:输入数据在33-60、62-126范围内的,直接输出;其它的需编码为“=”加两个字节的HEX码(大写)。为保证输出行不超过规定长度,可在行尾加“=\r\n”序列作为软回车。

int EncodeQuoted(const unsigned char* pSrc, char* pDst, int nSrcLen, int nMaxLineLen)
{
    int nDstLen;        // 输出的字符计数
    int nLineLen;       // 输出的行长度计数
 
    nDstLen = 0;
    nLineLen = 0;
 
    for (int i = 0; i < nSrcLen; i++, pSrc++)
    {
        // ASCII 33-60, 62-126原样输出,其余的需编码
        if ((*pSrc >= '!') && (*pSrc <= '~') && (*pSrc != '='))
        {
            *pDst++ = (char)*pSrc;
            nDstLen++;
            nLineLen++;
        }
        else
        {
            sprintf(pDst, "=%02X", *pSrc);
            pDst += 3;
            nDstLen += 3;
            nLineLen += 3;
        }
 
        // 输出换行?
        if (nLineLen >= nMaxLineLen - 3)
        {
            sprintf(pDst, "=\r\n");
            pDst += 3;
            nDstLen += 3;
            nLineLen = 0;
        }
    }
 
    // 输出加个结束符
    *pDst = '\0';
 
    return nDstLen;
}

 

Quoted-Printable解码很简单,将编码过程反过来。

int DecodeQuoted(const char* pSrc, unsigned char* pDst, int nSrcLen)
{
    int nDstLen;        // 输出的字符计数
    int i;
 
    i = 0;
    nDstLen = 0;
 
    while (i < nSrcLen)
    {
        if (strncmp(pSrc, "=\r\n", 3) == 0)        // 软回车,跳过
        {
            pSrc += 3;
            i += 3;
        }
        else
        {
            if (*pSrc == '=')        // 是编码字节
            {
                sscanf(pSrc, "=%02X", pDst);
                pDst++;
                pSrc += 3;
                i += 3;
            }
            else        // 非编码字节
            {
                *pDst++ = (unsigned char)*pSrc++;
                i++;
            }
  
            nDstLen++;
        }
    }
 
    // 输出加个结束符
    *pDst = '\0';
 
    return nDstLen;
}

 

.

发表在 c/c++ | Quoted-Printable已关闭评论

byte to integer

/**  
      
* 转换一个int为byte数组  
      
* @param $byt 目标byte数组  
      
* @param $val 需要转换的字符串  
      
*  
      
*/ 
   
    public static function integerToBytes($val) {  
        $byt = array();  
        $byt[0] = ($val & 0xff);  
        $byt[1] = ($val >> 8 & 0xff);  
        $byt[2] = ($val >> 16 & 0xff);  
        $byt[3] = ($val >> 24 & 0xff);  
        return $byt;  
    }  
   
     
/**  
      
* 从字节数组中指定的位置读取一个Integer类型的数据  
      
* @param $bytes 字节数组  
      
* @param $position 指定的开始位置  
      
* @return 一个Integer类型的数据  
      
*/ 
   
    public static function bytesToInteger($bytes, $position) {  
        $val = 0;  
        $val = $bytes[$position + 3] & 0xff;  
        $val <<= 8;  
        $val |= $bytes[$position + 2] & 0xff;  
        $val <<= 8;  
        $val |= $bytes[$position + 1] & 0xff;  
        $val <<= 8;  
        $val |= $bytes[$position] & 0xff;  
        return $val;  
    }  
   
     
/**  
      
* 转换一个shor字符串为byte数组  
      
* @param $byt 目标byte数组  
      
* @param $val 需要转换的字符串  
      
*  
      
*/ 
   
    public static function shortToBytes($val) {  
        $byt = array();  
        $byt[0] = ($val & 0xff);  
        $byt[1] = ($val >> 8 & 0xff);  
        return $byt;  
    }  
   
     
/**  
      
* 从字节数组中指定的位置读取一个Short类型的数据。  
      
* @param $bytes 字节数组  
      
* @param $position 指定的开始位置  
      
* @return 一个Short类型的数据  
      
*/ 
   
    public static function bytesToShort($bytes, $position) {  
        $val = 0;  
        $val = $bytes[$position + 1] & 0xFF;  
        $val = $val << 8;  
        $val |= $bytes[$position] & 0xFF;  
        return $val;  
    }  
   
} 

.

发表在 php | byte to integer已关闭评论

一个数值压缩思路

身份证号码的前17位,是数字类型的字符串:比如"53010119870615158",现在书写的时候占了17个字符,有没有办法将其压缩短一些,比如,10位之类的。

 

 提供个思路,仅供参考:
(1)17位身份证号末位(最右边一位)可能是X,可以保留不进行处理
(2)由于36*36 = 1296 > 1000,所以我们可以用一个36进制的两位数(最大是1295)完全表示一个10进制的3位数(最大是999),这样,我们可以把17位身份证的前15位压缩成10位(每三位压缩成两位),再直接拼接上身份证号的后2位,就成了一个12位的固定长度的身份证号(当然也可以再以某种方式处理后两位,使之变成一位)
(3)设计一个36进制,例如0~9a~z,这样的话,就可以写出如下的转换:

 

string compressId(const string& id)
{
    int num, i, k = 0, tmp;
    string compressed(12, ' ');//预留12个字符的空间
    for(i = 0; i < 15; i += 3){
        num = (id[i]-'0')*100 + (id[i+1]-'0')*10 + (id[i+2]-'0');
        tmp = num/36; 
        num %= 36;
        compressed[k++] = tmp > 9 ? tmp-10+'a' : tmp+'0';//36进制数的高位
        compressed[k++] = num > 9 ? num-10+'a' : num+'0';//36进制数的低位
    }
    compressed[10] = id[15];
    compressed[11] = id[16];
    return compressed;
}
string retrieveId(const string& compressed)
{
    int num, i, k = 0;
    string id(17, ' ');//预留17个字符的空间
    for(i = 0; i < 10; i += 2){
        num = (compressed[i] > '9' ? compressed[i]-'a'+10 : compressed[i]-'0') * 36 +
              (compressed[i+1] > '9' ? compressed[i+1]-'a'+10 : compressed[i+1]-'0');
        id[k++] = num/100 + '0';//10进制数的百位
        num %= 100;
        id[k++] = num/10 + '0';//10进制数的十位
        num %= 10;
        id[k++] = num + '0';//10进制数的个位
    }
    id[15] = compressed[10];
    id[16] = compressed[11];
    return id;
}

来源:网络

发表在 technologys | 一个数值压缩思路已关闭评论