2019.8.27笔记——netty应用

处理器(Handler)

处理器是每一条pipeline的重要组成部分,也是完成业务逻辑的核心,一共有三种类型,入栈、出栈和出入栈。这里先介绍一个我们平时经常使用的一个入栈处理器SimpleChannelInboundHandler,通常来说我们会继承这个类,然后实现它的方法或者重写它的方法,完成自己的业务。

在这个处理器中有许多回调方法,在这个处理器的各种生命周期会执行,下面是几个常用的回调方法,其中channelRead0是必须实现的抽象方法,其中第一个参数存储着当前channel的各种信息,第二个参数是传递过来的参数,可能是从客户端传来的数据,也可能是上一个处理器处理后传来的数据。

这里的fireChannelRead方法主要是将要传递的信息传给下一个处理器

public class TestServerHandler extends SimpleChannelInboundHandler<String>{

    private static ChannelGroup group=new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    //channel读取数据
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception {
        Channel channel = channelHandlerContext.channel();
        group.forEach(ch->{
            if(channel!=ch){
                ch.writeAndFlush(channel.remoteAddress()+":"+s+"\r\n");
            }
        });
        channelHandlerContext.fireChannelRead(s);
    }

    //channel 助手类(拦截器)的添加
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        group.writeAndFlush(channel.remoteAddress()+"加入\n");
        group.add(channel);
    }

    //channel 助手类(拦截器)移除
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        group.writeAndFlush(channel.remoteAddress()+"离开\n");
    }

    //channel活跃 通道准备就绪事件
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        System.out.println(channel.remoteAddress()+"上线\n");
        System.out.println(group.size());
    }

    //channel不活跃  通道关闭事件
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        System.out.println(channel.remoteAddress()+"下线\n");
    }

    //channel注册事件
    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelRegistered");
        super.channelRegistered(ctx);
    }

    //channel取消注册事件
    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelUnregistered");
        super.channelUnregistered(ctx);
    }

    //发生异常回调
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

注意这里定义了一个DefaultChannelGroup对象,这个对象简单来说就是一个set集合,主要用来存储和客户端通信的channel,其中有许多实用的方法
在这里插入图片描述
forEach方法会遍历出集合中所有通道
在这里插入图片描述
writeAndFlush方法会向集合中的所有通道发送消息

其中add方法是将通道加入到这个集合中,需要注意的是这个集合的通道是要手动添加的,但是如果集合中的通道如果关闭的话是会自动移除的,不需要手动移除
在这里插入图片描述
同时netty给我们提供了许多实用的处理器,如果需要调用的话只需要将之加入pipeline中去。

常用的处理器

  • StringDecoder

属于入栈处理器,可以将传输过来的数据通过指定的编码转换成字符串

下面将数据转换成编码为UTF-8的字符串

pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
  • StringEncoder

属于出栈处理器,可以将输出的字符串通过指定的编码转换成字节数组

下面将数据通过UTF-8编码转换为字节数组

pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));

StringDecoder和StringEncoder常常一起使用

  • HttpServerCodec

属于出栈入栈处理器,因为这个处理器其实包含着另外两个处理器,这点可以从源码中看出
在这里插入图片描述
其中HttpObjectDecoder属于入栈处理器,HttpResponseEncoder属于出栈处理器

HttpResponseEncoder,将HttpResponse或HttpContent编码成ByteBuf
HttpResponseDecoder,将ByteBuf解码成HttpResponse和HttpContent

这个处理器向下传递的数据是一个HttpObject对象,如果是入栈数据那么就是httprequest对象,如果想向浏览器响应,那么就需要自己组装一个http响应,示例如下

protected void channelRead0(ChannelHandlerContext channelHandlerContext, HttpObject httpObject) throws Exception {
    if(httpObject instanceof  HttpRequest){
        HttpRequest httpRequest= (HttpRequest) httpObject;
        String uri = httpRequest.uri();
        System.out.println(uri);
        ByteBuf byteBuf = Unpooled.copiedBuffer("helloworld", CharsetUtil.UTF_8);
        FullHttpResponse fullHttpResponse=new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK,byteBuf);
        fullHttpResponse.headers().set(HttpHeaderNames.CONTENT_TYPE,"text/plain");
        fullHttpResponse.headers().set(HttpHeaderNames.CONTENT_LENGTH,byteBuf.readableBytes());
        channelHandlerContext.writeAndFlush(fullHttpResponse);
    }
}
  • LengthFieldPrepender

