HTTP-kiszolgáló Netty-vel
1. Áttekintés
Ebben az oktatóanyagban fogunk valósítson meg egy egyszerű felső burkolatú kiszolgálót HTTP Netty-vel, egy aszinkron keretrendszer, amely rugalmasságot biztosít számunkra a hálózati alkalmazások fejlesztésére a Java-ban.
2. A kiszolgáló rendszerindítása
Mielőtt elkezdenénk, tisztában kell lennünk a Netty olyan alapfogalmaival, mint a csatorna, a kezelő, a kódoló és a dekóder.
Itt egyenesen a szerver bootstrapelésére ugrunk, amely többnyire megegyezik egy egyszerű protokoll-szerverrel:
public class HttpServer {private int port; privát statikus naplózó naplózó = LoggerFactory.getLogger (HttpServer.class); // konstruktor // fő módszer, ugyanaz, mint az egyszerű protokoll-kiszolgáló public void run () dobja a Kivételt {... ServerBootstrap b = new ServerBootstrap (); b.group (bossGroup, workerGroup) .channel (NioServerSocketChannel.class) .handler (új LoggingHandler (LogLevel.INFO)) .childHandler (új ChannelInitializer () {@Override protected void initChannel (SocketChannel {ChannelPlay ch) dobások .pipeline (); p.addLast (új HttpRequestDecoder ()); p.addLast (új HttpResponseEncoder ()); p.addLast (új CustomHttpServerHandler ());}}); ...}}
Ezért itt csak a childHandler különbözik a megvalósítani kívánt protokolltól, ami HTTP számunkra.
Három kezelőt adunk a szerver folyamatához:
- Netty's HttpResponseEncoder - sorosításhoz
- Netty's HttpRequestDecoder - deserializációhoz
- A sajátunk CustomHttpServerHandler - a szerver viselkedésének meghatározásához
Most nézzük meg részletesen az utolsó kezelőt.
3. CustomHttpServerHandler
Egyéni kezelőnk feladata a bejövő adatok feldolgozása és válasz küldése.
Bontjuk le, hogy megértsük a működését.
3.1. A kezelő felépítése
CustomHttpServerHandler kiterjeszti Netty absztraktját SimpleChannelInboundHandler és végrehajtja életciklus-módszereit: Ahogy a módszer neve is sugallja, channelReadComplete a csatorna utolsó üzenetének elfogyasztása után kiüríti a kezelő környezetet, hogy elérhető legyen a következő bejövő üzenethez. A módszer, a metódus kivételFogott az esetleges kivételek kezelésére szolgál. Eddig csak a kazán kódot láttuk. Most folytassuk az érdekes dolgokkal, a megvalósítással channelRead0. Felhasználási esetünk egyszerű, a szerver egyszerűen átalakítja a kérelem törzsét és a lekérdezési paramétereket, ha vannak, nagybetűvé. Vigyázat: a kérelem adatait a válaszban tükrözzük - ezt csak demonstrációs célokra tesszük, hogy megértsük, hogyan használhatjuk a Netty-t egy HTTP szerver megvalósítására. Itt, felhasználjuk az üzenetet vagy a kérést, és a protokoll ajánlása szerint állítjuk be a válaszát (vegye figyelembe, hogy RequestUtils pillanatok alatt írunk): Mint láthatjuk, amikor csatornánk kap egy HttpRequest, először ellenőrzi, hogy a kérés 100 Folytatás állapotot vár-e. Ebben az esetben azonnal visszaírunk egy üres státusszal FOLYTATNI: Ezt követően a kezelő inicializálja a válaszként küldendő karakterláncot, és hozzáadja a kérés lekérdezési paramétereit ahhoz, hogy visszaküldhető legyen. Most definiáljuk a módszert formatParams és helyezze a RequestUtils segítő osztály erre: Ezután egy HttpContent, átvesszük a kérelem törzsét, és átalakítjuk nagybetűvé: Továbbá, ha a kapott HttpContent egy LastHttpContent, hozzáadunk egy búcsú üzenetet és a záró fejléceket, ha vannak: Most, hogy az elküldendő adataink készen állnak, meg tudjuk írni a választ a ChannelHandlerContext: Ezzel a módszerrel hoztuk létre a FullHttpResponse HTTP / 1.1 verzióval, hozzáadva azokat az adatokat, amelyeket korábban készítettünk. Ha egy kérést életben akarunk tartani, vagy más szavakkal, ha a kapcsolatot nem kell lezárni, akkor a válasz kapcsolat fejléc as életben tartani. Ellenkező esetben lezárjuk a kapcsolatot. A szerver teszteléséhez küldjünk néhány cURL parancsot, és nézzük meg a válaszokat. Természetesen, az osztály futtatásával kell elindítanunk a szervert HttpServer ezt megelőzően. Először hívjuk meg a szervert, és egy cookie-t adunk a kéréshez: Válaszként a következőket kapjuk: Ütni is tudunk //127.0.0.1:8080?param1=one bármely böngészőből ugyanazt az eredményt láthatja. Második tesztként küldjünk POST-ot testtel mintatartalom: Íme a válasz: Ezúttal, mivel kérésünkben volt egy test, a szerver nagybetűvel küldte vissza. Ebben az oktatóanyagban láttuk, hogyan lehet a HTTP-protokollt megvalósítani, különös tekintettel a HTTP-kiszolgálóra a Netty segítségével. A Nettyben található HTTP / 2 bemutatja a HTTP / 2 protokoll kliens-szerver megvalósítását. Mint mindig, a forráskód is elérhető a GitHubon.public class CustomHttpServerHandler kiterjeszti a SimpleChannelInboundHandler {private HttpRequest kérést; StringBuilder responseData = új StringBuilder (); @Orride public void channelReadComplete (ChannelHandlerContext ctx) {ctx.flush (); } @Orride protected void channelRead0 (ChannelHandlerContext ctx, Object msg) {// követendő megvalósítás} @Orride public void kivételCaught (ChannelHandlerContext ctx, Throwable reason) {caus.printStackTrace (); ctx.close (); }}
3.2. A Csatorna olvasása
if (HttpRequest msg példánya) {HttpRequest kérés = this.request = (HttpRequest) msg; if (HttpUtil.is100ContinueExpected (kérés)) {writeResponse (ctx); } responseData.setLength (0); responseData.append (RequestUtils.formatParams (kérés)); } responseData.append (RequestUtils.evaluateDecoderResult (kérés)); if (HttpContent msg példánya) {HttpContent httpContent = (HttpContent) msg; responseData.append (RequestUtils.formatBody (httpContent)); responseData.append (RequestUtils.evaluateDecoderResult (kérés)); if (LastHttpContent msg példánya) {LastHttpContent trailer = (LastHttpContent) msg; responseData.append (RequestUtils.prepareLastResponse (kérés, trailer)); writeResponse (ctx, trailer, responseData); }}
private void writeResponse (ChannelHandlerContext ctx) {FullHttpResponse response = new DefaultFullHttpResponse (HTTP_1_1, CONTINUE, Unpooled.EMPTY_BUFFER); ctx.write (válasz); }
StringBuilder formatParams (HttpRequest kérés) {StringBuilder responseData = új StringBuilder (); QueryStringDecoder queryStringDecoder = új QueryStringDecoder (request.uri ()); Térkép
StringBuilder formatBody (HttpContent httpContent) {StringBuilder responseData = új StringBuilder (); ByteBuf tartalom = httpContent.content (); if (content.isReadable ()) {responseData.append (content.toString (CharsetUtil.UTF_8) .toUpperCase ()) .append ("\ r \ n"); } return responseData; }
StringBuilder PreparLastResponse (HttpRequest kérés, LastHttpContent trailer) {StringBuilder responseData = új StringBuilder (); responseData.append ("Viszlát! \ r \ n"); if (! trailer.trailingHeaders (). isEmpty ()) {responseData.append ("\ r \ n"); for (CharSequence neve: trailer.trailingHeaders (). names ()) {for (CharSequence értéke: trailer.trailingHeaders (). getAll (név)) {responseData.append ("P.S. Trailing Header:"); responseData.append (név) .append ("=") .append (érték) .append ("\ r \ n"); }} responseData.append ("\ r \ n"); } return responseData; }
3.3. A válasz megírása
private void writeResponse (ChannelHandlerContext ctx, LastHttpContent trailer, StringBuilder responseData) {boolean keepAlive = HttpUtil.isKeepAlive (kérés); FullHttpResponse httpResponse = new DefaultFullHttpResponse (HTTP_1_1, ((HttpObject) trailer) .decoderResult (). IsSuccess ()? OK: BAD_REQUEST, Unpooled.copiedBuffer (responseData.toString (), CharsetUtil.UT httpResponse.headers (). set (HttpHeaderNames.CONTENT_TYPE, "text / plain; charset = UTF-8"); if (keepAlive) {httpResponse.headers (). setInt (HttpHeaderNames.CONTENT_LENGTH, httpResponse.content (). readableBytes ()); httpResponse.headers (). set (HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE); } ctx.write (httpResponse); if (! keepAlive) {ctx.writeAndFlush (Unpooled.EMPTY_BUFFER) .addListener (ChannelFutureListener.CLOSE); }}
4.1. GET kérés
curl //127.0.0.1:8080?param1=one
Paraméter: PARAM1 = EGY Viszlát!
4.2. POST kérés
curl -d "mintatartalom" -X POST //127.0.0.1:8080
MINTATARTALOM Viszlát!
5. Következtetés