Spring Boot 整合 Netty服务

Netty简介

Netty是由JBOSS提供的一个java开源框架,现为 Github上的独立项目。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。
也就是说,Netty 是一个基于NIO的客户、服务器端编程框架,使用Netty 可以确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户、服务端应用。Netty相当于简化和流线化了网络应用的编程开发过程,例如:基于TCP和UDP的socket服务开发。
“快速”和“简单”并不用产生维护性或性能上的问题。Netty 是一个吸收了多种协议(包括FTP、SMTP、HTTP等各种二进制文本协议)的实现经验,并经过相当精心设计的项目。最终,Netty 成功的找到了一种方式,在保证易于开发的同时还保证了其应用的性能,稳定性和伸缩性。

Netty服务端

引入依赖

        <!--netty依赖-->
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.20.Final</version>
        </dependency>
        <!-- marshalling -->
        <dependency>
            <groupId>org.jboss.marshalling</groupId>
            <artifactId>jboss-marshalling-serial</artifactId>
            <version>2.0.0.Beta2</version>
        </dependency>

Netty服务端

package com.example.uts.netty;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
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 lombok.extern.slf4j.Slf4j;

/**
 * netty 服务端
 *
 * @author lanx
 * @date 2022/3/20
 */
@Slf4j
public class NettyServer {

    private static class SingletionHolder {
        static final NettyServer instance = new NettyServer();
    }

    public static NettyServer getInstance() {
        return SingletionHolder.instance;
    }

    //用户接收客户端连接的线程工作组
    EventLoopGroup bossGroup;
    //用于接收客户端连接读写操作的线程组
    EventLoopGroup workerGroup;
    //辅助类 帮我我们创建netty服务
    ServerBootstrap b;
    ChannelFuture cf;

    private NettyServer() {
        //用户接收客户端连接的线程工作组
        bossGroup = new NioEventLoopGroup();
        //用于接收客户端连接读写操作的线程组
        workerGroup = new NioEventLoopGroup();
        //辅助类 帮我我们创建netty服务
        b = new ServerBootstrap();
        b.group(bossGroup, workerGroup)//绑定两个工作组
                .channel(NioServerSocketChannel.class)//设置NIO模式
                //option 针对于服务端配置; childOption 针对于客户端连接通道配置
                .option(ChannelOption.SO_BACKLOG, 1024)//设置tcp缓冲区
                .childOption(ChannelOption.SO_SNDBUF, 32 * 1024)//设置发送数据的缓存大小
                .childOption(ChannelOption.SO_RCVBUF, 32 * 1024)//设置读取数据的缓存大小
                .childOption(ChannelOption.SO_KEEPALIVE, true)//设置保持长连接
                //初始化绑定服务通道
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel sc) throws Exception {
                        //序列化
                        //在实际处理前进行编解码,服务器端接收消息先解码,然后返回消息的时候编码【约定】
                        sc.pipeline().addLast(MarshallingCodeCfactory.buildMarshallingDecoder());
                        sc.pipeline().addLast(MarshallingCodeCfactory.buildMarshallingEecoder());
                        //为通道进行初始化:数据传输过来的时候会进行拦截和执行 (可以有多个拦截器)
                        sc.pipeline().addLast(new NettyServerHandler());
                    }
                });
    }

    /**
     * 启动服务
     */
    public void start(int port) {
        this.cf = b.bind(port);
        log.info("服务启动...........");
    }

    public ChannelFuture getChannelFuture() {
        return this.cf;
    }

    /**
     * 关闭连接
     *
     * @throws InterruptedException
     */
    public void close() throws InterruptedException {
        //释放连接
        this.cf.channel().closeFuture().sync();
        this.bossGroup.shutdownGracefully();
        this.workerGroup.shutdownGracefully();
    }

}
package com.example.uts.netty;

import com.alibaba.fastjson.JSON;
import com.example.uts.domain.RespData;
import com.example.uts.domain.ResqData;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;

import java.util.UUID;

/**
 * 服务端 监听器
 *
 * @author lanx
 * @date 2022/3/20
 */
