Client-Server-Kommunikation mit dem Command Pattern p.g.taboada | pgt technology scouting GmbH
Jun 24, 2015
Client-Server-Kommunikation mit dem Command Pattern
p.g.taboada | pgt technology scouting GmbH
Papick G. Taboada
pgt technology scouting GmbH!http://pgt.de
Orientation in Objects GmbH!http://oio.de
http://gwtreferencelist.appspot.com
‣Client-Server communication
‣Command pattern
‣Versioning
‣Batching, Caching
‣Scaling
Browser Server
user action
full html response
full html response
full html response
user action
user action
classi
c web !
development
Browser Server
event
first request
full html response
data
data request
data
data request
event
event
event
RIA web
development
Honor the A in JAX
‣ Javascript does not block !Get over it
‣ Latency is not a myth
‣ Results must not arrive in the call order
BUILDING A GENERIC FRAMEWORK FOR MARSHALLING/ UNSMARSHALLING DATA SUCKS
MARSHALLING / UNMARSHALLING IN JAVASCRIPT IS SLOW
DON‘T BIND YOU NEXT 3 YEARS OF
WORK AGAINST SOME
COMMUNICATION PROTOCOLL
LOADING TOO MUCH DATA WILL ALWAYS
HURT
‣Client-Server communication
‣Command pattern
‣Versioning
‣Batching, Caching
‣Scaling
GWT-RPC is a good solution if handled with care
SomeResult someMethodName(SomeParameter spo)
GWT-RPC binds many
methods into one interface
Interface Versioning
is a monstrous
thing
SomeResult someMethodName(SomeParameter spo)
this will be an object
this will be an object too
SomeResult someMethodName(SomeParameter spo)
the method names bind the requests to the result
typesafety all the way
USING GENERICS FOR !
TYPESAFETY, !
GET RID OF METHODS !
AND INTERFACES
<A extends Action<R>, R extends Result> R execute(A action);
now we just one interface with one method
typesafety all the way
!
command pattern
GOF Pattern !
commonly used in Rich Clients
!
someAction
someResult
someActionHandlerEXECUTE
someAction
someResult
someActionHandlerPOJOS
someAction
someResult
someActionHandler
GWT-RPC
client server
batching caching security
caching exception translation security
GWT client
someAction
someResult
someActionHandler
RMI / HTTPInvoker
client server
batching caching security
caching exception translation security
Java client
someAction
someResult
someActionHandler
JSON-servlet
client server
batching caching security
caching exception translation security
Mobile client
public class TerminLoadAction implements Action<DataResult<TerminData>> { ! private String terminId; ! public TerminLoadAction(String terminId) { this.terminId = terminId; } ! public String getTerminId() { return terminId; } }
public class DataResult<DATA extends Data> implements Result { ! private DATA data; ! public DataResult(DATA data) { this.data = data; } ! public void setData(DATA data) { this.data = data; } ! public DATA getData() { return data; } !}
aka DTOs
Action Result
Reusabletype safety
dispatch.execute( ! new TerminLoadAction(terminId), new AsyncCallback<DataResult<TerminData>>() { ! @Override public void onFailure(Throwable caught) { } ! @Override public void onSuccess(DataResult<TerminData> result) { } ! } !);
<A extends Action<R>, R extends Result> void execute(A action, AsyncCallback<R> callback)
public interface ActionHandler <A extends Action<R>, R extends Result> { ! Class<A> getActionType(); !!!! R execute( A action, ExecutionContext context) throws DispatchException; !}
Server side
type safety
handler to action
mapping
action execution
declared exception hiearchy
Execution context for server side command
execution
@ActionHandlerBean @Transactional public final class TerminDataLoadHandler implements ActionHandler<TerminLoadAction, DataResult<TerminData>> { ! @Autowired private TerminDAO terminDao; ! @Override public DataResult<TerminData> execute( TerminLoadAction action, ExecutionContext context) throws DispatchException { ! TerminBean termin = … ! TerminData data = … ! return new DataResult<TerminData>(data); ! } ! @Override public Class<TerminLoadAction> getActionType() { return TerminLoadAction.class; } !}
Server side
custom annotation
spring
access to backend
type safe result
business logic, etc…
‣Client-Server communication
‣Command pattern
‣Versioning
‣Batching, Caching
‣Scaling
RPC interface
hell?
public interface SomeNiceService extends RemoteService { ! String someService(String param); ! String someServiceV2(String param); ! String someServiceV3(String param); }
public interface SomeNiceService extends RemoteService { ! String someService(String param); !} !public interface SomeNiceServiceV2 extends RemoteService { ! String someService(String param); !} !public interface SomeNiceServiceV3 extends RemoteService { ! String someService(String param); !}
easy way right way?
maintainability?maintainability?
someAction
someResult
someActionHandlerPOJOS
someAction
someResult
someActionHandlermultiple versions
someActionV2
someActionHandlerV2
same result
different versions can coexist!
someAction
someResult
someActionHandlermultiple versions
someActionV2
someActionHandlerV2
someResultV2 different results
‣Client-Server communication
‣Command pattern
‣Versioning
‣Batching, Caching
‣Scaling
why batch?
solving common problems
• one batch call is better than 10 single calls
• less data
• less roundtrip latency
• avoid connection bottleneck
• ensure server side execution order
• less roundtrips
batchAction
someAction1
batchActionHandler
someAction2
someAction3
batchResult
someResult1
someResult2
someResult3
client server
batching can be manual or
automatic
server executes actions in given order
someAction1
someAction2
someAction3
automatic batching?
GWT code execution
IDLE
browser event loop
Scheduler.scheduleEntry(…)
Scheduler.scheduleFinally(…)
Scheduler.scheduleDeferred(…)
GWT code execution
IDLE
Scheduler.scheduleFinally(…)
collect commands
fire batch command
cmd 1!cmd 2!cmd 3!cmd …
BATCH EXECUTION ALLOWS FOR FINE GRAINED COMMANDS AND REUSE
toggleTerminMetadata
reloadDashboardTermine
BooleanResult
DataListResult<Termin>
toggleTerminMetadata
reloadTermin
BooleanResult
DataResult<Termin>
toggleTerminMetadata
loadMonthStats
BooleanResult
DataResult<MonthStats>
loadMonthTermine DataListResult<Termin>
Caching
• Introduce cacheable interface
• simple marker interface,
• or more elaborate version with cache id, expiration time, etc…
• Implement caching (client or server side)
Patient 1!!
Patient 2
Patient 1 details
0101010101010101001010101010101010101010101010101!0101010101010101001010101010101010101010101010101!0101010101010101001010101010101010101010101010101!0101010101010101001010101010101010101010101010101!0101010101010101001010101010101010101010101010101!0101010101010101001010101010101010101010101010101!0101010101010101001010101010101010101010101010101!0101010101010101001010101010101010101010101010101
Ops.
action 1
action 2
result 2
result 1
execution 1
execution 2
action 2
Ensure „only last“ resultaction 1
result 1
action 1 handler
action
result
„smart dispatch“
result 2
check resultdeliver if last!
last action is:
result 1
Re-Auth
• If server exception is security exception, try to reauth and than re-execute commands
‣Client-Server communication
‣Command pattern
‣Versioning
‣Batching, Caching
‣Scaling
• Every client brings his own CPU power
• The client does the page rendering
• GWT provides different ways to reduce number of requests even more
• The server must „only“ authenticate the user and provide the data, perform the actions requested by the client
GWT scaling is easy...
WHAT CAN POSSIBLY GO WRONG?
LETS TALK HIGH TRAFFIC... HIGH TRAFFIC IS WHEN ONE SERVER IS NOT ENOUGH
• Bandwith issues
• Connection pool bottlenecks
• Thread pool bottlenecks
• CPU bottlenecks caused by reflection API calls
• High JVM garbage collection CPU usage
HIGH TRAFFIC PROBLEMS
NOT REALLY GWT PROBLEMS, BUT WHAT CAN WE DO?
TX TX
TX TX
TX
TX
TX TX
TX
TXavoid „tx collisions“
5 gleichzeitige Transaktionen
TX TX TX TX
TX
TXTX
TX TX
TX
TX
TXTX
TX
TX
load small portions of data, do it often
IMPLEMENT REAL LOAD BALANCING EACH REQUEST GOES TO THE NEXT AVAILABLE SERVER
• Don‘t stick a session to a server. Why send a user over and over again to a possible overloaded server?
• Don‘t store anything on the HTTP session. Share session content outside web container
• Session replication is expensive and does not scale well
• Let the load balancer distribute calls to available servers
SCALING HOW-TO
Webserver
Webserver
LB Webserver
Webserver
Webserver
Session Cache
STATELESS WEBAPP
Webserver
Session Cache
Session state could contain user id, client id, session id, user roles
If session cache becomes bottleneck, use distributed cache
Session Cache
Session Cache
Thanks!