属于出栈处理器,作用是计算当前待发送消息的二进制字节长度,将该长度添加到ByteBuf的缓冲区头中,可以解决粘包拆包问题

这里的参数是存储计算出来数值的空间的字节大小

pipeline.addLast(new LengthFieldPrepender(4));//计算当前待发送消息的二进制字节长度,将该长度添加到ByteBuf的缓冲区头中
  • LengthFieldBasedFrameDecoder

属于入栈处理器,作用是解析LengthFieldPrepender处理器处理过的数据,可以根据数据头的数值截取完整的数据,同时将这个数值从数据中移除

 /**
  * 1) maxFrameLength    //数据的最大长度
  * 2) lengthFieldOffset  //长度字段的偏差
  * 3) lengthFieldLength  //长度字段占的字节数
  * 4) lengthAdjustment  //添加到长度字段的补偿值
  * 5) initialBytesToStrip  //从解码帧中第一次去除的字节数
  */
 pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,0,4,0,4));

一般LengthFieldBasedFrameDecoder和LengthFieldPrepender会在一起使用

  • DelimiterBasedFrameDecoder

属于入栈处理器,同样是用来解决粘包拆包问题的,基于分隔符的解码器,就是根据数据中的\r\n或者\n来切分数据的

其中第一个参数是数据的最大长度,第二个参数是分隔符

pipeline.addLast(new DelimiterBasedFrameDecoder(4096, Delimiters.lineDelimiter())); //基于分隔符的解码器

Delimiters.lineDelimiter()返回的是一个ByteBuf数组,其中存着两个ByteBuf,第一个存着13和10,第二个存着10,我们知道\n转成int就是10,\r是13,所以就代表着\r\n\n

public static ByteBuf[] lineDelimiter() {
    return new ByteBuf[]{Unpooled.wrappedBuffer(new byte[]{13, 10}), Unpooled.wrappedBuffer(new byte[]{10})};
}
  • HttpObjectAggregator

属于入栈出栈处理器,可以将http的消息进行聚合,聚合成FullHttpRequest或者FullHttpResponse

这里的参数是数据的最大值

//2.3 对于http的消息进行聚合,聚合成FullHttpRequest或者FullHttpResponse
pipeline.addLast(new HttpObjectAggregator(1024*64));
  • IdleStateHandler

属于入栈出栈处理器,可以监听客户端和服务端的读写状态,通常用来做心跳检测

第一个参数是readerIdleTime,表示超出的这个时间没有读数据就是读空闲状态

第二个参数是writerIdleTime,表示超出的这个时间没有写数据就是读空闲状态

第三个参数是allIdleTime,表示超出的这个时间没有写数据和读数据就是读写空闲状态

 //针对客户端,如果1分钟之内没有向服务器发送读写心跳,则主动断开
 pipeline.addLast(new IdleStateHandler(40,50,45));

这个处理器传递下去的数据是一个IdleStateEvent对象,这个对象的state方法可以得到此时的状态
在这里插入图片描述

聊天室

客户端

客户端入口及初始化配置

public class TestClient {
    public static void main(String[] args) {
        EventLoopGroup bossGroup=new NioEventLoopGroup();
        try {
            Bootstrap bootstrap=new Bootstrap();
            bootstrap.group(bossGroup)
                    .channel(NioSocketChannel.class)
                    .handler(new TestClientInitializer());
            ChannelFuture channelFuture = bootstrap.connect("127.0.0.1",8989).sync();
            Channel channel = channelFuture.channel();

            BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(System.in));
            for(; ;){
                channel.writeAndFlush(bufferedReader.readLine()+"\r\n");
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            bossGroup.shutdownGracefully();
        }
    }
}

客户端pipeline的初始化和添加Handler,这里使用的是基于分隔符的解码器来解决粘包拆包问题

