您的位置:首页 > 运维架构

Netty Tutorial Part 1.5: On Channel Handlers and Channel Options [z]

2013-09-18 17:28 671 查看

Intro:

AftersomefeedbackonPart1,andbeingpromptedbysomestackoverflowquestions,Iwanttoexpandonandclarifysometopics,sothisisPart1.5.

ChannelHandlerSharability&State

ChannelOptions

ChannelHandlers

Asdiscussedpreviously,mosttypesofChannelHandlershavethejobofeitherencodingobjectsintobytearrays(orspecifically,ChannelBuffers)ordecodingChannelBuffersintoobjects.Inmostcases,handlerswillbeinstantiatedandplacedintoaChannelPipelinebyadefinedChannelPipelineFactoryincodethatlookssomethinglikethis(borrowedfromtheClientBootstrapjavadoc):

Theseemingcomplexityofthepipelinefactorymaynotbeobvious,butit'sactuallyanelegantstructurethatflexiblyhandlessomevariabilityinthecapabilitiesofdifferentChannelHandlerimplementations.Firstofall,manyChannelHandlersrequiresomeconfiguration,adetailnotrepresentedintheaboveexample.ConsidertheDelimiterBasedFrameDecoderwhichdecodeschunksofbytesinaccordancewiththeconstructorspecifieddelimiter.Instancesofthisdecoderrequireconfiguration,sotheinterfaceonlyChannelPipelineFactoryallowsustoprovidethetemplatethatdefineshowitshouldbecreatedeverytimeanewpipelineiscreated.Anasarecap,everytimeanewchanneliscreated,apipelineiscreatedforit,althoughinsomecases,Isuppose,youmightendupwithanemptypipelinewithnochannelhandlers,whichmeansyouareusingasnazzyframeworkforpassingaroundChannelBuffers.That,combinedwiththeorderingandlogicalnamesofhandlersinthepipeline,iswhyoneneedssomecareandattentionwhencreatingapipeline.Here'sabetterexampleoftheuseofsomeparameterizedchannelhandlersinuse(again,pilferedliberallyfromtheStringEncoderjavadoc):

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.ThefollowingcodereimplementsMyStringServerFactoryusingasharedStringDecoderandStringEncoder:

Sohowisonetoknowifachannelhandlerclassissharableorexclusive?Nettydefinesaninformationalannotationcalled@Sharableandsharablechannelhandlerclassesareannotatedassuch.Toquotefromthe@Sharable'sjavadoc:

Indicatesthatthesameinstanceoftheannotated
ChannelHandler
canbeaddedtooneormore
ChannelPipeline
smultipletimeswithoutaracecondition.
Ifthisannotationisnotspecified,youhavetocreateanewhandlerinstanceeverytimeyouaddittoapipelinebecauseithasunsharedstatesuchasmembervariables.
Thisannotationisprovidedfordocumentationpurpose,justliketheJCIPannotations.


It'ssafetosaythattheauthorsummeditupwithmorebrevitythanIdid,butalsoinformsthereaderthattheannotationisinformationalonly,sothereisnobuilt-inuseoftheannotationwithintheAPIimplementationitself,althoughthe@RetentionoftheannotationisRUNTIMEsoitcanbedetectedbyyourcode,sousingabitofReflection[s],IwhippedupthistablethatcategorizesallthechannelhandlersincludedintheNettyAPIintoSharable,Exclusive,Upstream,Downstream,andBoth.ExcludedfromthistableareabstractandinnerclassesaswellasSimpleChannelHandler,SimpleChannelUpstreamHandlerandSimpleChannelDownstreamHandler.Thoselastthreeareconcretechannelhandlersandarenotannotatedas@Sharablebuttheyare,whatIrefertoas,logicallyabstract,oremptyimplementations.Bythemselves,theydonothingandareintendedforextendingwhenimplementingyourownhandlerclasses.
Allpackagesareinorg.jboss.netty.
DirectionSharableExclusive
Upstreamhandler.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

