Hi, today I would like to share with you SimpleServiceBus solution. I created this because I want to combine my all previous experience in communication components and both Multi-Core and Multi-Threading knowledge. The main reason for making this solution was to create the same simple abstraction for communication In-Proc, In-Host, and In-Net in exactly the same way. SimpleServiceBus was build as a combination of the 3 flavors 5 simple generic components Requester<ReqMsg>, Requester<ReqMsg, ResMsg>, Receiver<ReqMsg>, Receiver<ReqMsg, ResMsg>, Responder<ReqMsg, ResMsg>. Those components are needed to communicate. Messages are generic attributes (ReqMsg and ResMsg) for those components that have to implement the IMessage interface. And that interface is empty to make the solution simpler. I think it is straightforward to use SimpleServiceBus, and I will show you that. I did bunch of tests and first I want to show you results of integration tests. Later I will share with you the results of the performance tests. After that, I will show you usage examples of this solution.
As you can see I tested all possible combinations of the communication, both In-Proc (inside the process) and In-Host (between processes inside the host). In fact, communication In-Host uses loopback IP addresses. Still, when the code uses any outside IP address, the same components are changed without touch and can communicate In-Net (between processes on different hosts). The important thing is that when you are using communication inside 1 process, you still get benefits because you have endless threads during the process run. All those threads use all CPU cores, so you can simply scale your solution and turn it into a pure Multi-Core application or Cloud backed service. OK, so let’s look at how fast my solution is? It is not my final word. This is just the beginning start point.
Now it is a time for sharing with you code samples. I did that as 2 demo classes with declarations of communication component elements and comments. I think you will like it. Use this code as a guide documentation. But remember that each communication component has to be in a separate class, depending on your solution design. You can make them static for the best performance, but it is not the obligatory way. I used 1 class for In-Proc communication examples and 1 for In-Host or In-Net communication examples. I used loopback IP addresses, but you can use them as external addresses for your network communication. Only the Requester kind of components must open listeners’ TCP ports. And all of that components need to be accessible from the outside. If you are building services in Cloud, remember to open Firewall access to them. So, here there are code examples for you.
namespace SimpleServiceBusSandbox { using System; using CodingByToDesign.SimpleServiceBus.Communication.Contracts; using CodingByToDesign.SimpleServiceBus.Communication; // This is the definition of your message protocol // and you need to put it in shared assembly for // all components that need to communicate. // Only requirement is to derive from IMessage, // the interface is empty. public class MyMessageReq : IMessage { // this is an example of entity content. public string Uri { get; set; } public DateTime TimeStampUtc { get; set; } public string HttpMethod { get; set; } } // This is an example response message with data. public class MyMessageRes : IMessage { // this is an example of entity content. public string Data { get; set; } } class SimpleServiceBusDemoClassInProc { // receiver in-proc private static IReceiver<MyMessageReq> receiver1 = new Receiver<MyMessageReq>{RecieveAction = ReceiveMessage}; // receiver in-proc receive method private static void ReceiveMessage(MyMessageReq message) { // do what you want with received messages here } // requester in-proc private static IRequester<MyMessageReq> requester1 = new Requester<MyMessageReq>(); // in-proc send/request method private static void SendMessageAsync1() { // sending example message to any Receiver<MyMessageReq> requester1.RequestAsync(new MyMessageReq { Uri = new Uri("https://iblog.isowa.io"), TimeStampUtc = DateTime.UtcNow, HttpMethod = "GET" }); } // receiver in-proc that can respond but does not have to private static IReceiver<MyMessageReq, MyMessageRes> receiver2 = new Receiver<MyMessageReq, MyMessageRes> { RecieveAction = ReceiveMessage2 }; // method that receives messages private static void ReceiveMessage2(TransactId id, MyMessageReq mesage) { // you can respond back, but you do not have to receiver2.ResponseAsync(id, new MyMessageRes { Data = "CodingByToDesign.NET" }); } // requester in-proc that can get responses private static IRequester<MyMessageReq, MyMessageRes> requester2 = new Requester<MyMessageReq, MyMessageRes> { ResponseAction = ReceiveMessageBack2 }; private static void ReceiveMessageBack2(MyMessageRes message) { // do what you want with received response messages here } // in-proc send/request method 2 private static void SendMessageAsync2() { // sending example message requester2.RequestAsync(new MyMessageReq { Uri = new Uri("https://iblog.isowa.io"), TimeStampUtc = DateTime.UtcNow, HttpMethod = "GET" }, // optional parameter if it is null ReceiveMessageBack2 will be used new Action<MyMessageRes>( msgRes => { /* use your response message here */ })); } // responder in-proc that has to respond always to messages private static IResponder<MyMessageReq, MyMessageRes> responder3 = new Responder<MyMessageReq, MyMessageRes> { ResponseFunc = RespondMessage }; // method that receives messages and needs to create a response message private static MyMessageRes RespondMessage(MyMessageReq message) { return new MyMessageRes { Data = "CodingByToDesign.NET" }; } // requester in-proc that can get responses private static IRequester<MyMessageReq, MyMessageRes> requester3 = new Requester<MyMessageReq, MyMessageRes> { ResponseAction = ReceiveMessageBack3 }; private static void ReceiveMessageBack3(MyMessageRes message) { // do what you want with received response messages here } // in-proc send/request method 3 private static void SendMessageAsync3() { // sending example message requester3.RequestSync(new MyMessageReq { Uri = new Uri("https://iblog.isowa.io"), TimeStampUtc = DateTime.UtcNow, HttpMethod = "GET" }, // optional parameter if it is null ReceiveMessageBack3 will be used new Action<MyMessageRes>( msgRes => { /* use your response message here */ })); } } class SimpleServiceBusDemoClassInHostOrInNet { // recevier in-host or in-net private static IReceiver<MyMessageReq> receiver1 = new Receiver<MyMessageReq>( netTcpRequesterAddress: "net.tcp://127.0.0.1:27271/Demo" ){ RecieveAction = ReceiveMessage }; // receiver in-host or in-net receive method private static void ReceiveMessage(MyMessageReq message) { // do what you want with received messages here } // requester in-host or in-net private static IRequester<MyMessageReq> requester1 = new Requester<MyMessageReq>( netTcpRequesterAddress: "net.tcp://127.0.0.1:27271/Demo" ); // receive in-host or in-net send/request method private static void SendMessageAsync1() { // sending example message to any Receiver<MyMessageReq> requester1.RequestAsync(new MyMessageReq { Uri = new Uri("https://iblog.isowa.io"), TimeStampUtc = DateTime.UtcNow, HttpMethod = "GET" }); } // receiver in-host or in-net that can respond but does not have to private static IReceiver<MyMessageReq, MyMessageRes> receiver2 = new Receiver<MyMessageReq, MyMessageRes>( netTcpRequesterAddress: "net.tcp://127.0.0.1:27272/Demo" ) { RecieveAction = ReceiveMessage2 }; // method that receives messages private static void ReceiveMessage2(TransactId id, MyMessageReq mesage) { // you can respond back, but you do not have to receiver2.ResponseAsync(id, new MyMessageRes { Data = "CodingByToDesign.NET" }); } // requester in-host or in-net that can get responses private static IRequester<MyMessageReq, MyMessageRes> requester2 = new Requester<MyMessageReq, MyMessageRes>( netTcpRequesterAddress: "net.tcp://127.0.0.1:27272/Demo" ) { ResponseAction = ReceiveMessageBack2 }; private static void ReceiveMessageBack2(MyMessageRes message) { // do what you want with received response messages here } // in-host or in-net send/request method 2 private static void SendMessageAsync2() { // sending example message requester2.RequestAsync(new MyMessageReq { Uri = new Uri("https://iblog.isowa.io"), TimeStampUtc = DateTime.UtcNow, HttpMethod = "GET" }, // optional parameter if it is null ReceiveMessageBack2 will be used new Action<MyMessageRes>( msgRes => { /* use your response message here */ })); } // responder in-host or in-net that has to respond always to messages private static IResponder<MyMessageReq, MyMessageRes> responder3 = new Responder<MyMessageReq, MyMessageRes>( netTcpRequesterAddress: "net.tcp://127.0.0.1:27273/Demo" ) { ResponseFunc = RespondMessage }; // method that receives messages and needs to create a response message private static MyMessageRes RespondMessage(MyMessageReq message) { return new MyMessageRes { Data = "CodingByToDesign.NET" }; } // requester in-host or in-net that can get responses private static IRequester<MyMessageReq, MyMessageRes> requester3 = new Requester<MyMessageReq, MyMessageRes>( netTcpRequesterAddress: "net.tcp://127.0.0.1:27273/Demo" ) { ResponseAction = ReceiveMessageBack3 }; private static void ReceiveMessageBack3(MyMessageRes message) { // do what you want with received response messages here } // in-host or in-net send/request method 3 private static void SendMessageAsync3() { // sending example message requester3.RequestSync(new MyMessageReq { Uri = new Uri("https://iblog.isowa.io"), TimeStampUtc = DateTime.UtcNow, HttpMethod = "GET" }, // optional parameter if it is null ReceiveMessageBack3 will be used new Action<MyMessageRes>( msgRes => { /* use your response message here */ })); } } }
At the end of this blog entry you can find solutions for download SimpleServiceBus Binary Assembly (3116 downloads) and SimpleServiceBus Source Code (3051 downloads). One more thing, I tested this solution on both .NET 4.5 for Windows and Mono for Mac OSX, and both work great. If you have a chance to test it on Mono for GNU/Linux, please let me know. SimpleServiceBus depends only on pure .NET 4.5 or Mono. When you decide to use SimpleServiceBus, you can easily separate any sub-component and test it by emulating a real environment around it. This gives an easy way to both non-functional and functional separation of System and/or product to easily deal with testing. Enjoy!
p ;).
I recompiled binary assembly for Any CPU platform. Sorry if you tried use this assembly on x64 platform. Now, should be fine, file for download is updated and optimized for speed.
Pingback: Happy Holidays at The End of 2014 @ coding by to design
Pingback: The Best of all for Software Architects @ coding by to design
Pingback: Simple Service Bus Training @ coding by to design