忘记密码   免费注册 查看新帖 | 论坛精华区

ChinaUnix.net

  平台 论坛 博客 认证专区 大话IT HPC论坛 徽章 文库 沙龙 自测 下载 频道自动化运维 虚拟化 储存备份 C/C++ PHP MySQL 嵌入式 Linux系统
最近访问板块 发新帖
楼主: wlmqgzm

[C++] ASIO,无锁,高并发,高可靠, 统一,网络架构,抗DOS,低端4核心服务器CPU 每秒87万QPS ECHO [复制链接]

论坛徽章:
9
程序设计版块每日发帖之星
日期:2015-10-18 06:20:00程序设计版块每日发帖之星
日期:2015-11-01 06:20:00程序设计版块每日发帖之星
日期:2015-11-02 06:20:00每日论坛发贴之星
日期:2015-11-02 06:20:00程序设计版块每日发帖之星
日期:2015-11-03 06:20:00程序设计版块每日发帖之星
日期:2015-11-04 06:20:00程序设计版块每日发帖之星
日期:2015-11-06 06:20:00数据库技术版块每周发帖之星
日期:2015-12-02 15:02:47数据库技术版块每日发帖之星
日期:2015-12-08 06:20:00
发表于 2015-10-19 15:43 |显示全部楼层
本帖最后由 wlmqgzm 于 2015-10-19 15:43 编辑

回复 28# windoze

感谢指正. 我确实前面弄错了

由于缺乏测试工具, 最大每次只能测试1000个连接,1000个收发, 将就用吧. 

这个是新建连接的处理能力测试报告, 大约4.6万每秒新建连接的处理能力. 前面的最优测试报告是100个并发连接的包处理能力是每秒33万个数据包.