public class TestClientInitializer extends ChannelInitializer<SocketChannel>{

    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {
        ChannelPipeline pipeline = socketChannel.pipeline();
        pipeline.addLast(new DelimiterBasedFrameDecoder(4096, Delimiters.lineDelimiter()));//基于分隔符的解码器
        pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
        pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
        pipeline.addLast(new TestClientHandler());
    }
}

客户端的业务Handler

public class TestClientHandler extends SimpleChannelInboundHandler<String> {
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception {
        System.out.println(s.trim()+"\n");
    }


    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

服务端

客户端入口及初始化配置

public class TestServer {
    public static void main(String[] args) {
        EventLoopGroup bossGroup=new NioEventLoopGroup();
        EventLoopGroup workGroup=new NioEventLoopGroup();
        try {
            ServerBootstrap serverBootstrap=new ServerBootstrap();
            serverBootstrap.group(bossGroup,workGroup)
                            .channel(NioServerSocketChannel.class)
                            .childHandler(new TestServerInitializer());
            ChannelFuture channelFuture = serverBootstrap.bind(8989).sync();
            channelFuture.channel().closeFuture().sync();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }
    }
}

服务端初始化pipeline和添加Handler

public class TestServerInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {
        ChannelPipeline pipeline = socketChannel.pipeline();
        pipeline.addLast(new DelimiterBasedFrameDecoder(4096, Delimiters.lineDelimiter())); //基于分隔符的解码器
        pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
        pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
        pipeline.addLast(new TestServerHandler());
    }
}

服务端业务Handler,这里采用的是换行符分割每一条信息,通过ChannelGroup来广播每个客户端发来的信息

public class TestServerHandler extends SimpleChannelInboundHandler<String>{

    private static ChannelGroup group=new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    //channel读取数据
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception {
        Channel channel = channelHandlerContext.channel();
        group.forEach(ch->{
            if(channel!=ch){
                ch.writeAndFlush(channel.remoteAddress()+":"+s+"\r\n");
            }
        });
        channelHandlerContext.fireChannelRead(s);
    }

    //channel 助手类(拦截器)的添加
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        group.writeAndFlush(channel.remoteAddress()+"加入\n");
        group.add(channel);
    }

    //channel 助手类(拦截器)移除
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        group.writeAndFlush(channel.remoteAddress()+"离开\n");
    }

    //channel活跃 通道准备就绪事件
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        System.out.println(channel.remoteAddress()+"上线\n");
        System.out.println(group.size());
    }

    //channel不活跃  通道关闭事件
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        System.out.println(channel.remoteAddress()+"下线\n");
    }

    //channel注册事件
    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelRegistered");
        super.channelRegistered(ctx);
    }

    //channel取消注册事件
    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelUnregistered");
        super.channelUnregistered(ctx);
    }

    //发生异常回调
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

心跳检测

当客户端关闭通道时服务端是可以检测到客户端的存活的,但是当客户端的网络中断(比如开启飞行模式),那么服务端是不能通过netty直接发现客户端的死亡的,如果服务端不能及时发现,客户端又经常会出现这种情况的话,就会造成服务器性能的浪费。

这个时候就需要借助心跳检测来及时发现客户端的死亡,简单来说就是客户端间隔一定时间向服务端发送信息,服务端通过客户端和服务端的读写空闲时间来判断客户端时候已经死亡。

这里客户端是向每隔一定时间(小于50秒)服务端发送的http请求

服务端的入口

public class TestServer {
    public static void main(String[] args) {
        EventLoopGroup bossGroup=new NioEventLoopGroup();  //接收客户端连接的线程组
        EventLoopGroup workGroup=new NioEventLoopGroup(); //真正处理读写事件的线程组

        try {
            ServerBootstrap serverBootstrap=new ServerBootstrap();
            serverBootstrap.group(bossGroup,workGroup)
                    .channel(NioServerSocketChannel.class)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new TestServerLnitializer());
            ChannelFuture channelFuture = serverBootstrap.bind(8989).sync();
            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }
    }
}

初始化pipeline,添加Handler

