The MSMQ binding is designed to be employed in the intranet. It cannot go through
firewalls by default, and more importantly, it uses a Microsoft-specific encoding and
message format. Even if you could tunnel through the firewall, you would need the other
party to use WCF as well. While requiring WCF at both ends is a reasonable assumption in the
intranet, it is unrealistic to demand that from Internet-facing clients and services, and it
violates the core service-oriented principles that service boundaries should be explicit and
that the implementation technology used by a service should be immaterial to its clients.
That said, Internet services may benefit from queued calls just like intranet clients and
services, and yet the lack of an industry standard for such queued interoperability (and the
lack of support in WCF) prevents such interaction. The solution to that problem is a
technique I call the HTTP bridge. Unlike most of my other techniques
shown in this book, the HTTP bridge is a configuration pattern rather than a set of helper
classes. The HTTP bridge, as its name implies, is designed to provide queued calls support
for clients and services connected over the Internet. The bridge requires the use of the
WSHttpBinding
(rather than the basic binding) because
it is a transactional binding. There are two parts to the HTTP bridge. The bridge enables
WCF clients to queue up calls to an Internet service that uses the WS binding, and it
enables a WCF service that exposes an HTTP endpoint over the WS binding to queue up calls
from its Internet clients. You can use each part of the bridge separately, or you can use
them in conjunction. The bridge can only be used if the remote service contract can be
queued (that is, if the contract has only one-way operations), but that is usually the case;
otherwise, the client would not have been interested in the bridge in the first
place.
Since you cannot really queue up calls with the WS binding, you can facilitate that
instead using an intermediary bridging client and service. When the client wishes to queue
up a call against an Internet-based service, the client will in fact queue up the call
against a local (that is, intranet-based) queued service called MyClientHttpBridge
. In its processing of the queued call, the client-side
queued bridge service will use the WS binding to call the remote Internet-based service.
When an Internet-based service wishes to receive queued calls, it will use a queue. But
because non-WCF clients cannot access that queue over the Internet, the service will use a
façade: a dedicated connected service called MyServiceHttpBridge
that exposes a WS-binding endpoint. In its processing of
the Internet call, MyServiceHttpBridge
simply makes a
queued call against the local service. Figure 9-15 shows the HTTP
bridge architecture.
It is important to use transactions between MyClientHttpBridge
, the client side of the bridge, and the remote service,
and it is important to configure the service-side bridge (MyServiceHttpBridge
) to use the Client transaction mode discussed in Chapter 7. The rationale is that by using a single transaction from the
playback of the client call to the MyClientHttpBridge
to the MyServiceHttpBridge
(if present) you will
approximate the transactional delivery semantic of a normal queued call, as shown in Figure 9-16.
Compare Figure 9-16 with Figure 9-6. If the delivery transaction in the bridge
aborts for any reason, the message will roll back to the MyClientHttpBridge
queue for another retry. To maximize the chances of
successful delivery, you should also turn on reliability for the call between MyClientHttpBridge
and the remote service.
MyServiceHttpBridge
converts a regular connected
call over the WS binding into a queued call and posts it to the service queue. MyServiceHttpBridge
implements a contract that is similar, but
not identical, to that of the queued service. The reason is that the service-side bridge
should be able to participate in the incoming transaction, but transactions cannot flow
over one-way operations. The solution is to modify the contract to support (indeed,
mandate) transactions. For example, if this is the original service contract:
[ServiceContract]
public interface IMyContract
{
[OperationContract(IsOneWay = true
)]
void MyMethod( );
}
then MyServiceHttpBridge
should expose this
contract instead:
[ServiceContract] public interface IMyContractHttpBridge
{ [OperationContract] [TransactionFlow(TransactionFlowOption.Mandatory
)] void MyMethod( ); }
In essence, you need to set IsOneWay
to false
and use TransactionFlowOption.Mandatory
. For readability's sake, I recommend that you
also rename the interface by suffixing it with HttpBridge
. MyServiceHttpBridge
can be
hosted anywhere in the service's intranet, including in the service's own process. Example 9-30 shows the required configuration of
the service and its HTTP bridge.
Example 9-30. Service-side configuration of the HTTP bridge
<!-- MyService Config File --> <services> <service name = "MyService"> <endpoint address = "net.msmq://localhost/private/MyServiceQueue" binding = "netMsmqBinding" contract = "IMyContract" /> </service> </services> <!-- MyServiceHttpBridge Config File --> <services> <service name = "MyServiceHttpBridge"> <endpoint address = "http://localhost:8001/MyServiceHttpBridge" binding = "wsHttpBinding" bindingConfiguration = "ReliableTransactedHTTP" contract = "IMyContractHttpBridge" /> </service> </services> <client> <endpoint address = "net.msmq://localhost/private/MyServiceQueue" binding = "netMsmqBinding" contract = "IMyContract" /> </client> <bindings> <wsHttpBinding> <binding name = "ReliableTransactedHTTP" transactionFlow = "true"> <reliableSession enabled = "true"/> </binding> </wsHttpBinding> </bindings>
The service MyService
exposes a simple queued
endpoint with IMyContract
. The service MyServiceHttpBridge
exposes an endpoint with WSHttpBinding
and the IMyContractHttpBridge
contract. MyServiceHttpBridge
is also a client of the queued endpoint defined by the
service. Example 9-31 shows the corresponding
implementation. Note that MyServiceHttpBridge
is
configured for the Client transaction mode.
Example 9-31. Service-side implementation of the HTTP bridge
class MyService : IMyContract { //This call comes in over MSMQ [OperationBehavior(TransactionScopeRequired = true)] public void MyMethod( ) {...} } class MyServiceHttpBridge : IMyContractHttpBridge { //This call comes in over HTTP [OperationBehavior(TransactionScopeRequired = true)] public void MyMethod( ) { MyContractClient proxy = new MyContractClient( ); //This call goes out over MSMQ proxy.MyMethod( ); proxy.Close( ); } }
The client uses queued calls against the local MyClientHttpBridge
service. MyClientHttpBridge
can be hosted in the same process as the client, in a
different process, or even on a separate machine on the client's intranet. The local
MyClientHttpBridge
service uses the WSHttpBinding
to call the remote service. The client needs to
retrieve the metadata of the remote Internet service (such as the definition of IMyContractHttpBridge
) and convert it to a queued contract
(such as IMyContract
). Example 9-32 shows the required configuration of
the client and its HTTP bridge.
Example 9-32. Client-side configuration of the HTTP bridge
<!-- Client Config File --> <client> <endpoint address = "net.msmq://localhost/private/MyClientHttpBridgeQueue" binding = "netMsmqBinding" contract = "IMyContract" /> </client> <!-- MyClientHttpBridge Config File --> <services> <service name = "MyClientHttpBridge"> <endpoint address = "net.msmq://localhost/private/MyClientHttpBridgeQueue" binding = "netMsmqBinding" contract = "IMyContract" /> </service> </services> <client> <endpoint address = "http://localhost:8001/MyServiceHttpBridge" binding = "wsHttpBinding" bindingConfiguration = "ReliableTransactedHTTP" contract = "IMyContractHttpBridge" /> </client> <bindings> <wsHttpBinding> <binding name = "ReliableTransactedHTTP" transactionFlow = "true"> <reliableSession enabled = "true"/> </binding> </wsHttpBinding> </bindings>
MyClientHttpBridge
exposes a simple queued endpoint
with IMyContract
. MyClientHttpBridge
is also a client of the connected WS-binding endpoint
defined by the service. Example 9-33 shows
the corresponding implementation.
Example 9-33. Client-side implementation of the HTTP bridge
MyContractClient proxy = new MyContractClient( ); //This call goes out over MSMQ proxy.MyMethod( ); proxy.Close( ); //////////////// Client-Side Bridge Implementation //////////// class MyClientHttpBridge : IMyContract { //This call comes in over MSQM [OperationBehavior(TransactionScopeRequired = true)] public void MyMethod( ) { MyContractHttpBridgeClient proxy = new MyContractHttpBridgeClient( ); //This call goes out over HTTP proxy.MyMethod( ); proxy.Close( ); } }
3.136.22.179