Downstreamhandler.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

Bothhandler.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,wecanseethatitextendsLengthFieldBasedFrameDecoder,andabriefglanceatthesourceofthatclassshowsthefollowingnon-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'sObjectEncoder,whichhasconvenientlyplacedlengthindicatorsintothedownstreambytestream,specificallysotheupstreamdecodercanusethemtoparse.Whenimplementingyourowncustomdecoders,ifyouwantedtoimplementthistechnique,yourclientcanusetheLengthFieldPrependerencoder.

Therearetwopointstonotefromthis:

Mostnon-trivialdecodersareeitherimplementingaknownprotocolspecification(likeHTTPforexample)orareexpectingthepayloadtohavebeen"massaged"bytheencodingcounterpart(liketheObjectEncoder).

TheopportunityforoptimizationwiththeNettyencoder/decoderpairsisagood(thoughnottheonly)reasontouseNettyevenifyou'reusingthetraditionalblockingIO(OIO)model.

InthecaseoftheObjectDecoder,ithasit'sownframedecodingbuiltin,butinmanydecoderclasses,thereisnoframedecodingbuiltinandframingsupportneedstobeprovided.Fortunately,themodulararchitectureoftheChannelPipelineallowsustoinsertaframedecoderbeforeinstancesofdecodersthatneedthem.AgoodexampleofthisistheStringDecoder.Thejavadocemphasizesthatthisdecodershouldbeusedinconjunctionwithaframedecoder:

Pleasenotethatthisdecodermustbeusedwithaproper
FrameDecoder
suchas
DelimiterBasedFrameDecoder
ifyouareusingastream-basedtransportsuchasTCP/IP.


Thereareafewwaysthattransmittedstringsmightberepresentedandthecorrespondingframeencoderanddecoderthatcanbeused:

FixedLength:Nospecificencoder,justusefixedlengthstrings/FixedLengthFrameDecoder

LengthFieldPrepend:LengthFieldPrepender/LengthFieldBasedFrameDecoder

Stringdelimiter:Nospecificencoder,butstringsshouldbedelimitedbythespecifieddelimiters/DelimiterBasedFrameDecoder.

TheDelimiterBasedFrameDecodercanbeconfiguredwithmorethanonedelimeterandthepresenceofeitherinthestreamwilltriggeraframesegmentation.Typicalstringdelimitersareend-of-linecharactersornullcharacters.Delimitersarespecifiednotasstringsorevencharacters,butasChannelBufferssotheyareexpressedusingthesametypethatthedecoder'sinputandoutputwillbein.

TheclassDelimiterscontainsafewpre-definedandcommonlyuseddelimiters,andit'ssimpleenoughtodefineyourown:

ChannelBufferdelimiter=ChannelBuffers.wrappedBuffer("THEBIG-D".getBytes())

Toframe-upthisdiscussiononframes,channelhandlersareoftenputtoworkinconjunctionwitheachother.Thiscanbewithinonepipleline,suchasaframedecoderbeforeastringdecoder.Inaddition,adecoder'sdecodingalgorithmmayrelyonacounterpartencoder'sencoding.

ChannelHandlerState

Althoughthemostcommon(andrecommended)wayofhandlingstateinaChannelHandleristouseinstancefieldsandthenmakesurethehandlerisusedexclusively(anewinstanceperpipelineinsertedinto),thereareacoupleofwaysofkeepingstatesafelyinasharedChannelHandler.

TheChannelHandlerContextclassprovidesthenotionofanattachmentwhichisastorethatisspecifictoachannelandhandlerinstance.Itisaccessedusingthegetterandsettermethods:

voidchannelHandlerContext.setAttachment(Objectobj)
ObjectchannelHandlerContext.getAttachment()

