Network

服务器端的tcp有大量TIME_WAIT怎么办

公司的群里讨论tcp的TIME_WAIT的问题。TIME_WAIT这个状态可是面试时经常问的知识点啊。
讨论的问题是:有一个服务器,它对外网提供服务,同时它作为客户端连接很多后端的其它服务器。这位同事连后端其它服务器时用了短tcp连接。结果出现了大量的tcp TIME_WAIT状态,吞吐量上不去,怎么办?

先说结论:
1. 如果该服务器要处理外网IP的入流量,那最好的办法应该是用tcp长链接改造它与后端服务器的连接。如果用中间件,比如上zeromq那更省心了。
2. 如果不想做改动,并且在应用层确认已经交互已经完成,则可以用shutdown()直接发RST结束tcp会话,而不走正常的4次挥手。查阅shutdown系统调用的文档获知如何发RST。
3. 如果该服务器不需要处理处网IP请求的话,那么,启用port reuse 即打开net.ipv4.tcp_tw_reuse不会有太大问题(如果处理外网请求就有问题,和NAT相关,见https://vincent.bernat.im/en/blog/2014-tcp-time-wait-state-linux),或者更好的,把/proc/sys/net/ipv4/tcp_fin_timeout改小,改成1或者2。让TIME_WAIT只保持很短的时间。

首先,需要知道一个tcp会话由 src_ip, src_port, dest_ip, dest_port 四元组确定。
我们公司的IDC环境中, 上述的服务器作为客户端再发起tcp连接联后端服务器,则 src_ip 固定了, dest_ip是后端服务器的ip, port是后端服务器监听端口。这样只有 src_port 是可变化的,但port只有16个bits还不能用well-known ports,这样就更少了。

再说,主动关闭时,tcp连接需要进入TIME_WAIT状态,保持两倍MSL时长,是为了处理两种情况:
1) 让当前这个会话的迷路包在网路中消失。这样不会干扰到新建的会话。
2) 确保主动关闭方的FIN-ACK包,如果丢失的话,还能重发。
1981年的TCP RFC上把MSL设成两分钟,https://tools.ietf.org/html/rfc793

Maximum Segment Lifetime, the time a TCP segment can exist in
the internetwork system. Arbitrarily defined to be 2 minutes.

MSL设置这么长,因为以前的网络环境差啊。不过现在因为移动互联网,比较差的网络状态也是非常常见的。但RFC允许实现方减少MSL值。事实上linux的MSL写死在代码中为1分钟并且不可在运行期配置。
如果tcp连接的双方都在性能高速稳定的内网中,这个MSL就太长了。

调试时,还发现如果port用完了,connect的时候内核代码会用自旋锁忙等检查port资源。有linux内核开发基础的开发者都知道spin_lock的时候是不会让出cpu的,于是cpu全被spin_lock吃掉了。一直要等到有port被释放。再后来又发现,如果scoket没有设定SO_REUSEADDR选项,则connect()系统调用会返回EADDRINUSE。

tcp的连接双方都是确定且长期的,用什么tcp短连接呢??改造吧~~~

Be the first to comment - What do you think?  Posted by zausiu - October 20, 2017 at 14:03

Categories: Network, Unix / Linux   Tags:

用lftp为ftp服务器做镜像

用lftp为ftp服务器做镜像

命令是这样的:

$ lftp -d -u user_name,password ftp_server_host -e "mirror -c -e --parallel=5 / /path/to/mirror"

解释一下这里的选项:
* -d 调试模试,看到详细的输出
* -u 用户名和密码,用,隔开
* -e 让lftp执行lftp自有的一些命令
* mirror 执行做镜像命令
* -c continue a mirror job if possible 继续做镜像,如果可能的话。难道不用这个选项,做镜像的过程有可能被中断?
* -e 如果一个文件在ftp站点上没有,但在本机上有,则删除这个文件
* --parallel 并发数
http://blog.ykyi.net
mirror 命令还有一个 -R 参数,这样的话,会put文件到ftp服务器,效果和上例相反。

Be the first to comment - What do you think?  Posted by zausiu - September 27, 2017 at 19:54

Categories: Network, Tech Articles   Tags:

1. 前言:

  ZeroMq aka zmq是最知名的网络消息中间件之一。使有zmq的开源软件中最知名的莫过于Apache基金会下的Storm。我厂内部使用zmq的有即通的yaaf框架。

   ZMQ社区在20139月发布了zmq4zmq4最大的新功能即提供了一套安全机制,其中有IP黑白名单,用户名/密码鉴权,ECC(Elliptic Curve Cryptography)证书鉴权,以及通讯的加密(类似TSL)

   本文主要介绍zmq4.0的安全机制。

2. ECC的故事:

   这里的ECC不是我厂历史上的电商事业群,而是椭圆曲线加密算法。ECC据说被NSA(美国安全局)操作,在算法中设置了后门。不过好在不是只有一种椭圆曲线,而是有无数种,不同的曲线有不同的优点和缺点,只是其中由NIST(美国标准和技术研究署)推荐的曲线被怀疑设置了后门。

   ZMQ使用的椭圆曲线算法是Curve25519 ,它有开源实现而且没有专利保护。zmq用了长度为256bits的密钥,强度相当于RSA 3072比特的密钥长度。

   ECDH(Elliptic Curve Diffie-Hellman)则是一个密钥协商协议。非常简单地讲,当AB在一个不受信任的网络中通讯前,AB先生成一对公私钥,并且AB通过某个完全的渠道事先知道对方的公钥,然后AB在握手阶段协商出一个双方公知的私钥供加密接下来的通讯。

    可以理解成zmq设计了一个专门为自己定制的精简的TLS,更多的理论细节在http://curvezmq.org/page:read-the-docs

