最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

在SpringBoot中整合使用Netty框架的詳細教程

 更新時間:2020年06月23日 09:04:53   作者:朱由檢  
這篇文章主要介紹了在SpringBoot中整合使用Netty框架的教程,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下

Netty是一個非常優(yōu)秀的Socket框架。如果需要在SpringBoot開發(fā)的app中,提供Socket服務,那么Netty是不錯的選擇。

Netty與SpringBoot的整合,我想無非就是要整合幾個地方

  • 讓netty跟springboot生命周期保持一致,同生共死
  • 讓netty能用上ioc中的Bean
  • 讓netty能讀取到全局的配置

整合Netty,提供WebSocket服務

這里演示一個案例,在SpringBoot中使用Netty提供一個Websocket服務。

servlet容器本身提供了websocket的實現,但這里用netty的實現 :sparkling_heart:

添加依賴

<dependency>
	<groupId>io.netty</groupId>
	<artifactId>netty-all</artifactId>
</dependency>

是的,不用聲明版本號。因為 spring-boot-dependencies 中已經聲明了最新的netty依賴。

通過yaml配置基本的屬性

server:
 port: 80

logging:
 level:
  root: DEBUG

management:
 endpoints: 
 web:
  exposure:
  include: "*"
 
 endpoint:
 shutdown:
  enabled: true

netty:
 websocket:
 # Websocket服務端口
 port: 1024
 # 綁定的網卡
 ip: 0.0.0.0
 # 消息幀最大體積
 max-frame-size: 10240
 # URI路徑
 path: /channel

App使用了, actuator ,并且開啟暴露了 shutdown 端點,可以讓SpringBoot App優(yōu)雅的停機。 在這里通過 netty.websocket.* 配置 websocket服務相關的配置。

通過 ApplicationRunner 啟動Websocket服務

import java.net.InetSocketAddress;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.stereotype.Component;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
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.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketServerCompressionHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.springboot.netty.websocket.handler.WebsocketMessageHandler;

/**
 * 初始化Netty服務
 * @author Administrator
 */
@Component
public class NettyBootsrapRunner implements ApplicationRunner, ApplicationListener<ContextClosedEvent>, ApplicationContextAware {

	private static final Logger LOGGER = LoggerFactory.getLogger(NettyBootsrapRunner.class);
	
	@Value("${netty.websocket.port}")
	private int port;

	@Value("${netty.websocket.ip}")
	private String ip;
	
	@Value("${netty.websocket.path}")
	private String path;
	
	@Value("${netty.websocket.max-frame-size}")
	private long maxFrameSize;
	
	private ApplicationContext applicationContext;
	
	private Channel serverChannel;
	
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.applicationContext = applicationContext;
	}
	
	public void run(ApplicationArguments args) throws Exception {
		
		EventLoopGroup bossGroup = new NioEventLoopGroup();
		EventLoopGroup workerGroup = new NioEventLoopGroup();
		try {
			ServerBootstrap serverBootstrap = new ServerBootstrap();
			serverBootstrap.group(bossGroup, workerGroup);
			serverBootstrap.channel(NioServerSocketChannel.class);
			serverBootstrap.localAddress(new InetSocketAddress(this.ip, this.port));
			serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
				@Override
				protected void initChannel(SocketChannel socketChannel) throws Exception {
					ChannelPipeline pipeline = socketChannel.pipeline();
					pipeline.addLast(new HttpServerCodec());
					pipeline.addLast(new ChunkedWriteHandler());
					pipeline.addLast(new HttpObjectAggregator(65536));
					pipeline.addLast(new ChannelInboundHandlerAdapter() {
						@Override
						public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
							if(msg instanceof FullHttpRequest) {
								FullHttpRequest fullHttpRequest = (FullHttpRequest) msg;
								String uri = fullHttpRequest.uri();
								if (!uri.equals(path)) {
									// 訪問的路徑不是 websocket的端點地址,響應404
									ctx.channel().writeAndFlush(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.NOT_FOUND))
										.addListener(ChannelFutureListener.CLOSE);
									return ;
								}
							}
							super.channelRead(ctx, msg);
						}
					});
					pipeline.addLast(new WebSocketServerCompressionHandler());
					pipeline.addLast(new WebSocketServerProtocolHandler(path, null, true, maxFrameSize));

					/**
					 * 從IOC中獲取到Handler
					 */
					pipeline.addLast(applicationContext.getBean(WebsocketMessageHandler.class));
				}
			});
			Channel channel = serverBootstrap.bind().sync().channel();	
			this.serverChannel = channel;
			LOGGER.info("websocket 服務啟動,ip={},port={}", this.ip, this.port);
			channel.closeFuture().sync();
		} finally {
			bossGroup.shutdownGracefully();
			workerGroup.shutdownGracefully();
		}
	}

	public void onApplicationEvent(ContextClosedEvent event) {
		if (this.serverChannel != null) {
			this.serverChannel.close();
		}
		LOGGER.info("websocket 服務停止");
	}
}