Typically,ifyouonlyneedtokeepstatewithinahandleracrosscallstothehandler,theattachmentmodelissuitable.However,ifyouneedtoaccessstatespecifictoachanneloutside[and/orinside]thehandler,NettyprovidesananalogtoThreadLocalcalledaChannelLocal.Obviously,sincethisstateischannelspecific,butmaybeaccessedbymultipledifferentthreads,aThreadLocaldoesnotservethispurpose.WhileaThreadLocalisessentiallyabitofdatakeyedinpartbytheowningthread,aChannelLocalissimilarinthatitisabitofdatakeyedbytheowningchannel.Accordingly,youcandefineastaticfieldandusingthechannelasthekey,accessthevaluefromprettymuchanywhereinyourcode.Theprimarymethodsare:

voidchannelLocal.set(Channelchannel,Tvalue)
TchannelLocal.get(Channelchannel)

Thisconcludesthediscussionspecifictohandlers.Itseemstomethatamissingpiecehereisawalk-throughofbuildingareadworldencoder/decoderpairthatreallydoessomethinguseful,andIwillendeavortodothisinafutureentry.

ChannelOptions

Channeloptionsareconfigurationname/valuepairsthatareusedasalooselytypedwayofconfiguringvariousbits-and-piecesinNetty.Theyfallintofourcategories:

GeneralNettyConfiguration

ClientSocketConfiguration

ServerSocketConfiguration

ServerChildSocketConfiguration

Inessence,channeloptionsareplacedinaMapandthenpassedtotheBootstrap;theoptionsprovidedareappliedwhereapplicable,sothisisanabstractedwayofconfiguringthestack.

GeneralNettyChannelOptions

ThereareanumberofoptionkeysthatcanbeusedinplaceofspecificAPIcallstoconfigurehowchannelsarecreated.Thefollowingisapartialsummaryofthekeysandwhatthetypesarejusttogiveyouanideawhattheyare.

bufferFactory:AninstanceofaChannelBufferFactorythatwillbecalledtocreatenewChannelBuffers.Thismightbeusefulinordertocustomizethebyteorderofcreatedchannels,ortocreatedirect(offheap)buffers.

connectTimeoutMillis:Thenumberofmillisecondsbeforeaconnectionrequesttimesout.Ignoredifthechannelfactorycreatesconnectionlesschannels.

pipelineFactory:AninstanceofaChannelPipelineFactory

receiveBufferSizePredictor:UDPpacketsarereceivedintopre-createdbytearrays,whichisanunresolvabledilemainthatyoudon'tknowhowmuchspacetoallocateuntilyouhavereceivedthedatagram,buttoreceivethedatagram,youhavetoallocateabuffertoreceiveit.BufferSizePredictorsprovideawaytocustomizethesizeofthosearraysbasedonsomeintelligenceyoumighthaveabouttheexpectedsize.

receiveBufferSizePredictorFactory:Afactoryforcreatingbuffersizepredictors.

SocketChannelOptions

Mostofthesupportedchanneloptionsareforconfiguringsockets.Byandlarge,NettydoesnotreallyexposetheJavaSocketAPIthatmuch,whichmightseemoddforanetworkingAPI.However,allimplementationsofSocketChannelusesockets,andsocketshavevariouslowlevelconfigurationitemswhichcanbesetthroughchanneloptions.Ingeneral,thesearethetypesofsocketsweareworkingwith:

ClientSocket:AsocketontheclientsideofaTCPconnectiontoaserver.

DatagramSocket:Asocketontheclientand/orserversideofaunicastUDPconversation.

MulticastSocket:Asocketontheclientand/orserversideofamulticastUDPconversation.

ServerSocket:AspecializedsocketontheserversideofaTCPserverthatbrokersconnectionrequestsfromclientsocketsandspinsupanewSocketdedicatedtothatconnectioninanactionreferredtoasanaccept.Asidefromtheinitialconnection,aclientsocketdoesnotcommunicatewiththeserversocket.

ChildSocket:Thesocketcreatedontheserver,bytheserversocket,tocommunicatewithaclientsocket.

