Hot on the heels of Google Widget Toolkit(GWT) switching to Jetty, the little server that can has received some more Google luv’n! Google’s new App Engine Java service is powered by Jetty! With App Engine, you can build web applications using standard Java
technologies and run them on Google’s scalable infrastructure.
Initially it is a little difficult to see Jetty in use, but you can see the jetty classes in the SDK download and if your application throws an exception then investigation of the stack trace in the log reveals the Jetty servlet container is used.
Not only that, the stack trace and other documentation show that Google have really exploited the embeddablility and extensibility of Jetty at a number of levels:
- They are using their own RPC style connector to receive requests from their front end web servers.
- They use the google account authentication as the only supported authentication mechanism.
- The HTTP sessions are clustered via the database or memcache
- There is an appengine-web.xml configuration file
- Jetty is embedded in their SDK and eclipse development plugin.
Thus Google have plugged in many new and/or extended components in a way that validates our open, component based architecture. When it comes to application servers we do not believe that one size fits all and strongly encourage such customization for purpose.
We are really pleased that the google App team picked Jetty for their hosting service and welcome them to the ever growing list of Jetty powered projects!
The revealing exception:
javax.servlet.ServletException: This is an exception at com.acme.HelloWorld.doGet(HelloWorld.java:48) at javax.servlet.http.HttpServlet.service(HttpServlet.java:689) at javax.servlet.http.HttpServlet.service(HttpServlet.java:802) at org.mortbay.jetty.servlet.ServletHolder$SingleThreadedWrapper.service(ServletHolder.java:617) at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:487) at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1093) at com.google.apphosting.runtime.jetty.SaveSessionFilter.doFilter(SaveSessionFilter.java:35) at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084) at com.google.apphosting.utils.servlet.TransactionCleanupFilter.doFilter(TransactionCleanupFilter.java:43) at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084) at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:360) at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216) at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:181) at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:712) at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:405) at com.google.apphosting.runtime.jetty.AppVersionHandlerMap.handle(AppVersionHandlerMap.java:237) at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:139) at org.mortbay.jetty.Server.handle(Server.java:313) at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:506) at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:830) at com.google.apphosting.runtime.jetty.RpcRequestParser.parseAvailable(RpcRequestParser.java:63) at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:381) at com.google.apphosting.runtime.jetty.JettyServletEngineAdapter.serviceRequest(JettyServletEngineAdapter.java:125) at com.google.apphosting.runtime.JavaRuntime.handleRequest(JavaRuntime.java:235) at com.google.apphosting.base.RuntimePb$EvaluationRuntime$6.handleBlockingRequest(RuntimePb.java:4547) at com.google.apphosting.base.RuntimePb$EvaluationRuntime$6.handleBlockingRequest(RuntimePb.java:4545) at com.google.net.rpc.impl.BlockingApplicationHandler.handleRequest(BlockingApplicationHandler.java:24) at com.google.net.rpc.impl.RpcUtil.runRpcInApplication(RpcUtil.java:359) at com.google.net.rpc.impl.Server$2.run(Server.java:792) at com.google.tracing.LocalTraceSpanRunnable.run(LocalTraceSpanRunnable.java:56) at com.google.tracing.LocalTraceSpanBuilder.internalContinueSpan(LocalTraceSpanBuilder.java:489) at com.google.net.rpc.impl.Server.startRpc(Server.java:748) at com.google.net.rpc.impl.Server.processRequest(Server.java:340) at com.google.net.rpc.impl.ServerConnection.messageReceived(ServerConnection.java:422) at com.google.net.rpc.impl.RpcConnection.parseMessages(RpcConnection.java:319) at com.google.net.rpc.impl.RpcConnection.dataReceived(RpcConnection.java:290) at com.google.net.async.Connection.handleReadEvent(Connection.java:419) at com.google.net.async.EventDispatcher.processNetworkEvents(EventDispatcher.java:733) at com.google.net.async.EventDispatcher.internalLoop(EventDispatcher.java:207) at com.google.net.async.EventDispatcher.loop(EventDispatcher.java:101) at com.google.net.rpc.RpcService.runUntilServerShutdown(RpcService.java:249) at com.google.apphosting.runtime.JavaRuntime$RpcRunnable.run(JavaRuntime.java:373) at java.lang.Thread.run(Unknown Source)
Edited 11 Apr 2009:
In response to the questions about continuations, we found this discussion on the google app engine forums, which says:
“We do not support continuations. All requests need to complete their work within the ~30 second deadline and then return control back to App Engine. There is not currently any support for chunked transfer encodings, hanging gets, or background processing, so continuations would not be very useful.”
This is interesting, but we’d like to feedback to google that there are plenty of interesting uses for continuations that do not need waits longer than 30 seconds, background processing or chunked responses:
- We encourage the use of long polling techniques that always send complete responses and thus don’t need chunking etc.
- In many cases, background processing is not needed. Suspended requests can be resumed by other requests (eg chat, auctions, collaborative editing etc.) or even other responses completing (eg quality of service filter).
- For interactive web applications, having a long poll of 20s will still give great latency and throughput advantages over a more frequent polling solution.
Now it maybe that Googles RPC connector cannot operate asynchronously, so they are forced to have a thread allocated. If that is the case, then they should talk to us and we’d be please to teach them how to better scale their infrastructure (we also specialize in teaching egg sucking 🙂