4

我有一个客户端应用程序,它会偶尔通知它的服务进度。对服务的方法调用标记为 IsOneWay=true,因为通知不需要任何返回值,我不想延迟。

客户端可能会向服务通知错误,然后它会终止。

问题是:单向方法调用发送消息后是否返回调用者代码?还是将消息排队,然后由另一个线程发送?

两个进程(客户端和服务)在同一台机器上,我注意到有时(当机器过载时)服务没有收到错误通知。我怀疑我提到的第二种选择会发生,但我不确定。

如果我是对的,我如何确保发送通知并保持方法单向?

4

6 回答 6

8

好问题。在客户端应用程序调用方法之前,它会打开通道。该通道用于所有数据通信。有两种发送方式:1) 可靠会话 - 当您的数据包被可靠地卸载并重新发送破解的数据包时,2) 排序 - 当服务上的请求按照它们从客户端传输的顺序计算时(而不是它们的传递方式) . 如果您有可靠的有序会话并且服务主机在关闭应用程序后遇到一些数据问题,主机将尝试询问客户端重新发送数据,并且在没有响应后拒绝您的所有请求。在其他情况(不可靠)打开通道后,您可以发送数据并破坏通信,如果没有异常,单向方法将计算您的请求。

为了测试服务问题的一些可能性(但不完全是您的客户问题很有帮助),我创建了一个解决方案:

1) 带有一个文件“IService1.cs”的库项目“WcfContracts”:

    [ServiceContract]
    public interface IService1
    {
        [OperationContract]
        void ThrowException();

        [OperationContract(IsOneWay=true)]
        void ThrowExceptionUseIsOneWay();
    }

2) 控制台项目“WcfConsoleHoster”,它引用了两个“WcfContracts”,由三个文件组成:

a) Service1.cs,即服务实现

public class Service1 : WcfContracts.IService1
{    
        public void ThrowException()
        {
            throw new Exception("Basic exception");
        }

        public void ThrowExceptionUseIsOneWay()
        {
            throw new Exception("Basic exception using IsOneWay=true");
        }
}

b) Program.cs,它有默认入口点,只是启动服务

static void Main(string[] args)
    {
        ServiceHost host = new ServiceHost(typeof(Service1));
        host.Open();
        Console.WriteLine("host 1 opened");
        Console.ReadKey();
    }

c) 服务“App.config”

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <services>
      <service behaviorConfiguration="behavourHttpGet" name="WcfConsoleHoster.Service1">
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8732/Design_Time_Addresses/WcfConsoleHoster/Service1/" />
          </baseAddresses>
        </host>
        <endpoint binding="wsHttpBinding" contract="WcfContracts.IService1" />        
        <endpoint address ="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="behavourHttpGet">
          <serviceMetadata httpGetEnabled="true" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

3)控制台项目“WcfConsoleClient”,简单调用服务

a) 在“Program.cs”中

Console.WriteLine("Wcf client. Press any key to start");
Console.ReadKey();
ChannelFactory<IService1> factory = new ChannelFactory<IService1>("Service1_Endpoint");
IService1 channel = factory.CreateChannel();
//Call service method
channel.ThrowException();

Console.WriteLine("Operation executed");
Console.ReadKey();

b) 客户端“App.config”

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <client>
      <endpoint name="Service1_Endpoint"
        address="http://localhost:8732/Design_Time_Addresses/WcfConsoleHoster/Service1/"
        binding="wsHttpBinding"
        contract="WcfContracts.IService1">
      </endpoint>
    </client>
  </system.serviceModel>
</configuration>

1. 抛出异常。首先,我们调用两种方法在服务器主机中引发异常。然后这个异常返回到客户端,通道在客户端引发它,应用程序被销毁。当然,您可以使用 try()catch{} 块来处理这个问题。

让我们通过调用channel.ThrowExceptionUseIsOneWay();. 服务主机出现异常,但客户端没有异常,我们得到“操作已执行”。重要的是要意识到该频道将无法用于下一次使用。

所以IsOneWay=true按预期工作 - 它仅以一种方式发送消息。您不能从方法返回任何对象(预期为 void),也不能使用 FaultContract,或在服务启动后获取 InvalidOperationException。

2. 线程.睡眠()。下一个测试是使用Thread.Sleep(). IService1扩展到

  [OperationContract]
  int ThreadSleep();

  [OperationContract(IsOneWay=true)]

而Service1.cs中的实现是等待5秒

public int ThreadSleep()
{
    System.Threading.Thread.Sleep(TimeSpan.FromSeconds(5));
    return 1;
}

public void ThreadSleepUseIsOneWay()
{
    System.Threading.Thread.Sleep(TimeSpan.FromSeconds(5));
}

现在对客户端进行一些修改以计算经过的调用时间

System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
stopwatch.Start();
//call methode
channel.ThreadSleep();
stopwatch.Stop();
Console.WriteLine(string.Format("Operation executed in {0} seconds", stopwatch.Elapsed.Seconds));
Console.ReadKey();

调用双向方法 ThreadSleep()的结果是“操作在 7 秒内执行”(线程休眠 5 秒 + 通道初始化 2 秒)。

One way methodwith callchannel.ThreadSleepUseIsOneWay()的结果是“0 秒”!无需等待服务响应!

最好使用NetNamedPipeBinding,它是在同一台机器上可靠且快速的连接。

于 2012-01-24T22:11:29.977 回答
2

Check out this post for some detailed info: http://kennyw.com/?p=130

Also, I believe that if you have reliable messaging enabled that the request would be verified as sent successfully, but as the above post notes, the service will end the connection after that point.

于 2008-12-06T23:38:50.013 回答
1

这可能是使用 netMsmqBinding 的好地方。所有 MSMQ 消息本质上都是 OneWay,因为队列本质上是断开连接的。一旦客户端将消息与 netMsmq 客户端进行排队,它就可以安全地关闭,服务器稍后可以拾取消息并处理它。

于 2009-01-31T19:43:23.407 回答
1

您不能“确保发送通知”......并且“保持方法单一”。这有点违背“OneWay”的意思:)

如果要确保消息发送,可以做TwoWay。您很可能不会注意到轻微的性能影响。如果服务器和客户端与您提到的位于同一台机器上……那么您根本不会注意到性能下降。

于 2008-12-06T19:50:04.123 回答
1

我有一个类似的问题(单向调用不做任何事情或抛出异常)。我发现负责将方法参数从 xml 转换回对象的反序列化器正在创建异常。我的服务方法永远无法到达,也没有返回异常,因为它是单向调用。在我暂时禁用“单向”之前我没有检测到它,所以我的异常被传播回客户端。

希望这可以帮助处于类似情况的其他人。

于 2013-03-22T17:16:39.997 回答
0

我同意蒂莫西的观点。我还想补充一点,WCF 服务为传入消息保留一个队列。如果服务无法像消息传入一样快地处理消息,该队列可能会变满。当传入队列变满时,WCF 将丢弃新消息。

我不确定如果单向消息被丢弃,客户端会发生什么。我假设没有抛出异常/错误,但我不确定。

于 2009-01-03T21:54:45.000 回答