public class NettyServerHandler extends ChannelInboundHandlerAdapter {


    /**
     * 当我们的通道被激活的时候触发的监听
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("--------服务端通道激活---------");
    }

    /**
     * 当我们通道里有数据进行读取的时候触发的监听
     *
     * @param ctx netty服务上下文
     * @param msg 实际传输的数据
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        try {

            ResqData resqData = (ResqData)msg;
            System.out.println("服务端:获取客户端数据-->"+ JSON.toJSONString(resqData));

            RespData respData = new RespData();
            respData.setId(UUID.randomUUID().toString());
            respData.setCode(200);
            respData.setData(resqData.getData());
            //响应给客户端的数据
            ctx.writeAndFlush(respData);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //释放数据
            ReferenceCountUtil.release(msg);
        }

    }

    /**
     * 当我们读取完成数据的时候触发的监听
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        System.out.println("--------服务端数据读取完毕---------");
    }

    /**
     * 当我们读取数据异常的时候触发的监听
     *
     * @param ctx
     * @param cause
     * @throws Exception
     */
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("--------服务端数据读取异常---------");
        cause.printStackTrace();
        ctx.close();
    }
}
package com.example.uts.netty;

import io.netty.handler.codec.marshalling.*;
import org.jboss.marshalling.MarshallerFactory;
import org.jboss.marshalling.Marshalling;
import org.jboss.marshalling.MarshallingConfiguration;

/**
 * Marshalling 工厂类
 *
 * @author lanx
 * @date 2022/3/22
 */
public final class MarshallingCodeCfactory {

    /**
     * 创建Jboss Marshalling 解码器 MarshallingDecoder
     * @return
     */
    public static MarshallingDecoder buildMarshallingDecoder() {
        //首先通过Marshalling 工具类的静态方法获取Marshalling实例对象,参数serial标识创建的是java序列化工厂对象
        final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial");
        //创建 MarshallingConfiguration对象,配置了版本号为5
        final MarshallingConfiguration configuration = new MarshallingConfiguration();
        configuration.setVersion(5);
        //根据MarshallerFactory和MarshallingConfiguration 创建 provider
        UnmarshallerProvider provider = new DefaultUnmarshallerProvider(marshallerFactory, configuration);
        //构建Netty的MarshallingDecoder对象,两个参数分别是provider和单个消息序列化后的最大长度
        MarshallingDecoder decoder = new MarshallingDecoder(provider, 1024 * 1024 * 1);

        return decoder;
    }

    /**
     * 创建Jboss Marshalling 编码器 MarshallingEncoder
     * @return
     */
    public static MarshallingEncoder buildMarshallingEecoder() {
        //首先通过Marshalling 工具类的静态方法获取Marshalling实例对象,参数serial标识创建的是java序列化工厂对象
        final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial");
        //创建 MarshallingConfiguration对象,配置了版本号为5
        final MarshallingConfiguration configuration = new MarshallingConfiguration();
        configuration.setVersion(5);
        //根据MarshallerFactory和MarshallingConfiguration 创建 provider
        MarshallerProvider provider = new DefaultMarshallerProvider(marshallerFactory, configuration);
        //构建Netty的MarshallingEncoder对象,MarshallingEncoder用于实现序列化接口的POJO对象序列化为二进制数组
        MarshallingEncoder encoder = new MarshallingEncoder(provider);

        return encoder;
    }
}
package com.example.uts.listener;

import com.example.uts.netty.NettyServer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;

/**
 * 初始化netty客户端连接
 * @author lanx
 * @date 2022/3/27
 */
@Component
@Slf4j
public class SpringListener implements ApplicationListener<ContextRefreshedEvent> {

    @Value("${netty.port}")
    private int port;

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        if(event.getApplicationContext().getParent() == null){
            //需要执行的业务代码,当spring 容器初始化完成后执行
            NettyServer.getInstance().start(port);
            log.info("spring 加载完成...");
        }
    }
}

Netty 客户端

package com.example.uts.netty;


import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;

/**
 * @author lanx
 * @date 2022/3/27
 */
public class NettyClient {

