[ 
https://issues.apache.org/jira/browse/THRIFT-3593?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=15367643#comment-15367643
 ] 

Sebastian Zenker commented on THRIFT-3593:
------------------------------------------

In our company, we currently building up our next generation products based on 
Thrift. Before we selected Thrift, we also did some evaluations of competing 
frameworks, but finally decided to go with Thrift. The reason behind that is 
basically its layered design which allows to replace a particular layer when 
you are unhappy with it. Push notifications are an essential feature for us. To 
implement notifications, we 
* extended the IDL syntax with keyword "signal" (similar to the oneway keyword)
* added a new transport layer which allows to send and receive whole messages 
(byte arrays) asynchronously (we call it AsyncTransport) 
* wrote our own C++ code generator plugin

Example:
The following service:

{code}
service FanController
{
        void setTargetTemperature(1: i32 t)
        i32 getTargetTemperature()
        signal void targetTemperatureChanged(1: i32 t)
        signal void actualTemperatureChanged(1: i32 t)
        signal void actualRPMChanged(1: i32 t)
}
{code}

gets translated into:

{code}
struct IFanController :
        public virtual apache::thrift::IService
{
        virtual ~IFanController() = default;
        virtual kj::Promise<void> setTargetTemperature(std::int32_t t, const 
apache::thrift::Duration& timeout = apache::thrift::Duration::zero()) 
ASSERT_NOEXCEPT = 0;
        virtual kj::Promise<std::int32_t> getTargetTemperature(const 
apache::thrift::Duration& timeout = apache::thrift::Duration::zero()) 
ASSERT_NOEXCEPT = 0;
        boost::signals2::signal<void (std::int32_t t)> targetTemperatureChanged;
        boost::signals2::signal<void (std::int32_t t)> actualTemperatureChanged;
        boost::signals2::signal<void (std::int32_t t)> actualRPMChanged;
};
{code}

* All "normal" functions are now non-blocking and return a kj::Promise<> 
instead. A promise is just a placeholder for the real return value. This is 
very similar to std::future<> but is single threaded.
* All methods which are marked with the "signal" keyword are mapped to 
boost::signal2 signals.
* Everything is handled in one thread -> of course this requires some kind of 
mainloop behind the scenes. Currently we run on top of Qt's QEventloop, but I'm 
sure it should be very straight forward to support other mainloop 
implementations as well.

I really would love to make this publicly available on GitHub, but the culture 
and mind-set of the company I'm working for doesn't allow me to do that. If 
someone of you guys is interested, please leave a comment here. This may would 
make it easier for me to convince my employer to make it public. I also have 
some powerpoint slides available which describe the concept and implementation 
in more detail. If someone is interested, I probably can share them via a Webex 
session.

> Enable server-side to issue requests to clients on the same connection (push 
> notifications)
> -------------------------------------------------------------------------------------------
>
>                 Key: THRIFT-3593
>                 URL: https://issues.apache.org/jira/browse/THRIFT-3593
>             Project: Thrift
>          Issue Type: Wish
>          Components: AS3 - Compiler, AS3 - Library, C glib - Compiler, C glib 
> - Library, C# - Compiler, C# - Library, C++ - Compiler, C++ - Library, Cocoa 
> - Compiler, Cocoa - Library, Compiler (General), D - Compiler, D - Library, 
> Dart - Compiler, Dart - Library, Delphi - Compiler, Delphi - Library, 
> Documentation, Erlang - Compiler, Erlang - Library, Go - Compiler, Go - 
> Library, Haskell - Compiler, Haskell - Library, Haxe - Compiler, Haxe - 
> Library, Java - Compiler, Java - Library, JavaScript - Compiler, JavaScript - 
> Library, Lua - Compiler, Lua - Library, Node.js - Compiler, Node.js - 
> Library, Perl - Compiler, Perl - Library, PHP - Compiler, PHP - Library, 
> Python - Compiler, Python - Library, Ruby - Compiler, Ruby - Library, 
> Smalltalk - Compiler, Smalltalk - Library, Swift - Compiler, Test Suite, 
> Tutorial
>            Reporter: Sebastian Zenker
>              Labels: push
>
> In our applications, we have very often the use case, that we actively want 
> to inform all connected Thrift clients about state changes on the server 
> side. Let me use a stupid example to explain what I whish. Let's assume we 
> have service which represents a fan controller. This service allows to 
> configure a target temperature and can be requested for the actual 
> temperature and actual RPM. 
> {code}
> service FanController
> {
>   void setTargetTemperature(int t);
>   int getTargetTemperature();
>   int getActualTemperature();
>   int getActualRPM();
> }
> {code}
> Our client application allows the user to set the target temperature and 
> display the actual temperature and RPM. 
> To implement such an application, we currently have two options when using 
> the Thrift framework:
> 1.) Every client requests the actual temperature and RPM once per second. 
> With other words: every client implements polling.
> 2.) We split service FanController into two different Thrift services. One 
> which allows to configure the fan controller and a second one which is used 
> by the server to notify all its clients about state changes. The first one is 
> implemented by the "real" server and the second one is implemented by all 
> clients and consists of some oneway methods only. So from a Thrift point of 
> view, both sides are server & client. E.g.
> {code}
> service FanController
> {
>   void setTargetTemperature(int t);
>   int getTargetTemperature();
>   void RegisterEvents(string hostname, int port); //use to tell the server, 
> that it should establish a connection to hostname+port which implements 
> FanControllerEvents
>   void UnregisterEvents(string hostname, int port);
> }
> service FanControllerEvents
> {
>   oneway void targetTemperatureChanged(int t);
>   oneway void actualTemperatureChanged(int t);
>   oneway void actualRPMChanged(int rpm);
> }
> {code}
> Both approaches have massive drawbacks. I think it is not worth the effort to 
> explain why solution #1 (polling) sucks. But also solution #2 doesn't work 
> well, because:
>  * It requires every client to register its FanControllerEvents service at 
> the server side by using FanController::RegisterEvents(). This doesn't work, 
> in case the client resides behind a NAT-router, because so the "real" server 
> cannot establish a TCP connection to the client.
>  * It always requires at least two TCP connections which makes firewall 
> configurations more complex.
>  * The "real" server needs to maintain a list with all connected clients in 
> the application logic. In case the actual RPM or temperature changes, the 
> server needs to iterate over the list of all connected clients and call the 
> corresponding function. Maintaining the list in the application logic adds 
> extra complexity at the server side, which can be avoided and may be better 
> part of the Thrift framework.
>  * How to handle the case, if only 1 of the 2 TCP connections gets 
> interrupted?
>  * The fan controller service - which is logically one thing - gets splitted 
> into two Thrift services: FanController + FanControllerEvents which decreases 
> readability of the IDL file.
> To solve such a use case, my recommendation is the following: Add a new 
> keyword like "signal" to the IDL language. Wouldn't it be cool to be able to 
> define something like:
> {code}
> service FanController
> {
>   void setTargetTemperature(int t);
>   signal void targetTemperatureChanged(int t);
>   signal void actualTemperatureChanged(int t);
>   signal actualRPMChanged(int t);
> }
> {code}
> E.g. DBus (a IPC framework very often used in Linux environments) allows to 
> specify signals in their interfaces. See also: 
> http://dbus.freedesktop.org/doc/dbus-tutorial.html#signalprocedure
> It's a very intrusive wish, as it will effect all code generators and runtime 
> libraries. What do you think?



--
This message was sent by Atlassian JIRA
(v6.3.4#6332)

Reply via email to