NettyBootsrapRunner 實現了 ApplicationRunner, ApplicationListener<ContextClosedEvent> , ApplicationContextAware 接口。

這樣一來, NettyBootsrapRunner 可以在App的啟動和關閉時執(zhí)行Websocket服務的啟動和關閉。而且通過 ApplicationContextAware 還能獲取到 ApplicationContext

通過IOC管理 Netty 的Handler

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketCloseStatus;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import io.springboot.netty.service.DiscardService;
/**
 * 
 * @author Administrator
 *
 */
@Sharable
@Component
public class WebsocketMessageHandler extends SimpleChannelInboundHandler<WebSocketFrame> {
	
	private static final Logger LOGGER = LoggerFactory.getLogger(WebsocketMessageHandler.class);
	
	@Autowired
	DiscardService discardService;
	
	@Override
	protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame msg) throws Exception {
		if (msg instanceof TextWebSocketFrame) {
			TextWebSocketFrame textWebSocketFrame = (TextWebSocketFrame) msg;
			// 業(yè)務層處理數據
			this.discardService.discard(textWebSocketFrame.text());
			// 響應客戶端
			ctx.channel().writeAndFlush(new TextWebSocketFrame("我收到了你的消息:" + System.currentTimeMillis()));
		} else {
			// 不接受文本以外的數據幀類型
			ctx.channel().writeAndFlush(WebSocketCloseStatus.INVALID_MESSAGE_TYPE).addListener(ChannelFutureListener.CLOSE);
		}
	}
	
	@Override
	public void channelInactive(ChannelHandlerContext ctx) throws Exception {
		super.channelInactive(ctx);
		LOGGER.info("鏈接斷開:{}", ctx.channel().remoteAddress());
	}
	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		super.channelActive(ctx);
		LOGGER.info("鏈接創(chuàng)建:{}", ctx.channel().remoteAddress());
	}
}

handler已經是一個IOC管理的Bean,可以自由的使用依賴注入等Spring帶來的快捷功能。由于是單例存在,所有的鏈接都使用同一個hander,所以盡量不要保存任何實例變量。

這個Handler處理完畢客戶端的消息后,給客戶端會響應一條: "我收到了你的消息:" + System.currentTimeMillis() 的消息

為了演示在Handler中使用業(yè)務層,這里假裝注入了一個 DiscardService 服務。它的邏輯很簡單,就是丟棄消息

public void discard (String message) {
	LOGGER.info("丟棄消息:{}", message);
}

演示

啟動客戶端

<!DOCTYPE html>
	<html>
	<head>
		<meta charset="UTF-8">
		<title>Websocket</title>
	</head>
	<body>
	
	</body>
	<script type="text/javascript">
		;(function(){
			const websocket = new WebSocket('ws://localhost:1024/channel');
			websocket.onmessage = e => {
				console.log('收到消息:', e.data);
			}
			websocket.onclose = e => {
				let {code, reason} = e;
				console.log(`鏈接斷開:code=$[code], reason=${reason}`);
			}
			websocket.onopen = () => {
				console.log(`鏈接建立...`);
				websocket.send('Hello');
			}
			websocket.onerror = e => {
				console.log('鏈接異常:', e);
			}
		})();

	</script>
</html>

鏈接創(chuàng)建后就給服務端發(fā)送一條消息: Hello

關閉服務端

使用 PostMan 請求服務器的停機端點

 

日志

客戶端日志

 

服務端日志