    private static class SingletionHolder {
        static  final  NettyClient instance = new NettyClient();
    }

    public static  NettyClient getInstance(){
        return SingletionHolder.instance;
    }

    private String host ;
    private int port ;

    //线程工作组
    EventLoopGroup workerGroup;
    //辅助类 帮我我们创建netty服务
    Bootstrap b ;

    ChannelFuture cf;

    private NettyClient(){

        //线程工作组
        workerGroup = new NioEventLoopGroup();
        //辅助类 帮我我们创建netty服务
        b = new Bootstrap();
        b.group(workerGroup)//绑定两个工作组
                .channel(NioSocketChannel.class)//设置NIO模式
                //初始化绑定服务通道
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel sc) throws Exception {
                        //序列化
                        sc.pipeline().addLast(MarshallingCodeCfactory.buildMarshallingEecoder());
                        sc.pipeline().addLast(MarshallingCodeCfactory.buildMarshallingDecoder());
                        //为通道进行初始化:数据传输过来的时候会进行拦截和执行 (可以有多个拦截器)
                        sc.pipeline().addLast(new NettyClientHandler());
                    }
                });


    }

    public ChannelFuture getChannelFuture(){
        //如果为null
        if(this.cf == null){
            this.connect();
        }
        //如果没有激活
        if(!this.cf.channel().isActive()){
            this.connect();
        }

        return  this.cf;
    }

    /**
     * 连接服务器
     */
    public void connect(){
         this.cf = b.connect(host, port).syncUninterruptibly();
        System.out.println("远程服务端已经连接,可以数据传输");

    }

    /**
     * 关闭连接
     * @throws InterruptedException
     */
    public void close() throws InterruptedException {
        //释放连接
        this.cf.channel().closeFuture().sync();
        workerGroup.shutdownGracefully();
    }

    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }
}
package com.example.uts.netty;

import com.alibaba.fastjson.JSON;
import com.example.uts.domain.RespData;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * 客户端 监听器
 *
 * @author lanx
 * @date 2022/3/20
 */
@Slf4j
public class NettyClientHandler extends ChannelInboundHandlerAdapter {

    /**
     * 当我们的通道被激活的时候触发的监听
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("--------客户端通道激活---------");
    }

    /**
     * 当我们通道里有数据进行读取的时候触发的监听
     *
     * @param ctx netty服务上下文
     * @param msg 实际传输的数据
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        try {
            RespData respData = (RespData)msg;
            log.info("客户端:获取服务端响应数据-->{}", JSON.toJSONString(respData));
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //释放数据 (如果你读取数据后又写出去数据就不需要调用此方法,因为底层代码帮忙实现额释放数据)
            ReferenceCountUtil.release(msg);
        }
    }

    /**
     * 当我们读取完成数据的时候触发的监听
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        System.out.println("--------客户端数据读取完毕---------");
    }

    /**
     * 当我们读取数据异常的时候触发的监听
     *
     * @param ctx
     * @param cause
     * @throws Exception
     */
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("--------客户端数据读取异常---------");
        cause.printStackTrace();
        ctx.close();
    }
}
package com.example.uts.netty;

import io.netty.handler.codec.marshalling.*;
import org.jboss.marshalling.MarshallerFactory;
import org.jboss.marshalling.Marshalling;
import org.jboss.marshalling.MarshallingConfiguration;

/**
 * Marshalling 工厂类
 *
 * @author lanx
 * @date 2022/3/22
 */
public final class MarshallingCodeCfactory {

    /**
     * 创建Jboss Marshalling 解码器 MarshallingDecoder
     * @return
     */
    public static MarshallingDecoder buildMarshallingDecoder() {
        //首先通过Marshalling 工具类的静态方法获取Marshalling实例对象,参数serial标识创建的是java序列化工厂对象
        final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial");
        //创建 MarshallingConfiguration对象,配置了版本号为5
        final MarshallingConfiguration configuration = new MarshallingConfiguration();
        configuration.setVersion(5);
        //根据MarshallerFactory和MarshallingConfiguration 创建 provider
        UnmarshallerProvider provider = new DefaultUnmarshallerProvider(marshallerFactory, configuration);
        //构建Netty的MarshallingDecoder对象,两个参数分别是provider和单个消息序列化后的最大长度
        MarshallingDecoder decoder = new MarshallingDecoder(provider, 1024 * 1024 * 1);

        return decoder;
    }

