MSF的名字是 Message Service Framework 的简称,由于目前框架主要功能在于处理即时(immediately)消息,所以iMSF就是 immediately Message Service Framework,中文名称:即时消息服务框架,它是PDF.NET框架的一部分。在后续的文章中,iMSF跟MSF是一个意思,或者你也可以给它取一个好听的中文名称:爱美XX :)

在前一篇, “一切都是消息”--MSF(消息服务框架)入门简介, 我们介绍了MSF基于异步通信,支持请求-响应通信模式和发布-订阅通信模式,并且介绍了如何获取MSF。今天,我们来看看如何使用MSF来做一个请求-响应通信模式的例子。

MSF封装了WCF,所以使用MSF不能像使用WCF那样直接在客户端添加服务引用,你需要手工编写客户端代理类,这样有一个好处就是代理类写的更简单,使用更灵活。我们可以看看网友写的这篇文章《不引用服务而使用WCF,手动编写客户端代理类 》,看看直接使用WCF是如何手动编写客户端代理类的。我对作者文中有一句话很认同:

--我们应当把WCF理解为一种通信技术,而不只是服务。

这正是MSF的设计理念!

回到MSF,我们来看看实现请求-响应通信模式的步骤。

一,编写iMSF服务类

在上一篇文中搭建好的MSF解决方案中,我们创建了一个名字为 TestService的项目,首先,添加Nuget 的MSF服务端引用,

Install-Package PDF.Net.MSF.Service

现在添加一个类 Service1,让它继承MSF的IService 接口。具体代码如下:

using PWMIS.EnterpriseFramework.Service.Runtime;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;namespace TestService
{public class Service1:IService{public void CompleteRequest(IServiceContext context){throw new NotImplementedException();}public bool IsUnSubscribe{get { throw new NotImplementedException(); }}public bool ProcessRequest(IServiceContext context){throw new NotImplementedException();}}
}

