【架构之路(分布式三部曲)】--WS

       上文详细讨论了MQ的使用方法,MQ作为一种信息存储机制,将消息存储到了队列中,这样在做分布式架构时可以考虑将消息传送到MQ服务器上,然后开发相应的服务组件获取MQ中的消息,自动获取传送的消息,将消息发送给处理的程序。那么上文就MQ做了详细的讨论,并没有介绍有关服务组件的内容,也就是说如果我们开发的程序想要实现自动的消息接收和推送的服务的话,这里就必须考虑使用Windows Service来实现了,Windows Service是一种实时运行的组件,可以实时处理消息,该篇文章将会讨论Windows Service的开发方法。

一、WS理论篇


        MQ和WS技术相结合其实就可以看做是一个简单的ESB程序,这样可以通过调用服务来实现消息中间件的处理功能,可以开发包括消息推送、接收、处理的应用程序。WS是在Windows操作系统中才会有的,是集成到系统中的,一个WS在开启后会一直运行,直到停止该WS。在具体的项目中开发的WS是作为组件存在的,也就是说系统中的某部分需要实时运行,这时候可以考虑开发WS组件。


  1.1 windows service VS web service


        那么这里需要思考一个问题,windows Service和web Service它们两个都是服务,但是服务的对象是不同的。在web应用程序中web service是一个行业的信息服务标准,它为web应用程序之间架设了一个桥梁,使得应用程序之间可以互相的交流通信,也就是说web service是在web分布式应用中必然会用到的技术。
        相较下来windows Service就比较狭窄了,它只能在windows system中运行,可以为windows的应用提供功能服务上的支持,在开发windows Service时常常会考虑两种情况,一是运行的组件考虑需要实时运行,另外是组件可以需要被多个程序调用,比如经常在开发中用到的sql server,它有很多子程序,这些程序之间会共用某个功能,所以此时就把它做成了一个服务。


  1.2 ws开发


       ws的组成是比较简单的,因为它本身固有的属性很少,大部分功能是通过调用其它的类库或者组件来组合功能,具体的ws的组成如下导图所示:

         在开发ws时会有两个大的部分,分别是Service和Installer,Service是服务的内部功能逻辑,也就是服务的本体,服务要做的操作都是写到里面的;Installer是服务的安装部分,也就是配置了该服务在windows中显示的属性。

        Service:继承自ServiceBase,其中包括服务所有的运行内容,开发界面类似于winForm中的自定义控件的开发,能够添加系统控件及第三方控件。
        Installer:分为两种,其中的serviceProcessInstaller配置服务的被调用的用户类型,serviceInstaller配置服务本身属性,如服务名称、服务启动方法等。



二、WS Demo


        上文对WS的基本内容做了详细的了解,通过理论来指导实践,这样在开发的时候思路才能够很清晰,关于WS的理论部分这里不再详细的讨论,接下来我们开发一个简单的WS Demo。
       上篇文章详细讨论了MQ的开发方法,并开发了一个简单的MQ项目,这里就继续使用上文的MQ的项目来开发一个对MQ消息处理的WS组件,该组件主要是负责处理消息队列中的信息,获取消息,并把消息的主题内容保存到文本中。
       具体程序的分布图如下所示:
                                         

  2.1 服务代码

         上面的程序分布图是结合上文的MQ来绘制的详细的架构图,其中的数据发送部分的程序在上文已经有介绍,这里不再详细讨论,下面的代码就是演示了分布图中的Service Handle data和Save data部分的功能,具体功能如下代码:

using System;
using System.IO;
using System.ServiceProcess;
using System.Threading;
using System.Timers;
using System.Messaging;


namespace WindowsService1
{
    public partial class Service1 : ServiceBase
    {
        //declare a MQ
        private MessageQueue queue;
        public Service1()
        {
            InitializeComponent();

            //create a path for queue
            string strpath = ".\\Private$\\EKTestQueue";
            //create a messagequeue and assign a path for this queue
            queue= new MessageQueue(strpath);
            //the queue formatter that can get queuebody based on the formatter
            queue.Formatter = new XmlMessageFormatter(new Type[] { typeof(string) });
        }
        
        /// <summary>
        /// service start event
        /// when we start this service then it will run this method
        /// </summary>
        /// <param name="args"></param>
        protected override void OnStart(string[] args)
        {
            //used to debug the code when start this service
            Thread.Sleep(30000);
        }

