Search code examples
thrift

Service composability in IDL when multiplexing in Apache Thrift


Since now Apache Thrift supports service multiplexing, I want to know if one can compose the services like so

service A {
  int methodA(1: int param)
}

service B
{ 
   A serviceAProxy,
   int methodB(1: int param)
}

Is there a way to achieve this effect with respect to the client code generated, ie. the second service can only be retrieved through a call to the first service?

ServiceBClient.GetServiceAProxy().MethodA()

Solution

  • No, you can't do that, at least not in exactly that way. This is because services and methods on one hand, and data structures on the other hand are two completely different kinds of animals. There is no such built-in datatype that allows you to hold a service as a data field.

    However, things are not lost. You have plenty of options to achieve the desired behaviour. To make things easier, I will call service A the inner service and service B the outer service in the following.

    Example: Limit the access to the inner service

    Design the inner service in a way, so that each method call needs a token whose validity can be checked. The token is only valid for a limited time frame and must be obtained by calling the outer service first:

    service Outer {
       AccessToken IssueToken( 1: Credentials creds)
    }
    
    service Inner {
       void DoSomething( 1: AccessToken token, 2: other args) 
            throws (1: AccessDenied ade)
       i32  DoSomethingElse( 1: AccessToken token) 
            throws (1: AccessDenied ade, 2: MyOtherError moe)
    }
    

    How AccessToken and Credentials and so on look like is completely up to you. They are just placeholders above, they may even consist of just a plain string field.

    Example: Get the current location of the inner service

    There are scenarios where the inner service may be located on different physical servers over time. This could be for various reasons, e.g. to implement some sort of load balancing, or to establish a centralized service directory, where clients do not know the exact location of the InnerService beforehand. They only know, where to ask.

    service Outer {
       LocationInfo QueryBestServiceLocation( 1: string serviceName) 
                    throws (1 : UnknownService use)
       list<LocationInfo> QueryAllServiceLocations( 1: string serviceName) 
                    throws (1 : UnknownService use)
    }
    
    service Inner {
       void DoSomething( ...)  throws ( ...)
       i32 DoSomethingElse( ...)  throws ( ...)
    }
    

    Again, LocationInfo is just a placeholder for an arbitrary data structure. I might add however, especially for the "service directory" scenario, there are already some battle-tested products out there, such as Apache ZooKeeper, who do a good job. But I think you get the idea.

    And what about multiplexing?

    Not that much, really. In fact you can achieve everything above without multiplexing. Where multiplexing comes into play is when you want to share one Thrift protocol/transport stack between multiple services. This could be a socket, that offers multiple services over the same connection.