使用Netty3或Netty4發布Http協議服務
今天給大家簡單的介紹一下Netty,讓大家以後在使用到Netty的時候能夠有一定的瞭解和基礎,這樣深入學習Netty以及以後靈活應用這門技術也就不在話下了,萬丈高樓平地起,程式猿們平時還是要注重積累,多花些時間在技術上面,如果實在對代碼提不起興趣就早點規劃好自己的發展路線,死磕著也沒什麼必要,對自己沒啥好處,但是如果你至少不討厭編程,那麼還是多多學習吧!
Netty是什麼
Netty提供非同步、事件驅動的網路應用程式架構和工具,用以快速開發高效能、高可靠性的網路伺服器和用戶端程式。
Netty的架構
Netty的特性
設計:
------統一的API,適用於不同的協議(阻塞和非阻塞)
------基於靈活、可擴充的事件驅動模型
------高度可定製的執行緒模式
------可靠的無串連資料Socket支援(UDP)
效能:
------更好的輸送量,低延遲
------更省資源
------盡量減少不必要的記憶體拷貝
安全:
------完整的SSL/TLS和STARTTLS的支援
------能在Applet與GoogleAndroid的限制環境運行良好
健壯性:
------不再因過快、過慢或超負載串連導致OutOfMemoryError
------不再有在高速網路環境下NIO讀寫頻率不一致的問題
易用:
------完善的Java doc,使用者指南和範例
------簡潔簡單
------僅依賴於JDK1.5
Netty怎麼用
小Alan教大家使用Netty3或Netty4發布Http協議服務介面,來引導大家進入Netty的世界。
Netty3實現Http協議服務介面步驟:
第一步:建立Http業務處理服務類,代碼如下
package com.alanlee.http;
import static org.jboss.netty.handler.codec.http.HttpResponseStatus.OK;
import static org.jboss.netty.handler.codec.http.HttpVersion.HTTP_1_1;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.DynamicChannelBuffer;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelFutureListener;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
import org.jboss.netty.handler.codec.http.HttpRequest;
import org.jboss.netty.handler.codec.http.HttpResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* HTTP服務業務處理類.
*
* @author AlanLee
* @version 2018/01/11
*
*/
public class HttpServerHandler extends SimpleChannelUpstreamHandler
{
/**
* 日誌類.
*/
protected static final Logger LOGGER = LoggerFactory.getLogger(HttpServerHandler.class);
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception
{
HttpRequest request = (HttpRequest) e.getMessage();
String method = request.getMethod().getName();
String url = request.getUri().toLowerCase();
System.out.println(method);
System.out.println(url);
// 接收請求內容並列印
ChannelBuffer buffer = request.getContent();
String requestStr = new String(buffer.array(), "UTF-8");
System.out.println(requestStr);
// 處理響應訊息
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
ChannelBuffer responseBuffer = new DynamicChannelBuffer(2048);
responseBuffer.writeBytes("你是豬嗎?".getBytes("UTF-8"));
response.setContent(responseBuffer);
// 返回內容的MIME類型
response.setHeader("Content-Type", "text/html; charset=UTF-8");
// 響應體的長度
response.setHeader("Content-Length", response.getContent().writerIndex());
Channel ch = e.getChannel();
// 響應給用戶端
ChannelFuture f = ch.write(response);
// 資料發送完畢,則關閉串連通道.
f.addListener(new ChannelFutureListener()
{
public void operationComplete(ChannelFuture future) throws Exception
{
future.getChannel().close();
}
});
}
/**
* 發生異常
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
{
LOGGER.error("exceptionCaught(): ", e.getCause());
e.getCause().printStackTrace();
}
}
第二步:建立Http管道類工廠用來連接Http業務處理服務類,代碼如下
package com.alanlee.http;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.handler.codec.http.HttpRequestDecoder;
import org.jboss.netty.handler.codec.http.HttpResponseEncoder;
/**
* HTTP管道類工廠.
*
* @author AlanLee
* @version 2018/01/11
*
*/
public class ServerPipelineFactory implements ChannelPipelineFactory
{
/**
* 擷取管道.
*
* @return ChannelPipeline 管道
*/
public ChannelPipeline getPipeline()
{
ChannelPipeline pipeline = Channels.pipeline();
System.out.println("initChannel pipeline");
// 解碼
pipeline.addLast("decoder", new HttpRequestDecoder(1024, 1024, 1000 * 1024));
// 編碼
pipeline.addLast("encoder", new HttpResponseEncoder());
// 請求的業務類
pipeline.addLast("handler", new HttpServerHandler());
return pipeline;
}
}
最後,讓我們利用Netty3來發布Http協議服務介面,代碼如下
package com.alanlee.http;
import java.net.InetSocketAddress;
import java.util.concurrent.Executors;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
/**
* http服務啟動類
*
* @author AlanLee
* @version 2018/01/11
*
*/
public class HttpServer
{
public static void main(String[] args)
{
ChannelFactory factory = new NioServerSocketChannelFactory(Executors.newCachedThreadPool(),
Executors.newCachedThreadPool());
// 初始化channel的輔助類
ServerBootstrap bootstrap = new ServerBootstrap(factory);
bootstrap.setPipelineFactory(new ServerPipelineFactory());
// 建立伺服器端channel的輔助類,接收connection請求
bootstrap.bind(new InetSocketAddress(8080));
System.out.println("Start http server success!");
}
}
Netty4實現Http協議服務介面步驟:
第一步:建立Http業務處理服務類,代碼如下
package com.alanlee.netty2;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.util.AsciiString;
/**
* HttpServer業務處理
*
* @author AlanLee
* @version 2018/01/11
*
*/
public class HttpHandler extends SimpleChannelInboundHandler<FullHttpRequest>
{
private AsciiString contentType = HttpHeaderValues.TEXT_PLAIN;
@Override
protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest msg) throws Exception
{
String method = msg.method().name(); // 請求方式
String url = msg.uri().toLowerCase(); // 請求路徑
System.out.println(method);
System.out.println(url);
// 接收請求內容並列印
ByteBuf byteBuf = msg.content();
byte[] bytes = new byte[byteBuf.readableBytes()];
byteBuf.readBytes(bytes);
String requestStr = new String(bytes, "UTF-8");
System.out.println(requestStr);
DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK,
Unpooled.wrappedBuffer("你才是豬!".getBytes()));
HttpHeaders heads = response.headers();
// 返���內容的MIME類型
heads.add(HttpHeaderNames.CONTENT_TYPE, contentType + "; charset=UTF-8");
// 響應體的長度
heads.add(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());
// 表示是否需要持久串連
heads.add(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
// 響應給用戶端
ctx.write(response);
}
/**
* 資料發送完畢,則關閉串連通道.
*/
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception
{
System.out.println("channelReadComplete");
super.channelReadComplete(ctx);
ctx.flush();
}
/**
* 發生異常
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception
{
System.out.println("exceptionCaught");
if (null != cause)
cause.printStackTrace();
if (null != ctx)
ctx.close();
}
}
最後,讓我們利用Netty4來發布Http協議服務介面,代碼如下
package com.alanlee.netty2;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
/**
* 搭建HttpServer
*
* @author Alanlee
* @version 2018/01/11
*
*/
public class HttpServer
{
private final int port;
public HttpServer(int port)
{
this.port = port;
}
public static void main(String[] args) throws InterruptedException
{
new HttpServer(8081).start();
System.out.println("Start http server success!");
}
public void start() throws InterruptedException
{
// 初始化channel的輔助類
ServerBootstrap b = new ServerBootstrap();
NioEventLoopGroup group = new NioEventLoopGroup();
b.group(group).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>()
{
/**
* 初始化channel
*/
@Override
protected void initChannel(SocketChannel ch) throws Exception
{
System.out.println("initChannel ch:" + ch);
// 擷取管道
ch.pipeline().addLast("decoder", new HttpRequestDecoder()) // 解碼
.addLast("encoder", new HttpResponseEncoder()) // 編碼
/* aggregator,訊息彙總器(重要)。
Netty4中為什麼能有FullHttpRequest這個東西,
就是因為有他,HttpObjectAggregator,如果沒有他,
就不會有那個訊息是FullHttpRequest的那段Channel,
同樣也不會有FullHttpResponse,HttpObjectAggregator(512 * 1024)的參數含義是訊息合并的資料大小,
如此代表彙總的訊息內容長度不超過512kb。*/
.addLast("aggregator", new HttpObjectAggregator(512 * 1024))
.addLast("handler", new HttpHandler()); // 請求的業務類
}
}).option(ChannelOption.SO_BACKLOG, 128).childOption(ChannelOption.SO_KEEPALIVE, Boolean.TRUE);
// 建立伺服器端channel的輔助類,接收connection請求
b.bind(port).sync();
}
}
檢驗成果
我們可以通過運行Netty3以及Netty4對應的HttpServer中的main方法來啟動我們的Http伺服器類比。
啟動Netty3提供的Http協議伺服器,結果如下:
啟動成功之後,我們利用Postman工具來做個簡單的測試,注意這裡Netty3使用的是8080連接埠,結果如下:
控制台輸出內容如下:
我們再來啟動Netty4提供的Http協議伺服器,結果如下:
啟動成功之後,我們利用Postman工具來做個簡單的測試,注意這裡Netty4使用的是8081連接埠,結果如下:
控制台輸出內容如下:
這樣,我們就成功的使用了Netty3和Netty4發布了Http協議服務介面,是不是很類似於我們的web後端開發,在平時的工作中如果遇到需要高並發的項目往往會需要將項目拆成後台節點來開發,此時可不僅僅web端需要提供介面,後台節點也是需要提供介面的,這時Netty就正好符合需求,所以大家可以花些時間去掌握Netty,建議掌握Netty4版本,這樣版本升級的改動量會小很多,這裡小Alan給大家開了扇門,把你引進門,修行在個人,如果你是一個熱愛Java開發的人,也可以私聊Alan多多交流,只要Alan閒置時候。