2020-06-22 17:08:22.728  INFO 9392 --- [           main] io.undertow                              : starting server: Undertow - 2.1.3.Final
2020-06-22 17:08:22.740  INFO 9392 --- [           main] org.xnio                                 : XNIO version 3.8.0.Final
2020-06-22 17:08:22.752  INFO 9392 --- [           main] org.xnio.nio                             : XNIO NIO Implementation Version 3.8.0.Final
2020-06-22 17:08:22.839  INFO 9392 --- [           main] org.jboss.threads                        : JBoss Threads version 3.1.0.Final
2020-06-22 17:08:22.913  INFO 9392 --- [           main] o.s.b.w.e.undertow.UndertowWebServer     : Undertow started on port(s) 80 (http)
2020-06-22 17:08:22.931  INFO 9392 --- [           main] io.springboot.netty.NettyApplication     : Started NettyApplication in 4.536 seconds (JVM running for 5.175)
2020-06-22 17:08:23.653  INFO 9392 --- [           main] i.s.n.w.runner.NettyBootsrapRunner       : websocket 服務啟動,ip=0.0.0.0,port=1024
2020-06-22 17:08:28.484  INFO 9392 --- [  XNIO-1 task-1] io.undertow.servlet                      : Initializing Spring DispatcherServlet 'dispatcherServlet'
2020-06-22 17:08:28.484  INFO 9392 --- [  XNIO-1 task-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2020-06-22 17:08:28.492  INFO 9392 --- [  XNIO-1 task-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 8 ms
2020-06-22 17:08:28.724  INFO 9392 --- [ntLoopGroup-3-1] i.s.n.w.handler.WebsocketMessageHandler  : 鏈接創(chuàng)建:/0:0:0:0:0:0:0:1:12093
2020-06-22 17:08:28.790  INFO 9392 --- [ntLoopGroup-3-1] i.s.netty.service.DiscardService         : 丟棄消息:Hello
2020-06-22 17:08:33.688  INFO 9392 --- [     Thread-232] i.s.n.w.runner.NettyBootsrapRunner       : websocket 服務停止
2020-06-22 17:08:33.691  INFO 9392 --- [ntLoopGroup-3-1] i.s.n.w.handler.WebsocketMessageHandler  : 鏈接斷開:/0:0:0:0:0:0:0:1:12093
2020-06-22 17:08:33.699  INFO 9392 --- [     Thread-232] io.undertow                              : stopping server: Undertow - 2.1.3.Final
2020-06-22 17:08:33.704  INFO 9392 --- [     Thread-232] io.undertow.servlet                      : Destroying Spring FrameworkServlet 'dispatcherServlet'
2020-06-22 17:08:33.708  INFO 9392 --- [     Thread-232] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'

Netty會在SpringBoot App啟動后啟動,App停止后關閉,可以正常的對外提供服務 并且Handler交給IOC管理可以注入Service,完成業(yè)務處理。

總結

到此這篇關于在SpringBoot中整合使用Netty框架的文章就介紹到這了,更多相關SpringBoot整合Netty框架內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Java虛擬機如何運行Java字節(jié)碼

    Java虛擬機如何運行Java字節(jié)碼

    這篇文章主要介紹了Java虛擬機如何運行Java字節(jié)碼的問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-06-06
  • java實現打印正三角的方法

    java實現打印正三角的方法

    這篇文章主要為大家詳細介紹了java實現打印正三角的方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • mybatis-plugin插件執(zhí)行原理解析

    mybatis-plugin插件執(zhí)行原理解析

    這篇文章主要介紹了mybatis-plugin插件執(zhí)行原理,我們就需要來研究下Executor,ParameterHandler,ResultSetHandler,StatementHandler這4個對象的具體跟sql相關的方法,然后再進行修改,就可以直接起到aop的作用,需要的朋友可以參考下
    2022-10-10
  • Java隨機數的5種獲得方法(非常詳細!)

    Java隨機數的5種獲得方法(非常詳細!)

    這篇文章主要給大家介紹了關于Java隨機數的5種獲得方法,在實際開發(fā)中產生隨機數的使用是很普遍的,所以在程序中進行產生隨機數操作很重要,文中通過圖文介紹的非常詳細,需要的朋友可以參考下
    2023-10-10
  • 解決springboot mapper注入報紅問題

    解決springboot mapper注入報紅問題

    這篇文章主要介紹了解決springboot mapper注入報紅問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • Java處理科學計數法數字方式

    Java處理科學計數法數字方式

    這篇文章主要介紹了Java處理科學計數法數字方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-07-07
  • java中關于移位運算符的demo與總結(推薦)

    java中關于移位運算符的demo與總結(推薦)

    下面小編就為大家?guī)硪黄猨ava中關于移位運算符的demo與總結(推薦)。小編覺得挺不錯的,現在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-05-05
  • Java操作mongodb增刪改查的基本操作實戰(zhàn)指南

    Java操作mongodb增刪改查的基本操作實戰(zhàn)指南

    MongoDB是一個基于分布式文件存儲的數據庫,由c++語言編寫,旨在為WEB應用提供可擴展的高性能數據存儲解決方案,下面這篇文章主要給大家介紹了關于Java操作mongodb增刪改查的基本操作實戰(zhàn)指南,需要的朋友可以參考下
    2023-05-05
  • Plugin ‘org.springframework.boot:spring-boot-maven-plugin:‘ not found的解決方案(親測可用)

    Plugin ‘org.springframework.boot:spring-boot-maven-plug

    這篇文章給大家介紹了Plugin ‘org.springframework.boot:spring-boot-maven-plugin:‘ not found的解決方案,親測可用,文中給出了兩種解決方法,需要的朋友可以參考下
    2024-01-01
  • java實現合并圖片的方法示例

    java實現合并圖片的方法示例

    這篇文章主要介紹了java實現合并圖片的方法,結合具體實例形式分析了java基于圖片的讀取、設置、生成等操作實現圖片合并功能的相關實現技巧,需要的朋友可以參考下
    2017-02-02

最新評論

新宾| 巢湖市| 漯河市| 疏附县| 五原县| 宜黄县| 荣昌县| 枣强县| 河北区| 宝坻区| 丹凤县| 沙雅县| 潞西市| 乌兰县| 高尔夫| 额济纳旗| 高阳县| 霍林郭勒市| 乃东县| 都兰县| 绵竹市| 象山县| 三江| 托克逊县| 麟游县| 射洪县| 穆棱市| 吴堡县| 南昌市| 山西省| 漯河市| 屏东市| 西盟| 丽江市| 永新县| 昂仁县| 昆山市| 衡阳县| 景泰县| 北流市| 定结县|