Java >> Java tutorial >  >> Tag >> Netty

Netty:En anden slags web(socket) server

Netty bruges i dag i alle slags applikationer, overalt på internettet, til at håndtere tusindvis (hvis ikke millioner) af chatsamtaler, multiplayer-spil inklusive Minecraft, Twitter og mange andre applikationer. Det er dog ikke kommet ret langt ind i mindshare hos virksomhedsprogrammører, der udvikler forretningsapplikationer.

Jeg tror på, at Netty kan introducere en ny bølge af funktionalitet, som andre løsninger simpelthen ikke kan matche på grund af dens fuldt tovejs tekst og binære ikke-HTTP-datatransport, sammen med dens understøttelse af mange flere samtidige klienter end traditionelle "tråd-per-socket" ”-servere.

Du kender måske til Nettys dygtighed med WebSockets, men vidste du, at den kan fungere ekstremt godt som en traditionel webserver? På grund af dets meget gennemtænkte design kan Netty ved at tilføje passende handlere til sin pipeline håndtere stort set al trafik. Det kan også håndtere flere typer samtidigt, såsom WebSockets og HTTP over den samme port på samme tid. Ved at kombinere disse sammen, er programmører skånet for at håndtere gener såsom CORS (Cross Origin Resource Sharing), der kan rejse deres grimme hoved, når en browser forsøger at sende anmodninger til servere, den ikke downloadede fra.

Kraften ved Netty

For at give et fingerpeg om dets evne til at transformere virksomhedsapplikationer har jeg sammensat et kodeeksempel, der viser et af de traditionelle eksempler på nettet, som henter aktiekurser.

Andre applikationer skal lave AJAX-anmodninger, afstemninger, have opdateringsknapper osv. for at opdatere priser. WebSockets fjerner behovet for noget af det. Efter at have oprettet en konstant åben tovejsforbindelse, kan både klienten og serveren tale med hinanden, når der er behov for det, uden nogen forhandling. Så klienten lader serveren vide, når en bruger ændrer kriterier, og serveren opdaterer klienten, når relevante data ændres baseret på disse kriterier.

  • Du kan finde den fuldt funktionelle kode her.

Jeg har sat en lille JSON-baseret protokol op til klienten for at lade serveren vide, hvad brugeren har besluttet. For at tilføje et nyt symbol til listen, som serveren holder øje med for klienten, er et simpelt opkald alt, der er nødvendigt. Her er et eksempel:

doSend('{"command":"add", "tickerSymbol":"GOOG"}');

Dette tilføjer symbolet til listen. Den næste opdatering fra serveren inkluderer den aktuelle aktiekurs (fra Yahoo Finances REST API) for det nye symbol i dens data. Det er lige så nemt at fjerne et element:

doSend('{"command":"remove", "tickerSymbol":"GOOG"}');

Med disse to kommandoer styrer klienten listen over symboler, som serveren ser for hver bruger. På serversiden i Netty-handleren er det eneste, programmøren skal gøre for at tage højde for flere brugere, at sikre, at der oprettes en ny handler for hver ny forbindelse, og at der ikke bruges statiske medlemmer, hvor data ikke skal deles. . Medmindre andet er fortalt med en anmærkning, antager Netty, at handlere ikke kan deles.

Lad os se på, hvordan handlerne er defineret til Netty-pipelinen. Dette er fra StockTickerServer-klassen:

ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
p.addLast("encoder", new HttpResponseEncoder());
p.addLast("decoder", new HttpRequestDecoder());
p.addLast("aggregator", new HttpObjectAggregator(65536));
p.addLast("handler", new StockTickerServerHandler());
}
});

Rækkefølgen her er meget vigtig, da hver handler i pipelinen har en chance for at behandle (eller ikke behandle) data og videregive dem til den næste handler. Stock ticker handleren er i bunden, da det er den, der sender data tilbage til kunden og derfor er i slutningen af ​​pipelinen. Ved at oprette nye forekomster af handlerne får hver ny forbindelse sine egne forekomster af hver behandler. Hvis handlere er statsløse og trådsikre, kan singletons bruges i stedet, hvor det er relevant for at spare hukommelse. Ingen af ​​de handlere, jeg bruger, kan deles, så jeg viser ikke et eksempel på det.

Netty som webserver

Et par tricks bruges til at få Netty til at håndtere HTTP- og WebSocket-trafik på samme tid.

1. StockTickerServerHandler udvider SimpleChannelInboundHandler