public class TestServerLnitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {
        ChannelPipeline pipeline = socketChannel.pipeline();
        //2. 配置handler
        //2.1 websocket基于http协议,需要http编码和解码工具
        pipeline.addLast(new HttpServerCodec());
        //2.2 对于大数据流的支持
        pipeline.addLast(new ChunkedWriteHandler());
        //2.3 对于http的消息进行聚合,聚合成FullHttpRequest或者FullHttpResponse
        pipeline.addLast(new HttpObjectAggregator(1024*64));
        //针对客户端,如果1分钟之内没有向服务器发送读写心跳,则主动断开
        pipeline.addLast(new IdleStateHandler(40,50,45));
        //自定义的读写空闲状态检测
        pipeline.addLast(new HeartBeatHandler());
        pipeline.addLast(new WebSocketServerProtocolHandler("/ws"));
        //定义自己的handler,主要是对请求进行处理和发送
        pipeline.addLast(new ChatHandler());
    }
}

用来接收此时监听状态的处理器,通过判断此时服务端的状态来判断客户端是否存活,如果进入读写空闲就判断客户端死亡,主动关闭通道

/**
 * 用于处理客户端与服务端的心跳,在客户端空闲(如飞行模式)时关闭channel,节省服务器资源
 */

public class HeartBeatHandler extends ChannelInboundHandlerAdapter {
    /**
     * 用户事件触发的处理器
     * @param ctx
     * @param evt
     * @throws Exception
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        //判断evt是否属于IdleStateEvent,用于触发用户事件,包含读空闲,写空闲,读写空闲
        if(evt instanceof IdleStateEvent){
            IdleStateEvent event = (IdleStateEvent)evt;
            if(event.state() == IdleState.READER_IDLE){
                //读空闲,不做处理
                System.out.println("进入读空闲");
            }else if(event.state() == IdleState.WRITER_IDLE){
                //写空闲,不做处理
                System.out.println("进入写空闲");
            }else if(event.state() == IdleState.ALL_IDLE){
                System.out.println("channel关闭前,users的数量为:"+ChatHandler.users.size());
                //关闭channel
                Channel channel = ctx.channel();
                channel.close();
                System.out.println("channel关闭后,users的数量为:"+ChatHandler.users.size());
            }

        }
    }
}

用来处理客户端发来的数据的Handler

/**
 * 自定义handler,继承简单频道入站处理程序,范围为wen文本套接字Frame
 * websocket间通过frame进行数据的传递和发送
 * 此版本为user与channel绑定的版本,消息会定向发送和接收到指定的user的channel中。
 *
 */
public class ChatHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
    
