服务器设计笔记(3)-----消息队列

    摘抄的一篇文章,故拿出来记录下,下篇博客把解决代码分享出来。感谢这篇文章的原作者,解决了棘手的问题。

 

    我们所能想到的最简单的消息队列可能就是使用stl的list来实现了,即消息队列内部维护一个list和一个互斥锁,putMessage时将message加入到队列尾,getMessage时从队列头取一个message返回,同时在getMessage和putMessage之前都要求先获取锁资源。

实现虽然简单,但功能是绝对满足需求的,只是性能上可能稍稍有些不尽如人意。其最大的问题在频繁的锁竞争上。

对于如何减少锁竞争次数的优化方案,Ghost Cheng提出了一种。提供一个队列容器,里面有多个队列,每个队列都可固定存放一定数量的消息。网络IO线程要给逻辑线程投递消息时,会从队列容器中取一个空队列来使用,直到将该队列填满后再放回容器中换另一个空队列。而逻辑线程取消息时是从队列容器中取一个有消息的队列来读取,处理完后清空队列再放回到容器中。

这样便使得只有在对队列容器进行操作时才需要加锁,而IO线程和逻辑线程在操作自己当前使用的队列时都不需要加锁,所以锁竞争的机会大大减少了。

这里为每个队列设了个最大消息数,看来好像是打算只有当IO线程写满队列时才会将其放回到容器中换另一个队列。那这样有时也会出现IO线程未写满一个队列,而逻辑线程又没有数据可处理的情况,特别是当数据量很少时可能会很容易出现。Ghost Cheng在他的描述中没有讲到如何解决这种问题,但我们可以先来看看另一个方案。

这个方案与上一个方案基本类似,只是不再提供队列容器,因为在这个方案中只使用了两个队列,arthur在他的一封邮件中描述了这个方案的实现及部分代码。两个队列,一个给逻辑线程读,一个给IO线程用来写,当逻辑线程读完队列后会将自己的队列与IO线程的队列相调换。所以,这种方案下加锁的次数会比较多一些,IO线程每次写队列时都要加锁,逻辑线程在调换队列时也需要加锁,但逻辑线程在读队列时是不需要加锁的。

虽然看起来锁的调用次数是比前一种方案要多很多,但实际上大部分锁调用都是不会引起阻塞的,只有在逻辑线程调换队列的那一瞬间可能会使得某个线程阻塞一下。另外对于锁调用过程本身来说,其开销是完全可以忽略的,我们所不能忍受的仅仅是因为锁调用而引起的阻塞而已。  

时间: 2024-10-21 12:22:12

服务器设计笔记(3)-----消息队列的相关文章

服务器设计笔记(1)-----消息的封装

    消息的封装方式有多中,比如xml,json等,但是我依然觉得使用拼数据的方式最简单,也最节省带宽.比如我们处理一个逻辑就可以这样处理了:     int cast_net(MessageBlock &mb)     {         int  area_id,lvl;         mv >> area >> lvl;         //逻辑处理         //....         MessageBlock re_mb;         re_mb

服务器设计笔记(4)-----客户端通信模块

   整个底层通信模块修改过很多次,因为首次使用epoll,在其中遇见了很多问题,最终设计成下面的方式:        1: 对于epoll中EPOLLOUT事件的使用,因为频繁的调用send()函数,系统会在内核模式和用户模式之间切换太多消耗太大,所以最终启用了定时器模式,比如以50ms为间隔,定时的遍历所有的ClientPlayer 去发送 OutStream 里面的数据.    2: 最开始使用玩家的id 对应每个玩家的ClientPlayer的,在玩家没有进入的时候 使用玩家的账号名:

服务器设计笔记(1)-----定时器的实现(C++)

很久之前听著名页游服务器主程讲座时,讲到过定时器的实现,基本思路如下(易语言)        while(true)        {              对定时器进行排序.              for(遍历定时器)              {                    if 如果定时器到:                           callback;                    else                            bre

服务器设计笔记(2)-----定时器的实现(C++)

   很久之前听著名页游服务器主程讲座时,讲到过定时器的实现,基本思路如下(易语言)        while(true)        {              对定时器进行排序.              for(遍历定时器)              {                    if 如果定时器到:                           callback;                    else                           

服务器设计笔记(5)-----守护进程

守护进程的定义:        通常说的Daemon进程,是Linux中的后台服务进程.它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件.这事百科对此的解释,在游戏服务器里面也有守护进程,特点大概也类似. 游戏守护进程的定义:        游戏服务器里面的守护进程大概完成的职责有这些.1,负责多个进程的启动:2,在进程挂掉的时候负责启动相应的进程:3,可以查询程序运行的情况等等. 游戏守护进程的原理:       设计守护进程为父进程,验证进程,聊天

服务器设计笔记(5)-----分享几个线程安全的容器

    首先是 队列 PipeList    /* * PipeList.h * * Created on: Aug 28, 2012 * Author: archy_yu */ #ifndef PIPELIST_H_ #define PIPELIST_H_ #include <list> #include "Mutex.h" #include "GameUtil.h" template <class CObject> class PipeL

当设计消息队列时我们关心什么

应用消息队列可以对系统进行解耦,流量削峰,在分布式系统设计中,消息队列是重要的组件之一. 在开发中应用过ActiveMQ,kafka等mq,不过对消息队列背后的实现原理关注不多,其实了解消息队列背后的实现特别重要, 比如对一致性等实现的关注,可以帮助我们在开发中避免踩坑,规避问题的出现.这篇文章简单探讨下当设计和实现一个消息队列时,我们需要关心哪些地方.   消息队列功能和特性 一个传统意义上的消息队列,需要支持消息的发送,接受和消息暂存的功能. 在实际应用中,对消息队列的要求远不止于此,在不同

消息队列在VB.NET数据库开发中的应用

数据|数据库 我们先简单的了解一下什么是消息队列(MSMQ)?消息队列是 Windows 2000(NT也有MSMQ,WIN95/98/me/xp不含消息队列服务但是支持客户端的运行)操作系统中通讯的基础,也是用于创建分布式.松散连接通讯应用程序的工具.这些应用程序可以通过不同种类的网络进行通讯,也可以与脱机的计算机通讯.消息队列分为用户创建队列和系统队列,用户队列分为: · "公共队列"在整个可传递消息的"消息队列"网络中复制并传输,并且有可能由网络连接的所有站点

enode框架入门:消息队列的设计思路

上一篇文章,简单介绍了enode框架内部的整体实现思路,用到了staged event-driven architecture的思 想.通过前一篇文章,我们知道了enode内部有两种队列:command queue.event queue:用户发送的command 会进入command queue排队,domain model产生的domain event会进入event queue,然后等待被dispatch到所 有的event handlers.本文介绍一下enode框架中这两种消息队列到底