zoukankan      html  css  js  c++  java
  • 提高网络效率的总结

    1. 异步化,

    以epoll为代表。libevent也是基于epoll而实现的。

    2. 消息驱动,

    是跟异步化相结合,reactor模式。另有Scala的Actor模式,是完全的消息交互。

    3. 一些TCP参数,

    比如禁掉Nagle选项,不要打开CORK算法,使得发包和ack不要延迟太多。

    4. SO_LINGER

    解决TIME_WAIT状态过多的问题。尤其是短连接。可以参考 http://www.cnblogs.com/jdonson/p/4760110.html

    如下:

    不管长连接还是短连接,连接建立->数据传输->连接关闭的流程和处理都是一样的。

    正常的TCP客户端连接在关闭后,会进入一个TIME_WAIT的状态,持续的时间一般在1~4分钟,对于连接数不高的场景,1~4分钟其实并不长,对系统也不会有什么影响,
    但如果短时间内(例如1s内)进行大量的短连接,则可能出现这样一种情况:客户端所在的操作系统的socket端口和句柄被用尽,系统无法再发起新的连接!

    举例来说:假设每秒建立了1000个短连接(Web场景下是很常见的,例如每个请求都去访问memcached),假设TIME_WAIT的时间是1分钟,则1分钟内需要建立6W个短连接,
    由于TIME_WAIT时间是1分钟,这些短连接1分钟内都处于TIME_WAIT状态,都不会释放,而Linux默认的本地端口范围配置是:
    net.ipv4.ip_local_port_range
    = 32768 61000 不到3W,因此这种情况下新的请求由于没有本地端口就不能建立了

    可以通过如下方式来解决这个问题:
    1)可以改为长连接,但代价较大,长连接太多会导致服务器性能问题,而且PHP等脚本语言,需要通过proxy之类的软件才能实现长连接
    2)修改ipv4.ip_local_port_range,增大可用端口范围,但只能缓解问题,不能根本解决问题;
    3)客户端程序中设置socket的SO_LINGER选项
    4)客户端机器打开tcp_tw_recycle和tcp_timestamps选项;
    5)客户端机器打开tcp_tw_reuse和tcp_timestamps选项;
    6)客户端机器设置tcp_max_tw_buckets为一个很小的值;

    SO_LINGER的学习见http://blog.csdn.net/tengyft/article/details/45827193

    SO_LINGER选项用于控制close系统调用在关闭TCP连接时的行为。默认情况下,当我们使用close系统调用来关闭一个socket时,close将立即返回,TCP模块负责把该socket对应的TCP发送缓冲区中残留的数据发送给对方。

    #include <sys/socket.h>
    struct linger
    {
        int l_onoff;// 开启(非0)还是关闭(0)该选项
        int l_linger;// 滞留时间
    };

    根据linger结构体中两个成员变量的不同值,close系统调用可能产生如下3种行为之一: 

    (1)l_onoff等于0(关闭)。此时SO_LINGER选项不起作用,close用默认行为来关闭socket。 

    (2)l_onoff不为0(开启),l_linger等于0。此时close系统调用立即返回,TCP模块将丢弃被关闭的socket对应的TCP发送缓冲区中残留的数据,同时给对方发送一个复位报文段(RST)。因此,这种情况给服务器提供了异常终止一个连接的方法。 

    (3)l_onoff不为0(开启),l_linger大于0。此时close的行为取决于两个条件:一是被关闭的socket对应的TCP发送缓冲区是否还有残留的数据;二是该socket是阻塞的,还是非阻塞的。对于阻塞的socket,close将等待一段长为l_linger的时间,直到TCP模块发送完所有残留数据并得到对方的确认。如果这段时间内TCP模块没有发送完残留数据并得到对方的确认,那么close系统调用将返回-1并设置errno为EWOULDBLOCK。如果socket是非阻塞的,close将立即返回,此时我们需要根据其返回值和errno来判断残留数据是否已经发送完毕。

    注:Linux下 EWOULDBLOCK貌似就是EAGAIN

    除了上面这两个errno,要注意的是 EINTR指操作被中断唤醒,需要重新读/写

    EAGAIN不是一种错误。在VxWorks和Windows上,EAGAIN的名字叫做EWOULDBLOCK。

    另外,如果出现EINTR即errno为4,错误描述Interrupted system call,操作也应该继续

    最后,如果recv的返回值为0那表明连接已经断开,我们的接收操作也应该结束。

    5. SO_REUSEADDR

    另外还有 SO_REUSEADDR选项:

    SO_REUSEADDR可以用在以下四种情况下。

    (摘自《Unix网络编程》卷一,即UNPv1)

    1、当有一个有相同本地地址和端口的socket1处于TIME_WAIT状态时,而你启动的程序的socket2要占用该地址和端口,你的程序就要用到该选项。

    2、SO_REUSEADDR允许同一port上启动同一服务器的多个实例(多个进程)。但每个实例绑定的IP地址是不能相同的。在有多块网卡或用IP Alias技术的机器可以测试这种情况。

    3、SO_REUSEADDR允许单个进程绑定相同的端口到多个socket上,但每个socket绑定的ip地址不同。这和2很相似,区别请看UNPv1。

    4、SO_REUSEADDR允许完全相同的地址和端口的重复绑定。但这只用于UDP的多播,不用于TCP。

     

    还有一个:SO_REUSEPORT选项有如下语义:

        此选项允许完全重复捆绑,但仅在想捆绑相同IP地址和端口的套接口都指定了此套接口选项才行。

        如果被捆绑的IP地址是一个多播地址,则SO_REUSEADDR和SO_REUSEPORT等效。

    使用这两个套接口选项的建议:

        在所有TCP服务器中,在调用bind之前设置SO_REUSEADDR套接口选项;

     

    用法如下:

    int option = 1;
    if (setsockopt ( masterSocket, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option) ) < 0)
    {
       die( "setsockopt" );
    }

    Q: 编写 TCP/SOCK_STREAM 服务程序时,SO_REUSEADDR到底什么意思?

    A: 这个套接字选项通知内核,如果端口忙,但TCP状态位于 TIME_WAIT ,可以重用端口。如果端口忙,而TCP状态位于其他状态,重用端口时依旧得到一个错误信息,指明"地址已经使用中"。如果你的服务程序停止后想立即重启,而新套接字依旧使用同一端口,此时 SO_REUSEADDR 选项非常有用。必须意识到,此时任何非期望数据到达,都可能导致服务程序反应混乱,不过这只是一种可能,事实上很不可能。

    一个套接字由相关五元组构成,协议、本地地址、本地端口、远程地址、远程端口。SO_REUSEADDR 仅仅表示可以重用本地本地地址、本地端口,整个相关五元组还是唯一确定的。所以,重启后的服务程序有可能收到非期望数据。必须慎重使用 SO_REUSEADDR 选项。

     

     

  • 相关阅读:
    面向接口程序设计思想实践
    Block Chain Learning Notes
    ECMAScript 6.0
    Etcd Learning Notes
    Travis CI Build Continuous Integration
    Markdown Learning Notes
    SPRING MICROSERVICES IN ACTION
    Java Interview Questions Summary
    Node.js Learning Notes
    Apache Thrift Learning Notes
  • 原文地址:https://www.cnblogs.com/charlesblc/p/6260416.html
Copyright © 2011-2022 走看看