Managing Server to Server Channels

Options
mookanana
edited April 2014 in Photon Server
Hi, I have researched for management of operation calls via different channels but somehow it isn't working for me.

I am trying to get operation calls sent via server to server to execute concurrently through different channels.

My setup:

I have a game server(client) that needs to connect to another server(server) to retrieve information. Both are on photon c#.

1. client connects to server using ConnectToServerUdp(ip, "myclientname", null, 255, 512). This establishes connection with 255 possible channels.

2. on the server, CreatePeer() catches the connection and instantiates a ServerPeerBase with the request.

3. on the client, CreateServerPeer() catches the callback from the server and instantiates a ServerPeerBase.

4. operation calls are made by calling SendOperationRequest(opReq, new SendParameters() { ChannelId=getNewChannelID() }) on the client's ServerPeerBase object.

5. every time an operation is called, channelID is incremented and wraps around at 254 since it is a byte and 255 is reserved for photon internal calls.

6. on the server, each call has a different channel ID. However, they are still being processed sequentially even though the client sends them in quick succession.

7. An operation takes 5 seconds is called twice at the same time. The net result is 10 seconds+ of wait time instead of both executing simultaneously. The server only picks up the second channel request when the first channel has finished responding. What am I doing wrong here?

Any help would be appreciated!

Comments

  • Hi, I've been struggling with this issue for a couple days and read all that I could. Any help on sending non-sequenced operations via channels?
  • Please excuse the late response.

    The UDP channels are not the solution for your issue. The channels are used to handle network traffic on "low" level - a good use case is to have one channel for unreliable messages and another one for reliable messages, so that your (rare) reliable messages are not delayed when the channel for unreliable messages is flooded.

    In your case, you don't have a network saturation issue. All your data arrives at the target server quickly, and it is then passed on to the managed code (your .NET application). There, it is handled by the Peer object.

    The "problem" is that all operations that arrive at the server are handled sequentially. This is by design - the "OnOperationRequest" method is executed on the peer's fiber, so that it guaranteed that everything in OnOperationRequest() is threadsafe. (You can think of the fiber as a queue - first in, first out; and execution happens sequentially - only when the execution of operation #1 is finished, the execution of operation #2 starts). This happens long after the data has arrived on whichever channel.

    To solve your issue, you can modify the "OnOperationRequest" method and move the long-running task to a different thread, so that "OnOperationRequest" returns immediately and is then able to handle the next incoming operation.

    In general, I would recommend to use TCP for server-to-server operations and get rid of the whole channel stuff, it won't help you much in your use case.

    Does this make sense? Please let me know if you have further questions.
  • Thank you for the reply Nicole!


    I have switched protocol from rUDP to TCP, but there is no difference in performance - I initially chose rUDP because in theory it's supposed to be faster without the handshaking, but other than that I can't tell how it affects speed.


    The OnOperationRequest method handling operations sequentially makes a lot of sense in my case. I have done what you suggested and my server now creates a separate worker thread for each request that OnOperationRequest receives. Each operation seems to be getting their responses in proper time. I am worried that using the peer object in multiple threads will cause memory allocation issues, as you have pointed out that OnOperationRequest is meant to be threadsafe. Do you have any follow-up advice on this?

    Overall, great solution provided! Thanks! I will update if there is any problems.
  • I'm glad to hear that you could solve your issue. :-)

    You won't neccessarily get memory allocation issues, you only need to look after thread safety yourself now.

    If your OnOperationRequest method modifies any members of the peer, you need to synchronize access to the peer's members, the same way as you would do in any other multithreaded application. You could use simple locks or reader-writer-locks for example. There are a few examples in our SDK in classes that are accessed by multiple threads, like the Lite.Caching.RoomCacheBase class. Keep your lock as short as possible so that you don't re-introduce the original problem of "waiting" threads again.

    It's hard to give general advice without knowing the details of your architecture, sorry :) Hope this helps nevertheless.

    PS: rUDP adds TCP-like functionality on top of UDP, so it does not make much difference... we found that TCP is most efficient for S2S communication in almost every case.