Netty

Пример реализации не блокирующего сервера на Netty.

Опубликовано 20-12-2015
Эксперементы
Теги js, netty

Пример реализации не блокирующего сервера на Netty.

Сервер получает запрос HTTP по любому пути, запрашивает данные о погоде от сервиса yahoo, отрезает всю не нужную информацию из полученного JSON и отправляет в ответ. Пример написан на javascript под Nashorn. Для обработки json используется json-smart .

Список необходимых библиотек:

accessors-smart-1.1.jar  
json-smart-2.2.1.jar  
netty-all-4.0.33.Final.jar

Чтение HTTP запроса и запись HTTP ответа происходит асинхронно. В HTTP запросе не содержится необходимой информации. Поэтому наш сервер не ждет получения тела HTTP запроса, он сразу вызывает сервис yahoo в отдельном потоке.

Код сервера:

var netty = new JavaImporter(
Packages.io.netty.buffer,
Packages.io.netty.channel,
Packages.io.netty.bootstrap,
Packages.io.netty.channel.nio,
Packages.io.netty.channel.socket,
Packages.io.netty.channel.socket.nio,
Packages.io.netty.handler.codec.http,
Packages.io.netty.handler.stream,
Packages.io.netty.util,
Packages.io.netty.util.internal,
Packages.io.netty.handler.codec.http
);

var System = java.lang.System;
var JSONValue = Java.type("net.minidev.json.JSONValue");
var Thread = Java.type("java.lang.Thread");

var weatherHandler = {
  channelRead0:function(ctx,msg) {
  },
  channelActive:function(ctx) {
    ctx.fireChannelActive();
    print(">> "+ctx.channel());
    var res = ctx.alloc().buffer();

    res.writeBytes("HTTP/1.1 200 OK\r\n".getBytes());
    res.writeBytes("Content-Type: application/json\r\n".getBytes());
    res.writeBytes("Connection: close\r\n".getBytes());
    res.writeBytes("\r\n".getBytes());
    ctx.writeAndFlush(res);
    
    new Thread(function run() {
      try {
        res = ctx.alloc().buffer();
        var is = new java.net.URL("http://query.yahooapis.com/v1/public/yql?q=SELECT%20*%20FROM%20weather.bylocation%20WHERE%20location%3D'Moscow'%20AND%20unit%3D%22c%22&format=json&diagnostics=true&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys&callback=").getContent();
        var r = JSONValue.parse(is);
        res.writeBytes(r.query.results.weather.rss.channel.item.condition.toString().getBytes());
        res.writeBytes("\r\n".getBytes());
        ctx.writeAndFlush(res).addListener(netty.ChannelFutureListener.CLOSE);
      } catch (e) {
        e.printStackTrace();
        ctx.close();
      }
    }).start();
  },
  exceptionCaught:function(ctx, cause) {
    cause.printStackTrace();
    var res = ctx.alloc().buffer();
    res.writeBytes("HTTP/1.1 500 Error\r\n");
    ctx.writeAndFlush(res).addListener(netty.ChannelFutureListener.CLOSE);
  }
}

var weatherHandlerInit = {
  initChannel: function(ch) {
        var pipeline = ch.pipeline();
        pipeline.addLast(new netty.SimpleChannelInboundHandler(weatherHandler));
    }
};


config = {
  port: 8080,
  qsize: 128,
  timeout: 1000
}

var bossGroup = new netty.NioEventLoopGroup();
var workerGroup = new netty.NioEventLoopGroup();

try {
   var b = new netty.ServerBootstrap();
   b.group(bossGroup, workerGroup)
      .channel(netty.NioServerSocketChannel.class)
      .childHandler(new netty.ChannelInitializer(weatherHandlerInit))
      .option(netty.ChannelOption.SO_BACKLOG, config.qsize)
      .option(netty.ChannelOption.CONNECT_TIMEOUT_MILLIS,config.timeout)
      .option(netty.ChannelOption.SO_TIMEOUT,config.timeout)
      .childOption(netty.ChannelOption.SO_KEEPALIVE, true);

   var f = b.bind(config.port).sync();
   f.channel().closeFuture().sync();
} finally {
   workerGroup.shutdownGracefully();
   bossGroup.shutdownGracefully();
}

Запуск:

jjs -cp lib/netty-all-4.0.33.Final.jar:lib/json-smart-2.2.1.jar:lib/accessors-smart-1.1.jar weather.js