Allofthesesockettypessupportoptionsoutlinedinjava.net.SocketOptionsandmanyofthemcanhaveasignificantimpactonperformance.TheoptionsarelistedbelowwiththeSocketOptionconstantnameandtheNettychanneloptionequivalent.

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/:AllowsOutofBandTCPUrgentdatatransmissionstobereceivedthroughthenormaldatachannelratherthandiscarded.

SO_RCVBUF/receiveBufferSize:Thesizeinbytesofthesocket'sdatareceivebuffer.

SO_REUSEADDR/reuseAddress:Whensettotrue,allowsmultipleDatagramSocketstobeboundtothesamesocketaddressiftheoptionisenabledpriortobindingthesocket.

SO_SNDBUF/sendBufferSize:Thesizeinbytesofthesocket'sdatasendbuffer.

SO_TIMEOUT/connectTimeoutMillis:Setsatimeoutinms.onblockingsocketoperations.

TCP_NODELAY/tcpNoDelay:DisablesNagle'salgorithmonthesocketwhichdelaysthetransmissionofdatauntilacertainvolumeofpendingdatahasaccumulated.

SO_Timeoutvs.ChannelFutureAwait

TheconnectTimeoutMillis(orSocketSO_TIMEOUT)issometimesconfusedwiththetimeoutthatcanbesetonaChannelFutureusingawaitoroneofitsequivalents.TheSO_TIMEOUTisstrictlythetimelimit(inms.)thatanI/Ooperationonthesocketmustcompletein.IfaconnectI/Ooperationisnotcompletewithinthattime,thesocketinstancewillthrowajava.net.ConnectException:connectiontimedoutexception.Ontheotherhand,theChannelFuture'sawaittimeoutissimplytheamountoftimethecallingthreadwillwaitfortheoperationtocomplete.Thetimeoutitselfdoesnotimpacttheoperationandtheoperationisconsideredcompletewhenitconcludes,whetheritwassuccessfulornot.

Havingsaidthat,ifyouwantedanyasynchronousoperationexecutedonachanneltobecancelledifitdidnotcompletewithinaspecificperiodoftime,aChannelFutureawaittimeoutisusefulbecausewhenthetimeoutelapsesandtheoperationisfoundincomplete,itcanbecancelled.Thismightbeapplicable,forexample,inamobiledevicescenariowhereyoumightwanttosavebatteryconsumptionbyensuringthatnetworkoperationsdidnotrunanoverlylongtime.Justbecauseyourapplicationthreadsarenotrunning,doesnotmeanthedevicewouldnotbeconsumingpowerattemptingtocompletealongrunningnetworkoperationthattheapplicationhaslongsinceforgottenabout.

Thefollowingexamplewillattempttoillustratethedifferenceandhowtheycanbeusefullyimplemented.ThiscodesampleisbasedonsimpletestingframeworkclasscalledSimpleNIOClient.ThecodefortheexampleisTimeoutTest.ThetestperformedisverysimpleandjustattemptstoconnecttoaninternetaddressusingdifferentvaluesfortheChannelFutureawaittimeoutandtheclientsocket'sSO_TIMEOUT.

Thesalientpartofthistestistheconnectcodeandthetimersthatareplacedaroundit.