然后,将上面方法中的异常信息注释掉,并且添加一个 SayHello 方法,具体修改如下:

  public class Service1:IService{public string SayHello(string who){return string.Format("Hello {0} ,I am  MSF Server.", who);}public void CompleteRequest(IServiceContext context){//throw new NotImplementedException();
        }public bool IsUnSubscribe{//get { throw new NotImplementedException(); }get { //返回True ,表示当前服务不执行系统后续的服务方法的订阅处理过程,而是由用户自己输出结果数据return false; }}public bool ProcessRequest(IServiceContext context){//throw new NotImplementedException();return true;}}

可以看到,实现MSF服务类,不需要先定义一个WCF服务契约接口,也没用其它WCF的代码影子,不过有点类似 ASP.NET 的HTTP处理过程:

  1. 首先,每个MSF服务类都会执行 ProcessRequest 方法,它有一个IServiceContext 对象,通过它可以知道请求相关的上下文信息;
  2. 然后,会有一个 IsUnSubscribe 属性,表示本次请求是否是一个自定义的服务订阅而不再继续执行系统的服务订阅,虽然本次的示例是演示“请求-响应”的通信模式的,但是MSF本质上对此种通信模式还是通过“发布-订阅”通信模式实现的,也就是说,MSF的消息通信,始终是面向长连接的;
  3. 如果IsUnSubscribe 属性返回为False,紧接着,MSF会调用您真正的服务方法,比如这里的 SayHello 方法;
  4. 最后,你可以在 CompleteRequest 中执行一些本次服务处理的收尾工作。

二,编写iMSF客户端

我们在上一篇文中说的TestClient 项目中,来编写今天的MSF客户端代码,在原有代码基础上,做适当的修改。
MSF客户端调用可以分为多种方式:

2.1,使用服务请求的URI模式:

本质上,MSF的客户端请求服务端的时候,是将请求的服务信息转换成MSF固有的URI地址参数信息的,类似RESTfull WebAPI 的URI一样,本次请求的URI地址如下:

Service://Service1/SayHello/System.String=bluedoctor1

它表示我们请求的服务类名称是 Service1,请求的服务方法名称是 SayHello,有一个String类型的参数并且给它赋值为 bluedoctor 。

MSF请求服务的时候,服务方法的参数是不用区分参数名的,只跟参数的顺序,参数的类型和参数的数量有关系,这根我们使用委托方法调用一个实际的方法一样的方式。
相关调用代码如下:

  client.RequestService<string>("Service://Service1/SayHello/System.String=bluedoctor1",PWMIS.EnterpriseFramework.Common.DataType.Text,s =>{Console.WriteLine("1,Server Response:【{0}】", s);});

如果调用服务成功,将输出结果:

1,Server Response:【Hello bluedoctor1 ,I am is MSF Server.】

2.2,使用ServiceRequest 对象来封装服务请求信息

前面通过服务请求的URI模式虽然比较直观简洁,但使用对象来封装请求信息可能更可靠,对前面例子改写成 ServiceRequest 对象的代码如下:

            ServiceRequest request = new ServiceRequest();request.ServiceName = "Service1";request.MethodName = "SayHello";request.Parameters = new object[] { "bluedoctor23" };client.RequestService<string>(request,PWMIS.EnterpriseFramework.Common.DataType.Text,s =>{Console.WriteLine("2,Server Response:【{0}】", s);});

2.3,使用异步调用方法

前面两个调用示例,其实都是传入一个委托方法给RequestService 方法,然后服务端回调此委托方法的,而此委托方法的回调时机是不确定的,相对于调用线程它是异步执行的,所以我们称呼前面2种调用方式为“异步委托方法”。这种方式的一大缺点就是我们的代码中会有大量的难以阅读和调试的异步回调代码。.NET 4.0之后提供了Task对象它可以简化这种调用过程,使得代码写起来就跟同步调用代码一样。这种异步调用方式,MSF也提供了支持,使用服务请求的Async后缀的方法即可:

 string serverMsg= client.RequestServiceAsync<string>(request).Result;Console.WriteLine("3,Server Response:【{0}】", serverMsg);

调用 RequestServiceAsync 方法返回结果的 Result方法,能够同步阻塞调用结果,使得后续代码按照我们预期的顺序执行。

三、注册iMSF服务类

运行上面编写的服务端和客户端,调用并不成功,在服务端出现了下面的异常:

上面截图中显示的错误信息是 :“从注册的所有容器中没有找到符合当前类型的提供程序。”

这个错误信息会返回到客户端:

处理服务时错误:从注册的所有容器中没有找到符 合当前类型的提供程序。

这个错误提示我们没有注册我们的MSF服务类,因为MSF会通过IOC容器去寻找我们调用的服务类,所以需要注册下。
MSF采用了一个简单的IOC工具,它支持通过XML配置文件类注册我们自定义的MSF服务类。

在解决方案中,看到引用了MSF Host的主项目 MSFTest,nuget添加MSF Host的时候,已经添加了一个IOC配置文件:IOCConfig.xml

这个文件的使用,在MSF Host的配置文件 PdfNetEF.MessageServiceHost.exe.config 中做了配置:

<?xml version="1.0" encoding="utf-8"?>
<configuration><appSettings><add key="IOCConfigFile" value=".\IOCConfig.xml" /><add key="ServerIP" value="127.0.0.1" /><add key="ServerPort" value="8888" /></appSettings>... 其它配置内容略

IOCConfig.xml 文件已经配置了MSF必要的内容和一些示例的测试配置,具体内容为:

<?xml version="1.0" encoding="utf-8" ?>
<IOCConfig><!--IOC 依赖注入容器配置 ver 1.0 注:PDF.NET MSF Servic Host 使用PWMIS 消息服务框架,2011.12.7 创建--><GroupConfig><Group ID="1" ParentID="0" Name="ServiceRuntime" >分布式服务运行时</Group><Group ID="2" ParentID="0" Name="ServiceModel" >服务模型</Group><Group ID="3" ParentID="0" Name="TestService" >示例服务</Group></GroupConfig><SystemInterface><Add Name="IService" Interface="PWMIS.EnterpriseFramework.Service.IService" Assembly="PWMIS.EnterpriseFramework.Service.Runtime"/></SystemInterface><GroupSet><IOC Name="ServiceRuntime"><Add Key="CacheServer"  InterfaceName="IService"  FullClassName="PWMIS.EnterpriseFramework.Service.Runtime.CacheService" Assembly="PWMIS.EnterpriseFramework.Service.Runtime" /><Add Key="RegService"  InterfaceName="IService"  FullClassName="PWMIS.EnterpriseFramework.Service.Group.RegService" Assembly="PWMIS.EnterpriseFramework.Service.Group" /><Add Key="ManageService"  InterfaceName="IService"  FullClassName="TranstarAuction.Service.Runtime.ManageService" Assembly="TranstarAuctionServiceRuntime" /></IOC><IOC Name="TestService"><Add Key="Calculator" InterfaceName="IService" FullClassName="ServiceSample.TestCalculatorService" Assembly="ServiceSample" /><Add Key="TestTimeService" InterfaceName="IService" FullClassName="ServiceSample.TestTimeService" Assembly="ServiceSample" /><Add Key="AlarmClockService" InterfaceName="IService" FullClassName="ServiceSample.AlarmClockService" Assembly="ServiceSample" /><Add Key="Service1" InterfaceName="IService" FullClassName="TestService.Service1" Assembly="TestService" /></IOC><IOC Name="ServiceModel"><Add Key="TimeCount"  InterfaceName=""  FullClassName="Model.TimeCount" Assembly="Model" /><!-- 下面4个是消息服务框架必须的ServiceModel --><Add Key="ServiceIdentity"  InterfaceName=""  FullClassName="PWMIS.EnterpriseFramework.Service.Runtime.Principal.ServiceIdentity" Assembly="PWMIS.EnterpriseFramework.Service.Runtime" /><Add Key="CacheItemPolicy"  InterfaceName=""  FullClassName="System.Runtime.Caching.CacheItemPolicy" Assembly="System.Runtime.Caching, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" /><Add Key="ServiceRegModel"  InterfaceName=""  FullClassName="PWMIS.EnterpriseFramework.Service.Client.Model.ServiceRegModel" Assembly="PWMIS.EnterpriseFramework.Service.Client" /><Add Key="ServiceHostInfo"  InterfaceName=""  FullClassName="PWMIS.EnterpriseFramework.Service.Client.Model.ServiceHostInfo" Assembly="PWMIS.EnterpriseFramework.Service.Client" /></IOC></GroupSet>
</IOCConfig>

其中,Key=“Service1” 配置节,便是我们今天需要配置的Service1 服务类的内容。

四、返回复杂类型的服务方法

4.1,编写iMSF服务类

在前面的示中,服务类 Service1 的服务方法 SayHello 返回的是String 类型这样的简单类型,很多时候,我们需要服务方法返回结构复杂的自定义业务类型。在本次示例中,我们定义一个邮件消息类,我们新建一个C# 类库项目 TestDto,然后如下定义它:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace TestDto
{public class MailMessage{public string Sender { get; set; }public string Reply { get; set; }public string Message { get; set; }public DateTime RevoveryTime { get; set; }}
}

然后,回到TestService项目的Service1服务类,增加一个GetMailMessage 方法:

 public MailMessage GetMailMessage(string who){MailMessage mail = new MailMessage();mail.Reply = who;mail.Sender = "MSF Server";mail.Message = string.Format("Hello {0} ,I am  MSF Server.", who);mail.RevoveryTime = DateTime.Now;return mail;}

4.2,编写iMSF客户端代码

最后,我们在TestClient中添加调用此服务的客户端代码:

 client.RequestService<MailMessage>("Service://Service1/GetMailMessage/System.String=bluedoctor4",PWMIS.EnterpriseFramework.Common.DataType.Json,mail =>{Console.WriteLine("4,Server Response:【{0}】,\r\n Revovery Time:{1}", mail.Message,mail.RevoveryTime);});

调用结果将输出:

4,Server Response:【Hello bluedoctor4 ,I am is MSF Server.】

同样,我们也可以使用ServiceRequest 对象来调用这个服务:

 ServiceRequest request2 = new ServiceRequest();request2.ServiceName = "Service1";request2.MethodName = "GetMailMessage";request2.Parameters = new object[] { "bluedoctor567" };client.RequestService<MailMessage>(request2,PWMIS.EnterpriseFramework.Common.DataType.Json,mail =>{Console.WriteLine("5,Server Response:【{0}】,\r\n Revovery Time:{1}", mail.Message, mail.RevoveryTime);});

4.3,使用客户端自定义结果类型

我们调用服务方法的时候,客户端调用服务方法返回值的对象类型,可以跟服务方法定义的返回类型名字不一样,比如GetMailMessage 方法调用,我们可以在本地定义一个结构相同的不同的类:

namespace TestClient
{public class MailMessage2{public string Sender { get; set; }public string Reply { get; set; }public string Message { get; set; }public DateTime RevoveryTime { get; set; }}
}

然后在客户端调用,使用这个新的在客户端定义的类型作为服务方法调用的返回值:

 client.RequestService<MailMessage2>(request2,PWMIS.EnterpriseFramework.Common.DataType.Json,mail =>{Console.WriteLine("6,Server Response:【{0}】,\r\n Revovery Time:{1}", mail.Message, mail.RevoveryTime);});

输出结果跟上面是一样的。这是因为服务端和客户端使用的都是JSON序列化,它是不关心类型的名字的只关心内部数据结构是否一致。

五、小结

上面的过程演示了MSF编写服务端和客户端代码的简单过程,对MSF而言,服务是本质上都是异步调用和返回的,服务方法返回结果不仅支持简单类型,还支持复杂类型;客户端支持多种调用代码书写方式。

虽然MSF是基于WCF构建的,但是从本文的示例过程看,仅使用MSF,无需掌握任何WCF的知识。

下面是完整的MSF服务端代码:

namespace TestService
{public class Service1 : IService{public string SayHello(string who){return string.Format("Hello {0} ,I am  MSF Server.", who);}public MailMessage GetMailMessage(string who){MailMessage mail = new MailMessage();mail.Reply = who;mail.Sender = "MSF Server";mail.Message = string.Format("Hello {0} ,I am  MSF Server.", who);mail.RevoveryTime = DateTime.Now;return mail;}public void CompleteRequest(IServiceContext context){//throw new NotImplementedException();
        }public bool IsUnSubscribe{//get { throw new NotImplementedException(); }get{//返回True ,表示当前服务不执行系统后续的服务方法的订阅处理过程,而是由用户自己输出结果数据return false;}}public bool ProcessRequest(IServiceContext context){//throw new NotImplementedException();return true;}}
}

View Code

下面是完整的客户端代码:

namespace MSFTest
{class Program{static void Main(string[] args){Console.WriteLine("******** PDF.NET MSF 客户端测试程序 *********");Console.WriteLine();Proxy client = new Proxy();client.ErrorMessage += client_ErrorMessage;Console.Write("请输入服务器的主机名或者IP地址(默认 127.0.0.1):");string host = Console.ReadLine();if (string.IsNullOrEmpty(host))host = "127.0.0.1";Console.WriteLine("服务地址:{0}",host);Console.Write("请输入服务的端口号(默认 8888):");string port = Console.ReadLine();if (string.IsNullOrEmpty(port))port = "8888";Console.WriteLine("服务端口号:{0}", port);client.ServiceBaseUri = string.Format("net.tcp://{0}:{1}", host, port);Console.WriteLine("当前客户端代理的服务基础地址是:{0}",client.ServiceBaseUri);Console.WriteLine();Console.WriteLine("MSF 请求-响应 模式调用示例:");client.RequestService<string>("Service://Service1/SayHello/System.String=bluedoctor1",PWMIS.EnterpriseFramework.Common.DataType.Text,s =>{Console.WriteLine("1,Server Response:【{0}】", s);});ServiceRequest request = new ServiceRequest();request.ServiceName = "Service1";request.MethodName = "SayHello";request.Parameters = new object[] { "bluedoctor23" };client.RequestService<string>(request,PWMIS.EnterpriseFramework.Common.DataType.Text,s =>{Console.WriteLine("2,Server Response:【{0}】", s);});string serverMsg= client.RequestServiceAsync<string>(request).Result;Console.WriteLine("3,Server Response:【{0}】", serverMsg);client.RequestService<MailMessage>("Service://Service1/GetMailMessage/System.String=bluedoctor4",PWMIS.EnterpriseFramework.Common.DataType.Json,mail =>{Console.WriteLine("4,Server Response:【{0}】,\r\n Revovery Time:{1}", mail.Message,mail.RevoveryTime);});ServiceRequest request2 = new ServiceRequest();request2.ServiceName = "Service1";request2.MethodName = "GetMailMessage";request2.Parameters = new object[] { "bluedoctor567" };client.RequestService<MailMessage>(request2,PWMIS.EnterpriseFramework.Common.DataType.Json,mail =>{Console.WriteLine("5,Server Response:【{0}】,\r\n Revovery Time:{1}", mail.Message, mail.RevoveryTime);});client.RequestService<MailMessage2>(request2,PWMIS.EnterpriseFramework.Common.DataType.Json,mail =>{Console.WriteLine("6,Server Response:【{0}】,\r\n Revovery Time:{1}", mail.Message, mail.RevoveryTime);});MailMessage mail2 = client.RequestServiceAsync<MailMessage>(request2).Result;Console.WriteLine("7,Server Response:【{0}】,\r\n Revovery Time:{1}", mail2.Message, mail2.RevoveryTime);Console.WriteLine("按回车键继续");Console.ReadLine();Console.WriteLine();Console.WriteLine("MSF 发布-订阅 模式调用示例:");string repMsg = "你好!";client.SubscribeTextMessage("我是客户端", serverMessage => {Console.WriteLine();Console.WriteLine("[来自服务器的消息]::{0}", serverMessage);});while (repMsg != ""){Console.Write("回复服务器(输入为空,则退出):>>");repMsg = Console.ReadLine();client.SendTextMessage(repMsg);}Console.WriteLine("测试完成,退出");}static void client_ErrorMessage(object sender, MessageSubscriber.MessageEventArgs e){Console.WriteLine("---处理服务时错误:{0}",e.MessageText);}}
}

View Code

下面是运行客户端输出的结果示例:

******** PDF.NET MSF 客户端测试程序 *********请输入服务器的主机名或者IP地址(默认 127.0.0.1):
服务地址:127.0.0.1
请输入服务的端口号(默认 8888):
服务端口号:8888
当前客户端代理的服务基础地址是:net.tcp://127.0.0.1:8888MSF 请求-响应 模式调用示例:
1,Server Response:【Hello bluedoctor1 ,I am  MSF Server.】
3,Server Response:【Hello bluedoctor23 ,I am  MSF Server.】
2,Server Response:【Hello bluedoctor23 ,I am  MSF Server.】
7,Server Response:【Hello bluedoctor567 ,I am  MSF Server.】,Revovery Time:2017-10-9 15:50:33
按回车键继续
4,Server Response:【Hello bluedoctor4 ,I am  MSF Server.】,Revovery Time:2017-10-9 15:50:33
5,Server Response:【Hello bluedoctor567 ,I am  MSF Server.】,Revovery Time:2017-10-9 15:50:33
6,Server Response:【Hello bluedoctor567 ,I am  MSF Server.】,Revovery Time:2017-10-9 15:50:33

在下一篇,我们将演示MSF的“发布-订阅”通信模式。

本篇测试程序全部源码,已经上传到GitHub:

https://github.com/bluedoctor/MSFTest

欢迎加入我们的QQ群讨论MSF框架的使用,群号:敏思(PWMIS) .NET 18215717,加群请注明:PDF.NET技术交流,否则可能被拒。

“一切都是消息”--iMSF(即时消息服务框架)之【请求-响应】模式(点对点)...相关推荐

  1. Spring Cloud构建微服务架构:消息驱动的微服务(入门)【Dalston版】

    之前在写Spring Boot基础教程的时候写过一篇<Spring Boot中使用RabbitMQ>.在该文中,我们通过简单的配置和注解就能实现向RabbitMQ中生产和消费消息.实际上我 ...

  2. Spring Cloud架构教程 (六)消息驱动的微服务【Dalston版】

    Spring Cloud Stream是一个用来为微服务应用构建消息驱动能力的框架.它可以基于Spring Boot来创建独立的.可用于生产的Spring应用程序.它通过使用Spring Integr ...

  3. 即时消息IM系统设计

    1.确定需求 一对一聊天or群聊 群聊人数上限 发送消息大小限制 是否需要端到端加密 聊天记录存储时间 2.设计 聊天室功能设计 聊天室服务必须支持以下功能: •接收来自其他客户端的消息. •为每条消 ...

  4. 消息队列的消息大量积压怎么办

    目录 消息积压简介 生产端 消费端 已经消息积压,如何处理 总结 注意:本文参考  消息队列的消息大量积压怎么办?_JavaEdge.的博客-CSDN博客_消息队列积压了大量消息怎么处理 消息积压简介 ...

  5. 阿里分布式服务框架Dubbo的架构总结

    Dubbo是Alibaba开源的分布式服务框架,它最大的特点是按照分层的方式来架构,使用这种方式可以使各个层之间解耦合(或者最大限度地松耦合).从服务模型的角度来看,Dubbo采用的是一种非常简单的模 ...

  6. Java 微服务框架选型(Dubbo 和 Spring Cloud?),大厂 HR 如何面试

    写在最前面,我总结出了很多互联网公司的面试题及答案,并整理成了文档,以及各种学习的进阶学习资料,免费分享给大家.扫码加微信好友进[程序员面试学习交流群],免费领取.也欢迎各位一起在群里探讨技术. 微服 ...

  7. 分布式、集群、分布式服务框架

    分布式:  1.将不同功能数据放到不能的机器上.  2.将同一数据放到不同的服务器上(数据副本),服务器之间通过网络互通.(涉及到数据的一致局性问题). 分布式系统的CAP理论:  ● 一致性(C): ...

  8. Java 微服务框架选型

    Java 微服务框架选型(Dubbo 和 Spring Cloud?) 微服务(Microservices)是一种架构风格,一个大型复杂软件应用由一个或多个微服务组成.系统中的各个微服务可被独立部署, ...

  9. Java 微服务框架选型(Dubbo 和 Spring Cloud?)

    |来源:博客园 |链接:https://www.cnblogs.com/xishuai/archive/2018/04/13/dubbo-and-spring-cloud.html 微服务(Micro ...

最新文章

  1. java 扫描所有子类,是否可以获取类的所有子类?
  2. android ble 实现自动连接,Android:自动重新连接BLE设备
  3. MySQL索引背后的数据结构及算法原理(employees实例)
  4. i7 8750h支持linux,6核神U!i7-8750H游戏本评测:碾压7代
  5. 【笔记本触摸屏】实用技巧整理
  6. PS与CSS字间距转换
  7. Python自动化软件测试,解放我们的双手
  8. 最新校招笔试面试六十题
  9. Spring @Retryable 和 @Recover
  10. 车载产品Bootloader功能的应用与实现
  11. 中国大学计算机系写英语论文,计算机专业英语学论文题目 计算机专业英语论文题目怎样取...
  12. Java实现圆面积计算
  13. hackbar黑客插件
  14. 广州数控机器人编程讲解视频_广州数控工业机器人如何编程,使用什么语言?...
  15. 智能温湿度计原型设计-BLE 模组 SDK 开发
  16. 怎么制作和维护在线帮助文档?
  17. 动漫效果,教你做二次元漫画效果
  18. 计算机室+云平台,软件综合实验室教学云平台的研究与应用
  19. 适合小团队协作、任务管理、计划和进度跟踪的项目任务管理工具有哪些?
  20. css实现图片悬浮效果

热门文章

  1. 【C++】引用与变量
  2. golang内置类型和函数
  3. websocket工作原理
  4. 12月4日云栖精选夜读 | 你不知道的Web前端安全技术
  5. swift开发之--UISearchBar的使用/UISearchController的使用
  6. javascript动画系列第一篇——模拟拖拽
  7. 「iOS 面试之道」勘误(二)
  8. Spark 2.4 standalone 部署
  9. 2017 必备的八款最佳反勒索软件工具
  10. 《抓住听众心理——演讲者要知道的100件事》一20.人们学习的最优长度是20分钟...