加入收藏 | 设为首页 | 会员中心 | 我要投稿 衡阳站长网 (https://www.0734zz.cn/)- 数据集成、设备管理、备份、数据加密、智能搜索!
当前位置: 首页 > 站长资讯 > 外闻 > 正文

实例:一个服务器程序的架构介绍

发布时间:2019-01-29 18:45:33 所属栏目:外闻 来源:高性能服务器开发
导读:本文将介绍我曾经做过的一个项目的服务器架构和服务器编程的一些重要细节。 一、程序运行环境 操作系统:Centos 7.0 编译器:gcc/g++ 4.8.3、cmake 2.8.11 mysql数据库:5.5.47 项目代码管理工具:Visual Studio 2013 一、程序结构 该程序总共有 17 个线程

工作线程的流程:

  1. while (!m_bQuit)   
  2. {   
  3.     epoll_or_select_func();   
  4.  
  5.     handle_io_events();   
  6.  
  7.     handle_other_things();   

其中 epoll_or_select_func() 即是上文所说的通过 select()/poll()/epoll() 等 IO multiplex 技术,确定好了哪些 TcpConnection 上有数据到来。我的服务器代码中一般只会监测 socket 可读事件,而不会监测 socket 可写事件。至于如何发数据,文章后面会介绍。所以对于可读事件,以 epoll 为例,这里需要设置的标识位是:

  • EPOLLIN 普通可读事件(当连接正常时,产生这个事件,recv()/read()函数返回收到的字节数;当连接关闭,这两个函数返回0,也就是说我们设置这个标识已经可以监测到新来数据和对端关闭事件)
  • EPOLLRDHUP 对端关闭事件(linux man 手册上说这个事件可以监测对端关闭,但我实际调试时发送即使对端关闭也没触发这个事件,仍然是EPOLLIN,只不过此时调用recv()/read()函数,返回值会为0,所以实际项目中是否可以通过设置这个标识来监测对端关闭,仍然待考证)
  • EPOLLPRI 带外数据

muduo 里面将 epoll_wait 的超时事件设置为 1 毫秒,我的另一个项目将 epoll_wait 超时时间设置为 10 毫秒。这两个数值供大家参考。

这个项目中,工作线程和主线程都是上文代码中的逻辑,主线程监听侦听socket 上的可读事件,也就是监测是否有新连接来了。主线程和每个工作线程上都存在一个 epollfd。如果新连接来了,则在主线程的 handle_io_events() 中接受新连接。产生的新连接的socket句柄挂接到哪个线程的 epollfd 上呢?这里采取的做法是 round-robin 算法,即存在一个对象CWorkerThreadManager 记录了各个工作线程上工作状态。伪码大致如下:

  1. void attach_new_fd(int newsocketfd)   
  2. {   
  3.     workerthread = get_next_worker_thread(next);   
  4.     workerthread.attach_to_epollfd(newsocketfd);   
  5.     ++next;   
  6.     if (next > max_worker_thread_num)   
  7.         next = 0;   
  8. }   

即先从第一个工作线程的 epollfd 开始挂接新来 socket,接着累加索引,这样下次就是第二个工作线程了。如果所以超出工作线程数目,则从第一个工作重新开始。这里解决了新连接 socket “负载均衡”的问题。在实际代码中还有个需要注意的细节就是:epoll_wait 的函数中的 struct epoll_event 数量开始到底要设置多少个才合理?存在的顾虑是,多了浪费,少了不够用,我在曾经一个项目中直接用的是 4096:

  1. const int EPOLL_MAX_EVENTS = 4096;   
  2. const int dwSelectTimeout = 10000;   
  3. struct epoll_event events[EPOLL_MAX_EVENTS];   
  4. int nfds = epoll_wait(m_fdEpoll, events, EPOLL_MAX_EVENTS, dwSelectTimeout / 1000); 

我在陈硕的 muduo 网络库中发现作者才用了一个比较好的思路,即动态扩张数量:开始是 n个,当发现有事件的 fd 数量已经到达 n 个后,将 struct epoll_event 数量调整成 2n 个,下次如果还不够,则变成 4n 个,以此类推,作者巧妙地利用 stl::vector 在内存中的连续性来实现了这种思路:

  1. //初始化代码   
  2. std::vector<struct epoll_event> events_(16);   
  3.  
  4. //线程循环里面的代码   
  5. while (m_bExit)   
  6. {   
  7.     int numEvents = ::epoll_wait(epollfd_, &*events_.begin(), static_cast<int>(events_.size()), 1);   
  8.     if (numEvents > 0)   
  9.     {   
  10.         if (static_cast<size_t>(numEvents) == events_.size())   
  11.         {   
  12.             events_.resize(events_.size() * 2);   
  13.         }   
  14.     }   

(编辑:衡阳站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

热点阅读