SignalR step-by-step (3) simple cluster communication components

Source: Internet
Author: User

The previous article demonstrated the implementation of generic Hub. In June 17, Microsoft updated SignalR 2.1.0 and then brought a generic Hub, so you don't need to implement it on your own... (Why did you come with Microsoft one month earlier ...). However, it doesn't matter. SignalR is not a wildcard Hub. This article provides a Demo of the simple cluster communication component based on SignalR, which can be used for Distributed timing tasks.

Speaking of clusters, we naturally think of NLB, Cluster, HPC, and so on. NLB is subject to the number of members, and the Cluster uses the quantity to stack high availability. HPC is too complex. Based on the two-way asynchronous communication of SignalR, it can be used for Elastic Computing. The initial status consists of a computing task distribution node, a monitoring node, and a computing node. As the number of tasks in the task distribution queue increases, one execution node cannot consume the tasks to be executed in time. When a threshold is reached, a dynamic computing node is added to increase the computing throughput. Similarly, when the number of tasks in the queue is very low, a computing node is automatically removed to reduce resource consumption. Of course, in the case of large computing volumes, distribution nodes and queues should all be cluster-based and various computing node faults should also be considered. This is beyond the scope of this article, this article uses the initial state model to implement simple cluster communication components step by step.

Okay, you can't talk nonsense. The beginning of this article is.

Task distribution node

/// <Summary> /// cluster switch /// </summary> public class ClusterHub: hub <IClusterClient >{/// <summary> //// </summary> static ClusterHub () {aliveDictionary = new ConcurrentDictionary <string, Guid> ();} /// <summary> ///// </summary> /// <param name = "dispatcher"> </param> public ClusterHub (IDispatcher dispatcher) {this. dispatcher = dispatcher; db = OdbFactory. open (localDbFileName);} // <summar Y> /// local database file name /// </summary> const string localDbFileName = "ClusterStorage. dll "; // <summary> // monitor connection Id /// </summary> static string monitorConnectionId; /// <summary> /// scheduler /// </summary> IDispatcher dispatcher; /// <summary> /// online dictionary /// </summary> static ConcurrentDictionary <string, Guid> aliveDictionary; /// <summary> ///// </summary> static IOdb; // <summary> // complete the task // </summ Ary> // <param name = "jobResult"> </param> public void Finished (Contracts. messages. jobResultDto jobResult) {lock (db) {var members = db. asQueryable <MemberDo> (); var member = members. singleOrDefault (m => m. id = Guid. parse (jobResult. id); if (member! = Null) {member. UpdateStatisticsInfo (jobResult. ProcessedTime); db. Store (member); if (! String. isNullOrWhiteSpace (monitorConnectionId) {Clients. client (monitorConnectionId ). updateMemberStatisticsInfo (new Contracts. messages. memberStatisticsInfoDto () {Id = member. id. toString (), AverageProcessedTime = member. averageProcessedTime}) ;}} Clients. caller. runJob (dispatcher. getJobId ();} // <summary> // Add // </summary> void Join () {object ip = string. empty; var isMonitor = Context. R Equest. queryString ["ClientRole"] = "Monitor"; Context. request. environment. tryGetValue ("server. remoteIpAddress ", out ip); lock (db) {var members = db. asQueryable <MemberDo> (); var member = members. singleOrDefault (m => m. ip = ip. toString () & m. isMonitor = isMonitor); if (member! = Null) {member. memberStatusType = MemberStatusTypeEnum. connectioned;} else {member = new MemberDo (ip. toString (), isMonitor); if (isMonitor) {monitorConnectionId = Context. connectionId;} db. store (member); aliveDictionary. tryAdd (Context. connectionId, member. id); if (! IsMonitor) {if (! String. isNullOrWhiteSpace (monitorConnectionId) {Clients. client (monitorConnectionId ). memberJoin (member. id);} Clients. caller. getId (member. id. toString (); Clients. caller. runJob (dispatcher. getJobId () ;}}/// <summary >/// exit /// </summary> void Leave () {var id = Guid. empty; aliveDictionary. tryRemove (Context. connectionId, out id); lock (db) {var members = db. asQueryable <MemberDo> (); var member = Members. SingleOrDefault (m => m. Id = id); if (member! = Null) {member. MemberStatusType = MemberStatusTypeEnum. Disconnectioned; db. Store (member); if (member. IsMonitor) {monitorConnectionId = string. Empty;} else if (! String. isNullOrWhiteSpace (monitorConnectionId) {Clients. client (monitorConnectionId ). memberLeave (id) ;}}} public override Task OnConnected () {Console. writeLine (Context. connectionId + ": Connected"); Join (); return base. onConnected ();} public override Task OnDisconnected () {Console. writeLine (Context. connectionId + ": Disconnected"); Leave (); return base. onDisconnected ();} public override Task OnReconnected () {Console. writeLine (Context. connectionId + ": Reconnected"); return base. onReconnected ();}}

