MSMQ 通过 WCF 吞咽格式错误的消息,没有错误,没有警告

发布于 2024-09-25 09:08:38 字数 3874 浏览 4 评论 0原文

我有一个使用 WCF 响应 MSMQ 上的消息的服务。当消息格式正确时,它就可以正常工作,我们很高兴。当消息用于不同的服务并且操作不同时,消息保留在队列中,我们获取日志条目,我们修复,并且我们很高兴。

但是,当消息具有正确的操作,但包含服务器无法反序列化的 XML 元素时,消息就会从队列中消失,我们不会获得日志条目,而且我们几乎不知道发生了什么。我们不高兴。

我尝试添加 IErrorHandler。我尝试添加故障事件处理程序。我唯一能做的就是使用 WCF 中内置的诊断日志记录。有什么方法可以使用我自己的记录器或错误处理程序来响应代码中的此类错误?

我的队列恰好是事务性的,但这似乎对这个问题没有任何区别。这种情况发生在 Windows Server 2003 上的 MSMQ 版本 3.0 上。

此源代码显示了该问题:

class Program : IErrorHandler {
    private static Uri QueueUri = new Uri("net.msmq://localhost/private/server_queue");

    static void Main(string[] args) {
        Program program = new Program();

        if (args.Length > 0) {
            if (args[0] == "server") {
                program.Server();
            } else if (args[0] == "bad") {
                program.BadClient();
            }
        } else {
            program.Client();
        }
    }

    public void BadClient() {
        using (var client = new BadServiceClient(QueueUri)) {
            client.Do(new [] {new BadStuff { BadMessage = "hi" }});
        }
    }

    public void Client() {
        using (var client = new ServiceClient(QueueUri)) {
            client.Do(new [] {new Stuff {Message = "hi"}});
        }
    }

    public void Server() {
        var serviceHost = new ServiceHost(typeof(Service));
        serviceHost.AddServiceEndpoint(typeof (IService), new NetMsmqBinding(), QueueUri);
        serviceHost.Open();

        serviceHost.Faulted += serviceHost_Faulted;
        serviceHost.UnknownMessageReceived += new EventHandler<UnknownMessageReceivedEventArgs>(serviceHost_UnknownMessageReceived);
        foreach (ChannelDispatcher dispatcher in serviceHost.ChannelDispatchers) {
            dispatcher.ErrorHandlers.Add(this);
        }

        Console.WriteLine("press the any key");
        Console.ReadKey(true);
    }

    void serviceHost_UnknownMessageReceived(object sender, UnknownMessageReceivedEventArgs e) {
        Console.WriteLine("unknown message: {0}", e);
    }

    private static void serviceHost_Faulted(object sender, EventArgs e) {
        Console.WriteLine("fault: {0}", e);
    }

    public void ProvideFault(Exception error, MessageVersion version, ref Message fault) {
        Console.WriteLine("handling error: {0}", error);
    }

    public bool HandleError(Exception error) {
        Console.WriteLine("handling error: {0}", error);
        return true;
    }
}

[ServiceContract]
public interface IService {
    [OperationContract(IsOneWay = true, Action = "do")]
    void Do(IEnumerable<Stuff> stuffs);
}

[ServiceContract]
public interface IBadService {
    [OperationContract(IsOneWay = true, Action = "do")]
    void Do(IEnumerable<BadStuff> stuffs);
}

class ServiceClient : ClientBase<IService>, IService {
    public ServiceClient(Uri uri) : base(new NetMsmqBinding(), new EndpointAddress(uri)) {}

    public void Do(IEnumerable<Stuff> stuffs) {
        Channel.Do(stuffs);
    }
}

class BadServiceClient : ClientBase<IBadService>, IBadService {
    public BadServiceClient(Uri uri) : base(new NetMsmqBinding(), new EndpointAddress(uri)) {}

    public void Do(IEnumerable<BadStuff> stuffs) {
        Channel.Do(stuffs);
    }
}

[ServiceBehavior(TransactionTimeout = "00:5:00", InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Single)]
class Service : IService {
    [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
    public void Do(IEnumerable<Stuff> stuffs) {
        foreach (var stuff in stuffs) {
            Console.WriteLine("service doing stuff, message: " + stuff.Message);
        }
    }
}

[DataContract]
public class Stuff {
    [DataMember]
    public string Message;
}

[DataContract]
public class BadStuff : Stuff {
    [DataMember]
    public string BadMessage;
}

I have a service that responds to messages on an MSMQ, using WCF. When the message is well formatted, it works as normal, and we're happy. When the message is intended for a different service, and the action is different, the message stays on the queue, we get log entries, we fix, and we're happy.

But when the message has the right action, but contains XML elements that cannot be deserialized by the server, the message disappears from the queue, we don't get log entries and we have next to no idea what's happened. we're not happy.

I've tried adding an IErrorHandler. I've tried adding a Faulted event handler. The only thing I can do is use the diagnostic logging built into WCF. Is there any way I can respond to an error like this in code, with my own logger or error handler?

My queues happen to be transactional, but it doesn't seem to any difference to this problem. This happens on MSMQ version 3.0, on Windows Server 2003.

This source code exhibits the problem:

class Program : IErrorHandler {
    private static Uri QueueUri = new Uri("net.msmq://localhost/private/server_queue");

