Introduction
It was almost one year ago that I finished the last chapter. One year is kind of too long to remember everything, I have to spend a couple of hours to review what I have done one year ago. The good thing is I still remember how to bring up each application and connect them together, the better thing is they are still working great. The best thing is I can go on writing the article.
In the last chapter, we have successfully abstracted WCF service out of business logic. Based on what we have done, I’m going to create our service bus. Let's go back to the FuctionService
and MessageService
. FunctionService
is responsible for providing service to client call. MessageService
is responsible for subscripting message to clients.
For example, there are HubBeijing Service managing Beijing product inventory and HubChina Service managing China product inventory. They open FunctionService
and MessageService
respectively. And they manage their own inventory respectively. I put a simple rule that each Hub will send out Out of Stock message if client requests a product number that is more than product inventory. Let’s assume these two systems are independent. Beijing is a part of China, when Beijing hub is out of stock, China Hub will provide the number requested from client. Since Beijing hub and China hub are independent, they will not communicate directly, we need a third party to connect them together. This third party is what we said Service Bus. We have abstracted service out of business logic, now we are going to abstract the Service Bus out of business logic to be an independent component.
Background
Please check How to abstract a WCF Service out of business logic and How to implement subscription based on WCF to get more context.
Using the Code
ESB component is actually a client connecting different services and accept different subscriptions. ESB component has the ability to connect many service providers. It is able to call different service providers’ methods, and it is also able to listen to different service providers’ subscriptions.
BusEngine.RegisterNewService("Beijing Hub", "net.tcp://localhost:8886/Inventory");
BusEngine.RegisterNewService("China Hub", "net.tcp://localhost:8887/Inventory");
BusEngine.RegisterNewService
BusEngine.Disconnected += new EventHandler(BusEngine_Disconnected);
BusEngine.HeartBeatMessage += new EventHandler(BusEngine_HeartBeatMessage);
BusEngine.MessageDelivered += new EventHandler<trilobita.saot.object.message>
(BusEngine_MessageDelivered);
public class Message: EventArgs
{
#region Properties
[DataMember]
public string Title { get; set; }
[DataMember]
public string Content { get; set; }
[DataMember]
public string Context { get; set; }
[DataMember]
public DateTime Createtime { get; set; }
[DataMember]
public string PrimaryID { get; set; }
[DataMember]
public string Sender { get; set; }
[DataMember]
public string To { get; set; }
[DataMember]
public string CC { get; set; }
[DataMember]
public DateTime ExpiredTime { get; set; }
[DataMember]
public string Attachment { get; set; }
[DataMember]
public string FunctionCode { get; set; }
[DataMember]
public string ServiceName { get; set; }
#endregion
}
We still use the Hub example. ESB component connects HubBeijing service as well as HubChina service. When Beijing client requests a product more than HubBeijing inventory, HubBeijing will subscribe a message to all registers. ESB component will get the message as well and bring up MessageEvent
. Now, we need HubChina to provide the requested product. Therefore, we have an external application to capture the MessageEvent
from ESB component, and write relevant logic in to invoke HubChina service to provide the product. See the below diagram:
Step 0: Start Service [project: SAOTEngine]
ServiceController.AddKnowType(typeof(Inventory));
ServiceController.AddKnowType(typeof(Inventory[]));
ServiceController.ServiceOpenedEvent +=
new EventHandler(ServiceController_ServiceOpenedEvent);
ServiceController.ServiceFaultEvent +=
new EventHandler(ServiceController_ServiceFaultEvent);
ServiceController.OpenFunctionService<serviceadapter />(int.Parse(this.txtPort.Text), this.txtName.Text);
ServiceController.OpenMessageService(int.Parse(this.txtPort.Text), this.txtName.Text);
Step 0: Client connect service [Project:SAOTEndPoint]
FunctionServiceConnector.AddKnownType(typeof(SAOT.Inventory));
FunctionServiceConnector.AddKnownType(typeof(SAOT.Inventory[]));
this.MyService = new SAOTServiceStructre<istandardservice>
("myservice",this.txtServer.Text);
MessageDistributor messager = this.MyService.MessageService;
messager.HeartBeatFromServer += new EventHandler(Instance_HeartBeatFromServer);
messager.ServerDisconnectedEvent += new EventHandler(messager_ServerDisconnectedEvent);
messager.MessageDelivered += new EventHandler<trilobita.saot.object.message>
(messager_MessageDelivered);
Step 0: Service Bus connect Beijing hub as well as China hub [Project: ServiceBusMonitor, SAOTServiceBus]
FunctionServiceConnector.AddKnownType(typeof(SAOT.Inventory));
FunctionServiceConnector.AddKnownType(typeof(SAOT.Inventory[]));
BusEngine.RegisterNewService("Beijing Hub", "net.tcp://localhost:8886/Inventory");
BusEngine.RegisterNewService("China Hub", "net.tcp://localhost:8887/Inventory");
BusEngine.Disconnected += new EventHandler(BusEngine_Disconnected);
BusEngine.HeartBeatMessage += new EventHandler(BusEngine_HeartBeatMessage);
BusEngine.MessageDelivered += new EventHandler
<trilobita.saot.object.message>(BusEngine_MessageDelivered);
Step 1: Beijing Client Request New Product [Project: InventoryClient]
this.GetService().Inovke("ChangeInventory", this.txtUser.Text, inventory.ProductID,
inventory.CurrentInventory-inv.CurrentInventory);
object obj = typeof(T).InvokeMember(methodname, BindingFlags.Static |
BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.InvokeMethod,
null, null, args);
return obj;
Step 2: Beijing Hub Service subscribe the inventory is not enough [Project: InventoryServer]
public bool ChangeInventory(string user, int number)
{
int restnumber = this.CurrentInventory + number;
if (restnumber > 0)
{
this.CurrentInventory += number;
if (InventoryChangedEvent != null)
InventoryChangedEvent(null, new InventoryArgs
{ Inventory = this, ChangedNumber = number, Operator=user });
return true;
}
else
{
if (OutofStockEvent != null)
OutofStockEvent(null, new InventoryArgs
{ Inventory = this, ChangedNumber = number, Operator = user });
return false;
}
}
Inventory.InventoryChangedEvent +=
new EventHandler<inventoryargs>(Inventory_InventoryChangedEvent);
static void Inventory_OutofStockEvent(object sender, InventoryArgs e)
{
Trilobita.SAOT.Object.Message message = new Trilobita.SAOT.Object.Message();
message.Sender = e.Operator;
message.PrimaryID = e.Inventory.ProductID.ToString();
message.Title = e.ChangedNumber.ToString();
Trilobita.SAOT.Service.MessageBox box =
new Trilobita.SAOT.Service.MessageBox("OutofStock");
box.MessageEnable = true;
box.EmailEnable = false;
box.DatabaseEnable = false;
box.SendMessage(message);
}
</inventoryargs>
Step 3: Service Bus request inventory from China hub [Project: ServiceBusMonitor]
this.ChinaFunction.Inovke("ChangeInventory",e.Sender,productid,changednumber);
Step 4: ... I didn't really implement it. By default, china hub will fulfill the request.
How to Demonstrate the Application
- Double click SAOT.exe, confirm the
ServicePort
textbox value is 8886, Service Name is Inventory. Click Confirm to open the service, a message will display on the form if the service is successfully opened. SAOT Service is for remote call, MessageSender
is for subscription. This service simulates Beijing Hub. - Repeat step 1, only change the
ServicePort
value to 8887. This service simulates China Hub. - Double click ServiceBusMonitor.exe, make sure the list services are connected. This is ESB application.
- Double click ClientTest.exe and click the confirm button if you exactly followed the upper steps. The light on top-left corner will flash if the connection is good.
- In
ClientTest
form, input one product name and default inventory, like product name: computer, inventory:50. Switch to first SAOT.exe (Beijing Hub), you will see computer with inventory 50. Switch to the second SAOT.exe (China Hub), you will see computer with inventory 500, because I put one logic in ESB application, if there is a new product from Beijing Hub, China Hub will prepare 10 times inventory. Here is the point, the first SAOT.exe (Beijing Hub) doesn’t connect the second SAOT.exe (China Hub), and ClientTest.exe only connects the first SAOT.exe (Beijing Hub). New product inventory is started from ClientTest
. Why the second SAOT.exe (China hub) can add 10 times inventory is because ServiceBusMonitor
detected the new product inventory message from Beijing hub and added the China hub inventory automatically. - In
ClientTest
form, change computer inventory from 50 to 40 (to put the demonstration simple, I didn’t pup up a form to request the number of product). It equals to we request 10 computers. Switch to Beijing hub, you will see the product inventory changed to 40. - In
ClientTest
form, change computer inventory from 40 to -10 (sorry, I just want to take 50 computers). Switch to Beijing hub, the number didn’t change. Switch to China hub, the number is 450. The logic here is, when client requests 50 computer, Beijing hub will send out OutOfStock
message, ServicebusMonitor
gets the message and transfers 50 computers to Beijing hub to keep Beijing hub inventory on a safe level.