        /// <summary>
        /// service stop event
        /// </summary>
        protected override void OnStop()
        {
        }
        /// <summary>
        /// get message from queue and save the message to file
        /// </summary>
        private void GetAndWriteMessage()
        {
            //recevie a new one MQ
            var ms=queue.Receive();
            //save the data to file
            if (ms!=null)
            {
                this.AddTextLine(ms.Body.ToString());
            }
        }

        /// <summary>
        /// save the MQ message to file
        /// </summary>
        /// <param name="line">the message that want save</param>
        private void AddTextLine(string line)
        {
            try
            {
                //save the message to Message.txt file
                FileStream file=new FileStream("D:\\Message.txt",FileMode.OpenOrCreate,FileAccess.ReadWrite);

                StreamWriter writer=new StreamWriter(file);
                writer.BaseStream.Seek(0, SeekOrigin.End);
                writer.WriteLine(line+"\r\n");
                writer.Flush();
                writer.Close(); 
                file.Close();
                file.Dispose();
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }


        private void timer1_Elapsed(object sender, ElapsedEventArgs e)
        {
            GetAndWriteMessage();
        }
    }
}

        上面的代码主要是在服务中添加了信息处理的模块,这样就能够实现时时获取queue中的数据并对数据进行操作。
        Note1:在服务的OnStart事件中添加了Thread.Sleep(3000)是为了调试服务启动代码而存在的,因为服务启动的时候是很快的也就是说我们在调试代码时经常会将项目附加到服务中,但是这时候服务是已经启动的了,是没有办法调试它的,所以我们添加了改行代码,这样就可以延迟服务的启动时间,给我们制造了充足的时间将代码附加到服务中来调试服务的启动代码。
        Note2:Timer控件,该控件一定要是System.Timers.Timer控件,因为在添加时可能会误添加为在Thread命名空间下载Timer控件,这样就会导致服务在启动时出错,不能正常注册该控件。


  2.2 服务调试


        在开发完成服务后不能够直接运行使用,而是要将服务安装到系统的Services中才能够查看服务是否正常运行,因为服务不同于一般的应用程序,想要使用服务就必须将服务安装到系统当中。另外服务的调试也是很麻烦的一件事,因为服务不同于应用程序,想要调试它就不能采用普通的调试方法了,这里介绍一种附加进程的调试方法,还有其它如写日志等的调试方法,可以查看网上的文章来详细了解。
       

     2.2.1 安装服务

       首先要打开控制台并进入到C:/Windows/Microsoft.Net/Framework/v4.0.30319中,运行InstallUtil.exe工具,并添加相应的开发的服务程序,然后回车运行就可以安装,如下所示:


     C:/Windows/Microsoft.Net/Framework/v4.0.30319>InstallUtil.exe C:\Code\WindowsService1\WindowsService1\bin\Debug\WindowsService1.exe

        安装完成后查看Services工具中的服务如下图所示:


       安装后服务显示的名称是根据ServiceInstaller的属性来控制的,在开发时指定了ServiceName属性名称为Jack's Service所以安装后显示的服务名称如上图所示。


     2.2.2 将WS附加到进程

       首先打开Tools下面的Attach to Process窗体,然后在程序中找到该服务,具体如下面两张图所示:



        找到后双击该程序即可将添加源码到服务,这时候就能够对服务的代码进行调试了。
        Note3:在附加进程时,所附加的进程名称是和服务的程序名称相一致的,并不是和Services工具中的名称一致。因为该服务程序的名称为WindowsServices1所以我们要把代码附加到该服务中,如上图所示。

      2.2.3 删除服务

       在不使用服务后也可以删除服务,删除方法类似于服务的安装,具体方法如下所示:

          
     C:/Windows/Microsoft.Net/Framework/v4.0.30319>InstallUtil.exe/u C:\Code\WindowsService1\WindowsService1\bin\Debug\WindowsService1.exe

结语


        文章主要介绍了WS的基本构成,并通过一个具体的示例来演示开发WS时需要注意的地方,最后介绍了一种WS的调试方法,很简单,但是新手会有很多问题,具体的问题具体分析,可以留言讨论。
        WS在分布式开发中拥有重要作用,它能够协调各个程序之前的交互功能,可以作为程序的通信桥梁,但是WS本身有很多局限性,它只能运行在Windows系统中,另外WS的代码调试一直惨遭诟病,需要开发人员一次次的安装、附加进程、删除的操作,很耗费时间,所以在做分布式开发时一定要慎重考虑使用WS。


  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值