How does in duplex system WCF differentiate between different channel instances? - wcf

Uhm, I’m utterly lost so any help would be much appreciated
The
OperationContext.Current.InstanceContext
is the context of the current service
instance that the incoming channel is
using.
In a Duplex system, the service can
callback to the client via a
CallbackContract. This
CallbackContract is much like a
service on the client side that is
listening for calls from the service
on the channel that the client has
opened. This “client callback
service” can only be accessed via the
same channel it used on the service
and therefore only that service has
access to it.
a) So in duplex systems the same channel instance with which client-side sends messages to the service, is also used by client to receive messages from the service?
b) If in request-reply system a client uses particular channel instance clientChannel to send a message to the service, then I assume this same instance ( thus clientChannel ) needs to stay opened until service sends back a reply to this instance, while in duplex system clientChannel needs to stay opened until the session is closed?
c) I’m assuming such behaviour since as far as I can tell each channel instance has a unique address ( or ID ) which helps to differentiate it from other channel instances ) running on the same client? And when service sends back a message, it also specifies an ID of this channel?
Thus when in Duplex system client calls a service, WCF creates ( on client side ) a channel instance clientChannel, which sends a message over the wire. On server’s side WCF creates channel instance serverChannel, which delivers the message to requested operation(method). When this method wants to callback to the client via CallbackContract, it uses InstanceContext.GetCallBackChannel<> to create a channel, which among other things contains the ID of the channel that called a service ( thus it contains an exact address or ID of clientChannel )?
d) Does in duplex systems client use the same channel instance to call any of endpoint’s operations?
Thank you

I am not sure but here is how I understand this for a Duplex mode communication.
I had a look at the InstanceContext class defined in the System.ServiceModel assembly using dotPeek decompiler.
Internally there is a call
this.channels = new ServiceChannelManager(this);
That means, it is creating channel using a ServiceChannelManager passing in the instance of the same InstanceContext.
This way it keeping a track of the Channel with the instance of InstanceContext.
Then is binds Incoming channel (Service to Client) requests in method that is implemented as :
internal void BindIncomingChannel(ServiceChannel channel)
{
this.ThrowIfDisposed();
channel.InstanceContext = this;
IChannel channel1 = (IChannel) channel.Proxy;
this.channels.AddIncomingChannel(channel1);
if (channel1 == null)
return;
switch (channel.State)
{
case CommunicationState.Closing:
case CommunicationState.Closed:
case CommunicationState.Faulted:
this.channels.RemoveChannel(channel1);
break;
}
}
So to answer your queries :
a. Yes, it internally maintains the Service and InstanceContext (which creates a channel) relations for
calls between Client and Service.
b. Yes, the channel needs to stay opened untill the Service replies back to the context, in which the InstanceContext
will take care of closing the channel.
c. Each client has a unique Session Id, but the InstanceContext type at the Service depends on the InstanceContextMode
used at the Service on the implementation of the Contract.
d. It uses the same channel. InstanceContext maintains a count of IncomingChannel and Outgoing channel.
Incoming channel are the one that are Service to Client directed and Outgoing are Client to Service directed.
You can see this count using debugger in VS.
For the sake of further clarification, as far as the other behavior for a Duplex service is concerned, here is how we can look at the behavior of InstanceContext and how channel instance is created :
I created a Duplex service demo :
[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IServiceDuplexCallback))]
public interface IServiceClass
{
[OperationContract(IsOneWay = true)]
void Add(int num1);
}
This contract is implemented as :
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class ServiceClass : IServiceClass
{
int result = 0;
public void Add(int num1)
{
result += num1;
callBack.Calculate(result);
}
IServiceDuplexCallback callBack
{
get
{
return OperationContext.Current.GetCallbackChannel<IServiceDuplexCallback>();
}
}
}
In this implementation notice the first line where InstanceContextMode is set to PerCall. The default is PerSession.
This enumeration has three options:
PerCall - New instance of InstanceContext used for every call independent of Session
PerSession - New instance used for every session
Single - A single instance of InstanceContext used for all the clients.
I created a client which use NetTcpBinding to connect with Service :
InstanceContext ic = new InstanceContext(new TCPCallbackHandler(ref textBox1));
TCP.ServiceClassClient client = new TCP.ServiceClassClient(ic);
// First call to Add method of the Contract
client.Add(val1);
// Second call to Add method of the Contract
client.Add(val2);
TCPCallbackHandler is the class in the Client that implements the Callback contract as:
public class TCPCallbackHandler : TCP.IServiceClassCallback
{
TextBox t;
public TCPCallbackHandler(ref TextBox t1)
{
t = t1;
}
public void Calculate(int result)
{
t.Text += OperationContext.Current.SessionId + " " + result.ToString();
}
}
To see the behavior of the InstanceContext, I started the service and then started two clients with
each enumeration operation as discussed above. Here are the results :
1 - PerCall
Client 1 : urn:uuid:4c5f3d8b-9203-4f25-b09a-839089ecbe54 5 - urn:uuid:4c5f3d8b-9203-4f25-b09a-839089ecbe54 5
Client 2 : urn:uuid:e101d2a7-ae41-4929-9789-6d43abf97f01 5 - urn:uuid:e101d2a7-ae41-4929-9789-6d43abf97f01 5
Here - urn:uuid:4c5f3d8b-9203-4f25-b09a-839089ecbe54 is SessionId
Since for each client Add is called twice in the Client, and in PerCall -> new instance of InstanceContext is created every call, we create a new instance of ServiceClass for both the calls of every client. Point to note here is that new instance is created even for the same session
// First call to Add method of the Contract
client.Add(val1); -> New Instance of ServiceClass created and value will be incremented to 5
// Second call to Add method of the Contract
client.Add(val2); -> New Instance of ServiceClass created and value will be incremented to 5
2 - PerSession
Client 1 : urn:uuid:4c5f3d8b-9203-4f25-b09a-839089ecbe54 5 - urn:uuid:4c5f3d8b-9203-4f25-b09a-839089ecbe54 10
Client 2 : urn:uuid:e101d2a7-ae41-4929-9789-6d43abf97f01 5 - urn:uuid:e101d2a7-ae41-4929-9789-6d43abf97f01 10
Here the instance of ServiceClass is separate for both the client as they have different sessions running. So the increment in the calls is 0 -> 5 -> 10 (for both client separately)
3 - Single
Client 1 : urn:uuid:4c5f3d8b-9203-4f25-b09a-839089ecbe54 5 - urn:uuid:4c5f3d8b-9203-4f25-b09a-839089ecbe54 10
Client 2 : urn:uuid:e101d2a7-ae41-4929-9789-6d43abf97f01 15 - urn:uuid:e101d2a7-ae41-4929-9789-6d43abf97f01 20
Here the same instance of ServiceClass is shared by all clients so we have 0 -> 5 -> 10 in first client. The second client will increment in the same instance, so we get 10 -> 15 -> 20.
This will behave differently as per the call and may give result like when invoked at the same time from the clients.
Client 1 : urn:uuid:4c5f3d8b-9203-4f25-b09a-839089ecbe54 5 - urn:uuid:4c5f3d8b-9203-4f25-b09a-839089ecbe54 15
Client 2 : urn:uuid:e101d2a7-ae41-4929-9789-6d43abf97f01 10 - urn:uuid:e101d2a7-ae41-4929-9789-6d43abf97f01 20
Hope this helps!