Dette fortæller Netty, at vi ønsker, at al trafik kommer til denne handler. Ellers kunne vi bruge SimpleChannelInboundHandler, hvis vi kun ønskede at håndtere HTTP-trafik, eller SimpleChannelInboundHandler, hvis vi kun ville håndtere WebSocket-trafik.

2. ChannelRead0 (kanallæs nul) metoden

@Override
protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof FullHttpRequest) {
this.handleHttpRequest(ctx, (FullHttpRequest)msg);
} else if (msg instanceof WebSocketFrame) {
this.handleWebSocketFrame(ctx, (WebSocketFrame)msg);
}
}

Dette giver os mulighed for at behandle HTTP- og WebSocket-trafik i henhold til hver protokol. handleHttpRequest serverer HTML, billeder, CSS, JavaScript og al anden normal webtrafik, og handleWebSocketFrame finder ud af, hvad de skal gøre med de brugerdefinerede beskeder, der sendes fra klienten.

3. Mimetyper

Netty kommer ikke indbygget med understøttelse af behandling af mime-typer, da WebSocket-opkald ikke i sagens natur har brug for dem.

Jeg tilføjede en lidt modificeret version af Apaches mimetype-fil og indlæser den statisk. Jeg synkroniserer på belastningen, fordi Netty kan oprette en hel del handlere i starten til en pulje, hvis den vil, og konstruktøren kan udføres af mange handlere på samme tid. Da feltet er statisk, kan kortet indlæses mange gange, før det bliver ikke-nul. Synkronisering på en statisk lås (IKKE den aktuelle forekomst af klassen) forhindrer dette i at ske.

Andre detaljer

handleWebSocketFrame-metoden tager sig af de forskellige "kendte" typer rammer, som WebSocket-protokollen definerer. Når en fuldtekstramme er modtaget, videregiver jeg den til implementatoren af ​​den grænseflade, jeg oprettede, for at specificere, hvordan jeg skal håndtere forretningslogikken.

Den kode findes i StockTickerMessageHandler. Det opretter en baggrundstråd til at hente aktiekurserne og sende dem til klienten, og behandler de kommandoer, der sendes af klienten.

Der er lidt rodet kode derinde til håndtering af Gzip-komprimerede data sendt af Yahoo og parsing af JSON returneret af tjenesten, sammen med noget kode, der bruger java.util.concurrent klasser som Executor, AtomicBoolean, AtomicReference og CopyOnWriteArrayList til at beholde baggrundstråden og Netty-handleren fra at trampe på hinanden, mens de deler detaljerne om kanalen og den aktuelle liste over symboler.

Jeg bruger også Gson til at omdanne den indgående JSON til POJO'er, så de er nemmere at behandle. Bortset fra det, er det blot forretningsslutningen af ​​dette eksempel.

En note om godkendelse

Jeg havde ikke tid til at tilføje godkendelse til dette eksempel. Hvis jeg gjorde det, ville jeg have brugt Shiro, en super kraftfuld autentificering/autorisation/krypteringsramme, der fungerer med både normale applikationer og webapplikationer. HTTPS-understøttelse mangler også, da dette er en offentlig applikation til kontrol af aktiekurser. Der er et eksempel på tilføjelse af HTTPS (og WSS) her.

En ting, der er meget vanskelig (hvis ikke umulig) med JavaScript WebSockets, er at sende godkendelsesdata sammen med opgraderingsanmodningen (dvs. at kalde nye WebSocket(uri)). Af denne grund er det typisk først at sende et HTTPS-POST, som et normalt websted ville og indstille et auth-cookie-token. På den måde, når opgraderingsanmodningen sendes, sendes cookien automatisk sammen med den. Når du bruger godkendelse, skal du huske at bruge HTTPS og WSS i stedet for HTTP og WS for at beskytte data. Når godkendelsen er på plads, bliver det blot et spørgsmål om at tjekke for den godkendte bruger, hvor det er nødvendigt, og bemærke, at noget trafik altid skal passere igennem (HTML, billeder osv.).

  • Kodeprojekt

Konklusion

Netty er kommet til sin ret som en højtydende, spilskiftende måde at bygge nye applikationer på. Nutidens virksomhedsapplikationer kan være meget mere interaktive, end de er nu ved at udnytte de muligheder, som WebSockets tilbyder. Jeg håber, du har nydt dette lille eventyr i Netty, og tilgiv venligst den forfærdelige browserklient, jeg havde bare ikke tid til at lave en god Backbone.js-klientapp til dette eksempel.

Tak!

Java tag