    static void Main(string[] args) {
        Program program = new Program();

        if (args.Length > 0) {
            if (args[0] == "server") {
                program.Server();
            } else if (args[0] == "bad") {
                program.BadClient();
            }
        } else {
            program.Client();
        }
    }

    public void BadClient() {
        using (var client = new BadServiceClient(QueueUri)) {
            client.Do(new [] {new BadStuff { BadMessage = "hi" }});
        }
    }

    public void Client() {
        using (var client = new ServiceClient(QueueUri)) {
            client.Do(new [] {new Stuff {Message = "hi"}});
        }
    }

    public void Server() {
        var serviceHost = new ServiceHost(typeof(Service));
        serviceHost.AddServiceEndpoint(typeof (IService), new NetMsmqBinding(), QueueUri);
        serviceHost.Open();

        serviceHost.Faulted += serviceHost_Faulted;
        serviceHost.UnknownMessageReceived += new EventHandler<UnknownMessageReceivedEventArgs>(serviceHost_UnknownMessageReceived);
        foreach (ChannelDispatcher dispatcher in serviceHost.ChannelDispatchers) {
            dispatcher.ErrorHandlers.Add(this);
        }

        Console.WriteLine("press the any key");
        Console.ReadKey(true);
    }

    void serviceHost_UnknownMessageReceived(object sender, UnknownMessageReceivedEventArgs e) {
        Console.WriteLine("unknown message: {0}", e);
    }

    private static void serviceHost_Faulted(object sender, EventArgs e) {
        Console.WriteLine("fault: {0}", e);
    }

    public void ProvideFault(Exception error, MessageVersion version, ref Message fault) {
        Console.WriteLine("handling error: {0}", error);
    }

    public bool HandleError(Exception error) {
        Console.WriteLine("handling error: {0}", error);
        return true;
    }
}

[ServiceContract]
public interface IService {
    [OperationContract(IsOneWay = true, Action = "do")]
    void Do(IEnumerable<Stuff> stuffs);
}

[ServiceContract]
public interface IBadService {
    [OperationContract(IsOneWay = true, Action = "do")]
    void Do(IEnumerable<BadStuff> stuffs);
}

class ServiceClient : ClientBase<IService>, IService {
    public ServiceClient(Uri uri) : base(new NetMsmqBinding(), new EndpointAddress(uri)) {}

    public void Do(IEnumerable<Stuff> stuffs) {
        Channel.Do(stuffs);
    }
}

class BadServiceClient : ClientBase<IBadService>, IBadService {
    public BadServiceClient(Uri uri) : base(new NetMsmqBinding(), new EndpointAddress(uri)) {}

    public void Do(IEnumerable<BadStuff> stuffs) {
        Channel.Do(stuffs);
    }
}

[ServiceBehavior(TransactionTimeout = "00:5:00", InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Single)]
class Service : IService {
    [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
    public void Do(IEnumerable<Stuff> stuffs) {
        foreach (var stuff in stuffs) {
            Console.WriteLine("service doing stuff, message: " + stuff.Message);
        }
    }
}

[DataContract]
public class Stuff {
    [DataMember]
    public string Message;
}

[DataContract]
public class BadStuff : Stuff {
    [DataMember]
    public string BadMessage;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

鸠书 2024-10-02 09:08:38

这已经很晚了,但为可能有像我这样的类似问题的其他人添加答案。请注意,使用 MSMQ 与无法提取格式错误的消息无关。正如 Tim 提到的,如果由于 XML 格式错误(例如包含 & 字符)而无法反序列化 XML,那么使用 IDispatchMessageInspector 实现将不会向您提供消息。

我唯一能找到的是打开常规 WCF 消息跟踪,其中 logMalformedMessages 根据本文档在 messageLogging 元素下设置为 true (希望 MS 不要移动链接)

: microsoft.com/en-us/library/ms730064%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396" rel="nofollow noreferrer">配置消息日志记录

This is very late, but adding an answer for others who may have a similar issue like me. Note the use of MSMQ is not related to not being able to extract the malformed message. As Tim has mentioned using a IDispatchMessageInspector implementation will not give you the message if it fails to deserialise the XML because it's malformed (e.g contains an & char).

The only thing I could find is to turn on regular WCF message tracing where logMalformedMessages is set to true under the messageLogging element as per this documentation (hopefully MS don't move the link):

Configuring Message Logging

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文