自上次使用Openresty+Lua+Nginx的來加速本身的網站,用上了比較時髦的技術,感受算是讓本身的網站響應速度達到極限了,直到看到了Netty,公司就是打算用Netty來替代Openresty這一套,因此,本身也學了很久,琢磨了好一趟才知道怎麼用,如今用來寫一套HTTP代理服務器吧,以後再測試一下性能。html
以前相關的文章以下:
【網頁加速】lua redis的二次升級
使用Openresty加快網頁速度java
參考自《Netty實戰》git
FullHttpRequest:
github
FullHttpResponse:
redis
在實現Http代理服務器以前,咱們先來查看一下Netty實現代理服務器的完整流程:
centos
Netty的Http服務的流程是:
一、Client向Server發送http請求,在一般的狀況中,client通常指的是瀏覽器,也能夠由本身用netty實現一個客戶端。此時,客戶端須要用到HttpRequestEncoder將http請求進行編碼。
二、Server端對http請求進行解析,服務端中,須要用到HttpRequestDecoder來對請求進行解碼,而後實現本身的業務需求。
三、Server端向client發送http響應,處理完業務需求後,將相應的內容,用HttpResponseEncoder進行編碼,返回數據。
四、Client對http響應進行解析,用HttpResponseDecoder進行解碼。瀏覽器
而Netty實現Http代理服務器的過程跟上面的所說無心,只不過是在本身的業務層增長了回源到tomcat服務器這一過程。結合上本身以前實現過的用OpenResty+Nginx來作代理服務器這一套,此處的Netty實現的過程也與此相似。此處粘貼一下OpenResty+Nginx實現的流程圖:
tomcat
而使用了Netty以後,即是將中間的OpenResty+Nginx換成了Netty,下面咱們來看一下具體的實現過程。服務器
public class HttpServer { public void start(int port) throws Exception { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .handler(new LoggingHandler(LogLevel.DEBUG)) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { // server端發送的是httpResponse,因此要使用HttpResponseEncoder進行編碼 ch.pipeline().addLast( new HttpResponseEncoder()); // server端接收到的是httpRequest,因此要使用HttpRequestDecoder進行解碼 ch.pipeline().addLast( new HttpRequestDecoder()); ch.pipeline().addLast( new HttpServerHandler()); //增長自定義實現的Handler ch.pipeline().addLast(new HttpServerCodec()); } }).option(ChannelOption.SO_BACKLOG, 128) .childOption(ChannelOption.SO_KEEPALIVE, true); ChannelFuture f = b.bind(port).sync(); f.channel().closeFuture().sync(); } finally { workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); } } public static void main(String[] args) throws Exception { HttpServer server = new HttpServer(); server.start(8080); } }
@Slf4j public class HttpServerHandler extends ChannelInboundHandlerAdapter { private RedisUtil redisUtil = new RedisUtil(); @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { if (msg instanceof HttpRequest) { DefaultHttpRequest request = (DefaultHttpRequest) msg; String uri = request.uri(); if ("/favicon.ico".equals(uri)) { return; } log.info(new Date().toString()); Jedis jedis = redisUtil.getJedis(); String s = jedis.get(uri); if (s == null || s.length() == 0) { //這裏咱們的處理是回源到tomcat服務器進行抓取,而後 //將抓取的內容放回到redis裏面 try { URL url = new URL("http://119.29.188.224:8080" + uri); log.info(url.toString()); URLConnection urlConnection = url.openConnection(); HttpURLConnection connection = (HttpURLConnection) urlConnection; connection.setRequestMethod("GET"); //鏈接 connection.connect(); //獲得響應碼 int responseCode = connection.getResponseCode(); if (responseCode == HttpURLConnection.HTTP_OK) { BufferedReader bufferedReader = new BufferedReader(new InputStreamReader (connection.getInputStream(), StandardCharsets.UTF_8)); StringBuilder bs = new StringBuilder(); String l; while ((l = bufferedReader.readLine()) != null) { bs.append(l).append("\n"); } s = bs.toString(); } jedis.set(uri, s); connection.disconnect(); } catch (Exception e) { log.error("", e); return; } } jedis.close(); FullHttpResponse response = new DefaultFullHttpResponse( HTTP_1_1, OK, Unpooled.wrappedBuffer(s != null ? s .getBytes() : new byte[0])); response.headers().set(CONTENT_TYPE, "text/html"); response.headers().set(CONTENT_LENGTH, response.content().readableBytes()); response.headers().set(CONNECTION, HttpHeaderValues.KEEP_ALIVE); ctx.write(response); ctx.flush(); } else { //這裏必須加拋出異常,要否則ab測試的時候一直卡住不動,暫未解決 throw new Exception(); } } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { ctx.flush(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { ctx.close(); } }
下面的是ab測試,在1GHz、2G內存的centos7機器(阿里雲服務器)下進行測試,測試命令ab -c 100 -n 10000 localhost:8000/,併發數爲100,總數爲10000。併發
性能:
總體響應時間的分佈比(單位:ms):
看完以後,我本身也震驚了,Netty實現的不只穩定、吞吐率還比OpenResty的高出一倍,OpenResty的竟然還有那麼多的失敗次數,不知是否是個人代碼的問題仍是測試例子不規範,至今,我仍是OpenResty的腦殘粉。整體的來講,Netty實現的服務器性能仍是比較強的,不只可以快速地開發高性能的面向協議的服務器和客戶端,還能夠在Netty上輕鬆實現各類自定義的協議。
https://github.com/Zephery/myway
參考: