前言

公司目前楼主负责的项目正在改版升级,对之前的服务也在作调整,项目里有个操作日志的模块,就决定把日志单独提取出来,做个日志服务,所以就有了这篇文章

正文

MSMQ作为消息队列,B/S项目调用日志服务,日志服务往消息队列发送消息,事件代理服务负责处理消息队列的消息,贴下核心代码

事件代理服务契约

using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using TestService.Contract.Faults; namespace TestService.Contract
{
/// <summary>
/// 事件代理
/// </summary>
[ServiceContract(Namespace = "http://TestService.Contract",
SessionMode = SessionMode.Required,
CallbackContract = typeof(IEventBrokerCallback))]
public interface IEventBroker
{
[OperationContract(IsOneWay = false)]
[FaultContract(typeof(EventBrokerException))]
void Subscribe(Guid subscriptionId, string[] eventNames); [OperationContract(IsOneWay = true)]
void EndSubscription(Guid subscriptionId);
}
}

事件代理服务回调处理契约

using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using TestService.Contract.Data; namespace TestService.Contract
{
/// <summary>
/// 事件代理回调
/// </summary>
public interface IEventBrokerCallback
{
[OperationContract(IsOneWay = true)]
void ReceiveStreamingResult(RealTimeEventMessage streamingResult);
}
}

事件代理服务异常实体

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text; namespace TestService.Contract.Faults
{
[DataContract]
public class EventBrokerException
{
[DataMember]
public string Message
{
get;
set;
} public EventBrokerException(string message)
{
Message = message;
}
}
}

消息处理实体

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text; namespace TestService.Contract.Data
{
/// <summary>
/// 数据实体
/// </summary>
[DataContract]
public class RealTimeEventMessage
{ public RealTimeEventMessage()
{ } public RealTimeEventMessage(MessageModel msg, string eventName, string entityIdType,
string description, string additionalData, DateTime date)
{
this.Msg = msg;
this.EventName = eventName;
this.EntityIdType = entityIdType;
this.Description = description;
this.AdditionalData = additionalData;
this.Date = date;
} [DataMember]
public MessageModel Msg { get; set; } [DataMember]
public string EventName { get; set; } [DataMember]
public string EntityIdType { get; set; } [DataMember]
public string Description { get; set; } [DataMember]
public string AdditionalData { get; set; } [DataMember]
public DateTime? Date { get; set; }
}
}

以上是事件代理服务的契约部分,下面看看实现,先看EventBroker的实现

