在没有 HttpClient 的情况下从同一站点上的另一个分离的 WCF 服务调用 WCF 服务

Call WCF Service from another decoupled WCF service on the same site without HttpClient

我有一个插件模型架构来创建我的 Restful WCF 服务。

(我们需要几年时间才能从 WCF 迁移到 Web Api,因此,迁移到 Web Api 并不是一个完全正确的解决方案。)

我解耦了不相互引用的 WCF 微服务。

EnityAWebService 从配置中知道服务 EntityBWebService 存在,但不引用它。

EntityAWebService 和 EntityBWebService 是插件。因此,它们可以在同一站点上加载。

EntityAWebService 使用配置信息调用 EntityBWebService。 EntityBWebService 可以在同一台服务器或不同的服务器上。 - 如果在不同的服务器上,代码将继续使用 HttpClient。 - 如果在同一台服务器上,则生成消息并通过通道发送它,而无需通过 HttpClient、操作系统的网络和 IIS。

下面是架构。橘子是我要创造的。

使用 HttpClient 意味着 EntityAWebService 发送一条消息,该消息将到达操作系统网络层并通过 IIS。两者都不是必需的。它会导致性能问题,并且随着实体插件的增加,套接字的数量也会增加,甚至使用单例 httpclient,套接字也会泄漏。

体系结构中的橙色是尚不存在的。

代码知道调用实体 B Web 服务的 Url、消息内容和 headers。在橙色框表示的代码中,我将如何模拟 IIS 如何通过行为将调用转发到端点?

仅供参考,我当前的项目太复杂 post,所以我会创建一个示例并尽快 post。

示例项目:https://github.com/rhyous/DecoupledWcfServices

原来我不需要使用命名管道。然而,研究如何使用命名管道教会了我需要知道的东西。我只需要使用反射和 ChannelFactory。由于 IIS 托管的 ChannelFactory 已经存在,命名管道将是多余的。

此处的示例项目:https://github.com/rhyous/DecoupledWcfServices

下面是适当的片段(解决方案的核心)。

using System;
using System.Collections.Specialized;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Web;

namespace DecoupledWcfServices
{
    /// <summary>
    /// Service 1 and Service 2 are in the same namespace in this project
    /// </summary>
    public class MessageBus
    {
        public string CallOtherWcfService(string url, object content, NameValueCollection headers)
        {
            var service = GetServiceName(url);
                try
            {
                var netPipeUrl = $"http://localhost:54412/{service}/{service}.svc";
                var serviceContractType = typeof(IService2);
                var genericChannelFactoryType = typeof(WebChannelFactory<>).MakeGenericType(serviceContractType);
                var binding = new WebHttpBinding();

                var channelFactory = Activator.CreateInstance(genericChannelFactoryType, binding, new Uri(netPipeUrl)) as WebChannelFactory<IService2>; // I actually won't know it is an IService2 in my project, but getting this far should be enough
                var proxy = channelFactory.CreateChannel() as IService2; 
                using (new OperationContextScope((IContextChannel)proxy))
                {
                    var task = proxy.GetData("some data"); // Might need more work here to know which method to call based on the Url
                    task.Wait();
                    return task.Result; // Serialized JSON
                }
            }
            catch (Exception)
            {
                throw;
            }
        }

        internal string GetServiceName(string url)
        {
            var index = url.IndexOf(".svc");
            var sub = url.Substring(0, index);
            index = sub.LastIndexOf("/") + 1;
            var sub2 = url.Substring(index, sub.Length - index);
            return sub2;
        }
    }
}