Netty Tutorial Part 1.5: On Channel Handlers and Channel Options [z]
2013-09-18 17:28
671 查看
Intro:
AftersomefeedbackonChannelHandlerSharability&State
ChannelOptions
ChannelHandlers
Asdiscussedpreviously,mosttypesofTheseemingcomplexityofthepipelinefactorymaynotbeobvious,butit'sactuallyanelegantstructurethatflexiblyhandlessomevariabilityinthecapabilitiesofdifferentChannelHandlerimplementations.Firstofall,manyChannelHandlersrequiresomeconfiguration,adetailnotrepresentedintheaboveexample.Considerthe
SharedandExclusiveChannelHandlers
However,acrucialfactorintheuseofchannelhandlersisthis:Somechannelhandlersaresuchgoodmultitaskers,thatyouonlyeverneedoneinstanceandthatsingleinstancecanbereusedineverysinglepipelinecreated.Thatistosay,thesetypesofchannelhandlersmaintainnostatesotheyareexhibitchannelsafety,avariationonthenotionofthreadsafety.Withnostatetomaintain,there'snoreasontocreatemorethanone.Ontheotherhand,otherchannelhandlerskeepstateininstancevariablesandthereforeassumethattheyareexclusivetoonechannelandonepipeline.Boththeaboveexamplescreatepipelineswithexclusivechannelhandlerinstances,inasmuchastheycreateanewhandlerinstanceeachtimeanewpipelineiscreated.Thesecondexample,creatingapipelinefactorycalledMyStringServerFactory(whichusesactualreal-lifechannelhandlers)containsanexampleoftwochannelhandlerswhichcouldactuallybeshared,althoughtheyhavebeencreatedinexclusivemode.TheyaretheStringEncoderandStringDecoder(andIwillexplainthereasoningbehinditshortly).Creatingandregisteringachannelhandlerinsharedmodeissimplyamatterofinstantiatingoneinstanceandre-usingitinallcreatedpipelines.ThefollowingcodereimplementsMyStringServerFactoryusingashared
Sohowisonetoknowifachannelhandlerclassissharableorexclusive?Nettydefinesaninformationalannotationcalled
ChannelHandlercanbeaddedtooneormore
ChannelPipelinesmultipletimeswithoutaracecondition.
Ifthisannotationisnotspecified,youhavetocreateanewhandlerinstanceeverytimeyouaddittoapipelinebecauseithasunsharedstatesuchasmembervariables.
Thisannotationisprovidedfordocumentationpurpose,justlike
It'ssafetosaythattheauthorsummeditupwithmorebrevitythanIdid,butalsoinformsthereaderthattheannotationisinformationalonly,sothereisnobuilt-inuseoftheannotationwithintheAPIimplementationitself,althoughthe
Allpackagesareinorg.jboss.netty.
Direction | Sharable | Exclusive |
---|---|---|
Upstream | handler.codec.string.StringDecoder handler.codec.protobuf.ProtobufDecoder handler.timeout.IdleStateHandler handler.timeout.ReadTimeoutHandler handler.codec.base64.Base64Decoder | handler.codec.http.HttpResponseDecoder handler.codec.rtsp.RtspRequestDecoder handler.codec.protobuf.ProtobufVarint32FrameDecoder handler.codec.frame.LengthFieldBasedFrameDecoder handler.codec.frame.DelimiterBasedFrameDecoder handler.codec.rtsp.RtspResponseDecoder handler.codec.spdy.SpdyHttpDecoder handler.codec.serialization.ObjectDecoder handler.codec.http.websocket.WebSocketFrameDecoder handler.timeout.IdleStateAwareChannelUpstreamHandler handler.codec.http.websocketx.WebSocket08FrameDecoder handler.codec.http.HttpChunkAggregator handler.codec.serialization.CompatibleObjectDecoder handler.codec.compression.ZlibDecoder handler.codec.http.websocketx.WebSocket00FrameDecoder handler.codec.spdy.SpdyFrameDecoder handler.codec.http.HttpRequestDecoder handler.codec.frame.FixedLengthFrameDecoder handler.queue.BlockingReadHandler handler.codec.http.HttpContentDecompressor handler.codec.http.websocketx.WebSocket13FrameDecoder |
Downstream | handler.codec.base64.Base64Encoder handler.timeout.WriteTimeoutHandler handler.codec.rtsp.RtspResponseEncoder handler.codec.protobuf.ProtobufEncoder handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender handler.codec.http.websocket.WebSocketFrameEncoder handler.codec.http.websocketx.WebSocket00FrameEncoder handler.codec.rtsp.RtspRequestEncoder handler.codec.serialization.ObjectEncoder handler.codec.frame.LengthFieldPrepender handler.codec.string.StringEncoder | handler.codec.serialization.CompatibleObjectEncoder handler.codec.http.HttpResponseEncoder handler.codec.compression.ZlibEncoder handler.codec.spdy.SpdyFrameEncoder handler.codec.spdy.SpdyHttpEncoder handler.codec.http.websocketx.WebSocket13FrameEncoder handler.codec.http.websocketx.WebSocket08FrameEncoder handler.codec.http.HttpRequestEncoder |
Both | handler.logging.LoggingHandler handler.execution.ExecutionHandler | handler.timeout.IdleStateAwareChannelHandler handler.queue.BufferedWriteHandler handler.codec.spdy.SpdyFrameCodec handler.codec.spdy.SpdySessionHandler handler.codec.spdy.SpdyHttpCodec handler.ssl.SslHandler handler.stream.ChunkedWriteHandler handler.codec.http.HttpClientCodec handler.codec.http.HttpContentCompressor handler.codec.http.HttpServerCodec |
Decoders
Youmightnoticeafewinstancesofasymetrywhenitcomestosharability.Forexample,theObjectEncoderissharable,buttheObjectDecoderisnot.Thisisrevealingaboutthenatureofhandlers.AnObjectEncoder'sjobistoconvertanobjectintoabytearray(ok,aChannelBuffer),anditispassedthecompletepayload(theobjecttobeencoded)sothereisnoneedtoretainanystate.Incontrast,theObjectDecoderhastoworkwithabunchofbyteswhichmay,ormaynotbecompleteforthepurposesofreconstitutingbackintoanobject.Thinkaboutaserializedobjectbeingsentfromaremoteclient.Ithasbeenserializedintoabytestreamofpossiblyseveralthousandbytes.Whenthedecoderisfirstcalled,thebytestreammaybeincomplete,withsomeportionofthebytestreamstillbeingtransported.Don'teventhinkabouthavingthehandlerwaitfortherestofthebytestreamtoarrivebeforeproceeding.Thatsortofthingisjustnotdonearoundtheseparts,becauseNettyisanasynchronousAPIandwe'renotgoingtoallocateapoolofworkerthreads(remmeber,theydoallrealwork)tositaroundwaitingforI/Otocomplete.Accordingly,whentherearebytesavailableforprocessing,aworkerthreadwillbeallocatedtoprocessitthroughexecutionoftheObjectDecoder'sdecodemethod.Thisisthesignatureofthatmethod:
Objectdecode( ChannelHandlerContextctx, Channelchannel, ChannelBufferbuffer)
Whenthismethodiscalled,ifthebuffercontainsthecompletesetofbytesrequiredtodecodeintoanobject,thenthedecodedobjectwillbereturnedandpassedontothenextupstreamhandler.Ifnot,thehandlerreturnsanull,ineffectsignalingaNoOp.Eitherway,theworkerthreaddoingtheworkisoffontodoingsomethingelseoncetheinvocationiscomplete.
That'sastandardpatternfordecoders.ExaminingObjectDecoderspecifically,wecanseethatitextends LengthFieldBasedFrameDecoder,andabriefglanceatthe sourceofthatclassshowsthefollowingnon-finalinstancefields:
privatebooleandiscardingTooLongFrame;
privatelongtooLongFrameLength;
privatelongbytesToDiscard;
Thesefieldsarethestatebeingprotectedbymakingthishandlerexclusive.Theactualbytestreambeingdecodedisnotstoredinstate,butratheraccumulatesinthesameinstanceoftheChannelBufferpassedtothedecodeeachtimeaninvocationismadetodecodethespecificbytestream.Thisisanimportantconceptualpointtounderstand:Adecodermaybeinvokedmorethanonceinordertodecodeabytestream.Completionresultsinareturnedobject;in-completionresultsareturnednull(andanassumptionofmoreinvocationstofollow).
Non-Blocking=Scalability?
Animportanttakeawayfromthisisthenotionthatworkerthreadsonlykickinwhenthereisworktodo,ratherthanblockingwaitingforI/Otodeliverbytestoprocess.Considerasimpleandcontrivedscenariowhereaserverlistensforclientssubmittingserializedjavaobjects.ImplementingablockingI/Oreader,thethreadspawnedtoprocessagivenclient'sobjectsubmissionmightcreateajava.io.ObjectInputStreamtoreadfromtheSocketInputStream.Bytesarereadinandobjectsarespitouttheotherside.Ifthebytestreamisconstantlyfull,thenwewouldhaveefficientuseofworkerthreads,althoughthemodelstillrequiresonethreadtobededicatedtoeachconnectedclient.Fairenough,sincethereisaconstantsupplyofdatatoprocess,allthingsbeingequal.
However,whathappensifthesupplyofbytestoprocessisintermitent?Perhapstheclientsonlysupplydataintermitently,orperhapsthenetworkhaslimitedbandwidthand/oriscongested,delayingtheobjecttransmissions.Nowtheserverhasallocatedthreadstoread(andblock)fromtheinputstream,buttherearelongperiodsoftimewherethereisnothingtodobutwaitforworktoarrive.
Thisisoneofthereasonsthatnon-blockingI/OtendstoscalebetterthantraditionalblockingI/O.Note,itdoesnotnecessarillymakeitfaster,itallowstheservertoacceptandprocessanescalatingnumberofclientsforamorecontrolledandreducedlevelofresourceallocation.Forsmallernumbersofclients,itisquitelikelythatblockingI/Owillbefaster,sincededicatedthreadsrequirelesssupervision.Ofcourse,sinceNettysupportsbothblocking(OIO)andnon-blocking(NIO)channels,aNettydevelopercancreateallsortsofservicesthatcanbeconfiguredtouseeither.Whichoneisappropriateforanygivenapplicationissodependentonmanyapplicationspecificfactorsthatitisrarelypossibletostateanyhardrulesaboutthis.TheNettyjavadocfortheorg.jboss.netty.channel.socket.oiopackageattemptstosetaruleofthumbregardingtheuseofOIO:
OldblockingI/ObasedsocketchannelAPIimplementation-recommendedforasmallnumberofconnections(<1000).
Thatseemshightome,buthardware,network,traffictypesandotherfactorswillcertainlyinfluencethemagicnumber.
Framing(buffers,notpictures)
ObjectDecoderextendsLengthFieldBasedFrameDecoderbecausethelatterperformsacriticalfunctionnecessaryinmanydecoders.Withoutaframedecoder,theobjectdecoderwouldbefacedwithanentireunsegmentedbytestream,notknowingwhereoneserializedobjectendedandthenextonestarted.
Ofcourse,theCompatibleObjectDecodercandothisbecauseitexpectsastandardjavaserializationstream,butthereareseveraldownsidestothis,nottheleastofwhichisthattheypayloadswillbesignificantlylarger.InordertohelptheObjectDecoderfigureoutwhichbytesbelongtowhichserializedobject,theframedecoderpartoftheclassknowshowtosegmentthebytestreamintodiscretechunksthattheObjectDecodercanassumearethecontentsofoneobject.
Theframedecoderknowshowtosegmentthebytestreambecauseitworksinconcertwiththesender's
Therearetwopointstonotefromthis:
Mostnon-trivialdecodersareeitherimplementingaknownprotocolspecification(likeHTTPforexample)orareexpectingthepayloadtohavebeen"massaged"bytheencodingcounterpart(liketheObjectEncoder).
TheopportunityforoptimizationwiththeNettyencoder/decoderpairsisagood(thoughnottheonly)reasontouseNettyevenifyou'reusingthetraditionalblockingIO(OIO)model.
InthecaseoftheObjectDecoder,ithasit'sownframedecodingbuiltin,butinmanydecoderclasses,thereisnoframedecodingbuiltinandframingsupportneedstobeprovided.Fortunately,themodulararchitectureoftheChannelPipelineallowsustoinsertaframedecoderbeforeinstancesofdecodersthatneedthem.Agoodexampleofthisisthe
FrameDecodersuchas
DelimiterBasedFrameDecoderifyouareusingastream-basedtransportsuchasTCP/IP.
Thereareafewwaysthattransmittedstringsmightberepresentedandthecorrespondingframeencoderanddecoderthatcanbeused:
FixedLength:Nospecificencoder,justusefixedlengthstrings/
LengthFieldPrepend:
Stringdelimiter:Nospecificencoder,butstringsshouldbedelimitedbythespecifieddelimiters/
TheDelimiterBasedFrameDecodercanbeconfiguredwithmorethanonedelimeterandthepresenceofeitherinthestreamwilltriggeraframesegmentation.Typicalstringdelimitersareend-of-linecharactersornullcharacters.Delimitersarespecifiednotasstringsorevencharacters,butasChannelBufferssotheyareexpressedusingthesametypethatthedecoder'sinputandoutputwillbein.
Theclass
ChannelBufferdelimiter=
Toframe-upthisdiscussiononframes,channelhandlersareoftenputtoworkinconjunctionwitheachother.Thiscanbewithinonepipleline,suchasaframedecoderbeforeastringdecoder.Inaddition,adecoder'sdecodingalgorithmmayrelyonacounterpartencoder'sencoding.
ChannelHandlerState
Althoughthemostcommon(andrecommended)wayofhandlingstateinaChannelHandleristouseinstancefieldsandthenmakesurethehandlerisusedexclusively(anewinstanceperpipelineinsertedinto),thereareacoupleofwaysofkeepingstatesafelyinasharedChannelHandler.The
voidchannelHandlerContext.setAttachment(Objectobj)
ObjectchannelHandlerContext.getAttachment()
Typically,ifyouonlyneedtokeepstatewithinahandleracrosscallstothehandler,theattachmentmodelissuitable.However,ifyouneedtoaccessstatespecifictoachanneloutside[and/orinside]thehandler,Nettyprovidesananalogto
voidchannelLocal.set(Channelchannel,Tvalue)
TchannelLocal.get(Channelchannel)
Thisconcludesthediscussionspecifictohandlers.Itseemstomethatamissingpiecehereisawalk-throughofbuildingareadworldencoder/decoderpairthatreallydoessomethinguseful,andIwillendeavortodothisinafutureentry.
ChannelOptions
Channeloptionsareconfigurationname/valuepairsthatareusedasalooselytypedwayofconfiguringvariousbits-and-piecesinNetty.Theyfallintofourcategories:GeneralNettyConfiguration
ClientSocketConfiguration
ServerSocketConfiguration
ServerChildSocketConfiguration
Inessence,channeloptionsareplacedinaMapandthenpassedtothe
GeneralNettyChannelOptions
ThereareanumberofoptionkeysthatcanbeusedinplaceofspecificAPIcallstoconfigurehowchannelsarecreated.Thefollowingisapartialsummaryofthekeysandwhatthetypesarejusttogiveyouanideawhattheyare.bufferFactory:Aninstanceofa
connectTimeoutMillis:Thenumberofmillisecondsbeforeaconnectionrequesttimesout.Ignoredifthechannelfactorycreatesconnectionlesschannels.
pipelineFactory:AninstanceofaChannelPipelineFactory
receiveBufferSizePredictor:UDPpacketsarereceivedintopre-createdbytearrays,whichisanunresolvabledilemainthatyoudon'tknowhowmuchspacetoallocateuntilyouhavereceivedthedatagram,buttoreceivethedatagram,youhavetoallocateabuffertoreceiveit.
receiveBufferSizePredictorFactory:Afactoryforcreatingbuffersizepredictors.
SocketChannelOptions
Mostofthesupportedchanneloptionsareforconfiguringsockets.Byandlarge,NettydoesnotreallyexposetheJavaClientSocket:AsocketontheclientsideofaTCPconnectiontoaserver.
DatagramSocket:Asocketontheclientand/orserversideofaunicastUDPconversation.
MulticastSocket:Asocketontheclientand/orserversideofamulticastUDPconversation.
ServerSocket:AspecializedsocketontheserversideofaTCPserverthatbrokersconnectionrequestsfromclientsocketsandspinsupanewSocketdedicatedtothatconnectioninanactionreferredtoasan
ChildSocket:Thesocketcreatedontheserver,bytheserversocket,tocommunicatewithaclientsocket.
Allofthesesockettypessupportoptionsoutlinedin
IP_MULTICAST_IF/networkInterface:Thenameoftheinterfaceonwhichoutgoingmulticastpacketsshouldbesent.Whenahosthasmultiplenetworkinterfaces,thistendstobequiteimportant.
IP_MULTICAST_IF2/networkInterface:ThesameasIP_MULTICAST_IFbutdefinedagainforgoodmeasure.
IP_MULTICAST_LOOP/loopbackModeDisabled:Definesifmulticastpacketsshouldbereceivedbythesenderofthesame.
IP_TOS/trafficClass:Setsthetypeofserviceortrafficclassintheheadersofpackets.
SO_BINDADDR/:Areadonlyoptionrepresentingthebindinterfaceofalocalsocket.
SO_BROADCAST/broadcast:EnablesordisablesaDataGramSocket'sabilitytosendbroadcastmessages,
SO_KEEPALIVE/keepAlive:Aflagindicatingthatprobesshouldbeperiodicallysentacrossthenetworktotheoposingsockettokeeptheconnectionalive.
SO_LINGER/soLinger:AllowsthecloseofasockettobedelayedforaperiodoftimetoallowcompletionofI/O.
SO_OOBINLINE/:Allows
SO_RCVBUF/receiveBufferSize:Thesizeinbytesofthesocket'sdatareceivebuffer.
SO_REUSEADDR/reuseAddress:Whensettotrue,allowsmultipleDatagramSocketstobeboundtothesamesocketaddressiftheoptionisenabledpriortobindingthesocket.
SO_SNDBUF/sendBufferSize:Thesizeinbytesofthesocket'sdatasendbuffer.
SO_TIMEOUT/connectTimeoutMillis:Setsatimeoutinms.onblockingsocketoperations.
TCP_NODELAY/tcpNoDelay:Disables
SO_Timeoutvs.ChannelFutureAwait
TheconnectTimeoutMillis(orSocketSO_TIMEOUT)issometimesconfusedwiththetimeoutthatcanbesetonaHavingsaidthat,ifyouwantedanyasynchronousoperationexecutedonachanneltobecancelledifitdidnotcompletewithinaspecificperiodoftime,aChannelFutureawaittimeoutisusefulbecausewhenthetimeoutelapsesandtheoperationisfoundincomplete,itcanbecancelled.Thismightbeapplicable,forexample,inamobiledevicescenariowhereyoumightwanttosavebatteryconsumptionbyensuringthatnetworkoperationsdidnotrunanoverlylongtime.Justbecauseyourapplicationthreadsarenotrunning,doesnotmeanthedevicewouldnotbeconsumingpowerattemptingtocompletealongrunningnetworkoperationthattheapplicationhaslongsinceforgottenabout.
Thefollowingexamplewillattempttoillustratethedifferenceandhowtheycanbeusefullyimplemented.Thiscodesampleisbasedonsimpletestingframeworkclasscalled
Thesalientpartofthistestistheconnectcodeandthetimersthatareplacedaroundit.
//Createamapwiththechanneloptions
Map<'>
IrunthetestusingarbitrarilylowtimeoutssinceIamtryingtotriggerconnecttimeouts.Theinvocationcodelookslikethis:
//TimeoutonI/O
timeoutTest(10,500);
//Waitabitsooutputisclear
try{Thread.sleep(2000);}catch(Exceptione){}
System.out.println("===============================");
//Timeoutonfuture
timeoutTest(500,10);
try{Thread.currentThread().join(5000);}catch(Exceptione){}
Inthefirstcase,online2,IwanttheSO_TIMEOUTtimeouttooccur,soIinvokewithalowI/OtimeoutandahigherChannelFuturetimeout.Online7,Ireversethevaluesforahighertimeoutonthesocket,butalowtimeoutontheChannelFuture.Theoutputisasfollows:
[Client][Thread[ClientBossPoolThread#1,5,ClientBossPoolThreadGroup]]:SocketTimeoutException:Success:falseDone:trueCause:java.net.ConnectException:connectiontimedout
===============================
[Client][Thread[main,5,main]]:RequestCancelled
[Client][Thread[main,5,main]]:ChannelFutureStillWaiting.Cancelled:true
Inthefirstcase,thesockettimeoutissolowthattheoperationcompletesquitequickly(althoughitdoesnotsucceed)sothefuturelistenerreportsthattheoperation:
Wasnotsuccessful
IsComplete
ResultedinaConnectExceptiononaccountofatimedoutconnection.
Inthesecondcall,thehighersockettimeoutmeansthefuturetimeoutexpiresfirst,andwhenitdoes,theoperationisstillrunning,sothethreadcancelsit.
SocketBufferSizes
Anothercriticalperformanceaffectingchanneloptionarethesocketbuffersizes,whicharethesizesofthesendandreceivenativeplatformbuffersallocatedbythenetworkingkerneltobufferI/O.Itispossibletosimplysetthesevaluesarbitrarilyhighandleaveitatthat,butinsituationswheretherearealargenumberofsocketsinuse,thismaywastenativememoryresources.ThenextexamplecodeIwanttodemonstrateusesanothertestharnesscalled
ChannelHandlerProviderFactory
Thisisachannelhandlerfactorythatunderstandswhetherachannelhandlerclassissharableorexclusive,sowhenaninstanceofaCustomServerBootstrap
Thisisacustomizedserverbootstrapthatallowsmetochangethechanneloptionsonthefly.Inthenativeversion,onceabootstraphasbeencreated,thechanneloptionsarecommittedandIwanttotobeabletochangethesocketbuffersizesofcreatedchildchannelsonthefly.(Onceachangeismade,itaffectsonlynewconnectionsfromthatpointon....itdoesnotchangeexistingsocketbuffersizes).IalsocreatedamanagementinterfacecalledAlright,thetestclass,
Theserver'spipelinefactorycreatespipelineswiththefollowinghandlers:
Upstream:
InstrumentedDelimiterBasedFrameDecoderwitha"|"delimiter(exclusive)
StringDecoder(shared)
StringReporter(shared)
Downstream:
CompatibleObjectEncoder(shared)
Thetestisfairlystraightforward:
TheclientmakesaJMXcalltotheservertospecifythesizeoftheserverallocatedchildchannel'ssocketreceivebuffersize.
Theclientmakesanewconnectionandsendsa[longish]stringtotheserver.
TheFrameDecodersplitsthestringcontentandsendsthefragmentsupstreamtotheStringDecoder.
TheStringDecoderdecodesthebytestreamintoaStringandsendsthestringsupstreamtotheStringReporter.
TheStringReportermeasuresthelengthoftheString,createsanintarraywiththelengthofthepassedstringandwritesthearraydownstreambacktotheclient.
Onitswaydownstream,theint[]isencodedusingstandardJavaserializationintheCompatibleObjectEncoder.
Theclientrecordstheelapsedtimetogetaresponsefromtheserver.
Theclientincreasestherequestedserverallocatedchildchannel'ssocketreceivebuffersize.
Rinse,Repeat,(Goto#1.)foreachsubsequentdefinedbuffersize
Thescriptloopinghasanouterloopandaninnerloop:
OuterLoop:Iterateseachsocketreceivebuffersizedefinedinanarray,setsthissizeontheserver'sbootstrapandexecutestheinnerloops.Size:5
InnerLoop:Submitstheconfiguredstringpayloadandcapturestheelapsedtimetoresponse.Size:100
IamgoingtoruntheclientsideofthetestfromaGroovyscriptusingtherawTCPSocketAPI,sotheserverpipelineusesaCompatibleObjectEncodersincetheclientcannotdecodetheoptimizedObjectDecoder'spayload.(Itcould,butIamstickingtoagenericclient).
InstrumentedDelimiterBasedFrameDecoder
Rememberthatforeachlogicalrequest,serversidedecodersmayneedtobeinvokedseveraltimesinordertocompletethetaskofreadingthefullbytestreamanddecodingit.Whenitisinvoked,ifthebytestreamisnotcomplete,itreturnsanull.Eventually,thebytestreamwillbecompleteandthedecodercancompletethetaskandreturnthe(non-null)decodedpayload.Thisbeingthecase,mytheorywasthatthesmallerthereceivingsocket'sreceivebuffersize,themoretimesthedecoderwouldneedtobecalled.Orincontrast,thelargerthereceivingbuffersize,themorelikelythatthefullbytestreamwouldbetransmittedinoneshot,reducingthenumberofrequiredframedecodercallstoone.Totestthistheory,ImadeasimpleextensionoftheDelimiterBasedFrameDecoderthatcountsthenumberoftimesitisinvokedbyincrementingacounterinaninstancevariable.Whenthedecodecompletes,thehighwatercountervalueiswrittentoaChannelLocalandthecounterisresetbacktozero.Upstream,theStringReporter,whichdeterminesthelengthofthepassedstringandreturnsittotheclient,alsoreturnsthecountofframedecoderinvocationsbyreadingitfromtheChannelLocalandaddingittotheint[]payloadreturnedtotheclient.Inaddition,itoccurredtomethatthesocketbuffersizemightnotactuallybesettowhattheclientrequested.(Willitreallysetthebuffersizeto2bytes??!!??)SoIaddedareadofthatvalueandappendedittotheresultsentbacktotheclient.Itisusefultonotethatwhenreadingorwritingchanneloptionsfromalive(socketbased)channel,theyarereadfromandappliedtotheactualsocketinstance.Withthat,theclientcanreport:
Thelengthofthesubmittedstringreportedbytheserver.(Notusefulexceptasavalidatingassertion)
Thenumberoftimestheframedecoderwascalledforthemostrecentlysubmittedstring.
Theelapsedtimeoftherequestexecution.
Theactualserversocketreceivebuffersize.
TheserverpipelineusesaCompatibleObjectEncodersincewe'resendingbackanint[]andsincetheclientisasimplesocketclient,notaNettybasedclient.Itisabitofoverkillthough.Wheneveroneissendingprimitives,orarraysofprimitives,itispossibletobypassmodularencodingandsimplywritedirectlytoaChannelBuffer.Here'showwecouldremovetheCompatibleObjectEncoderfromthepipelineandrewritetheStringReporter'swriteoftheint[].
//=====================================================================
//UncommentnextsectiontoditchtheCompatibleObjectEncoderfromthepipeline
//=====================================================================
ChannelBufferintBuffer=ChannelBuffers.buffer(12);'>ObjectInputStreamtoreadobjectsfromtheSocketInputStreamwouldbereplacedwitha
/*
*Thepriorcode
if(ois==null){
ois=newObjectInputStream(socket.getInputStream());
}
result=ois.readObject();
*/
if(ois==null){
ois=newDataInputStream(socket.getInputStream());
}
result=newint[3];
for(indexin0..2){
result[index]=ois.readInt();
}
GroovyClientScriptandNetworkKernelTuning
Theclientsendsthesamestringeachtime,sincethevaryingvaluebeingadjustedtomeasuretheeffectistheserver'ssocketreceivebuffersize.
ThestringcontentisfairlyarbitraryandgeneratedbyappendingalltheJVM'ssystempropertykey/valuepairsacoupleoftimes.Oncegenerated,thesamestringisusedineachcall.
Thesocketreceivebuffersizestestedaredefinedinthisintarray:[1144,2288,4576,9152,18304,36608,73216,146432,292864,585728]
Sincethescriptassertivelyvalidatesthatthesocketreceivebuffersizeisactuallywhattheclientsetitto,IfoundthatwhenIrequestedabuffersizeof146,432,Igotanassertionfailurewiththeactualreportedbuffersizebeingonly131,071.Asithappens,myLinuxnetworkkernelconfigurationhadlimitedthemaximumsizeofofsocketreceivebuffersto131,071.Inordertoraisethislimit,runninginasudo'edshell,Ididthis:
sysctl-a|grepmem>~/sysctl-mem.txt(Savedthecurrentsettingsforrollbackpurposes)
sysctl-wnet.core.rmem_max=8388608(Setthemaximumbuffersizeto8.3MB)
sysctlnet.core.rmem_max(Verifiedthevaluewasset.Outputwas"net.core.rmem_max=8388608")
sysctl-wnet.ipv4.route.flush=1(Flushedthesettingssotheytookeffectimmediately)
Herearethechartedresultsofthetest,withthesocketreceivebuffersizeonthex-axisandtheaverageelapsedtimeofeachrequestinms.onthefirsty-axis.Thesecondy-axisrepresentstheaveragenumberoftimestheFrameDecoderwascalledforeachrequest.
TheresultsareprettymuchwhatIexpectedandIthinkquiteintuitive:
Theaverageelapsedtimeoftherequestexecutionsisinverselyproportionaltotheserverchildsocket'sreceivebuffersizeuntilthebuffersizeislargeenoughtocontainthewholerequestpayload.
Theaveragenumberofframedecoderinvocationsperrequestexecutionisinverselyproportionaltotheserverchildsocket'sreceivebuffersizeuntilthebuffersizeislargeenoughtocontainthewholerequestpayload.
Onemightbetemptedtosimplysetthesocketbuffersizetothemax,butkeepinmindthatonabusysystem,manysocketsmaybeallocatedandeventhoughthebuffermemorydoesnotcomeoutofyourJavaheapspace,italladdsupinyournativememoryandcouldbequitewasteful.Furthermore,itisquiteobviousfromthechartthatforapredictablepayloadsize,thereisasweetspotforbuffersizes,soinconcertwithNetty's
ThespreadsheetIusedforthistestis
That'sitforentry1.5.Ihopeithasbeenhelpful,ifnotoutrightentertaining.Iamdelightedtogetanyfeedback,andgenuinelypleasedtoansweranyquestions.Aspromised,inentry2.0,IwillbediscussingimplementingAjaxpushinNetty(withnofurtherdecimalreleasesinthe1range).
Part1ofthisseries: