使用 Azure 服务结构的默认客户端时如何将消息头添加到请求中?

Posted

技术标签:

【中文标题】使用 Azure 服务结构的默认客户端时如何将消息头添加到请求中?【英文标题】:How to add message header to the request when using default client of Azure service fabric? 【发布时间】:2016-03-13 23:33:12 【问题描述】:

我想知道是否可以将自定义消息标头注入到传出请求中以携带附加信息,而无需反序列化有效负载以完成诸如通过 messagesinspector 提供的 wcf 之类的请求的身份验证、验证或关联等功能?

【问题讨论】:

我创建了一个NuGet package,您可以轻松地将其添加到您的项目中以添加自定义标头和消息拦截。代码是公开的,如果你想看看。 【参考方案1】:

更新

使用 SDK v2,您现在可以(相对)轻松地修改 Reliable Services 和 Actors 的标头。请注意,在下面的示例中,为简洁起见,省略了一些包装器成员。

客户

我们使用ServiceProxyFactory 来创建代理,而不是静态的ServiceProxy。然后我们可以包装IServiceRemotingClientFactoryIServiceRemotingClient并拦截服务调用。 ActorProxyFactory 也可以这样做。请注意,这会覆盖 WcfServiceRemotingProviderAttribute 等属性的行为,因为我们自己明确指定了客户端工厂。

_proxyFactory = new ServiceProxyFactory(c => new ServiceRemotingClientFactoryWrapper(
 // we can use any factory here
 new WcfServiceRemotingClientFactory(callbackClient: c)));

    private class ServiceRemotingClientFactoryWrapper : IServiceRemotingClientFactory
    
        private readonly IServiceRemotingClientFactory _inner;

        public ServiceRemotingClientFactoryWrapper(IServiceRemotingClientFactory inner)
        
            _inner = inner;
        

        public async Task<IServiceRemotingClient> GetClientAsync(Uri serviceUri, ServicePartitionKey partitionKey, TargetReplicaSelector targetReplicaSelector,
            string listenerName, OperationRetrySettings retrySettings, CancellationToken cancellationToken)
        
            var client = await _inner.GetClientAsync(serviceUri, partitionKey, targetReplicaSelector, listenerName, retrySettings, cancellationToken).ConfigureAwait(false);
            return new ServiceRemotingClientWrapper(client);
        
    

    private class ServiceRemotingClientWrapper : IServiceRemotingClient
    
        private readonly IServiceRemotingClient _inner;

        public ServiceRemotingClientWrapper(IServiceRemotingClient inner)
        
            _inner = inner;
        

        public Task<byte[]> RequestResponseAsync(ServiceRemotingMessageHeaders messageHeaders, byte[] requestBody)
        
            // use messageHeaders.AddHeader() here
            return _inner.RequestResponseAsync(messageHeaders, requestBody);
        

        public void SendOneWay(ServiceRemotingMessageHeaders messageHeaders, byte[] requestBody)
        
            // use messageHeaders.AddHeader() here
            _inner.SendOneWay(messageHeaders, requestBody);
        
    

服务器

ServiceRemotingDispatcherActorServiceRemotingDispatcher 继承以检查标头。

class CustomServiceRemotingDispatcher : ServiceRemotingDispatcher

    public override async Task<byte[]> RequestResponseAsync(IServiceRemotingRequestContext requestContext, ServiceRemotingMessageHeaders messageHeaders, byte[] requestBody)
    
        // read messageHeaders here
        // or alternatively put them in an AsyncLocal<T> scope
        // so they can be accessed down the call chain
        return base.RequestResponseAsync(requestContext, messageHeaders, requestBody);
    

要使用这个类,我们需要通过直接创建通信监听器来覆盖ServiceRemotingProviderAttribute

class MyService : StatelessService

     protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
     
          yield return new ServiceInstanceListener(context => new WcfServiceRemotingListener(context, new CustomServiceRemotingDispatcher());
     

【讨论】:

我正朝着同一个方向前进,您的代码示例确实有很大帮助。但我仍然希望 Microsoft 提供相同的消息处理程序机制来启用此类用例。 ServiceInstanceListener 没有与 WcfServiceRemotingListener 相同的调度程序的构造函数参数。使用 Remoting 时如何添加调度程序? 问题是 ServiceInstanceListener 采用 ICommunicationListener 而不是 IServiceRemotingMessageHandler,并且该接口没有返回消息头的方法。 ServiceInstanceListener 中创建一个通信侦听器,所有远程通信侦听器(例如WcfServiceRemotingListener)都接受一个消息处理程序作为构造函数参数。我有这个代码工作。确保您使用的是最新的 (v2) SDK。 我已尝试实现此功能,但我得到:“提供的 URI 方案 'localhost' 无效;应为 'net.tcp'。”。我正在创建这样的代理:return proxyFactory.CreateServiceProxy&lt;IMyInterface&gt;( new Uri("fabric:/myapp/myserviceService"), new ServicePartitionKey(0));【参考方案2】:

几周前我问过the same question on the MSDN forum,但没有得到回复。

我查看了客户端库的源代码,但没有找到添加标头的方法。恐怕唯一的方法是将它们添加为方法调用的一部分。这可以通过使用请求类作为方法参数并为它们使用继承来完成。 (例如,带有标头 [Authorization, ClientInfo, ...] 的 RequestBase 类)。然后,您必须通过包装所有调用或手动设置来确保为每个请求设置这些标头。

非常感谢 Service Fabric 团队的进一步澄清。

【讨论】:

您在哪里找到客户端源代码?我检查了 api,有一个 ServiceRemotingHeaders 类让我觉得有办法将自定义标头添加到传出请求中。此外,客户端似乎使用 wcf 来执行通信。如果是这样,将 messageinspector 暴露给开发人员应该是可行的。 @Xiangdong 我用 ILSpy 查看了 Microsoft.ServiceFabric.Services.dll。 ServiceRemotingHeaders 似乎是要走的路,是的。但是我还没有找到从外部设置它们的方法。我认为 ServiceProxy.InvokeAsync() 是发起请求的地方,它在内部创建 ServiceRemotingMessageHeaders 变量并将其传递给下游 ServicePartitionClient。但也许我错过了什么......

以上是关于使用 Azure 服务结构的默认客户端时如何将消息头添加到请求中?的主要内容,如果未能解决你的问题,请参考以下文章

使用azure服务总线,如何将单个消息发布到多个队列?

如何配置 Azure 服务总线队列以将消息推送到客户端而不进行轮询?

使用 Windows Azure 的基于消息的体系结构

如何在节点js中向azure服务总线队列发送消息时将内容类型指定为application/json?

如何通过azure设备配置服务从azure功能向iot设备发送自定义错误消息?

如何删除 Azure 服务总线主题上的死信消息