文章出处:http://topic.csdn.net/u/20100601/07/f453af5a-abba-465e-bb9f-1cdc4788903d.html
超时控制:
对于网络IO,我们一般情况下都需要超时机制来避免进行操作的线程被handle住,经典的做法就是采用select+非阻塞IO进行判断,select在超时时间内判断是否可以读写操作,然后采用非阻塞读写,不过一般实现的时候读操作不需要设置为非阻塞,上面已经说过读操作只用在没有数据的时候才会阻塞,select的判断成功说明存在数据,所以即使是阻塞读在这种情况下也是可以做到非阻塞的效果,就没有必要设置成非阻塞的情况了。这部分的代码可以参考ullib中ul_sreado_ms_ex和ul_swriteo_ms_ex。
采用ul_sreado_ms_ex读取数据也是不能保证返回大于0就一定读到指定的数据长度,对于读写操作,都是需要判断返回的读长度或者写长度是否是需要的长度,不能简单的判断一下返回值是否小于0.对于ul_sreado_ms_ex的情况,如果出现了发送端数据发送一般就被close掉的情况就有可能导致接收端读不到完整的数据包。
errno只有在函数返回值为负的时候才有效。如果返回0或者大于0的数,errno的结果是无意义的。有些时候会出现read到0,但是我们认为是错误的情况然后输出errno造成误解,一般建议在这种情况要同时输出返回值和errno的结果,有些情况由于只有errno造成了对于问题的判断失误。
长连接和短连接的各种可能的问题及相应的处理
这里主要是发起连接的客户端的问题,这里列出的主要是在采用同步模型的情况下才会存在的问题。
短连接:
采用短连接的情况一般是考虑到下面的一些问题:
后端服务的问题,考虑最简单的情况下一个线程一个连接,如果这个连接采用了长连接那么就需要我们处理连接的线程和后端保持一一对应,然后按照某些原则进行处理(n对n的关系),但由于一方面服务器可能增加,这样导致需要前后端保持一致,带来了更多的麻烦。另一方面线程数上不去对应处理能力也会产生影响,而短连接每次连接的时候只需要关注当前的机器,问题相对会少一些。其实这个问题可以采用连接池的方式来解决,后面会提到。不需要考虑由于异常带来的脏数据。负载均衡方面可以简单考虑,无论线程数使多少还是后端服务器的数量是多少都没有关系,每次考虑单个连接就可以了。当然如果负载逻辑简单,并且机器相对固定,一个线程一个长连接问题也不大。
规避一些问题,在过去有些情况下出现长连接大延时,数据没响应等问题,测试的时候发现换短连接问题就解决了,由于时间关系就没有再继续追查,事实上这些问题现在基本上都已经定位并且有相关的解决方案了。
不足:
效率不足,由于连接操作一般会有50ns~200ns的时间消耗,导致短连接需要消耗更多的时间会产生TIME_WAIT问题,需要做更多的守护。
长连接:
长连接比短连接减少了连接的时间消耗,可以承受更高的负载。但在使用的时候需要考虑一些问题脏数据,在一些特殊情况(特别是逻辑错误的情况下)会存在一些我们并不需要的数据,这个时候的处理比较安全的方式是一旦检测到就关闭连接,检测的方式在发起请求前,用前面“为什么socket写错误,但用recv检查依然成功?”介绍的方式进行检查。不过有些程序会采用继续把所有不需要的数据读完毕(读到EAEGIN),不过这种方式过分依赖逻辑了,存在了一定的风险,不如直接断开来的简单。前面也提到了,在这种情况我们一般会采用连接池的方式来解决问题,比如(public / connectpool 中就可以维护不同的连接,使每个线程都可以均匀的获取到句柄)服务端的处理这个时候需要考虑连接的数量,简单的方式就是一个长连接一个线程,但是线程也不能无线增加(增加了,可能造成大量的上下文切换使得性能下降),我们一般在长连接的情况采用pendingpool的模型,通过一个一步队列来缓冲,这样不需要考虑客户端和服务端的线程数问题,可以任意配置(可以通过线下测试选择合适的线程数)
一些特殊的问题,主要是长连接的延时,在后面的FAQ中会有详细的说明。
一般来说,对于我们多数的内部业务逻辑都是可以采用长连接模式,不会产生太多的问题。