小知識:21天效應
在行爲心理學中,人們把一個人的新習慣或新理念的形成並得以鞏固至少需要21天的現象,稱之爲21天效應。也就是說,一個人的動作或想法,如果重複21天就會變成一個習慣性的動作或想法。
步驟
1 先寫好基本的Netty客戶端和Netty服務的代碼。參考文章【netty初識】
2.搭建好基本的Springboot項目。
3.將Netty服務端代碼的啓動代碼和關閉代碼分離,服務端加上@Component註解,交由Spring管理實例。
4.Springboot啓動時,將Netty服務給啓動;同時Springboot停止時,將Netty服務銷燬。
實現
Netty服務端
主要工作:
將Netty服務端代碼的啓動代碼和關閉代碼分離,服務端加上@Component註解,交由Spring管理實例。
/**
* 服務端
* 1.創建一個ServerBootstrap的實例引導和綁定服務器。
* 2.創建並分配一個NioEventLoopGroup實例以進行事件的處理,比如接受連接以及讀寫數據。
* 3.指定服務器綁定的本地的InetSocketAddress。
* 4.使用一個EchoServerHandler的實例初始化每一個新的Channel。
* 5.調用ServerBootstrap.bind()方法以綁定服務器。
*/
@Slf4j
@Component
public class EchoServer {
/**
* NioEventLoop並不是一個純粹的I/O線程,它除了負責I/O的讀寫之外
* 創建了兩個NioEventLoopGroup,
* 它們實際是兩個獨立的Reactor線程池。
* 一個用於接收客戶端的TCP連接,
* 另一個用於處理I/O相關的讀寫操作,或者執行系統Task、定時任務Task等。
*/
private final EventLoopGroup bossGroup = new NioEventLoopGroup();
private final EventLoopGroup workerGroup = new NioEventLoopGroup();
private Channel channel;
/**
* 啓動服務
* @param hostname
* @param port
* @return
* @throws Exception
*/
public ChannelFuture start(String hostname,int port) throws Exception {
final EchoServerHandler serverHandler = new EchoServerHandler();
ChannelFuture f = null;
try {
//ServerBootstrap負責初始化netty服務器,並且開始監聽端口的socket請求
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.localAddress(new InetSocketAddress(hostname,port))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
// 爲監聽客戶端read/write事件的Channel添加用戶自定義的ChannelHandler
socketChannel.pipeline().addLast(serverHandler);
}
});
f = b.bind().sync();
channel = f.channel();
log.info("======EchoServer啓動成功!!!=========");
} catch (Exception e) {
e.printStackTrace();
} finally {
if (f != null && f.isSuccess()) {
log.info("Netty server listening " + hostname + " on port " + port + " and ready for connections...");
} else {
log.error("Netty server start up Error!");
}
}
return f;
}
/**
* 停止服務
*/
public void destroy() {
log.info("Shutdown Netty Server...");
if(channel != null) { channel.close();}
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
log.info("Shutdown Netty Server Success!");
}
}
服務端業務處理handler
服務端的生命週期以及接收客戶端的信息收發和處理。這裏不建議使用阻塞的操作,容易影響netty的性能。
/***
* 服務端自定義業務處理handler
*/
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
/**
* 對每一個傳入的消息都要調用;
* @param ctx
* @param msg
* @throws Exception
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf in = (ByteBuf) msg;
System.out.println("server received: "+in.toString(CharsetUtil.UTF_8));
ctx.write(in);
}
/**
* 通知ChannelInboundHandler最後一次對channelRead()的調用時當前批量讀取中的的最後一條消息。
* @param ctx
* @throws Exception
*/
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
}
/**
* 在讀取操作期間,有異常拋出時會調用。
* @param ctx
* @param cause
* @throws Exception
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
Springboot啓動服務端代碼
CommandLineRunner #run()
這裏主要是通過CommandLineRunner 接口的run方法,實現在項目啓動後執行的功能,SpringBoot提供的一種簡單的實現方案就是添加一個model並實現CommandLineRunner接口,實現功能的代碼放在實現的run方法中。
addShutdownHook()
而 Runtime.getRuntime().addShutdownHook(shutdownHook); 這個方法的意思就是在jvm中增加一個關閉的鉤子,當jvm關閉的時候,會執行系統中已經設置的所有通過方法addShutdownHook添加的鉤子,當系統執行完這些鉤子後,jvm纔會關閉。所以這些鉤子可以在jvm關閉的時候進行內存清理、對象銷燬等操作。
詳細代碼如下:
@SpringBootApplication
public class SpringNettyApplication implements CommandLineRunner {
@Value("${netty.port}")
private int port;
@Value("${netty.url}")
private String url;
@Autowired
private EchoServer echoServer;
public static void main(String[] args) {
SpringApplication.run(SpringNettyApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
ChannelFuture future = echoServer.start(url,port);
Runtime.getRuntime().addShutdownHook(new Thread(){
@Override
public void run() {
echoServer.destroy();
}
});
//服務端管道關閉的監聽器並同步阻塞,直到channel關閉,線程纔會往下執行,結束進程
future.channel().closeFuture().syncUninterruptibly();
}
}
Netty客戶端
它在本文中的作用主要是爲了測試服務端是否正常運行,通過客戶端連接Netty的服務端,看到消息是否正常通信。
/**
* 客戶端
* 1.爲初始化客戶端,創建一個Bootstrap實例
* 2.爲進行事件處理分配了一個NioEventLoopGroup實例,其中事件處理包括創建新的連接以及處理入站和出站數據;
* 3.當連接被建立時,一個EchoClientHandler實例會被安裝到(該Channel的一個ChannelPipeline中;
* 4.在一切都設置完成後,調用Bootstrap.connect()方法連接到遠程節點。
*/
public class EchoClient {
private final String host;
private final int port;
public EchoClient(String host, int port) {
this.host = host;
this.port = port;
}
/**
* 運行流程:
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
new EchoClient("127.0.0.1",10010).start();
}
private void start() throws Exception {
/**
* Netty用於接收客戶端請求的線程池職責如下。
* (1)接收客戶端TCP連接,初始化Channel參數;
* (2)將鏈路狀態變更事件通知給ChannelPipeline
*/
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.remoteAddress(new InetSocketAddress(host,port))
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new EchoClientHandler());
}
});
//綁定端口
ChannelFuture f = b.connect().sync();
f.channel().closeFuture().sync();
} catch (Exception e) {
group.shutdownGracefully().sync();
}
}
}
Netty客戶端業務處理類
主要是監控Netty客戶端的生命週期以及接收服務端的消息,往服務端發送消息等。
/**
* 客戶端處理類
*/
public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
/**
* 在到服務器的連接已經建立之後將被調用
* @param ctx
* @throws Exception
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush(Unpooled.copiedBuffer("Netty rocks !", CharsetUtil.UTF_8));
}
/**
* 當從服務器接收到一個消息時被調用
* @param channelHandlerContext
* @param byteBuf
* @throws Exception
*/
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf) throws Exception {
System.out.println("Client received: "+ byteBuf.toString(CharsetUtil.UTF_8));
}
/**
* 在處理過程中引發異常時被調用
* @param ctx
* @param cause
* @throws Exception
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
總結
從整體來看,只不過是將Netty服務端從main函數啓動方式改爲交給Spring來管理啓動和銷燬的工作,也就說以後你有個什麼代碼要交給Spring管理的也是可以這樣子處理。
最後
如果對 Java、大數據感興趣請長按二維碼關注一波,我會努力帶給你們價值。覺得對你哪怕有一丁點幫助的請幫忙點個贊或者轉發哦。
關注公衆號【愛編碼】,小編會一直更新文章的哦。
作者:xbmchina
鏈接:https://www.jianshu.com/p/2a2562f85241
來源:簡書
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。