消息转发的应用场景在现实中的应用非常普遍,我们常用的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. Composer PHP依赖管理的新时代

    安装Composer Composer需要PHP 5.3.2+才能运行. $ curl -sS https://getcomposer.org/installer | php 这个命令会将compos ...

  2. Struts2拦截器介绍

    一.拦截器简介 Struts拦截器和Action的关系如图: 为了在使用拦截器时制定参数值,应通过<interceptor-ref -/>元素添加<param -/>子元素来为 ...

  3. 按钮(Buton)组价的功能和用法

    Button继承了TextView,它主要是在UI界面上生成一个按钮,该按钮可以供用户单机,当用户单击按钮时,按钮会触发一个onClick事件. 按钮使用起来比较容易,可以通过为按钮指定android ...

  4. Java策略模式(Strategy模式) 之体验

    <JAVA与模式>之策略模式 在阎宏博士的<JAVA与模式>一书中开头是这样描述策略(Strategy)模式的: 策略模式属于对象的行为模式.其用意是针对一组算法,将每一个算法 ...

  5. servlet中的8个Listener

    servlet中的8个Listener: Xml: <?xml version="1.0" encoding="UTF-8"?> <web-a ...

  6. HMX-Server C++ 分步式服务器大版本更新了(有源码)

    原文地址:http://www.cnblogs.com/hellohuang/p/6294763.html # HMX-ServerHMX-Server分步式服务器框架,主要分为网关.登录.世界.场景 ...

  7. <C++Primer>第四版 阅读笔记 第三部分 “类和数据抽象”

    类定义了数据成员和函数成员:数据成员用于存储与该类类型的对象相关联的状态:而函数成员则负责执行赋予数据意义的操作. 第12章 类 一个类可以包含若干公有的.私有的和受保护的部分:在public部分定义 ...

  8. jQuery + CSS3实现环形进度条

    实现原理 原理非常的简单,在这个方案中,最主要使用了CSS3的transform中的rotate和CSS3的clip两个属性.用他们来实现半圆和旋转效果. 半环的实现 先来看其结构. HTML < ...

  9. java_web总结(一)

    1.struts1ajax返回值 public ActionForward preChangeAccountPwd(ActionMapping mapping, ActionForm form, Ht ...

  10. oracle中的net manager 无法配置

    今天遇到配置oracle net manager时无法配置,点击服务命名下面没有节点,绿色加号也没有反应 后在网上查到是因为tnsnames文件中的有空格存在导致 文件路径:oracle/produc ...