    /**
     * 创建Jboss Marshalling 编码器 MarshallingEncoder
     * @return
     */
    public static MarshallingEncoder buildMarshallingEecoder() {
        //首先通过Marshalling 工具类的静态方法获取Marshalling实例对象,参数serial标识创建的是java序列化工厂对象
        final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial");
        //创建 MarshallingConfiguration对象,配置了版本号为5
        final MarshallingConfiguration configuration = new MarshallingConfiguration();
        configuration.setVersion(5);
        //根据MarshallerFactory和MarshallingConfiguration 创建 provider
        MarshallerProvider provider = new DefaultMarshallerProvider(marshallerFactory, configuration);
        //构建Netty的MarshallingEncoder对象,MarshallingEncoder用于实现序列化接口的POJO对象序列化为二进制数组
        MarshallingEncoder encoder = new MarshallingEncoder(provider);

        return encoder;
    }
}
package com.example.uts.listener;

import com.example.uts.netty.NettyClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;

/**
 * 初始化netty客户端连接
 * @author lanx
 * @date 2022/3/27
 */
@Component
@Slf4j
public class SpringListener implements ApplicationListener<ContextRefreshedEvent> {

    @Value("${netty.port}")
    private int port;
    @Value("${netty.host}")
    private String host;

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        if(event.getApplicationContext().getParent() == null){
            //需要执行的业务代码,当spring 容器初始化完成后执行
            log.info("spring 加载完成...");
            NettyClient nettyClient = NettyClient.getInstance();
            nettyClient.setHost(host);
            nettyClient.setPort(port);
            nettyClient.connect();
        }
    }
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Spring Cloud可以很方便地与Netty框架进行整合,实现高并发、高性能的分布式系统。下面是一个简单的示例代码,演示了如何在Spring Cloud中使用Netty框架: 1. 首先,我们需要在pom.xml文件中添加以下依赖: ```xml <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.65.Final</version> </dependency> ``` 2. 接下来,我们需要编写一个Netty服务器,用于接收来自客户端的请求。以下是一个简单的Netty服务器示例代码: ```java import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; 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 org.springframework.stereotype.Component; @Component public class NettyServer { public void start(int port) throws Exception { // 创建两个EventLoopGroup,用于处理客户端和服务器端的I/O事件 EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { // 创建ServerBootstrap对象,用于启动服务器 ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 128) .childOption(ChannelOption.SO_KEEPALIVE, true) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new NettyServerHandler()); } }); // 绑定端口,启动服务器 ChannelFuture future = bootstrap.bind(port).sync(); // 等待服务器关闭 future.channel().closeFuture().sync(); } finally { // 关闭EventLoopGroup workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); } } } ``` 3. 接下来,我们需要编写一个Netty服务器处理程序,用于处理客户端发送的请求。以下是一个简单的处理程序示例代码: ```java import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; public class NettyServerHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { ByteBuf buf = (ByteBuf)msg; byte[] bytes = new byte[buf.readableBytes()]; buf.readBytes(bytes); String request = new String(bytes, "UTF-8"); System.out.println("Netty Server: " + request); String response = "Hello, Client!"; ctx.writeAndFlush(response); } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { ctx.flush(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } } ``` 4. 最后,我们需要在Spring Cloud应用程序中启动Netty服务器。以下是一个简单的Spring Boot应用程序示例代码: ```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class SpringCloudNettyApplication implements CommandLineRunner { @Autowired private NettyServer nettyServer; public static void main(String[] args) { SpringApplication.run(SpringCloudNettyApplication.class, args); } @Override public void run(String... args) throws Exception { nettyServer.start(8888); } } ``` 这样,我们就可以在Spring Cloud应用程序中使用Netty框架。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

终遇你..

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值