Skip to content
This repository has been archived by the owner on Mar 8, 2023. It is now read-only.

Client/Server API #103

Open
abhinav opened this issue Jan 9, 2016 · 4 comments
Open

Client/Server API #103

abhinav opened this issue Jan 9, 2016 · 4 comments

Comments

@abhinav
Copy link
Contributor

abhinav commented Jan 9, 2016

Since we want thriftrw to better support HTTP (and presumably other transports that use Thrift message envelopes), let's get this discussion started.

Here's an inital API proposal for sync and async clients and servers.


For client-side support, we create a separate thriftrw-client Python package (probably a subdirectory under this project for now) that provides a thriftrw_client module with the following API:

import thriftrw
import requests
from thriftrw_client import Client

keyvalue = thriftrw.load('keyvalue.thrift')
client = Client(keyvalue.KeyValue, transport)
client.putValue("foo", keyvalue.Value("bar"))

try:
  client.getValue("foo")
except keyvalue.KeyNotFoundError:
  # ...

Where transport is any function that takes bytes to send the request and returns bytes of the response. This should suffice for synchronous clients and gevent based clients. We can provide default transport constructors like http_transport("http://.../thrift") will use httplib or something.

For asynchronous clients, we'll probably want to use thriftrw_client.tornado (and maybe later thriftrw_client.asyncio if needed) with the same interface except transport returns a future instead of the response as-is. These can be extras in the thriftrw-client's dependencies.

Open question: A generic Client class like that will have to do a bunch of introspection of the Thrift module at runtime. Do we want to do something similar to TChannel's client_for instead which generates the Client class specific to that service in one go?


For server-side, same story: A thriftrw-server package in a subdirectory with a thriftrw_server module that provides the following API:

import thriftrw
from thriftrw_server import Dispatcher

keyvalue = thriftrw.load('keyvalue.thrift')

dispatcher = Dispatcher(keyvalue.KeyValue)

@dispatcher.register
def getValue(self, key):
    # ...

@dispatcher.register
def putValue(self, key, value):
    # ...

# You can now do dispatcher(request_bytes) -> response_bytes 
#
# So, if using Flask:

@app.route('/thrift')
def keyvalue():
    return dispatcher(request.data)

Same API for async servers except calling dispatcher returns a future. It'll plug into Tornado like:

class ThriftRequestHandler(tornado.web.RequestHandler):

    @gen.coroutine
    def post(self):
        response = yield disptacher(self.request.body)
        self.write(response)

Open question: What to name Dispatcher?


Other things to consider while looking at this API:

  • These APIs expect to do message envelope wrapping. We want that to be very clear. For non-TChannel users, that's the expected default. For TChannel, it's not.
  • Thrift has a TMultiplexedProtocol that wraps the real protocol (binary/json/whatever) except that it changes the method name in the message envelope to "$serviceName:$methodName". This allows the server to dispatch to multiple Thrift services on the same endpoint. How will this API be affected if we wanted to support that? The reason I want us to consider this early on is that with multiple Thrift services in the same file, it's definitely possible and common to multiplex Thrift services.
@abhinav abhinav changed the title HTTP API proposal Server/client API Jan 9, 2016
@abhinav abhinav changed the title Server/client API Client/Server API Jan 9, 2016
@abhinav
Copy link
Contributor Author

abhinav commented Jan 9, 2016

@abhinav
Copy link
Contributor Author

abhinav commented Jan 9, 2016

Server side API may need a little bit more thought. This API won't support per request state to be passed in. Like if you're implementing an HTTP service you may want to inject information from the raw HTTP request (headers, tracing information, etc.) into the endpoint handler.

Actually that brings up the same issue in the client API. We want users to be able to inject per request information from the call to the transport.

@HelloGrayson
Copy link
Contributor

Looking good to me. One thing:

Seems like you want to always return concurrent.futures.Future in the case of sync - any reason we can't do that?

@HelloGrayson
Copy link
Contributor

@abhinav I'm not sure that we need to support request metadata in this library - after all, Apache Thrift suffers from the same issue. We'll be solving this in our RPC library.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants