消息转发的应用场景在现实中的应用非常普遍,我们常用的IM工具也是其中之一;现有很多云平台也提供了这种基础服务,可以让APP更容易集成相关功能而不必投入相应的开发成本。对于实现这样一个简单功能并不复杂,对于现有的技术来说用.net提个通讯服务器支持几十W用户相信也不是件困难的事情;但如果考虑可用性和更大规模那就需要下点功夫,并且对相关技术有深入的了解才能实现了。而在这里主要讲解一下如何通过SmartRoute来实现一个大规模的消息转发集群的基础服务。

说到集群那肯定由N个服务组成的一组应,那做一个消息转发集群的基础服务需要那些服务节点呢?分析一下主要包括两大块:注册中心和消息网关;网关用于和应用对接,而注册中心则是明确应用所在位置。为了达到更好的可用性和更大规模支撑注册中心和网关都是N-N的关系。

看到这样一个图估计会把很不了解这方面的朋友会卡住,这样一个东西实现会很复杂吧!其实在SmartRoute基础之上实现这样这样一个集群服务并不困难,不过对于消息交互原理和设计还是需要了解一下。接下来讲解一下如何用SmartRoute实现相应注册中心和网关服务。

注册中心

注册中心的作用很简单就是保存应用标识所在位置,当网关需要转发消息的时候告诉网关这个应用标识在那个位置上。除了这一功能外当然还要考虑可用性,主要包括多中心发现和注册信息现步等;同样网关也具行指向多台中心的负载能力。

	public interface ICenter : IDisposable
{ String ID { get; } INode Node { get; set; } IUserService UserService { get; set; } void Open(); }

中心的接口定义很简单,主要都是内部针对SmartRoute的INode进行相关消息操作。

		public void Open()
{
mCenterSubscriber = Node.Register<EventSubscriber>(ID);
mCenterSubscriber.Register<Protocol.SyncUsers>(OnSyncUsers);
mCenterSubscriber.Register<Protocol.CenterStarted>(OnOtherCenterStarted);
mCenterSubscriber.Register<Protocol.Register>(OnSyncUser);
mCenterSubscriber.Register<Protocol.UnRegister>(OnSyncUnRegister);
Node.SubscriberRegisted += OnSubscriberRegisted;
mStartServiceTimer = new System.Threading.Timer(OnOpen, null, 5000, 5000);
Node.Loger.Process(LogType.INFO, "search other center...");
} //处理用户上线所在网关信息
private void OnReceiveUsersInfo(Message msg, Protocol.GetUsersInfo e)
{
string[] users = e.Receiver.Split(';');
Protocol.GetUserInfoResponse response = new Protocol.GetUserInfoResponse();
response.RequestID = e.RequestID;
Protocol.OperationStatus status = new Protocol.OperationStatus();
foreach (string user in users)
{
Protocol.UserInfo info = UserService.GetUserInfo(user, status);
if (info != null)
response.Items.Add(info);
}
msg.Reply(response);
}
//网关用户下线
private void OnUserUnregister(Message msg, Protocol.UnRegister e)
{
Protocol.OperationStatus status = new Protocol.OperationStatus();
UserService.Remove(e.Name, status);
msg.Reply(status);
Node.Loger.Process(LogType.INFO, "{0} user unregister", e.Name);
//同步到其他中心节点
if (mHasOtherCenter)
mCenterSubscriber.Publish(CENTER_OTHER_TAG, e, ReceiveMode.Regex);
}
//网关用户上线
private void OnUserRegister(Message msg, Protocol.Register e)
{
Protocol.OperationStatus status = new Protocol.OperationStatus();
UserService.Register(new Protocol.UserInfo() { Name = e.Name, Gateway = e.GatewayID }, status);
msg.Reply(status);
Node.Loger.Process(LogType.INFO, "{0} user register from {1}", e.Name, e.GatewayID);
//同步到其他中心节点
if (mHasOtherCenter)
mCenterSubscriber.Publish(CENTER_OTHER_TAG, e, ReceiveMode.Regex);
} //同步下线
private void OnSyncUnRegister(Message msg, Protocol.UnRegister e)
{
Protocol.OperationStatus status = new Protocol.OperationStatus();
UserService.Remove(e.Name, status);
Node.Loger.Process(LogType.INFO, "{0} user unregister", e.Name);
}
//同步上线
private void OnSyncUser(Message msg, Protocol.Register e)
{
Protocol.OperationStatus status = new Protocol.OperationStatus();
UserService.Register(new Protocol.UserInfo() { Name = e.Name, Gateway = e.GatewayID }, status);
Node.Loger.Process(LogType.INFO, "{0} user register from {1}", e.Name, e.GatewayID);
} //同步其他中心上线信息
private void OnSyncUsers(Message msg, Protocol.SyncUsers e)
{
Node.Loger.Process(LogType.INFO, "sync user info to local!");
Protocol.OperationStatus status = new Protocol.OperationStatus();
foreach (Protocol.UserInfo item in e.Items)
{
UserService.Register(item, status);
}
} private void OnSubscriberRegisted(INode node, ISubscriber subscriber)
{
//发现其他中心服务,向服务发起同步用户请求
if (subscriber.Name.IndexOf(CENTER_TAG) == 0 && subscriber.Name != ID)
{
mHasOtherCenter = true;
mReadyToStart = false;
Node.Loger.Process(LogType.INFO, "find {0} center", subscriber.Name);
Protocol.CenterStarted started = new Protocol.CenterStarted();
started.Name = ID;
mCenterSubscriber.Publish(subscriber.Name, started);
Node.Loger.Process(LogType.INFO, "request sync user info ....");
}
} public INode Node
{
get; set;
}

