引言

并发是什么?企业在进行产品开发过程中为什么需要考虑这个问题?想象一下天猫的双11和京东的618活动,一秒的点击量就有几十万甚至上百万,这么多请求一下子涌入到服务器,服务器需要对这么多的请求逐个进行消化掉,假如服务器一秒的处理能力就几万,那么剩下的不能及时得到处理的这些请求作何处理?总不能让用户界面一直等着,因此消息队列应运而生,所有的请求都统一放入消息队列,工作线程从消息队列不断的消费,消息队列相当于一个缓冲区,可达到解藕、异步和削峰的目的。

Kafka、ActiveMQ、RabbitMQ和RockerMQ都是消息队列的典型,每一种都有其自身的优势和劣势。本文我用自己编写的Buffer类模拟消息队列,如果是企业级需要上线的应用,一般都是基于业界已有的MQ框架上开发。

需求原型

  1. N个Client从标准输入接收数据,然后连续不断的发送到Server端;
  2. Server端接收来自每个Client的数据,将数据中的小写字母全部转换成大写字母,其他字符保持不变,最后把转换结果发送给对应的Client。

需求分解

  1. 拿到需求,第一步要做的就是分析需求并选择合适的设计架构,考虑到Server需要和Client进行通信,Client来自四面八方,端对端通信自然选择TCP,因此Server端需要能够监听新的连接请求和已有连接的业务请求;
  2. 又由于Server需要响应多个Client的业务请求,我们希望把业务处理交给Server端的工作线程(消费者)来做;
  3. 同时还需要一个IO线程负责监听Socket描述符,当IO线程监听到已有连接的业务请求时,立即把请求内容封装成一个任务推入消息队列尾;
  4. IO线程与工作线程互斥访问消息队列,当然工作线程消费一个任务或者IO线程添加一个任务都需要通知对方,也就是同步;
  5. 工作线程处理完毕后,把处理结果交给IO线程,由IO线程负责把结果发送给对应的Client,也就是IO线程与工作线程的分离,这里工作线程通知IO线程的方式我用eventfd来实现;
  6. 我们希望引入Log4cpp记录服务端的日志,并能够保存到文件中;
  7. 分析完这些,一个整体架构和大体的样子在脑海中就已经形成了,接着就需要编写设计文档和画流程图、类图和时序图了。

详细设计文档

1.UML静态类图:

2.UML动态时序图:

效果

1.如图,开了三个Client,运行结果正确:

2.Server端通过Log4cpp把日志写到文件中:

源码获取

https://github.com/icoty/cs_threadpool_epoll_mq

目录结构

.
├── client // 客户端Demo
│   ├── Client.cc
│   ├── Client.exe
│   ├── client.sh // 进入该目录下启动Client Demo: sh client.sh
│   ├── Log4func.cc // 引入日志模块重新疯转
│   ├── Log4func.h
│   └── Makefile // 编译方式:make
├── conf
│   └── my.conf // IP,Port配置文件, 从这里进行修改
├── include // 头文件
│   ├── Configuration.hpp // 配置文件,单例类,my.conf的内存化
│   ├── FileName.hpp // 全局定义,Configuration会用到
│   ├── log // 日志模块头文件
│   │   └── Log4func.hpp
│   ├── net // 网络框架模块头文件
│   │   ├── EpollPoller.hpp
│   │   ├── InetAddress.hpp
│   │   ├── Socket.hpp
│   │   ├── SockIO.hpp
│   │   ├── TcpConnection.hpp
│   │   └── TcpServer.hpp
│   ├── String2Upper.hpp // 工作线程转换成大写实际走的这里面的接口
│   ├── String2UpperServer.hpp // Server端的整个工厂
│   └── threadpool // 线程池、锁、条件变量和消息队列的封装
│   ├── Buffer.hpp
│   ├── Condition.hpp
│   ├── MutexLock.hpp
│   ├── Noncopyable.hpp
│   ├── Pthread.hpp
│   ├── Task.hpp
│   └── Threadpool.hpp
├── log // Server端的日志通过Log4cpp记录到这个文件中
│   └── log4test.log
├── Makefile // 编译方式:make
├── README.md
├── server // server端Demo
│   ├── server.exe
│   └── server.sh // 进入该目录下启动Server Demo:sh server.sh
└── src // 源文件
├── Configuration.cpp
├── log
│   └── Log4func.cpp
├── main.cpp
├── net
│   ├── EpollPoller.cpp
│   ├── InetAddress.cpp
│   ├── Socket.cpp
│   ├── SockIO.cpp
│   ├── TcpConnection.cpp
│   └── TcpServer.cpp
├── String2Upper.cpp
├── String2UpperServer.cpp
└── threadpool
├── Buffer.cpp
├── Condition.cpp
├── MutexLock.cpp // MutexLockGuard封装
├── Pthread.cpp
└── Threadpool.cpp

参考文献

[1] UNIX环境高级编程第3版

[2] cpp reference