using System;
using System.Collections.Generic;
using System.Linq;
using System.Messaging;
using System.ServiceModel;
using System.Threading.Tasks;
using TestService.ApplicationService.Data;
using TestService.ApplicationService.Services.Interface;
using TestService.Common.IoC;
using TestService.Contract;
using TestService.Contract.Data;
using TestService.Contract.Faults; namespace TestService.ApplicationService
{
[IocServiceBehavior]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class EventBroker : ApplicationBaseService, IEventBroker
{
Dictionary<string, List<UniqueCallbackHandle>> eventNameToCallbackLookups = new Dictionary<string, List<UniqueCallbackHandle>>();
private static Object syncObj = new Object();
private static string inputQueueName = "";
private bool shouldRun = true;
private static readonly TimeSpan queueReadTimeOut = TimeSpan.FromSeconds();
private static readonly TimeSpan queuePeekTimeOut = TimeSpan.FromSeconds();
private IXmlParserService _xmlParserService; public EventBroker(IXmlParserService xmlParserService)
{
inputQueueName = AppSettings.InputQueueName;
StartCollectingMessage();
_xmlParserService = xmlParserService;
} public void StartCollectingMessage()
{
try
{
GetMessageFromQueue();
}
catch (Exception ex)
{
throw new FaultException<EventBrokerException>(new EventBrokerException(ex.Message), new FaultReason(ex.Message));
}
} public void Subscribe(Guid subscriptionId, string[] eventNames)
{
try
{
CreateSubscription(subscriptionId, eventNames);
}
catch (Exception ex)
{
throw new FaultException<EventBrokerException>(new EventBrokerException(ex.Message), new FaultReason(ex.Message));
}
} public void EndSubscription(Guid subscriptionId)
{
lock (syncObj)
{
//create new dictionary that will be populated by those remaining
Dictionary<string, List<UniqueCallbackHandle>> remainingEventNameToCallbackLookups =
new Dictionary<string, List<UniqueCallbackHandle>>(); foreach (KeyValuePair<string, List<UniqueCallbackHandle>> kvp in eventNameToCallbackLookups)
{
//get all the remaining subscribers whos session id is not the same as the one we wish to remove
List<UniqueCallbackHandle> remainingMessageSubscriptions =
kvp.Value.Where(x => x.CallbackSessionId != subscriptionId).ToList();
if (remainingMessageSubscriptions.Any())
{
remainingEventNameToCallbackLookups.Add(kvp.Key, remainingMessageSubscriptions);
}
}
//now left with only the subscribers that are subscribed
eventNameToCallbackLookups = remainingEventNameToCallbackLookups;
}
} #region 私有方法 /// <summary>
/// 从消息队列中获取消息
/// </summary>
private void GetMessageFromQueue()
{
try
{
Task messageQueueReaderTask = Task.Factory.StartNew(() =>
{
using (MessageQueue queue = new MessageQueue(inputQueueName, QueueAccessMode.Receive))
{ queue.Formatter = new XmlMessageFormatter(new[] { typeof(string) }); while (shouldRun)
{
Message message = null; try
{
if (!queue.IsEmpty())
{
//Logs.Debug("接受队列里的消息");
message = queue.Receive(queueReadTimeOut);
ProcessMessage(message);
}
}
catch (MessageQueueException e)
{
if (e.MessageQueueErrorCode != MessageQueueErrorCode.IOTimeout)
{
Log.Warn("消息队列出现异常:", e);
}
}
catch (Exception e)
{
// Write the message details to the Error queue
Log.Warn("操作异常:", e);
}
}
}
}, TaskCreationOptions.LongRunning);
}
catch (AggregateException ex)
{
throw;
}
} /// <summary>
/// 处理消息
/// </summary>
/// <param name="msmqMessage"></param>
private void ProcessMessage(Message msmqMessage)
{
string messageBody = (string)msmqMessage.Body; #if DEBUG
Log.Info(string.Format("接受消息 : {0}", messageBody));
#endif RealTimeEventMessage messageToSendToSubscribers = _xmlParserService.ParseRawMsmqXml(messageBody);
if (messageToSendToSubscribers != null)
{
lock (syncObj)
{
if (messageToSendToSubscribers.Msg != null)
{
// 保存到数据库 } List<Guid> deadSubscribers = new List<Guid>(); if (eventNameToCallbackLookups.ContainsKey(messageToSendToSubscribers.EventName))
{
List<UniqueCallbackHandle> uniqueCallbackHandles =
eventNameToCallbackLookups[messageToSendToSubscribers.EventName];
foreach (UniqueCallbackHandle uniqueCallbackHandle in uniqueCallbackHandles)
{
try
{
uniqueCallbackHandle.Callback.ReceiveStreamingResult(messageToSendToSubscribers); }
catch (CommunicationObjectAbortedException coaex)
{
deadSubscribers.Add(uniqueCallbackHandle.CallbackSessionId);
}
}
} //end all subcriptions for dead subscribers
foreach (Guid deadSubscriberId in deadSubscribers)
{
EndSubscription(deadSubscriberId);
}
}
}
} private void CreateSubscription(Guid subscriptionId, string[] eventNames)
{
//Ensure that a subscription is created for each message type the subscriber wants to receive
lock (syncObj)
{
foreach (string eventName in eventNames)
{
if (!eventNameToCallbackLookups.ContainsKey(eventName))
{
List<UniqueCallbackHandle> currentCallbacks = new List<UniqueCallbackHandle>();
eventNameToCallbackLookups[eventName] = currentCallbacks;
}
eventNameToCallbackLookups[eventName].Add(
new UniqueCallbackHandle(subscriptionId, OperationContext.Current.GetCallbackChannel<IEventBrokerCallback>()));
}
}
} #endregion
}
}

事件代理实现里的回调处理实体

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TestService.Contract; namespace TestService.ApplicationService.Data
{
public class UniqueCallbackHandle
{
public UniqueCallbackHandle(Guid callbackSessionId, IEventBrokerCallback callback)
{
this.CallbackSessionId = callbackSessionId;
this.Callback = callback;
} public Guid CallbackSessionId { get; private set; } public IEventBrokerCallback Callback { get; private set; }
}
}

其中事件代理服务构造方法的AppSettings.InputQueueName是读取配置文件里的专用队列名称,这里有个构造方法,后面会使用Autofac进行注入,另外还有个IXmlParserService普通业务操作的,主要是解析消息队列里存放的xml消息

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TestService.Contract.Data; namespace TestService.ApplicationService.Services.Interface
{
public interface IXmlParserService
{
RealTimeEventMessage ParseRawMsmqXml(string messageBody);
}
}

实现

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using TestService.ApplicationService.Services.Interface;
using TestService.Contract.Data; namespace TestService.ApplicationService.Services.Impl
{
public class XmlParserService : IXmlParserService
{
private MessageModel msg; public XmlParserService(MessageModel msg)
{
this.msg = msg;
} public RealTimeEventMessage ParseRawMsmqXml(string messageBody)
{
try
{
RealTimeEventMessage info = new RealTimeEventMessage();
XElement xelement = XElement.Parse(messageBody);
MessageModel model = new MessageModel();
model.MessageId = GetSafeString(xelement, "messageId");
model.Content = GetSafeString(xelement, "content");
model.CreateTime = GetSafeDate(xelement, "createTime") ?? DateTime.Now; info.Msg = model;
info.EventName = GetSafeString(xelement, "eventName");
//info.EntityIdType = GetSafeString(xelement, "entityIdType");
//info.Description = GetSafeString(xelement, "description").Replace("\n\n", "\n\r");
//info.Date = GetSafeDate(xelement, "date");
//info.AdditionalData = GetSafeString(xelement, "additionalData");
return info; }
catch (Exception ex)
{
Log.Error("解析Xml消息出现异常:" + ex);
return null;
}
} public static Int32 GetSafeInt32(XElement root, string elementName)
{
try
{
XElement element = root.Elements().Where(node => node.Name.LocalName == elementName).Single();
return Convert.ToInt32(element.Value);
}
catch
{
return ;
}
} private static DateTime? GetSafeDate(XElement root, string elementName)
{
try
{
XElement element = root.Elements().Where(node => node.Name.LocalName == elementName).Single();
return DateTime.Parse(element.Value);
}
catch
{
return null;
}
} public static String GetSafeString(XElement root, string elementName)
{
try
{
XElement element = root.Elements().Where(node => node.Name.LocalName == elementName).Single();
return element.Value;
}
catch
{
return String.Empty;
}
} public static bool GetSafeBool(XElement root, string elementName)
{
try
{
XElement element = root.Elements().Where(node => node.Name.LocalName == elementName).Single();
return Convert.ToBoolean(element.Value);
}
catch
{
return false;
}
}
}
}

这里的xml节点主要是根据消息服务里发送消息的xml节点来定的,事件代理服务的就是上面的这么多,下面看看消息服务,

using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using TestService.Contract.Data; namespace TestService.Contract
{
[ServiceContract]
public interface IMessageService
{
[OperationContract]
bool Send(MessageModel model);
}
}

实现

using System;
using System.Collections.Generic;
using System.Linq;
using System.Messaging;
using System.Text;
using TestService.Common;
using TestService.Common.IoC;
using TestService.Contract;
using TestService.Contract.Data; namespace TestService.ApplicationService
{
/// <summary>
/// 消息服务
/// </summary>
[IocServiceBehavior]
public class MessageService : ApplicationBaseService, IMessageService
{
private static string inputQueueName = ""; public MessageService()
{
inputQueueName = AppSettings.InputQueueName;
} public bool Send(MessageModel model)
{
using (MessageQueue queue = new MessageQueue(inputQueueName, QueueAccessMode.Send))
{
queue.Formatter = new XmlMessageFormatter(new[] { typeof(string) });
try
{
Message message = new Message(
GetXmlData(model));
Log.Info(string.Format("发送消息: {0}", message.Body.ToString()));
queue.Send(message); return true;
}
catch (MessageQueueException mex)
{
if (mex.MessageQueueErrorCode != MessageQueueErrorCode.IOTimeout)
{
Log.Error(string.Format("Message queue exception occured", mex));
}
return false;
}
catch (Exception ex)
{
// Write the message details to the Error queue
Log.Error(ex);
return false;
}
}
} private string GetXmlData(MessageModel model)
{
StringBuilder sb = new StringBuilder("<realtimeEvent>");
sb.AppendFormat("<eventName>ClientDealEvent</eventName>");
sb.AppendFormat("<messageId>{0}</messageId>", model.MessageId);
sb.AppendFormat("<createTime>{0}</createTime>", model.CreateTime);
sb.AppendFormat("<content>{0}</content>", model.Content);
sb.AppendLine("</realtimeEvent>");
return sb.ToString();
}
}
}

消息服务比较简单,就是往消息队列里发送消息,细心的人会发现,我在每个服务实现里都加了个IocServiceBehavior特性,这个主要是标识了注入了该服务

核心代码就是上面介绍的那么多,有些操作没将代码贴出来,后面会将代码开源到码云上,今天就先写到这儿了

WCF基于MSMQ的事件代理服务的更多相关文章

  1. 基于MSMQ绑定的WCF服务实现总结

    一. 创建消息队列    1 1) 创建一个非事物性的私有队列    1 2)设置消息队列访问权限    2 二.创建WCF服务并绑定消息队列    4 1)创建HelloService服务    4 ...

  2. 基于A2DFramework的事件机制实现

    随笔- 102  文章- 3  评论- 476  发布订阅 - 基于A2DFramework的事件机制实现   SUMMARY 能做什么 DEMO 原理图 应用场景 能做什么 A2DFramework ...

  3. atitit.基于组件的事件为基础的编程模型--服务器端控件(1)---------服务器端控件和标签之间的关系

    atitit.基于组件的事件为基础的编程模型--服务器端控件(1)---------服务器端控件和标签之间的关系 1. server控件是要server了解了标签.种类型的server控件: 1 1. ...

  4. 重温.NET下Assembly的加载过程 ASP.NET Core Web API下事件驱动型架构的实现(三):基于RabbitMQ的事件总线

    重温.NET下Assembly的加载过程   最近在工作中牵涉到了.NET下的一个古老的问题:Assembly的加载过程.虽然网上有很多文章介绍这部分内容,很多文章也是很久以前就已经出现了,但阅读之后 ...

  5. 发布订阅 - 基于A2DFramework的事件机制实现

    SUMMARY 能做什么 DEMO 原理图 应用场景 能做什么 A2DFramework的事件机制是基于发布订阅模式改进得来的一套API,中间件部分实现了msmq.redis.Supersocket可 ...

  6. 跟我一起学WCF(1)——MSMQ消息队列

    一.引言 Windows Communication Foundation(WCF)是Microsoft为构建面向服务的应用程序而提供的统一编程模型,该服务模型提供了支持松散耦合和版本管理的序列化功能 ...

  7. 基于window.onerror事件 建立前端错误日志

    QA不是万能的,用户的浏览环境非常复杂,很多情况无法靠测试用例去覆盖,所以最好建立一个前端错误日志,在真实用户端收集bug. try&catch是一个捕获前端错误的常见方法,比如: { //给 ...

  8. C# 基于委托的事件

    事件基于多播委托的特性. 多播委托事实上就是一组类型安全的函数指针管理器,调用则执行顺序跳转到数组里所有的函数指针里执行. class Program { public class CarInfoEv ...

  9. WCF基于Cookie回传的系列(概述)

    1  WCF的基本知识(不作细述,园子里有很多的经典的文章系列) 2 WCF的执行过程 3 让服务通信像浏览器发送请求应答一样回传Cookie,并实现Cookie在不同的服务间共享 4  基于共享后的 ...

随机推荐

  1. HTML5 画一张图

    笔者:本笃庆军 原文地址:http://blog.csdn.net/qingdujun/article/details/33344289 一.绘制图像 使用drawImage()方法绘制图像. 画图环 ...

  2. oracle_连接数_查看

    查看oracle数据库的连接数以及用户   .查询oracle的连接数 select count(*) from v$session; .查询oracle的并发连接数 select count(*) ...

  3. oracle_彻底删除oracle

    例如ORACLE安装路径为:C:\ORACLE 实现方法: 1. 开始->设置->控制面板->管理工具->服务 停止所有Oracle服务. 2. 开始->程序->O ...

  4. C#-简单的定时器(C# ConsoleApp) ---ShinePans

    watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc2hpbmVwYW4=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA ...

  5. 表达式树 Expression

    转载泛型方法动态生成表达式树 Expression public string GetGridJSON(TraderInfo model) { IQueryable<TraderInfo> ...

  6. [转载]CSS元素的定位position

    CSS元素的定位position 属性position   值 描述 absolute                             生成绝对定位的元素,相对于 static 定位以外的第一 ...

  7. HDU1325 Is It A Tree? 【并查集】

    Is It A Tree? Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) To ...

  8. s3c2440 的 rtc 操作

    实时时钟(RTC)其主要功能是电源故障的制度下,使用后备电源,时钟继续.为了不浪费时间信息. s3c2440内部集成了RTC模块,并且用起来也十分简单. 其内部的寄存器BCDSEC,BCDMIN,BC ...

  9. Java NIO的多路复用及reactor

    (from:http://developer.51cto.com/art/201112/306489.htm) 以下描述,为了说明问题,就提提历史(类似的东西,网上一搜一大把,但是希望你能在这里止步, ...

  10. DRP学习进化模型

    曾经做的就是按照思维做的三级制,这是U .B ,D .坐在坐,开始增加设计模式,增加sqlhelper ,逐渐了解系统可分为只三层,层的,随着学习的不断深入明确了"为什么会出现分层" ...