实现并不复杂,主要是开启相关订阅并注册消息处理方法即可,主要针对注册,同步和获取用户所在网关信息。

网关

网关的作用主要是接收消息,从注册中心获取用户标识对应的网关并把消息推送过去;所以功能也并不复杂主要也是针对INode的操作。

	public interface IGateway : IDisposable
{
INode Node { get; set; } Protocol.OperationStatus Register(UserToken userToken); Protocol.OperationStatus UnRegister(string username); void SendMessage(string receivers, string sender, object message); void Open();
}

功能比较简单用户标识注册和注销功能,还加上一个消息推送方法即可。

public OperationStatus Register(UserToken userToken)
{
OperationStatus result;
Register register = new Register();
register.Name = userToken.Name;
register.GatewayID = Node.DefaultEventSubscriber.Name;
result = Node.DefaultSwitchSubscriber.SyncToService<Protocol.OperationStatus>(Center.USER_SERVICE_TAG, register);
mUserActions[userToken.Name] = userToken;
return result;
} public void SendMessage(string receivers, string sender, object message)
{
MessageQueue.MessageItem item = new MessageQueue.MessageItem();
item.ID = GetRequestID();
item.Receives = receivers;
item.Sender = sender;
item.Data = message;
mMsgQueue.Push(item);
GetUsersInfo getinfo = new GetUsersInfo();
getinfo.RequestID = item.ID;
getinfo.Receiver = receivers;
Node.DefaultSwitchSubscriber.ToService(Center.USER_SERVICE_TAG, getinfo);
} public void Dispose()
{
if (mMsgQueue != null)
mMsgQueue.Dispose();
} public void Open()
{
mMsgQueue = new MessageQueue(this, 2);
mMsgQueue.Open();
Node.DefaultSwitchSubscriber.DefaultEventSubscriber.Register<GetUserInfoResponse>(OnGetUserInfoRequest);
Node.DefaultEventSubscriber.Register<UserMessage>(OnUserMessage);
} public OperationStatus UnRegister(string username)
{
UnRegister unregister = new UnRegister();
unregister.Name = username;
UserToken token = null;
mUserActions.TryRemove(username, out token);
return Node.DefaultSwitchSubscriber.SyncToService<OperationStatus>(Center.USER_SERVICE_TAG, unregister);
}

中心启动

由于基于SmartRoute的设计,所以中心的启动并不需要进行其他配置,直接开启动行即可;对于多节点的中心怎办?如果有需要多启一个实例即可达到多中心负载能力。

	public class Program
{
public static void Main(string[] args)
{
INode node = NodeFactory.Default;
node.Loger.Type = LogType.ALL;
node.AddLogHandler(new SmartRoute.ConsoleLogHandler(LogType.ALL));
node.Open();
MRC.MCRFactory.Center.Open();
System.Threading.Thread.Sleep(-1);
}
}

网关应用

网关的启动和中心一样,不过需要根据实际需要发起用户标识注册,注册后就可以向集群中的任何标识发送消息。

    public class Program
{
public static void Main(string[] args)
{
INode node = NodeFactory.Default;
node.Loger.Type = LogType.ALL;
node.AddLogHandler(new SmartRoute.ConsoleLogHandler(LogType.ALL));
node.Open();
MRC.MCRFactory.Gateway.Open();
System.Threading.ThreadPool.QueueUserWorkItem(OnTest);
System.Threading.Thread.Sleep(-1);
} private static void OnTest(object state)
{
System.Threading.Thread.Sleep(10000);
UserToken token = new UserToken("ken");
token.Register();
token.Receive = OnUserReceive;
} private static void OnUserReceive(UserToken token, Protocol.UserMessage e)
{
Console.WriteLine("receive message from {0} {1}", e.Sender, e.Data);
}
}

构建相应标识的UserToken注册到网关,网关会自动把标识同步到中心;然后定义UserToken相应的消息接收方法即可处理接收的消息。实际应用中可以继承UserToken并挂相应的客户端连接然后当接收消息做相应的网络转发就可以达到用户和用户间的通讯。

由一这样功能并不复杂所以已经封装起方便扩展应用,具体项目地址:https://github.com/IKende/SmartRoute.MRC