后面, 还会做这个测试, 如果找到测试合适的方法后, 会专门针对这个出一份更全的测试报告.

  Requests per second:    46251.33 [#/sec] (mean)

guo@guo-desktop:~$ ab -n 1000 -c 1000 -k ht tp://127.0.0.1:1971/jjjjjjjjjjjjj  
This is ApacheBench, Version 2.3 <$Revision: 1604373 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd,
Licensed to The Apache Software Foundation,


Benchmarking 127.0.0.1 (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Completed 600 requests
Completed 700 requests
Completed 800 requests
Completed 900 requests
Completed 1000 requests
Finished 1000 requests


Server Software:        
Server Hostname:        127.0.0.1
Server Port:            1971

Document Path:          /jjjjjjjjjjjjj
Document Length:        0 bytes

Concurrency Level:      1000
Time taken for tests:   0.022 seconds
Complete requests:      1000
Failed requests:        0
Non-2xx responses:      1000
Keep-Alive requests:    1000
Total transferred:      119000 bytes
HTML transferred:       0 bytes
Requests per second:    46251.33 [#/sec] (mean)
Time per request:       21.621 [ms] (mean)
Time per request:       0.022 [ms] (mean, across all concurrent requests)
Transfer rate:          5374.91 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        5    9   2.3      8      14
Processing:     2    4   0.7      4       5
Waiting:        2    4   0.7      4       5
Total:          8   12   3.0     12      19

Percentage of the requests served within a certain time (ms)
  50%     12
  66%     14
  75%     15
  80%     16
  90%     17
  95%     18
  98%     18
  99%     19
100%     19 (longest request)


   

论坛徽章:
9
程序设计版块每日发帖之星
日期:2015-10-18 06:20:00程序设计版块每日发帖之星
日期:2015-11-01 06:20:00程序设计版块每日发帖之星
日期:2015-11-02 06:20:00每日论坛发贴之星
日期:2015-11-02 06:20:00程序设计版块每日发帖之星
日期:2015-11-03 06:20:00程序设计版块每日发帖之星
日期:2015-11-04 06:20:00程序设计版块每日发帖之星
日期:2015-11-06 06:20:00数据库技术版块每周发帖之星
日期:2015-12-02 15:02:47数据库技术版块每日发帖之星
日期:2015-12-08 06:20:00
发表于 2015-10-20 15:34 |显示全部楼层
本帖最后由 wlmqgzm 于 2015-10-21 16:06 编辑

如何避免服务器端大量出现 TIME_WAIT的情况处理机制的说明:

目前正在做的服务器代码,  设计机制上要避免出现大量WAIT_WAIT,
TCP/IP连接, 谁先发起拆线, 谁就要socket TIME_WAIT(30秒-4分钟,取决于unix版本和设置)
  我做的服务器代码, 目前只有一种情况下, 服务器端将主动发起拆线, 就是 读超时,  也是空闲超时.

服务器端读超时前, 服务器端长时间处于收态, 服务器端定时器任务在该socket上发shutdown receive, 关服务器端receive,  EOF标志被附加到服务器端接收缓冲区的最后,  
  服务器端receive收完缓冲区的数据, 将收EOF,
  客户端TCP/IP底层将收到EOF标志, 客户端send如果发包,  也检测Error_code收EOF, 则客户端立即能够知道 , 此包未发成功, 可立即发起双向拆线 , 重连, 重发,  对方先拆, 服务器端无TIME_WAIT.   
  客户端中间只有1个很小的窗口期属于不确定状态, 对此期间的send数据包, 客户端send只能确认数据包进入本地发送缓冲区, 无法确认数据包最终下落,  发送后, 进入收态, .
  客户端如果send未发包, 则不能检测send通道EOF, 处于收态,

  服务器端receive收完缓冲区的数据, 并处理发送完毕, 再接收 将收EOF, BOOST asio receive_handler error_code=2,  同时服务器端检测发现内部read_time_out_set标志, 将执行关闭服务器端send,  然后定时器等待, 此EOF标志被附加到服务器端发送缓冲区的最后,  客户端在收完缓冲区所有数据后,  客户端receive EOF, 客户端将发起双向拆线, 服务器端无TIME_WAIT

      服务器端在延迟一段时间后, 99%以上的概率, 对方已经主动发起了拆线, 尤其是本地局域网,数据收发很快, 只有很小的概率, 数据和EOF未能在此期间发送到对方,
定时器的间隔可以设的很小, 然后轮询计数, 检查TCP/IP底层is_socket_connected( int socket_handle ), 如果对方已经断开, 就可以不等到最大等待时长, 先拆了.
在99%以上的概率中, 你只需要等几毫秒, 客户端就主动拆线了.  
超时后, 服务器端定时器发起主动拆线, 如果采用hard_close, 此时将清空所有缓冲区的数据, reset连接, 服务器端无TIME_WAIT, 满足高性能服务器的要求;  
如果采用soft_close,  就是普通的拆线流程, 此时所有缓冲区的数据被保留, 服务器端主动发起拆线, 系统将进入TIME_WAIT(30秒-240秒,取决于*inx版本和设置), 此方式可满足高可靠服务器的要求.  
     

     这里需要说明的是: 对于高可靠性设计原则的客户端, 需要在上述的"receive EOF, 客户端发起双向拆线"时, 增加 检查并确认, 是否收到响应包.并启动重连, 没有收到响应包, 要重连重发.做到不丢包.

     对于普通客户端, 例如:linux telnet等, 都会在上面的过程中, 主动发起disconnect, 并 退出服务.

     其余的所有情况都可以ASIO析构方式关闭, 或者close方式关闭.     因为都是对方主动拆的, TIME_WAIT在对方


is_socket_connected( int socket_handle )代码

bool is_socket_connected( int socket_handle )
{
  struct tcp_info info;
  int length = sizeof(info);
  if( socket_handle <= 0 )   return false;
  memset( &info,0,sizeof(info) );
  getsockopt( socket_handle, IPPROTO_TCP, TCP_INFO, &info, (socklen_t *)&length );
  if( info.tcpi_state== 1 )     return true;
  return false;
}

论坛徽章:
9
程序设计版块每日发帖之星
日期:2015-10-18 06:20:00程序设计版块每日发帖之星
日期:2015-11-01 06:20:00程序设计版块每日发帖之星
日期:2015-11-02 06:20:00每日论坛发贴之星
日期:2015-11-02 06:20:00程序设计版块每日发帖之星
日期:2015-11-03 06:20:00程序设计版块每日发帖之星
日期:2015-11-04 06:20:00程序设计版块每日发帖之星
日期:2015-11-06 06:20:00数据库技术版块每周发帖之星
日期:2015-12-02 15:02:47数据库技术版块每日发帖之星
日期:2015-12-08 06:20:00
发表于 2015-10-21 14:46 |显示全部楼层
本帖最后由 wlmqgzm 于 2015-10-21 16:18 编辑

目前性能30.7万QPS ECHO,  增加新功能模块, 增加了一个shared_ptr, 总共使用shared_ptr数量增加到2个.  时间精度改进为微秒级别, 增加指定socket进行debug功能, 性能指标有所下降, 尤其,发现shared_ptr数量对性能有较大影响, 应该是shared_ptr内部使用了锁的原因,  时间精度修改,从秒级改为微秒级别, 30秒有0.4秒的影响, 大约1%的时间.  
后台管理定时器由每1秒活跃一次, 改进为每100毫秒活跃一次.
目前, 还在进行功能扩充中, 下一个扩充调试的功能是: 防简单DDOS攻击的模块. 总之, 最终性能指标还会变化.

guo@guo-desktop:~$ ab -n 10000000 -c 100 -k http ://127.0.0.1:1971/jjjjjjjjjjjjj
This is ApacheBench, Version 2.3 <$Revision: 1604373 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd,
Licensed to The Apache Software Foundation,

Benchmarking 127.0.0.1 (be patient)
Completed 1000000 requests
Completed 2000000 requests
Completed 3000000 requests
Completed 4000000 requests
Completed 5000000 requests
Completed 6000000 requests
Completed 7000000 requests
Completed 8000000 requests
Completed 9000000 requests
Completed 10000000 requests
Finished 10000000 requests


Server Software:        
Server Hostname:        127.0.0.1
Server Port:            1971

Document Path:          /jjjjjjjjjjjjj
Document Length:        0 bytes

Concurrency Level:      100
Time taken for tests:   32.576 seconds
Complete requests:      10000000
Failed requests:        0
Non-2xx responses:      10000000
Keep-Alive requests:    10000000
Total transferred:      1190000000 bytes
HTML transferred:       0 bytes
Requests per second:    306975.49 [#/sec] (mean)
Time per request:       0.326 [ms] (mean)
Time per request:       0.003 [ms] (mean, across all concurrent requests)
Transfer rate:          35673.91 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       4
Processing:     0    0   0.2      0      29
Waiting:        0    0   0.2      0      29
Total:          0    0   0.2      0      29

Percentage of the requests served within a certain time (ms)
  50%      0
  66%      0
  75%      0
  80%      0
  90%      0
  95%      1
  98%      1
  99%      1
100%     29 (longest request)

论坛徽章:
14
射手座
日期:2014-11-29 19:22:49黑曼巴
日期:2017-07-13 19:13:4715-16赛季CBA联赛之四川
日期:2017-02-07 21:08:572015年亚冠纪念徽章
日期:2015-11-06 12:31:58每日论坛发贴之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-07-12 22:20:002015亚冠之浦和红钻
日期:2015-07-08 10:10:132015亚冠之大阪钢巴
日期:2015-06-29 11:21:122015亚冠之广州恒大
日期:2015-05-22 21:55:412015年亚洲杯之伊朗
日期:2015-04-10 16:28:252015年迎新春徽章
日期:2015-03-04 09:50:28
发表于 2015-10-21 15:24 |显示全部楼层
本帖最后由 yulihua49 于 2015-10-21 15:31 编辑

回复 32# wlmqgzm
我的服务器端这么做的:
//避免 TIME_WAIT
        so_linger.l_onoff=1;
        so_linger.l_linger=0;
        ret=setsockopt(sock, SOL_SOCKET, SO_LINGER, &so_linger, sizeof so_linger);
        if(ret) ShowLog(1,"set SO_LINGER err=%d,%s",errno,strerror(errno));

服务器主动关闭连接出现在如下情形:
1.客户端已经关闭。
2.客户端超时
3.服务器端出现致命错误。
此时,优雅的保护通信buf的数据已经没有任何意义,野蛮的关闭它!!!



   

论坛徽章:
9
程序设计版块每日发帖之星
日期:2015-10-18 06:20:00程序设计版块每日发帖之星
日期:2015-11-01 06:20:00程序设计版块每日发帖之星
日期:2015-11-02 06:20:00每日论坛发贴之星
日期:2015-11-02 06:20:00程序设计版块每日发帖之星
日期:2015-11-03 06:20:00程序设计版块每日发帖之星
日期:2015-11-04 06:20:00程序设计版块每日发帖之星
日期:2015-11-06 06:20:00数据库技术版块每周发帖之星
日期:2015-12-02 15:02:47数据库技术版块每日发帖之星
日期:2015-12-08 06:20:00
发表于 2015-10-21 15:52 |显示全部楼层
本帖最后由 wlmqgzm 于 2015-10-21 16:11 编辑

回复 34# yulihua49

对, 这个就是硬关闭, 我的代码中也做了, 但是没有应用的模块.

如果做高可靠服务器的话, 就没有这么霸气, 还是考虑主动协调对方, 让对方主动发起disconnect, 避免丢包, 避免TIME_WAIT;
在目前的TCP/IP机制下, 一般的客户端, 例如telnet等, 都可以协调成功, 避免TIME_WAIT

//  这个是硬关闭,且closesocket立刻返回,同时释放所有资源。
void Asio_tcp_server::socket_hard_close( boost::asio::ip::tcp::socket &socket1,  int socket_handle )
{
  boost::asio::socket_base::linger option( true, 0 );
  socket1.set_option(option);
  socket1.close();
  if( d_display_level>=5 )  {
    log_message_int("socket_hard_close. socket=", socket_handle );
    }
  return;
}


   

论坛徽章:
9
程序设计版块每日发帖之星
日期:2015-10-18 06:20:00程序设计版块每日发帖之星
日期:2015-11-01 06:20:00程序设计版块每日发帖之星
日期:2015-11-02 06:20:00每日论坛发贴之星
日期:2015-11-02 06:20:00程序设计版块每日发帖之星
日期:2015-11-03 06:20:00程序设计版块每日发帖之星
日期:2015-11-04 06:20:00程序设计版块每日发帖之星
日期:2015-11-06 06:20:00数据库技术版块每周发帖之星
日期:2015-12-02 15:02:47数据库技术版块每日发帖之星
日期:2015-12-08 06:20:00
发表于 2015-10-21 18:44 |显示全部楼层
贴原版的shutdown简介:

Shutting down the writing end of a socket solves a number of thorny problems. They are
    • Flushes out the kernel buffers that contain any pending data to be sent. Data is buffered by the kernel networking software to improve performance.
    • Sends an end-of-file indication to the remote socket. This tells the remote reading process that no more data will be sent to it on this socket.
    • Leaves the partially shutdown socket open for reading. This makes it possible to receive confirmation messages after the end-of-file indication has been sent on the socket.
    • Disregards the number of open references on the socket. Only the last close(2) on a socket will cause an end-of-file indication to be sent.

总之, 无论是客户端, 还是服务器端, 如果要实现高可靠设计, 都要按照规范的要求进行设计, 最终可以保证正常重启动模块, 不丢失一个数据包.

论坛徽章:
9
程序设计版块每日发帖之星
日期:2015-10-18 06:20:00程序设计版块每日发帖之星
日期:2015-11-01 06:20:00程序设计版块每日发帖之星
日期:2015-11-02 06:20:00每日论坛发贴之星
日期:2015-11-02 06:20:00程序设计版块每日发帖之星
日期:2015-11-03 06:20:00程序设计版块每日发帖之星
日期:2015-11-04 06:20:00程序设计版块每日发帖之星
日期:2015-11-06 06:20:00数据库技术版块每周发帖之星
日期:2015-12-02 15:02:47数据库技术版块每日发帖之星
日期:2015-12-08 06:20:00
发表于 2015-10-21 18:56 |显示全部楼层
使用shutdown对收发依次进行关闭, 最后再hard_close或者soft_close,  比较安全

int  Asio_tcp_server::socket_shutdown_receive( boost::asio::ip::tcp::socket  &socket1 )
{
  boost::system::error_code ec;
  socket1.shutdown( boost::asio::ip::tcp::socket::shutdown_receive, ec );
  if( ec )   {
    if( d_display_level>=2 )  {
      int  socket_handle = socket1.native_handle();
      int value =  ec.value();
      std::string str_msg   = "socket=";
      str_msg += std::to_string( socket_handle );
      str_msg +=  ", error value=";
      str_msg += std::to_string( value );
      str_msg +=  ", error=";
      str_msg += ec.message();
      warn_message_str( str_msg );
      }
    return -1;
    }
  if( d_display_level>=5 )  {
    int  socket_handle = socket1.native_handle();
    log_message_int("ok. shutdown receive. socket=", socket_handle);
    }
  return  0;
}


int  Asio_tcp_server::socket_shutdown_send( boost::asio::ip::tcp::socket  &socket1 )
{
  boost::system::error_code ec;
  socket1.shutdown( boost::asio::ip::tcp::socket::shutdown_send, ec );
  if( ec )   {
    if( d_display_level>=2 )  {
      int  socket_handle = socket1.native_handle();
      int value =  ec.value();
      std::string str_msg   = "socket=";
      str_msg += std::to_string( socket_handle );
      str_msg +=  ", error value=";
      str_msg += std::to_string( value );
      str_msg +=  ", error=";
      str_msg += ec.message();
      warn_message_str( str_msg );
      }
    return -1;
    }
  if( d_display_level>=5 )  {
    int  socket_handle = socket1.native_handle();
    log_message_int("ok. shutdown send. socket=", socket_handle);
    }
  return 0;
}


int  Asio_tcp_server::socket_shutdown_both( boost::asio::ip::tcp::socket  &socket1 )
{
  boost::system::error_code ec;
  socket1.shutdown( boost::asio::ip::tcp::socket::shutdown_both, ec );
  if( ec )   {
    if( d_display_level>=2 )  {
      int  socket_handle = socket1.native_handle();
      int value =  ec.value();
      std::string str_msg   = "socket=";
      str_msg += std::to_string( socket_handle );
      str_msg +=  ", error value=";
      str_msg += std::to_string( value );
      str_msg +=  ", error=";
      str_msg += ec.message();
      warn_message_str( str_msg );
      }
    return -1;
    }
  if( d_display_level>=2 )  {
    int  socket_handle = socket1.native_handle();
    log_message_int("ok. shutdown send. socket=", socket_handle);
    }
  return 0;
}

论坛徽章:
9
程序设计版块每日发帖之星
日期:2015-10-18 06:20:00程序设计版块每日发帖之星
日期:2015-11-01 06:20:00程序设计版块每日发帖之星
日期:2015-11-02 06:20:00每日论坛发贴之星
日期:2015-11-02 06:20:00程序设计版块每日发帖之星
日期:2015-11-03 06:20:00程序设计版块每日发帖之星
日期:2015-11-04 06:20:00程序设计版块每日发帖之星
日期:2015-11-06 06:20:00数据库技术版块每周发帖之星
日期:2015-12-02 15:02:47数据库技术版块每日发帖之星
日期:2015-12-08 06:20:00
发表于 2015-10-21 18:57 |显示全部楼层
本帖最后由 wlmqgzm 于 2015-10-21 19:04 编辑


void Asio_tcp_server::socket_hard_close( boost::asio::ip::tcp::socket &socket1,  int socket_handle )
{
  boost::asio::socket_base::linger option( true, 0 );
  socket1.set_option(option);
  socket1.close();
  if( d_display_level>=5 )  {
    log_message_int("socket_hard_close. socket=", socket_handle );
    }
  return;
}


void Asio_tcp_server::socket_soft_close( boost::asio::ip::tcp::socket &socket1, int socket_handle )
{
  boost::asio::socket_base::linger option( false, 0 );
  socket1.set_option(option);
  socket1.close();
  if( d_display_level>=5 )  {
    log_message_int("socket_soft_close. socket=", socket_handle);
    }
  return;
}

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
发表于 2015-10-21 19:23 |显示全部楼层
首先赞一个,可见楼主的网络编程功底炉火纯青 ^_^

然后谈谈这些年来我对服务端设计的一些看法。

可能是我身处于金融行业,技术老大说稳定比性能更重要,于是我们的设计方案相对保守,简单=稳定,举个例子,互联网行业的日志库都是打开日志文件,写写写...并做大量优化,我们的日志库是打开日志文件,写,关闭文件,再打开日志文件...技术老大说宁可速度慢,也要保证一旦core发生时,应用已经写出的日志必须能查到。我们的后台是自己研发的,多进程Leader-Follow模型,多进程而不是多线程是防止我们的交易开发人员写出的程序core时,不会影响其它正在处理的交易,Leader-Follow模型是因为其能简单的实现并发,仅此而已,没有很高大上很炫很酷的功能和机制,也没有很高的QPS。

这几年随着业务量大幅提升,单台服务器吃不消了,怎么办?技术老大说做横向扩展负载均衡,我们在不动原有服务端设计的前提下,又一次简单的大幅提高了整个后台的处理性能。

这么多年培养了我质朴的系统设计观,但有时候在网上看到伙伴们为了追求性能极致而提出不断优化的优秀方案时还保有一些激动。

最后谈一下我个人对服务端设计的一个看法,用户态的性能优化差不多已经到达极限了,如果还想有更高的提升,不妨考虑内核态,就好像现在的很多负载均衡软件性能其实都差不多,但始终都对LVS望尘莫及。

论坛徽章:
243
射手座
日期:2013-08-23 12:04:38射手座
日期:2013-08-23 16:18:12未羊
日期:2013-08-30 14:33:15水瓶座
日期:2013-09-02 16:44:31摩羯座
日期:2013-09-25 09:33:52双子座
日期:2013-09-26 12:21:10金牛座
日期:2013-10-14 09:08:49申猴
日期:2013-10-16 13:09:43子鼠
日期:2013-10-17 23:23:19射手座
日期:2013-10-18 13:00:27金牛座
日期:2013-10-18 15:47:57午马
日期:2013-10-18 21:43:38
发表于 2015-10-21 19:55 |显示全部楼层
我看lz处于一种亢奋的状态,就不说啥了
您需要登录后才可以回帖 登录 | 注册

本版积分规则

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号 北京市公安局海淀分局网监中心备案编号:11010802020122
广播电视节目制作经营许可证(京) 字第1234号 中国互联网协会会员  联系我们:
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP