SpringBoot 에서 Netty 프레임 워 크 를 통합 하 는 상세 한 튜 토리 얼

13884 단어 SpringBoot통합Netty
Netty 는 매우 우수한 Socket 프레임 워 크 입 니 다.SpringBoot 가 개발 한 app 에서 Socket 서 비 스 를 제공 해 야 한다 면 Netty 는 좋 은 선택 입 니 다.
Netty 와 SpringBoot 의 통합 은 단지 몇 곳 을 통합 시 키 는 것 이 라 고 생각 합 니 다.
  • netty 와 springboot 의 생명 주 기 를 일치 시 키 고 생사
  • netty 가 ioc 의 Bean
  • 을 사용 할 수 있 도록
  • netty 가 전체 설정 을 읽 을 수 있 도록 합 니 다
  • Netty 통합,웹 소켓 서비스 제공
    SpringBoot 에서 Netty 를 사용 하여 Websocket 서 비 스 를 제공 하 는 사례 를 보 여 줍 니 다.
    servlet 용기 자체 가 웹 소켓 의 실현 을 제 공 했 지만,여기 서 netty 의 실현:sparklingheart:
    의존 도 를 높이다
    
    <dependency>
    	<groupId>io.netty</groupId>
    	<artifactId>netty-all</artifactId>
    </dependency>
    네,버 전 번 호 를 밝 힐 필요 가 없습니다.spring-boot-dependencies 에서 최신 netty 의존 도 를 밝 혔 기 때문이다.
    Yml 를 통 해 기본 속성 설정
    
    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
    앱 은'actuator'를 사용 하고 노출shutdown점 을 켜 서 SpringBoot 앱 을 우아 하 게 정지 시 킬 수 있 습 니 다.웹 소켓 서비스 와 관련 된 설정 을netty.websocket.*통 해 설정 합 니 다.
    응용 프로그램 Runner 를 통 해 웹 소켓 서 비 스 를 시작 합 니 다.
    
    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     ");
    	}
    }
    NettyBootsrapRunnerApplicationRunner,ApplicationListener<ContextClosedEvent>,ApplicationContextAware인 터 페 이 스 를 실현 했다.
    이렇게 되면NettyBootsrapRunner앱 이 시작 되 고 닫 힐 때 Websocket 서비스의 시작 과 닫 기 를 실행 할 수 있 습 니 다.그리고ApplicationContextAware를 통 해 얻 을 수 있 습 니 다ApplicationContextIOC 를 통 해 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;
    			//        
    			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("    :{}", ctx.channel().remoteAddress());
    	}
    }
    handler 는 이미 IOC 가 관리 하 는 Bean 으로 주입 등 Spring 에 의존 하 는 빠 른 기능 을 자 유 롭 게 사용 할 수 있다.단일 사례 가 존재 하기 때문에 모든 링크 는 같은 hander 를 사용 하기 때문에 인 스 턴 스 변 수 를 저장 하지 마 십시오.
    이 Handler 는 클 라 이언 트 의 메 시 지 를 처리 한 후에 클 라 이언 트 에 게 다음 과 같은 메 시 지 를 응답 합 니 다." :" + System.currentTimeMillis()Handler 에서 업무 층 을 사용 하 는 것 을 보 여주 기 위해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>
    링크 생 성 후 서버 에 메 시 지 를 보 냅 니 다: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       : 웹 소켓 서비스 시작,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  : 링크 생 성:/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       : 웹 소켓 서비스 정지
    2020-06-22 17:08:33.691  INFO 9392 --- [ntLoopGroup-3-1] i.s.n.w.handler.WebsocketMessageHandler  : 링크 차단:/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'
    넷 티 는 스프링 부 트 앱 이 시 작 된 후 시작 하고,앱 이 멈 춘 뒤 닫 히 면 정상적으로 대외 서 비 스 를 제공 할 수 있 으 며,핸들 러 가 IOC 관리 에 맡 기 면 서 비 스 를 주입 해 업무 처 리 를 완료 할 수 있다.
    총결산
    SpringBoot 에서 Netty 프레임 워 크 를 통합 하여 사용 하 는 것 에 관 한 이 글 은 여기까지 소개 되 었 습 니 다.더 많은 SpringBoot 통합 Netty 프레임 워 크 내용 은 예전 의 글 을 검색 하거나 아래 의 관련 글 을 계속 찾 아 보 세 요.앞으로 도 많은 응원 부 탁 드 리 겠 습 니 다!

    좋은 웹페이지 즐겨찾기