消息转发的应用场景在现实中的应用非常普遍,我们常用的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. MI & CI

    目前,很多特征选择文献主要是依据对共信息的直观认识使用它,即:正值表示表型的存在使特征间依赖程度增加,是特征间存在相互作用的证据:负值表示表型的存在使特征间冗余性增加:零表示特征是相互独立的,或者说, ...

  2. Delphi 常用函数记录

    //判断是否是数字 function IsNumeric(sDestStr: string): Boolean; //简写多余汉字 function SimplifyWord(sWord: strin ...

  3. Java 原型模式

    http://www.cnblogs.com/itTeacher/archive/2012/12/02/2797857.html http://www.cnblogs.com/java-my-life ...

  4. HDU5058

    So easy Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Sub ...

  5. 2017年Unity开发环境与插件配置安装(总体介绍)

    最近(2017年初)有朋友问,Unity客户端开发如何在机器配置一般的情况下,配置更高效的开发环境,进一步加快开发进度. 推荐如下: Win10(或者Win8)+Unity5.5.1版本(2017年2 ...

  6. [html5]学习笔记一 新增的非主体结构元素

    html新增加的非主体结构元素,主要是用来表示附加信息的,包括header,footer,hgroup,address元素. 1.header元素 header元素是一种具有引导和导航作用的结构元素, ...

  7. TimSort算法分析

    Timsort是一种混合稳定的排序算法,采用归并排序混合插入排序的设计,在多种真实数据上表现良好. 它基于一个简单的事实,实际中大部分数据都是部分有序(升序或降序)的. 它于2002年由Tim Pet ...

  8. Import Statements 导入语句

    Syntax of an Import Statement 导入语句的语法 An import statement allows clients to tell the engine which mo ...

  9. setObject:forKey和setValue:forKey的区别

    setObject:forKey: 是NSMutableDictionary类的方法                              key参数类型可以是任意类型对象             ...

  10. 安卓UDP通信2

    服务器实现一发一收 服务器代码: import java.net.*; import java.io.*; public class udpRecv2 { /* * 创建UDP传输的接收端 * 1.建 ...