publicstaticvoidtimeoutTest(longioTimeout,longfutureTimeout){
//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.

ThenextexamplecodeIwanttodemonstrateusesanothertestharnesscalledSimpleNIOServer.ThetestclassiscalledAdjustableSocketBufferSizeServer.Itrequiresalittleexplanationaroundsomeofthecustomizations,sopleasebearwith....

ChannelHandlerProviderFactory

Thisisachannelhandlerfactorythatunderstandswhetherachannelhandlerclassissharableorexclusive,sowhenaninstanceofaChannelHandlerProviderisgiventotheChannelPipelineFactoryImplanditcreatesanewpipeline,itwilleithercreateabrandnewhandlerinstance(ifexclusive)orprovidethe"singleton"instancealreadycreated.Thisisnotcriticaltotheexample,buttoavoidconfusion,Iammentioningtheiruse.

CustomServerBootstrap

Thisisacustomizedserverbootstrapthatallowsmetochangethechanneloptionsonthefly.Inthenativeversion,onceabootstraphasbeencreated,thechanneloptionsarecommittedandIwanttotobeabletochangethesocketbuffersizesofcreatedchildchannelsonthefly.(Onceachangeismade,itaffectsonlynewconnectionsfromthatpointon....itdoesnotchangeexistingsocketbuffersizes).IalsocreatedamanagementinterfacecalledBootstrapJMXManagerthatallowsmetomakethesechangesthroughJMX.

Alright,thetestclass,AdjustableSocketBufferSizeServer,createsanewserverusingSimpleNIOServer.ItalsocreatesaJMXConnectorServerandRMIRegistrysotheMBeanServercaneasilybeconnectedtosothechildsocketreceivebuffersizescanbeadjustedprogramatically,sothatinturnthetestcasecanberunautomaticallyendtoend.

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);'>ObjectInputStreamtoreadobjectsfromtheSocketInputStreamwouldbereplacedwithaDataInputStreamandthecodewouldlooklikethis:

/*
*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

ThisisthegroovytestclientIusedtotesttheserver.Iadmitthatthisscriptisabitmessy,butitdoesthejob.Iwon'tgooveritintoomuchdetail,exceptingdrawingyourattentiontothefollowing:

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)

ThisisausefulreferencefortuningthenetworkkernelinLinux.HereisaguideforTCPtuningondifferentoperatingsystems.IshouldalsonotethatthestringIgeneratedasthesampletestpayloadwas8,088bytesbeforebeingdelimitedwithasingle"|".

Herearethechartedresultsofthetest,withthesocketreceivebuffersizeonthex-axisandtheaverageelapsedtimeofeachrequestinms.onthefirsty-axis.Thesecondy-axisrepresentstheaveragenumberoftimestheFrameDecoderwascalledforeachrequest.



TheresultsareprettymuchwhatIexpectedandIthinkquiteintuitive:

Theaverageelapsedtimeoftherequestexecutionsisinverselyproportionaltotheserverchildsocket'sreceivebuffersizeuntilthebuffersizeislargeenoughtocontainthewholerequestpayload.

Theaveragenumberofframedecoderinvocationsperrequestexecutionisinverselyproportionaltotheserverchildsocket'sreceivebuffersizeuntilthebuffersizeislargeenoughtocontainthewholerequestpayload.

Onemightbetemptedtosimplysetthesocketbuffersizetothemax,butkeepinmindthatonabusysystem,manysocketsmaybeallocatedandeventhoughthebuffermemorydoesnotcomeoutofyourJavaheapspace,italladdsupinyournativememoryandcouldbequitewasteful.Furthermore,itisquiteobviousfromthechartthatforapredictablepayloadsize,thereisasweetspotforbuffersizes,soinconcertwithNetty'sReceiveBufferSizePredictor,itwouldbeinterestingtocreateanauto-tuningsocketbuffersizingalgorithm,butthat'sforanotherday.

ThespreadsheetIusedforthistestishere,andissimpletouse.ThegroovytestscriptgeneratesaCSVfilewithallthetabulatedresults,soyoucanjustimportitintoOpenOffice(et.al.)andthenpastethenumbersin.

That'sitforentry1.5.Ihopeithasbeenhelpful,ifnotoutrightentertaining.Iamdelightedtogetanyfeedback,andgenuinelypleasedtoansweranyquestions.Aspromised,inentry2.0,IwillbediscussingimplementingAjaxpushinNetty(withnofurtherdecimalreleasesinthe1range).

Part1ofthisseries:NettyTutorialPart1:IntroductiontoNetty
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐
章节导航