消息转发的应用场景在现实中的应用非常普遍,我们常用的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. 多因子降维法(MDR,multifactor dimensionality reduction)

    多因子降维法(MDR,Multifactor Dimensionality Reduction ) MDR是近年统计学中发展起来的一种新的分析方法.其中,“因子” 即交互作用研究中的变量,“维” 是指 ...

  2. 浅谈Java分页技术

    话不多言.我们要实现java分页技术,我们首先就需要定义四个变量,他们是: int  pageSize;//每页显示多少条记录 int pageNow;//希望现实第几页 int pageCount; ...

  3. delphi 字符串查找替换函数 转

    1.       提取字符串中指定子字符串前的字符串 Function Before( Src:string ; S:string ): string ; Var   F: Word ; begin ...

  4. swift中标签的使用

    1,标签的创建 1 2 3 4 5 6 7 8 9 10 import UIKit class ViewController: UIViewController {     override func ...

  5. Linux笔记(十) - 权限管理

    (1)ACL权限1.查看分区ACL权限是否开启:dumpe2fs -h /dev/sda3-h 仅显示超级块中信息,而不显示磁盘块组的详细信息2.临时开启分区ACL权限:mount -o remoun ...

  6. linux下安装TensorFlow(centos)

    一.python安装 centos自带python2.7.5,这一步可以省略掉. 二.python-pip pip--python index package,累世linux的yum,安装管理pyth ...

  7. 如何让Docker容器随宿主机的启动而自动启动

    使用Docker容器部署服务时,不仅需要让服务随Docker容器的启动而启动,还需要让Docker容器随宿主机的启动而自动启动,为此Docker提供了Docker run的restart参数 #doc ...

  8. 锁 和 CopyOnWrite的实现

    1.普通锁 只有lock功能, Java实现:ReentrantLock lock = new ReentrantLock(); lock和unlock: lock.lock(); lock.unlo ...

  9. 新生命组件XAgent使用心得

    1.简单介绍 XAgent为大石头带领下的新生命团队自己开发的一个.Net下的常用的Windows服务管理组件利器,通过在控制台中简单的输入1,2,3,4,5等数字可以实现一步安装.卸载Windows ...

  10. .Net程序员学用Oracle系列(13):合并语句(MERGE)

    - 1.[**语法说明**](#segment1) - 1.1.[UPDATE 和 INSERT 可以只出现一个](#point11) - 1.2.[UPDATE 后面还可以再跟 WHERE](#po ...