Related

RSocket Client Side Load balancing for multilevel Microservices

In our microservices based application we have layer of Microservices i.e User/Rest client calling MS1 and MS1 calling MS2 .. and so on. For simplicity and to present the actual problem I will mention only client, MS1 and MS2 here. We are tyring to implement MS to MS calls using RSocket communication protocol
(Request Response Interaction Model).
We also need to implement client side load balancing in RSocket as we will be running mulitple PODS(instances) of MSs in kubernetes enviroment.
We are observing following problem in client side Load Balancing in local Unit testing also(mentioning this so as to rule out any issue with deployment/kubernetes env. etc)
1.) Client -> MS1(Instance1) -> MS2(Instance1) ( RSocket Load Balancing code is Working fine and each request is processed)
Client -> MS1(Instance1,Intanse2) -> MS2(Instance1) (Load Balancing is working fine)
Client -> MS1(Instance1)->S2(Instance1c,Instance2) (Only 1st request passes i.e Client -> MS1(Instance1 -> MS2(Instance1)
and then 2nd Request Client -> MS1(Intance1) (Stops here and call to MS2(Instance2) is not initiated
Clinet -> MS1(Instance1,Instance2) -> MS2(Instance1,Instance2)
Only 2 request gets successfully processed Client -> MS1(Instance1) -> MS2(Intstance1)
and Client -> MS1(Instance2) -> MS2(Instance2)
futher the RSocket call does not happen and as per KeepAliveInterval and KeepAliveMaxLifeTime
client RSocket connection id disposed with error
Caused by: ConnectionErrorException (0x101): No keep-alive acks for 30000 ms
at io.rsocket.core.RSocketRequester.lambda$tryTerminateOnKeepAlive$2(RSocketRequester.java:299)
Now let us see how I have implmented client side Load Balancing code.
I am relying on Flux<List and 3 important Beans are
private Mono<List<LoadbalanceTarget>> targets()
{
Mono<List<LoadbalanceTarget>> mono = Mono.fromSupplier(()->serviceRegistry.getServerInstances()
.stream()
.map(server -> LoadbalanceTarget.from(getLoadBalanceTargetKey(server),
TcpClientTransport.create(TcpClient
.create()
.option(ChannelOption.TCP_NODELAY, true)
.option(ChannelOption.ALLOW_HALF_CLOSURE,true)
.host(server.getHost())
.port(server.getPort()))))
.collect(Collectors.toList()));
return mono;
#Bean
public Flux<List<LoadbalanceTarget>> targetFluxForMathService2()
{
return Flux.from(targets());
}
Note: for testing I am faking serviceRegistry and returing list of hard coded RSocket server instances( Host and port)
#Bean
public RSocketRequester rSocketRequester2(Flux<List<LoadbalanceTarget>> targetFluxForMathService2) {
RSocketRequester rSocketRequester = this.builder.rsocketConnector(configurer->
configurer.keepAlive(Duration.ofSeconds(10), Duration.ofSeconds(30))
.reconnect(Retry.fixedDelay(3,
Duration.ofSeconds(1))
.doBeforeRetry(s-> System.out.println("Disconnected, retrying to connect"))))
.transports(targetFluxForMathService2, new RoundRobinLoadbalanceStrategy());
return rSocketRequester;
}
private String getLoadBalanceTargetKey(RSocketServerInstance server)
{
return server.getHost() + server.getPort();
}
Any help will be highly appreciated.

Native way to send custom object in JMS queue? (citrus-simulator)

I'm looking for a native (correct, in terms of the framework) way to send thirdparty (custom) Object to JMS in citrus-simulator?
I have tried:
scenario
.send()
.payloadModel(myObject);
but in JMS queue myObject appears as com.consol.citrus.message.DefaultMessage instead of com...myObject.
Example:
Scenario - receives http POST request (as trigger) and send's JMS Object to message queue.
config
...
.useObjectMessages(true)
...
scenario
...
import com.temafon.data.MORequest;
#Scenario("morequest")
#RequestMapping(value = "/simulator/morequest",method = RequestMethod.POST)
public class JmsMoRequestScenario extends AbstractSimulatorScenario {
#Override
public void run(ScenarioDesigner scenario) {
MORequest request = new MORequest(12345678901L, "USSD", "1172", "ON",
11L);
request.setSourcePort(3);
scenario
.receive()
.payload("getmorequest");
scenario
.send("jms.queue.destination")
.payloadModel(request);
After this case, I expect to get in jms.queue.destination something like screen 1
Expectation
Reality
P.S. I have implemented workaround already, with JavaActionBuilder and jmsTemplate.send in additional class and
scenario
.java(//Object).method(//instance");
But it doesn't seems like correct citrus-simulator way
Setting the payloadModel() in Citrus Java DSL will always result in some kind of marshalled representation of the object and this is not what you want. Neither is using .useObjectMessages(true) working for you because this results in the whole Citrus message object to be used as message payload.
You need to define a complete Citrus message object and that will remain untouched in terms of payload creation for the JMS destination. Citrus is then automatically using a JMS object message with proper object payload.
MORequest request = new MORequest(12345678901L, "USSD", "1172", "ON", 11L);
request.setSourcePort(3);
scenario
.receive()
.payload("getmorequest");
scenario
.send("jms.queue.destination")
.message(new JmsMessage(request));
Note that I am using the .message() fluent API instead of the payloadModel() API. The message API receives a Citrus message object such as com.consol.citrus.jms.message.JmsMessage or com.consol.citrus.message.DefaultMessage
Your custom domain model object request is used as constructor arg and will result in the JMS message as object payload as is. Of course MORequest must be of type java.io.Serializable

User destinations in a multi-server environment? (Spring WebSocket and RabbitMQ)

The documentation for Spring WebSockets states:
4.4.13. User Destinations
An application can send messages targeting a specific user, and Spring’s STOMP support recognizes destinations prefixed with "/user/" for this purpose. For example, a client might subscribe to the destination "/user/queue/position-updates". This destination will be handled by the UserDestinationMessageHandler and transformed into a destination unique to the user session, e.g. "/queue/position-updates-user123". This provides the convenience of subscribing to a generically named destination while at the same time ensuring no collisions with other users subscribing to the same destination so that each user can receive unique stock position updates.
Is this supposed to work in a multi-server environment with RabbitMQ as broker?
As far as I can tell, the queue name for a user is generated by appending the simpSessionId. When using the recommended client library stomp.js this results in the first user getting the queue name "/queue/position-updates-user0", the next gets "/queue/position-updates-user1" and so on.
This in turn means the first users to connect to different servers will subscribe to the same queue ("/queue/position-updates-user0").
The only reference to this I can find in the documentation is this:
In a multi-application server scenario a user destination may remain unresolved because the user is connected to a different server. In such cases you can configure a destination to broadcast unresolved messages to so that other servers have a chance to try. This can be done through the userDestinationBroadcast property of the MessageBrokerRegistry in Java config and the user-destination-broadcast attribute of the message-broker element in XML.
But this only makes the it possible to communicate with a user from a different server than the one where the web socket is established.
I feel I'm missing something? Is there anyway to configure Spring to be able to safely use MessagingTemplate.convertAndSendToUser(principal.getName(), destination, payload) in a multi-server environment?
If they need to be authenticated (I assume their credentials are stored in a database) you can always use their database unique user id to subscribe to.
What I do is when a user logs in they are automatically subscribed to two topics an account|system topic for system wide broadcasts and account|<userId> topic for specific broadcasts.
You could try something like notification|<userid> for each person to subscribe to then send messages to that topic and they will receive it.
Since user Ids are unique to each user you shouldn't have an issue within a clustered environment as long as each environment is hitting the same database information.
Here is my send method:
public static boolean send(Object msg, String topic) {
try {
String destination = topic;
String payload = toJson(msg); //jsonfiy the message
Message<byte[]> message = MessageBuilder.withPayload(payload.getBytes("UTF-8")).build();
template.send(destination, message);
return true;
} catch (Exception ex) {
logger.error(CommService.class.getName(), ex);
return false;
}
}
My destinations are preformatted so if i want to send a message to user with id of one the destinations looks something like /topic/account|1.
Ive created a ping pong controller that tests websockets for users who connect to see if their environment allows for websockets. I don't know if this will help you but this does work in my clustered environment.
/**
* Play ping pong between the client and server to see if web sockets work
* #param input the ping pong input
* #return the return data to check for connectivity
* #throws Exception exception
*/
#MessageMapping("/ping")
#SendToUser(value="/queue/pong", broadcast=false) // send only to the session that sent the request
public PingPong ping(PingPong input) throws Exception {
int receivedBytes = input.getData().length;
int pullBytes = input.getPull();
PingPong response = input;
if (pullBytes == 0) {
response.setData(new byte[0]);
} else if (pullBytes != receivedBytes) {
// create random byte array
byte[] data = randomService.nextBytes(pullBytes);
response.setData(data);
}
return response;
}

WCF Unique ID for each service method call

I'm logging using log4net, and I want to log a id that is unique for each serice method call. I dont need it unique across service calls, just within a method call. Is there any built in id i can use in wcf? I don't want to manually create a guid or something at the start of the method call.
e.g.
wcfMethod(int x)
{
log("xxx");
somework
log("yyy");
}
private log(string message)
{
var frame = new StackFrame(1);
var method = frame.GetMethod();
var type = method.DeclaringType;
var name = method.Name;
var log = LogManager.GetLogger(type);
// LOG ID HERE
ThreadContext.Properties["MessageId"] = OperationContext.Current.IncomingMessageHeaders.MessageId; // SOMETHING HERE
}
I've tried OperationContext.Current.IncomingMessageHeaders.MessageId but thats always null.
I've read about wcf instance correlation but i don't need something that complicated (e.g. unique across different method calls).
Please if anyone can help that would be much apprieciated. Thanks in advance.
Plain SOAP or REST has no such identification included in messages. You must use some additional feature or transport protocol (for example MSMQ) supporting identifications of messages. In case of MessageId you have to use SOAP service with WS-Addressing and this information must be passed from client.

WCF client timeout after 400 instance calls when not closing proxy

I am using the following code to investigate what happens when you fail to close the proxy:
class Program
{
static void Main()
{
for (int i = 1; i < 500; i++)
{
MakeTheCall(i);
}
Console.WriteLine("DONE");
Console.ReadKey();
}
private static void MakeTheCall(int i)
{
Console.Write("Call {0} - ", i);
var proxy = new ServiceReference1.TestServiceClient();
var result = proxy.LookUpCustomer("123456", new DateTime(1986, 1, 1));
Console.WriteLine(result.Email);
//proxy.Close();
}
}
The service is using net.Tcp binding, WAS hosted, all default values.
Running it, I get a timeout when i > 400. Why 400 - is this a setting somwhere? I expected it to be much less - equal to maxConnections.
By not closing the proxy, you are maintaining a session on the service. The maxConcurrentSessions throttling attribute controls how many sessions the service can accommodate. The default (in .NET 4.0) is 100 * Processor Count, so I am guessing that you have 4 processors (or cores) = 400 concurrent sessions?
The reason your test code is timing out is probably due to the default WCF service throttling and doesn't have anything to do with not disposing of the proxy object. To conserve client-side resource, you should always properly dispose the proxy instance.
I believe that a service host will only create up to 16 instances of a service by default which may be even less if the binding is set to use session of some sort. You're flooding it with around 400 requests within a few seconds. There are a set of WCF performance counters you can fire up and view the instancing of a WCF service. I knew all that prep for the WCF certification exam would come in really useful sometime :)