SmartRoute之大规模消息转发集群实现的更多相关文章

  1. 在Centos 7上安装配置 Apche Kafka 分布式消息系统集群

    Apache Kafka是一种颇受欢迎的分布式消息代理系统,旨在有效地处理大量的实时数据.Kafka集群不仅具有高度可扩展性和容错性,而且与其他消息代理(如ActiveMQ和RabbitMQ)相比,还 ...

  2. 使用ARM模板在Azure中国大规模部署DCOS集群

    容器技术是目前非常流行的技术,尤其是在以Docker作为容器引擎的推动下,让容器的轻量级,可移植,自包含,隔离性等的上了一个新的台阶,目前谈及Dev/Ops,CI/CD很少能够绕过Docker的. A ...

  3. 消息队列集群kafka安装配置

    1. 下载wget http://mirror.rise.ph/apache/kafka/0.11.0.0/kafka_2.12-0.11.0.0.tgz2. 安装tar xf kafka_2.12- ...

  4. ActiveMQ消息队列集群的搭建

    1.准备activemq apache-activemq-5.12.0-bin.tar 2.解压文件 3.并将文件cp一份命名为activemq1 进入conf文件进行修改 修改属性为brokerNa ...

  5. 大规模Elasticsearch集群管理心得

    转载:http://elasticsearch.cn/article/110 ElasticSearch目前在互联网公司主要用于两种应用场景,其一是用于构建业务的搜索功能模块且多是垂直领域的搜索,数据 ...

  6. 用更云原生的方式做诊断|大规模 K8s 集群诊断利器深度解析

    背景 通常而言,集群的稳定性决定了一个平台的服务质量以及对外口碑,当一个平台管理了相当规模数量的 Kubernetes 集群之后,在稳定性这件事上也许会"稍显被动". 我们可能经常 ...

  7. vivo大规模 Kubernetes 集群自动化运维实践

    作者:vivo 互联网服务器团队-Zhang Rong 一.背景 随着vivo业务迁移到K8s的增长,我们需要将K8s部署到多个数据中心.如何高效.可靠的在数据中心管理多个大规模的K8s集群是我们面临 ...

  8. Karmada大规模测试报告发布:突破100倍集群规模

    摘要:在本文中,我们将介绍用于测试的相关指标,如何进行大规模测试,以及我们如何实现大规模的集群接入. 本文分享自华为云社区<突破100倍集群规模!Karmada大规模测试报告发布>,作者: ...

  9. Redis(十)集群:Redis Cluster

    一.数据分布 1.数据分布理论 2.Redis数据分区 Redis Cluser采用虚拟槽分区,所有的键根据哈希函数映射到0~16383整数槽内,计算公式:slot=CRC16(key)&16 ...

随机推荐

  1. Java经典案例之-“成绩等级划分”

    /** * 描述:在对话框中输入学习成绩,利用条件运算符的嵌套来完成此题:学习成绩>=90分的同学用A表示,60-89分之间的用B表示, * 60分以下的用C表示. * 分析:可以利用三目运算符 ...

  2. QT第二天学习

    回顾: qmake: qmake -project //生成*.pro文件 qmake //makefile make 注:if(qmake -v  >=5) then QT += widget ...

  3. 学习笔记: JavaScript/JQuery 的cookie操作

    转自:http://blog.csdn.net/barryhappy/archive/2011/04/27/6367994.aspx cookie是网页存储到用户硬盘上的一小段信息.最常见的作用是判断 ...

  4. HTML 布局

    网站布局 大多数网站会把内容安排到多个列中(就像杂志或报纸那样). 大多数网站可以使用 <div> 或者 <table> 元素来创建多列.CSS 用于对元素进行定位,或者为页面 ...

  5. pureMVC简单示例及其原理讲解三(View层)

    本篇说的是View层,即视图层,在本示例中包括两个部分:MXML文件,即可视控件:Mediator. 可视控件 可视控件由UserForm.mxml(图1)和UserList.mxml(图2)两个文件 ...

  6. C++实现具有基本功能的智能指针

    C++中的智能指针实际上是代理模式与RAII的结合. 自定义unique_ptr,主要是release()和reset().代码如下. #include <iostream> using ...

  7. 使用AngularJS的三个重要原因

    入门教程:http://www.ituring.com.cn/minibook/303 : http://angularjs.cn/tag/AngularJS 原因一:Google开发的框架 要知道开 ...

  8. CodeForces 512B(区间dp)

    D - Fox And Jumping Time Limit:2000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64 ...

  9. 简单的java Hadoop MapReduce程序(计算平均成绩)从打包到提交及运行

    [TOC] 简单的java Hadoop MapReduce程序(计算平均成绩)从打包到提交及运行 程序源码 import java.io.IOException; import java.util. ...

  10. [html5] 学习笔记- html拖放

    拖放是一种常见的特性,即抓取对象以后拖到另一个位置,在HTML5中,拖放是标准的一部分,任何元素都能够拖放. 1.html5拖放:(drag和drop)是HTML5标准的组成部分 拖动开始:ondra ...