[3] UML时序图

[4] Log4cpp官网下载

[5] Log4cpp安装

基于线程池、消息队列和epoll模型实现并发服务器架构的更多相关文章

  1. HQueue:基于HBase的消息队列

    HQueue:基于HBase的消息队列   凌柏   ​1. HQueue简介 HQueue是一淘搜索网页抓取离线系统团队基于HBase开发的一套分布式.持久化消息队列.它利用HTable存储消息数据 ...

  2. 设计模式:基于线程池的并发Visitor模式

    1.前言 第二篇设计模式的文章我们谈谈Visitor模式. 当然,不是简单的列个的demo,我们以电商网站中的购物车功能为背景,使用线程池实现并发的Visitor模式,并聊聊其中的几个关键点. 一,基 ...

  3. 基于线程池的多并发Socket程序的实现

    Socket“服务器-客户端”模型的多线程并发实现效果的大体思路是:首先,在Server端建立“链接循环”,每一个链接都开启一个“线程”,使得每一个Client端都能通过已经建立好的线程来同时与Ser ...

  4. 【Java TCP/IP Socket】基于线程池的TCP服务器(含代码)

    了解线程池 在http://blog.csdn.net/ns_code/article/details/14105457(读书笔记一:TCP Socket)这篇博文中,服务器端采用的实现方式是:一个客 ...

  5. Windows 消息以及消息处理算法--线程和消息队列详解

    Windows以消息驱动的方式,使得线程能够通过处理消息来响应外界. Windows 为每个需要接受消息和处理消息的线程建立消息队列(包括发送消息队列,登记消息队列,输入消息队列,响应消息队列),其中 ...

  6. 深入浅出Win32多线程设计之MFC的多线程-线程与消息队列(经典)

    1.创建和终止线程 在MFC程序中创建一个线程,宜调用AfxBeginThread函数.该函数因参数不同而具有两种重载版本,分别对应工作者线程和用户接口(UI)线程. 工作者线程 CWinThread ...

  7. 基于线程池的多线程售票demo2.0(原创)

    继上回基于线程池的多线程售票demo,具体链接: http://www.cnblogs.com/xifenglou/p/8807323.html以上算是单机版的实现,特别使用了redis 实现分布式锁 ...

  8. 基于线程池的多线程售票demo(原创)

    废话不多说,直接就开撸import org.springframework.util.StopWatch;import java.util.concurrent.*;/** * 基于线程池实现的多线程 ...

  9. requests模块session处理cookie 与基于线程池的数据爬取

    引入 有些时候,我们在使用爬虫程序去爬取一些用户相关信息的数据(爬取张三“人人网”个人主页数据)时,如果使用之前requests模块常规操作时,往往达不到我们想要的目的,例如: #!/usr/bin/ ...

随机推荐

  1. cmake学习资料收集

    CMake 学习笔记 : https://www.jianshu.com/p/c417e4ab8b30

  2. druid监控sql完整版

    利用Druid实现应用和SQL监控 一.关于Druid Druid是一个JDBC组件,它包括三部分: DruidDriver 代理Driver,能够提供基于Filter-Chain模式的插件体系. D ...

  3. vb.net与vb的区别

    本文链接:https://blog.csdn.net/dfshsdr/article/details/63255645最近接触了vb.net,它增加了vb的很多特性,而且演化成为完全面向对象的编程语言 ...

  4. SpringBoot与Lombok

    Lombok 在项目中使用Lombok可以减少很多重复代码的书写.具体详细资料可以浏览 https://blog.csdn.net/motui/article/details/79012846 一.引 ...

  5. 关于盒模型的外边距padding和内边距margin

    边框border属性值  solid实线   dashed虚线   dotted点线   double双实线 /* 内边距 */padding:20px 30px 30px 30px;若有四个值代表  ...

  6. LeetCode 876. Middle of the Linked List(获得链表中心结点)

    题意:获得链表中心结点.当有两个中心结点时,返回第二个. 分析:快慢指针. /** * Definition for singly-linked list. * struct ListNode { * ...

  7. spring mvc注解之@RequestBody和@RequestParm

    @RequestBody http://localhost:8080/user/login { "username":"jack", "passwar ...

  8. 列举出给定数量的字符的所有组成情况-java实现

    老早以前刚学程序的时候碰到了这么个问题,当时没想出来,今天突然想起来了这么个问题于是写了下,也算留个纪念吧 public static String itr; public static void m ...

  9. 第2节 Scala中面向对象编程:12、13、14、15、16、trait

    6.4.  Scala中面向对象编程之trait 6.4.1.    将trait作为接口使用 Scala中的trait是一种特殊的概念: 首先先将trait作为接口使用,此时的trait就与Java ...

  10. QT5静态编译工程(arm交叉编译)

    1.首先,QT编译环境默认是动态库,要编译静态程序是不可能的,所以要下载QT源码,重新编译QT编译环境 2.下载QT源码(5.13版本):http://download.qt.io/developme ...