zoukankan      html  css  js  c++  java
  • code4fun:host wcf service just in time

    当ServiceContract非常多的情况下,比如要self-host hundred of service的时候,or更多的时候,每次hosting都是建立一个tcp listen.这样,host的init工作会占用非常多的时间和资源。对于一些对start有较快需求的case,这明显会影响用户体验。本文意在寻求一种技术手段解决这个问题,让service只有在运行时才host。我们姑且称之为host just in time吧

    先说一下思路。

    client如果能发送消息给service,并且消息可达,先决条件就是消息到达service之前,service已经被host,注意,是说消息到达之前,而并不是在客户端发送消息之后,这就给我们提供了一个可以做手脚的时期,即消息从客户端发送出去到消息在被服务端接收之前这段时间,如果我们能Inspect它,并根据其self description,便能在消息发送到真正的服务之前,host那个service,而做这样一个事情,rounter明显是最在行的了。下面是实现host just in time的原理图。

    image

    按照上面的思路,做了一个简单的demo,过程如下

    首先创建Contracts项目Jillzhang.Wcf.HostJIT.Contracts,包含两个ServiceContract:ICalculator和IActivity,代码分别为:

    ICalculator:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ServiceModel;
    namespace Jillzhang.Wcf.HostJIT.Contracts
    {
    [ServiceContract]
    public interface  ICalculator
    {
    [OperationContract]
    int Add(int a, int b);
    }
    }

    IActivity:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ServiceModel;
    namespace Jillzhang.Wcf.HostJIT.Contracts
    {
    [ServiceContract]
    public interface IActivity
    {
    [OperationContract]
    void Excute();
    }
    }

    demo只模拟对这两个ServiceContract的host just in time

    建立好契约项目之后,新建服务项目:Jillzhang.Wcf.HostJIT.Services,添加两个类:Caculator和Activity,分别实现ICalculator和IActivity,其代码如下:

    Caculator:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Jillzhang.Wcf.HostJIT.Contracts;
    namespace Jillzhang.Wcf.HostJIT.Services
    {
    public class Calculator : ICalculator
    {
    public int Add(int a, int b)
    {
    return a + b;
    }
    }
    }

    Activity:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Jillzhang.Wcf.HostJIT.Contracts;
    namespace Jillzhang.Wcf.HostJIT.Services
    {
    public class Activity:IActivity
    {
    public void Excute()
    {
    Console.WriteLine("activity is running!");
    }
    }
    }

    接着,我们需要简历本实例中最重要的项目:Jillzhang.Wcf.HostJIT.Dispatcher,这个项目的release自身就是一个对wcf service的self-host的hosting app.为了实现路由,截获以及调度的功能,实现了路由接口IRounter和类Dispatcher,下面是他们的code:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ServiceModel;
    using System.ServiceModel.Channels;
    using System.Collections;
    using Jillzhang.Wcf.HostJIT.Services;
    namespace Jillzhang.Wcf.HostJIT.Dispatcher
    {
    [ServiceContract]
    public interface IRounter
    {
    [OperationContract(Action = "*", ReplyAction = "*")]
    Message ProcessMessage(Message msg);
    }
    [ServiceBehavior(AddressFilterMode = AddressFilterMode.Any)]
    public class Dispatcher : IRounter
    {
    public static readonly Hashtable hostTable = new Hashtable();
    public Message ProcessMessage(Message msg)
    {
    string to = msg.Headers.To.ToString();
    string serviceName = "";
    //todo:if no host,host first
    if (to.IndexOf("8031") > -1)
    {
    if (!hostTable.ContainsKey("Calculator"))
    {
    ServiceHost host = new ServiceHost(typeof(Calculator));
    host.Opened += new EventHandler(delegate(object sender, EventArgs arg)
    {
    Console.WriteLine("Calculator service is opened!");
    });
    host.Open();
    hostTable.Add("Calculator", host);
    }
    serviceName = "Calculator";
    }
    else
    {
    if (!hostTable.ContainsKey("Activity"))
    {
    ServiceHost host = new ServiceHost(typeof(Activity));
    host.Opened += new EventHandler(delegate(object sender, EventArgs arg)
    {
    Console.WriteLine("activity service is opened!");
    });
    host.Open();
    hostTable.Add("Activity", host);
    }
    serviceName = "Activity";
    }
    //todo:rounter the message
    using (ChannelFactory<IRounter> channelFactory = new ChannelFactory<IRounter>(serviceName))
    {
    var chanenl = channelFactory.CreateChannel();
    using (chanenl as IDisposable)
    {
    return chanenl.ProcessMessage(msg);
    }
    }
    }
    }
    }

    通过代码可以看出,首先Dispatcher是一个有路由功能的服务,它会根据message中Header中的To判断要调用的Service是哪一个,然后判断是否已经承载了那个最终目的的Service,如果没有,host frist and add it to hostTable ,host之后,就可以将消息调度给真正的服务了。

    app.config的代码如下:

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
    <system.serviceModel>
    <services>
    <service name="Jillzhang.Wcf.HostJIT.Services.Calculator">
    <host>
    <baseAddresses>
    <add baseAddress="net.tcp://127.0.0.1:8031"/>
    </baseAddresses>
    </host>
    <endpoint address="Calculator" binding="netTcpBinding" contract="Jillzhang.Wcf.HostJIT.Contracts.ICalculator"></endpoint>
    </service>
    <service name="Jillzhang.Wcf.HostJIT.Services.Activity">
    <host>
    <baseAddresses>
    <add baseAddress="net.tcp://127.0.0.1:8032"/>
    </baseAddresses>
    </host>
    <endpoint address="Activity" binding="netTcpBinding" contract="Jillzhang.Wcf.HostJIT.Contracts.IActivity"></endpoint>
    </service>
    <service name="Jillzhang.Wcf.HostJIT.Dispatcher.Dispatcher">
    <host>
    <baseAddresses>
    <add baseAddress="net.tcp://127.0.0.1:8030"/>
    </baseAddresses>
    </host>
    <endpoint address="" binding="netTcpBinding" contract="Jillzhang.Wcf.HostJIT.Dispatcher.IRounter"></endpoint>
    </service>
    </services>
    <client>
    <endpoint  address="net.tcp://127.0.0.1:8031/Calculator" binding="netTcpBinding" contract="Jillzhang.Wcf.HostJIT.Dispatcher.IRounter" name="Calculator"></endpoint>
    <endpoint  address="net.tcp://127.0.0.1:8032/Activity" binding="netTcpBinding" contract="Jillzhang.Wcf.HostJIT.Dispatcher.IRounter" name="Activity"></endpoint>
    </client>
    </system.serviceModel>
    </configuration>

    而在这个项目中,我们开始只需要host IRounter契约。host代码为:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ServiceModel;
    namespace Jillzhang.Wcf.HostJIT.Dispatcher
    {
    class Program
    {
    static void Main(string[] args)
    {
    using (ServiceHost host = new ServiceHost(typeof(Dispatcher)))
    {
    host.Opened += new EventHandler(delegate(object sender, EventArgs arg)
    {
    Console.WriteLine("dispatcher service is opened!");
    });
    host.Open();
    Console.Read();
    }
    }
    }
    }

    建立客户端程序Jillzhang.Wcf.HostJIT.Client,并且将app.config更改为:

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
    <system.serviceModel>
    <client>
    <endpoint behaviorConfiguration="via" address="net.tcp://127.0.0.1:8031/Calculator" binding="netTcpBinding" contract="Jillzhang.Wcf.HostJIT.Contracts.ICalculator" name="Calculator"></endpoint>
    <endpoint behaviorConfiguration="via" address="net.tcp://127.0.0.1:8032/Activity" binding="netTcpBinding" contract="Jillzhang.Wcf.HostJIT.Contracts.IActivity" name="Activity"></endpoint>
    </client>
    <behaviors>
    <endpointBehaviors>
    <behavior name="via">
    <clientVia viaUri="net.tcp://127.0.0.1:8030/"/>
    </behavior>
    </endpointBehaviors>
    </behaviors>
    </system.serviceModel>
    </configuration>

    Programe.cs为:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ServiceModel;
    using Jillzhang.Wcf.HostJIT.Contracts;
    namespace Jillzhang.Wcf.HostJIT.Client
    {
    class Program
    {
    static void Main(string[] args)
    {
    Console.WriteLine("press any key when dispatcher service is ok!");
    Console.Read();
    CallCalculator();
    CallActivity();
    CallCalculator();
    CallActivity();
    Console.ReadKey(true);
    }
    static void CallCalculator()
    {
    using (ChannelFactory<ICalculator> channelFactory = new ChannelFactory<ICalculator>("Calculator"))
    {
    var channel = channelFactory.CreateChannel();
    using (channel as IDisposable)
    {
    Console.WriteLine(channel.Add(1, 2).ToString());
    }
    }
    }
    static void CallActivity()
    {
    using (ChannelFactory<IActivity> channelFactory2 = new ChannelFactory<IActivity>("Activity"))
    {
    var channel = channelFactory2.CreateChannel();
    using (channel as IDisposable)
    {
    channel.Excute();
    }
    }
    }
    }
    }
    好了,在解决方案中设置多启动项目,启动Jillzhang.Wcf.HostJIT.Client和Jillzhang.Wcf.HostJIT.Dispatcher,f5,然后按照提示进行输入
    会得到下面的效果:
    客户端:
    image 
    服务端:
    image 
    最终验证效果和原来想象中一致。OK!
    demo项目:https://files.cnblogs.com/jillzhang/Jillzhang.Wcf.HostJIT.rar
    作者:jillzhang
    出处:http://jillzhang.cnblogs.com/
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    ‘==’运算符和equals方法的区别
    ‘==’与equals的使用
    重写equals()方法的原则
    三目运算符---自动转换数据类型
    Spring安全框架——细粒度权限控制实现步骤
    Http协议学习笔记---Http协议介绍、协议格式、响应码说明
    Xml&Tomcat学习笔记03-----javaweb介绍、Tomcat介绍和使用
    Xml&Tomcat学习笔记02-----IDEA配置TomCat服务器
    Xml&Tomcat学习笔记01-----XML简介、语法、元素、属性、dom4j
    MVC概念
  • 原文地址:https://www.cnblogs.com/jillzhang/p/1320086.html
Copyright © 2011-2022 走看看