    //定义channel集合,管理channel,传入全局事件执行器
    public static ChannelGroup users = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    /**
     * 定义信道的消息处理机制,该方法处理一次,故需要同时对所有客户端进行操作(channelGroup)
     * @param ctx 上下文
     * @param msg 文本消息
     * @throws Exception
     */
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
        //1. 获取客户端传递过来的消息,其对象为TextWebSocketFrame
        String text = msg.text();
        System.out.println("接收到数据为: "+ text);
    }

    /**
     * 当客户端连接服务端之后(打开连接)----->handlerAdded
     * 获取客户端的channel,并且放到ChannelGroup中去管理
     * @param ctx
     * @throws Exception
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        users.add(ctx.channel());
        String asShortText = ctx.channel().id().asShortText();
        System.out.println("客户端添加,channelId为:" + asShortText);
    }

    //处理器移除时,移除channelGroup中的channel
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        //打印移除的channel
        String asShortText = ctx.channel().id().asShortText();
        System.out.println("客户端被移除,channelId为:" + asShortText);
        users.remove(ctx.channel());
    }

    /**
     * 发生异常时,关闭连接(channel),随后将channel从ChannelGroup中移除
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("出错啦, 原因是:"+cause.getMessage());
        ctx.channel().close();
        users.remove(ctx.channel());
    }
}

RPC远程调用

通过netty实现远程调用服务,基本流程如下
在这里插入图片描述

客户端

程序入口,将想要实现的类的接口的class对象传过去

public class ClientSocketNetty {
    public static void main(String[] args) throws InterruptedException {
//        while (true){
//            long start = System.currentTimeMillis();
//            TestService o = (TestService) ClientRpcProxy.create(TestService.class);
//            System.out.println(o.listAll());
//            long end = System.currentTimeMillis();
//            System.out.println(end-start);
//            Thread.sleep(1000);
//        }
        TestService o2 = (TestService) ClientRpcProxy.create(TestService.class);
        System.out.println(o2.listByid(0));
    }
}

通过class对象获得类名、方法名、参数类型和参数,然后将其组装成一个ClassInfo对象,最后将这个对象发送给服务端,因为这个对象要通过网络传输,所以需要进行序列化,这里是使用的jdk的序列化,使用服务端和客户端都得使用java语言写。

注意这里返回的是一个代理对象

这样平台拓展性就很低,所以如果要实现跨平台可以换一个序列化方法。

public class ClientRpcProxy {

    public static Object create(Class clazz){
        return Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                ClassInfo classInfo=new ClassInfo();
                classInfo.setClassName(clazz.getName());
                classInfo.setMethodName(method.getName());
                classInfo.setArgs(args);
                classInfo.setClazzType(method.getParameterTypes());

                EventLoopGroup eventExecutors=new NioEventLoopGroup();    //创建一个线程组
                //创建客户端启动助手  完成相关配置
                Bootstrap bootstrap=new Bootstrap();
                //创建业务处理类
                ClientSocketNettyHendler nettyClientHendler = new ClientSocketNettyHendler();
                try {
                    bootstrap.group(eventExecutors)    //设置线程组
                            .channel(NioSocketChannel.class) //设置使用SocketChannel为管道通信的底层实现
                            .handler(new ChannelInitializer<SocketChannel>() {
                                @Override
                                protected void initChannel(SocketChannel socketChannel) throws Exception {
                                    ChannelPipeline pipeline = socketChannel.pipeline();
                                    //添加编码器
                                    pipeline.addLast("encoder",new ObjectEncoder());

                                    //添加解码器
                                    //maxObjectSize:序列化的对象的最大长度,一旦接收到的对象长度大于此值,抛出StreamCorruptedException异常
                                    //classResolver:这个类(ClassResolver)会去加载已序列化的对象,
                                    //常用调用方式:ClassResolvers.cacheDisabled(Plan.class.getClassLoader())
                                    //或者直接ClassResolvers.cacheDisabled(null)
                                    pipeline.addLast("decoder",new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.cacheDisabled(null)));//
                                    //将自己编写的客户端业务逻辑处理类加入到pipeline链中
                                    pipeline.addLast(nettyClientHendler);
                                }
                            });
                    System.out.println("......client init......");
                    //设置服务端的ip和端口  异步非阻塞
                    ChannelFuture future = bootstrap.connect("127.0.0.1", 9090).sync();  //connect方法是异步的    sync方法是同步的
                    future.channel().writeAndFlush(classInfo).sync();
                    //关闭连接  异步非阻塞
                    future.channel().closeFuture().sync();
                }catch (Exception e){
                    e.printStackTrace();
                }
                return nettyClientHendler.getResponse();
            }
        });
    }
}

需要传输的classInfo对象,注意需要实现序列化

public class ClassInfo implements Serializable{
    private String ClassName;
    private String methodName;
    private Object[] args;
    private Class[] clazzType;

    public ClassInfo() {
    }

    public ClassInfo(String className, String methodName, Object[] args, Class[] clazzType) {
        ClassName = className;
        this.methodName = methodName;
        this.args = args;
        this.clazzType = clazzType;
    }

    public void setClassName(String className) {
        ClassName = className;
    }

    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }

    public void setArgs(Object[] args) {
        this.args = args;
    }

    public void setClazzType(Class[] clazzType) {
        this.clazzType = clazzType;
    }

    public String getClassName() {
        return ClassName;
    }

    public String getMethodName() {
        return methodName;
    }

    public Object[] getArgs() {
        return args;
    }

    public Class[] getClazzType() {
        return clazzType;
    }
}

最后服务端会将方法调用的结果发送回来

public class ClientSocketNettyHendler extends ChannelInboundHandlerAdapter {

    private Object response;

    public Object getResponse(){
        return response;
    }

    //读取数据事件
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        this.response=msg;
        ctx.close();
    }
}

接口是必须有的,而且必须和服务端的一样

public interface TestService {
    List<String> listAll();

    String listByid(Integer id);
}

服务端

服务端入口

public class ServerSocketNetty {
    public static void main(String[] args) throws InterruptedException {
        //创建一个线程组:接受客户端连接
        EventLoopGroup bossGroup=new NioEventLoopGroup();
        //创建一个线程组:接受网络操作
        EventLoopGroup workerGroup=new NioEventLoopGroup();
        //创建服务器启动助手来配置参数
        ServerBootstrap serverBootstrap=new ServerBootstrap();
        try {
            serverBootstrap.group(bossGroup,workerGroup)  //设置两个线程组
                    .channel(NioServerSocketChannel.class)   //设置使用NioServerSocketChannel作为服务器通道的实现
                    .option(ChannelOption.SO_BACKLOG,128) //设置线程队列中等待连接的个数
                    .childOption(ChannelOption.SO_KEEPALIVE,true)//保持活动连接状态
                    .childHandler(new ChannelInitializer<SocketChannel>() {    //创建一个初始化管道对象
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            ChannelPipeline pipeline = socketChannel.pipeline();
                            //添加编码器
                            pipeline.addLast("encoder",new ObjectEncoder());
                            //添加解码器
                            pipeline.addLast("decoder",new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.cacheDisabled(null)));
                            //将自己编写的服务器端的业务逻辑处理类加入pipeline链中
                            pipeline.addLast(ServerSocketNettyHendler.serverSocketNettyHendler);
                        }
                    });
            System.out.println(".........server  init..........");
            ChannelFuture future = serverBootstrap.bind(9090).sync();//设置端口  非阻塞
            System.out.println(".........server start..........");
            //关闭通道  关闭线程组  非阻塞
            future.channel().closeFuture().sync();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

这里是通过发送过来的classInfo对象得到class的信息,通过这些信息找到对应的接口,然后通过reflections框架中的方法找到这个接口的所有实现类,通过这个实现类创建一个实例化对象,然后通过classInfoz中的信息通过反射调用客户端调用的方法。

最后将得到的返回值返回给客户端

@ChannelHandler.Sharable
public class ServerSocketNettyHendler extends ChannelInboundHandlerAdapter {

    public static ServerSocketNettyHendler serverSocketNettyHendler=new ServerSocketNettyHendler();


    private static ExecutorService executorService= Executors.newFixedThreadPool(1000);

    //得到某个接口下的实现类
    public String getImplClassName(ClassInfo classInfo) throws Exception {
        //服务器接口与实现类地址;
        String iName="com.zdd.lbrpc.server.service";
        int i = classInfo.getClassName().lastIndexOf(".");
        String className=classInfo.getClassName().substring(i);
        Class aClass = Class.forName(iName + className);
        Reflections reflections=new Reflections(iName);
        //通过接口的class对象获得所有的实现类
        Set<Class<?>> classes=reflections.getSubTypesOf(aClass);
        if(classes.size()==0){
            System.out.println("未找到实现类");
            return null;
        }else if(classes.size()>1){
            System.out.println("找到多个实现类,未明确使用哪个实现类");
            return null;
        }else{
            Class[] classes1 = classes.toArray(new Class[0]);
            return classes1[0].getName();
        }
    }


    //读取数据事件
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                try {
                    ClassInfo classInfo= (ClassInfo) msg;
                    Object o = Class.forName(getImplClassName(classInfo)).newInstance();
                    Method method = o.getClass().getMethod(classInfo.getMethodName(), classInfo.getClazzType());
                    Object invoke = method.invoke(o, classInfo.getArgs());
                    ctx.channel().writeAndFlush(invoke);
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        });
    }
}

这是

public class TestServiceImpl implements TestService {

    static ArrayList<String> list = new ArrayList<>();

    static {
        list.add("张三");
        list.add("李四");
    }

    @Override
    public List<String> listAll() {
        return list;
    }

    @Override
    public String listByid(Integer id) {
        return list.get(id);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值