作为一个码农,下面用代码介绍使用zmq4的Curve安全机制

3. 证书鉴权,通讯加密,IP 白名单

编译zmq4.x以后,会生成一个名叫curve_keygen的程序,用它可以生成zmq 的证书。证书是一个一般的文本文件,如下是一个curve_keygen生成的zmq证书的例子。

#   ****  Generated on 2017-01-02 14:24:20 by CZMQ  ****
#   ZeroMQ CURVE **Secret** Certificate
#   DO NOT PROVIDE THIS FILE TO OTHER USERS nor change its permissions.

metadata
    email = "kamuszhou@tencent.com"
curve
    public-key = "!Upjrn]2Dk)jQkYREsceBnpgoIL7koE{CVnV1j4D"
    secret-key = "ZPT#=l/#Rtg:TeLbofh:uPi7#/w(GDZq[0^qPZA1"

 

字段很好理解,最重要的是public-keysecret-key字段。

下面是一段客户端的相关代码片断(使用官方的czmq库,czmq是官方维护的High-level C库封装了底层的C API接口)

 // 创建一个DEALER类型的zmq socket
  zsock_t* dealer = zsock_new(ZMQ_DEALER);
  assert (dealer_ != NULL)

  // 假设路径~/my.cert存了客户端的证书,证书里需要既有公钥又有私钥,载入证书再应用这个证书到socket句柄上。然后就可以销毁证书句柄了
  zcert_t* my_cert = zcert_load("~/my.cert");
  assert (my_cert != NULL);
  zcert_apply(my_cert, dealer_);
  zcert_destroy(&my_cert);

  // 设置服务器的公钥
  zsock_set_curve_serverkey(dealer, "I7[{YV4[}q[9a)]b&d>bisoT]UXa/7b$Tp:6yoyq");
    
  // 连接在本机监听8888端口的服务器
  zsock_connect(dealer, "%s", "tcp://localhost:8888");

下面是服务端的相关代码片断:

   // 创建一个ROUTER zmq socket
   zsock_t* router_sk = zsock_new(ZMQ_ROUTER);
   assert(router_sk != NULL);

  /* CZMQ4库封装了一个actor服务器模型,详细的官方接口说明在http://api.zeromq.org/CZMQ3-0:zactor 其中zactor_new用来创建一个actor,回调函数填zauth。zauth是CZMQ定义好的一个回调函数,里面做了很多鉴权的准备工作。照抄就行。通过向actor通讯设置如何鉴权。*/
   zactor_t* auth = zactor_new(zauth, NULL);

   // 打印详细的鉴权相关日志
   zstr_send(auth, "VERBOSE");
   // 必须调用这个函数与actor同步。下同
   zsock_wait(auth);

   // 这里开启白名单,允许”127.0.0.1”和"127.0.0.2"访问本服务。如果用黑名单也类似,但第二个参数设为”DENY”
   zstr_sendx(auth, "ALLOW", "127.0.0.1", "127.0.0.2", NULL);
   zsock_wait(auth);

   /* 假设 ~/cert目录夹下面存放了客户端的证书(只有公钥) 下面的代码让只有拥有证书的客户端才能连接服务*/
   zstr_sendx(auth, "CURVE", "~/cert", NULL);
   zsock_wait(auth);

   // router_sk 这个zmq套接字是作为服务端使用的
   zsock_set_curve_server(router_sk, 1);

  // 加载服务端自己的证书,这个证书文件里需要既有公钥也有私钥
   zcert_t* my_cert = zcert_load("~/server.cert");
   assert(my_cert != NULL);
   zcert_apply(my_cert, router_sk);
   zcert_destroy(&my_cert);

   // router套接字监听8888端口
   int ret = zsock_bind(router_sk, "%s", "tcp://localhost:8888");
   assert(ret != -1);

 至此,服务端只接受来自127.0.0.1并且拥有正确证书的客户端的连接,而且通讯通道被加密。

4. Notice & Bug

a. 在实战过程中,发现一个czmq4库的bug,如果一个zmq socket作为server端并使用curve鉴权,就必须调用bind(),而不能调connect(),否则进程会崩溃。但是在zmq的世界中,server端的socket也是可以调connect的。在czmq4的官方github页面上找到有人已经报告了这个bug,可能下个版本会修复吧。

https://github.com/zeromq/czmq/issues/1470

b. czmq4实现的reactor模型不支持监控zactor对象.

c. czmq4有很多函数的参数个数是不确定的,这些函数的最后一个参数并须填NULL,否则编译期检查不到错误在运行期会崩溃。

Be the first to comment - What do you think?  Posted by zausiu - January 17, 2017 at 17:35

Categories: Network, Security, Tech Articles   Tags:

Next Page »