Wednesday, March 30, 2011

Re: Making RPC access logs intelligible

Philippe, Kelsey - thanks for the very helpful responses. This sort
of additional proxy behavior feels like something that would be of
considerable value as a standard part of GWT. I'm surprised there
aren't considerably more complaints in this group.

setServiceEntryPoint() was the magic button I was looking for.
However, I ended up coming up with an alternative that fits my
(heavily Guice-dependent) architecture a little better. Since your
responses were so good (I wish I could give you stackoverflow points),
I feel obligated explain what I did.

This involves a larger number of artifacts than I would prefer, but it
feels fairly natural to me:

* I derived a new GuiceRemoteServiceServlet that overrides
processCall() to look up the interface in the Guice injector and
execute code on throwaway objects.
* I created separate interface, interfaceAsync, and implementation
classes for every single method. Each interface has a different
@RemoteServiceRelativePath. Some of the impl classes handle multiple
interfaces when there is appropriate shared logic.
* I map the interfaces to implementation in GuiceConfig.
* I created a Gin singleton that implements all the async interfaces
and proxies to the individual gwt-rpc proxies; this keeps my client
code blissfully ignorant of the complexity.

It's a little painful to add an RPC method:

* Create new interface and interfaceAsync
* Implement the interface with a new impl class or on an existing impl class
* Map the interface to impl in Guice
* Add the async interface to the Gin singleton proxy

On the other hand my biz logic is now the way I want it to be -
wrapped up in small, modular, reusable, appropriately scoped,
guice-injected chunks. Parameter/method changes refactor nicely with
Eclipse. I could eliminate the mapping step with annotations if I was
willing to accept the startup time penalty of classpath scanning, and
I could probably even generate the Gin singleton if I wanted. But my
remote interface doesn't change that often.

This may not be for everyone, but for a DI addict like me it seems
pretty reasonable. And my appengine logs are now beautiful again -
current load, errors, and individual log entries are now broken down
by method call.

Jeff

On Wed, Mar 30, 2011 at 4:33 AM, Kelsey Francis <kelseyfrancis@gmail.com> wrote:
> Jeff,
> You could create a common subclass (let's call it JeffRemoteServiceServlet)
> of RemoteServiceServlet that overrides the
> onAfterRequestDeserialized(RPCRequest) method. That method is called, as the
> name implies, everytime the servlet receives an RPC request, so it's the
> perfect spot to collect information for logging.
> That doesn't get you the timing information you're after, though, so you
> could instead override RemoteServiceServlet.processCall(String) to
> 1. Start a timer (i.e., record System.nanoTime() or whatever)
> 2. Call super.processCall
> 3. Stop the timer
> Now, so long as all of your service implementations override
> JeffRemoteServiceServlet, you've got the desired behavior everywhere. I'm
> not sure where the best spot to do the actual averaging would be, but this
> should at least let you hook in at the right spot.
> Another (much more difficult) option, that you and Philippe have alluded to,
> is to call setServiceEntryPoint client-side and pipe all of your RPC
> requests through a single servlet. You can make this happen automatically,
> or even completely take over the RPC transport process by creating a custom
> proxy generator. We've done this, and as you mentioned, it's a little
> difficult to wrap your head around at first, but once you have, the changes
> are actually pretty minor (unless you decide to start adding features like
> batching, etc.). You'd need:
> 1. A new subclass of RemoteServiceProxy (let's call it
> DispatchedRemoteServiceProxy) that overrides doInvoke() to take control of
> the transport of the request
> 2. A new subclass of ProxyCreator (let's call it
> DispatchedRemoteProxyCreator) that overrides getProxySupertype() to
> return DispatchedRemoteServiceProxy.class
> 3. A new subclass of ServiceInterfaceProxyGenerator (let's call it
> DispatchedRemoteServiceGenerator) that overrides createProxyCreator() to
> return a new DispatchedRemoteProxyCreator
> 4. Have your services inherit a new interface (DispatchedRemoteService) and
> set up your .gwt.xml file generate impls of that interface with
> DispatchedRemoteServiceGenerator.
> The trickiest part is what to do in DispatchedRemoteServiceProxy.doInvoke().
> You'll want to make a request to your special dispatch servlet, of course,
> and then decode it once you get a response. RequestCallbackAdapter holds the
> keys to making this happen. Dispatching the request server-side is
> relatively trivial once you have the decoded RPCRequest.
> The point of all this is of course to have all your requests one through a
> single servlet, which would, among other things, probably make collecting
> information about requests a little easier.
> -Kelsey
>
> --
> You received this message because you are subscribed to the Google Groups
> "Google Web Toolkit" group.
> To post to this group, send email to google-web-toolkit@googlegroups.com.
> To unsubscribe from this group, send email to
> google-web-toolkit+unsubscribe@googlegroups.com.
> For more options, visit this group at
> http://groups.google.com/group/google-web-toolkit?hl=en.
>

--
You received this message because you are subscribed to the Google Groups "Google Web Toolkit" group.
To post to this group, send email to google-web-toolkit@googlegroups.com.
To unsubscribe from this group, send email to google-web-toolkit+unsubscribe@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/google-web-toolkit?hl=en.

No comments:

Post a Comment