ClusterHub hosts the interaction, computing nodes, and monitoring of two client roles.

A lightweight C #-based engine-free object database is used to store client information.

First, reload the following:

OnConnected-when a client is connected, the Join method is executed.

OnDisconnected-when a client is offline, run the Leave method.

Then the private method:

Join-determine whether the client type is a computing node or a monitor based on QueryString. If the client type is a computing node, directly notify the monitor that a Member is added, and then obtain the task Id through IDispatcher, notifies the computing node to start executing the task.

Leave-Notification monitor when the computing node is offline.

Public method:

Finished-this method is called after the computing node completes the task. The Hub updates the computing statistics to the local storage and notifies the monitor to update the computing results.

Private variable:

IDispatcher-Task Scheduler interface, which is implemented by external components.

Computing node

Computing nodes have two actions:

GetId-get the node identity.

RunJob-execute the task.

/// <Summary> /// cluster client /// </summary> public class ClusterClient {// <summary> //// </summary> // /<param name = "jobProvider"> </param> public ClusterClient (IJobProvider jobProvider) {this. jobProvider = jobProvider; url = ConfigurationManager. appSettings ["HubAddress"]; var queryStrings = new Dictionary <string, string> (); queryStrings. add ("ClientRole", "Normal"); connection = new HubConnection (url, queryStrings); hubProxy = connection. createHubProxy (typeof (IClusterHub ). getCustomAttributes (typeof (DescriptionAttribute), false ). ofType <DescriptionAttribute> (). first (). description); InitClientEvents (); connection. start (). wait ();} string url; HubConnection connection; IHubProxy hubProxy; IJobProvider jobProvider; string id; // <summary> //// </summary> void InitClientEvents () {hubProxy. on ("GetId", (id) => GetId (id); hubProxy. on ("RunJob", (jobId) => RunJob (jobId ));} /// <summary> /// execute the task // </summary> /// <param name = "id"> </param> void GetId (string id) {this. id = id ;} /// <summary> /// execute the task /// </summary> /// <param name = "jobId"> </param> void RunJob (string jobId) {var startTime = DateTime. now; jobProvider. invoke (jobId); var stopTime = DateTime. now; hubProxy. invoke ("Finished", new JobResultDto () {Id = id, JobId = jobId, ProcessedTime = (stopTime-startTime ). totalMilliseconds });}}

The implementation of the client is very simple. The core is to provide interfaces for injection tasks through constructor, and the interfaces execute tasks through task IDs.

Monitor

/// <Reference path = "jquery-2.1.1.js"/> // <reference path = "jquery. signalR-2.1.0.js "/> (function ($) {var members = []; var methods = {reloadList: function () {var list =" "; $. each (members, function (I, n) {list + = "<li id = 'Member _" + n. id + "'> [" + n. id + "]: AverageProcessedTime" + n. averageProcessedTime + "Milliseconds </li>" ;}); then ('your members'{.html (list) ;}} var hubs = {clusterHub: $. co Nnection. clusterHub, init: function () {$. connection. hub. logging = true; $. connection. hub. url = 'HTTP: // 192.168.1.124: 10086/signalr'; $. connection. hub. qs = {"ClientRole": "Monitor"} $. connection. hub. start (). done (function () {}) ;}} var cluster ={ on: {updateMemberStatisticsInfo: function (data) {$. each (members, function (I, n) {if (n. id = data. id) {n. averageProcessedTime = data. averageP RocessedTime; return ;}}); methods. reloadList () ;}, memberJoin: function (id) {members. push ({"Id": id, "AverageProcessedTime": 0}); methods. reloadList () ;}, memberLeave: function (id) {members =$. grep (members, function (n) {return n. id! = Id}); methods. reloadList () ;}}$ (function () {hubs. clusterHub. client. updateMemberStatisticsInfo = cluster. on. updateMemberStatisticsInfo; hubs. clusterHub. client. memberJoin = cluster. on. memberJoin; hubs. clusterHub. client. memberLeave = cluster. on. memberLeave; hubs. init () ;}) ;}( jQuery );

<! DOCTYPE html> 

The monitor is implemented on the real-time Web platform, and three methods are registered in total.

Final Effect

 

After the Hub is started, start the monitor and then start the computing end on different machines. The figure shows two computing nodes, and the monitor also displays two nodes, after each node executes a JobId, the result is refreshed on the monitor.

Further consideration and Expansion

The simple cluster component is here. This article demonstrates an idea that can be expanded to the SignalR-Based Cluster component with high performance and high availability as described at the beginning of the article. You are welcome to discuss and make a discussion.

Reprinted please indicate the source: http://www.cnblogs.com/royding/p/3811169.html

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.