C#编写Windows服务程序 (服务端),client使用 消息队列 实现淘宝 订单全链路效果
需求: 针对 淘宝提出的 订单全链路 产品接入 .http://open.taobao.com/doc/detail.htm?id=102423&qq-pf-to=pcqq.group
oms(订单管理系统) 实现 , 完毕后 效果:在千牛工作台 --订单全链路 可看到效果例如以下图
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
目标: client 使用消息队列 将订单信息保存, 服务端使用 Windows服务 ,将消息队列中的订单信息 通过淘宝api 上传到淘宝.
---(截图上传了 4次 才看到效果 ,最后从简 见谅)
一、创建一个Windows Service
1)创建Windows Service项目
2)对Service重命名
将Service1重命名为你服务名称。这里我们命名为TradeTraceService。
二、创建服务安装程序
1)加入安装程序
之后我们能够看到上图,自己主动为我们创建了ProjectInstaller.cs以及2个安装的组件。
2)改动安装服务名
右键serviceInsraller1。选择属性。将ServiceName的值改为TradeTraceService。
- 补充:
1.Service启动属性:
Manual 服务安装后。必须手动启动。
Automatic 每次计算机又一次启动时,服务都会自己主动启动。
Disabled 服务无法启动。
3)改动安装权限
右键serviceProcessInsraller1。选择属性,将Account的值改为LocalSystem。
1) 创建类库
2) 创建接口 和相应实现
3) 代码展示
ITradeTraceService.cs
using System; using System.Collections.Generic; using System.Linq; using System.ServiceModel; using System.Text; namespace TradeTrace.Service { [ServiceContract] public interface ITradeTraceService { /// <summary> /// 把订单信息插入到队列里 /// </summary> /// <param name="orderSource"></param> /// <param name="orderNum"></param> /// <param name="status"></param> /// <param name="createtime"></param> [OperationContract(IsOneWay = true)] void Insert(int orderSource, string orderNum, int status, DateTime createtime ); } }TradeTraceService.cs
using ECERP.FrameWork.Log; using System; using System.Collections.Generic; using System.Linq; using System.ServiceModel; using System.Text; namespace TradeTrace.Service { [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Single)] public class TradeTraceService : ITradeTraceService { public void Insert(int orderSource, string orderNum, int status, DateTime createtime) { Logger.Info(string.Format("[淘宝订单 全链路]读取店铺:{0},订单:{1},状态:{2}", orderSource, orderNum, status)); DateTime dt = DateTime.Now; if (string.IsNullOrEmpty(orderNum) || orderNum == "0" ||orderSource==0) { Logger.Info(string.Format("[淘宝订单 全链路]公布消息 时读取到 异常单号:{0},店铺:{1},时间:{2}", orderNum, orderSource, dt)); return; } try { var list = ECERP.AddIn.ShopService.ShopList; var shop = list.FirstOrDefault(c => c.ID == orderSource); //var shop = ECERP.Service.ShopService.Instance.GetShopByID(orderSource); if (shop != null) { ECERP.AddIn.TaoBao.Service.TradeTraceService trace = new ECERP.AddIn.TaoBao.Service.TradeTraceService(shop); trace.TradeTraceMessageProduce(orderNum, status, createtime); Logger.Info("OrderNum:" + orderNum + " createtime:" + createtime + " OrderSource:" + orderSource + "耗时:" + (DateTime.Now - dt)); } else { Logger.Info("shop is null" + orderSource); } } catch (Exception ex) { Logger.Error("淘宝订单 全链路 (正向交易状态跟踪消息) 异常", ex); } } } }
4)
写入服务代码:打开TradeTraceService代码
右键TradeTraceService.cs,选择查看代码。
TradeTraceService.cs
using ECERP.FrameWork.Log; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Diagnostics; using System.Linq; using System.Messaging; using System.ServiceModel; using System.ServiceProcess; using System.Text; namespace TradeTrace.Host { public partial class TradeTraceService : ServiceBase { ServiceHost host = new ServiceHost(typeof(TradeTrace.Service.TradeTraceService)); public TradeTraceService() { InitializeComponent(); } protected override void OnStart(string[] args) { if (!MessageQueue.Exists(@".\private$\tradetracelist")) { Logger.Info("不存在队列创建"); var mq = MessageQueue.Create(@".\private$\tradetracelist", true); //能够依据实际情况自己设置 mq.SetPermissions("Everyone", MessageQueueAccessRights.FullControl); } else { Logger.Info("已经存在队列"); } try { host.Open(); Logger.Info("服务開始启动了"); } catch (Exception ex) { Logger.Error("err", ex); } } protected override void OnStop() { try { host.Close(); Logger.Info("服务关闭了"); } catch (Exception ex) { host.Abort(); Logger.Error("err",ex); } } } }
应用程序入口 Program.cs
using ECERP.FrameWork.Log; using System; using System.Collections.Generic; using System.Configuration.Install; using System.Linq; using System.ServiceProcess; using System.Text; namespace TradeTrace.Host { static class Program { /// <summary> /// 应用程序的主入口点。 /// </summary> static void Main(string[] args) { string f = AppDomain.CurrentDomain.BaseDirectory + "Log.config"; log4net.Config.XmlConfigurator.ConfigureAndWatch(new System.IO.FileInfo(f)); Logger.Info("start..."); try { // 执行服务 if (args.Length == 0) { try { ServiceBase[] ServicesToRun = new ServiceBase[] { new TradeTraceService() }; ServiceBase.Run(ServicesToRun); } catch (Exception ex) { Logger.Error("err", ex); } } // 安装服务 else if (args[0].ToLower() == "/i" || args[0].ToLower() == "-i") { try { string[] cmdline = { }; string serviceFileName = System.Reflection.Assembly.GetExecutingAssembly().Location; TransactedInstaller transactedInstaller = new TransactedInstaller(); AssemblyInstaller assemblyInstaller = new AssemblyInstaller(serviceFileName, cmdline); transactedInstaller.Installers.Add(assemblyInstaller); transactedInstaller.Install(new System.Collections.Hashtable()); } catch (Exception ex) { Logger.Error("err", ex); //string msg = ex.Message; } } // 删除服务 else if (args[0].ToLower() == "/u" || args[0].ToLower() == "-u") { try { string[] cmdline = { }; string serviceFileName = System.Reflection.Assembly.GetExecutingAssembly().Location; TransactedInstaller transactedInstaller = new TransactedInstaller(); AssemblyInstaller assemblyInstaller = new AssemblyInstaller(serviceFileName, cmdline); transactedInstaller.Installers.Add(assemblyInstaller); transactedInstaller.Uninstall(null); } catch (Exception ex) { Logger.Error("err", ex); } } } catch (Exception ex) { Logger.Error("err", ex); } } } }
5) log.config 日志文件 和app.config 数据库配置 文件
log.config 基本 没啥大变化 不做展示
app.config
<?xml version="1.0" encoding="utf-8" ?> <configuration> <connectionStrings> <add name="local" connectionString="server=.;database=txttt0808;uid=sa;Pwd=" providerName="sql2005"/> </connectionStrings> <system.serviceModel> <services> <service name="TradeTrace.Service.TradeTraceService"> <endpoint address="net.msmq://localhost/private/tradetracelist" binding="netMsmqBinding" bindingConfiguration="msmq" contract="TradeTrace.Service.ITradeTraceService" /> </service> </services> <bindings> <netMsmqBinding> <binding name="msmq" exactlyOnce="true"> <security mode="None" /> </binding> </netMsmqBinding> </bindings> </system.serviceModel> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/> </startup> </configuration>需注意的 是 消息队列 服务地址配置
6) 项目总体 展示
四、安装 服务
用.net framework工具INSTALLUTIL安装服务程序就可以。
用项目的输出作为參数。从命令行执行 InstallUtil.exe。在命令行中输入下列代码:
installutil yourproject.exe
五、卸载 服务
用项目的输出作为參数,从命令行执行 InstallUtil.exe。
installutil
/u yourproject.exe
六 . client 代码
公共方法 : 在你任务 须要的 地方 自己调用
/// <summary> /// 把订单信息保存到msmq里 /// </summary> /// <param name="list"></param> private void SaveTradeTraceToMsmq(List<SalesOrder> list) { ChannelFactory<TradeTrace.Service.ITradeTraceService> channelFactory = new ChannelFactory<TradeTrace.Service.ITradeTraceService>("TradeTraceServiceClient"); TradeTrace.Service.ITradeTraceService calculate = channelFactory.CreateChannel(); int i = 0; DateTime beginTime = DateTime.Now; foreach (var order in list) { calculate.Insert(order.OrderSource.GetValueOrDefault(0), order.OrderNum, order.Status.GetValueOrDefault(0),DateTime.Now); Logger.Info(string.Format("orderNum:{0} createtime:{1} shop:{2} no:{3}", order.OrderNum, order.ModifyTime, shop.Name, ++i)); } Logger.Info(string.Format("{0}保存进队列{1}/{2}条 time:{3} 耗时:{4}", shop.Name, i, list.Count, DateTime.Now, DateTime.Now.Subtract(beginTime))); }client配置文件 截图
总结: Windows服务 和消息队列的 结合使用 会让你的 程序 焕然一新,更加灵活 . 消息队列的 相关知识 请自寻资料了解.
小小心得 不正确的地方 请多多不吝赐教 .