#案例:Netty实现

当消费者(customer)和提供者(provider)分别在两台不同的服务器时,通过网络通信socket来实现方法调用的过程称为RPC即Remote Procedure Call (远程过程调用)。

1.整体架构

2.代码实现

Customer

public class ClientBootstrap {
    /*定义协议头*/
    private final static String PROVIDER_NAME = "###";

    public static void main(String[] args) throws InterruptedException {
        NettyClient customer = new NettyClient();
        customer.startClient();
        HelloService helloServiceProxy = (HelloService) customer.getBean(HelloService.class, PROVIDER_NAME);

        for (int i = 0; i < 10; i++) {
            String res = helloServiceProxy.hello("hello, server" + i);
            System.out.println("调用的结果:" + res);
        }
    }
}

netty

public class NettyClient {
    //创建线程池
    private static ExecutorService executor = Executors
            .newFixedThreadPool(Runtime.getRuntime().availableProcessors());
    private static NettyClientHandler clientHandler;
    private NioEventLoopGroup group;
    private Bootstrap bootstrap;

    public NettyClient() {
        clientHandler = new NettyClientHandler();
        group = new NioEventLoopGroup();
        bootstrap = new Bootstrap();
        bootstrap.group(group).channel(NioSocketChannel.class)
                .option(ChannelOption.TCP_NODELAY, true)
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new StringEncoder())
                                .addLast(new StringDecoder())
                                .addLast(clientHandler);
                    }
                });
    }


    /**
     * 使用代理模式,获取一个代理对象
     * Thread.currentThread().getContextClassLoader() 获取合适的加载器
     */
    public Object getBean(final Class<?> serverClass, final String providerName) {
        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                new Class<?>[]{serverClass}, (proxy, method, args) -> {
                    /*设置发送给服务器的信息,providerName是协议头*/
                    clientHandler.setPara(providerName + args[0]);
                    /*callable传入线程池某个线程,可再次传给线程池其他线程*/
                    return executor.submit(clientHandler).get();
                });
    }

    public void startClient() throws InterruptedException {
        bootstrap.connect("127.0.0.1", 7000).sync();
        System.out.println("connecting the server successfully...");
    }
}

  

public class NettyClientHandler extends ChannelInboundHandlerAdapter implements Callable {
    private ChannelHandlerContext context;
    private String result;
    private String para;

    /**
     * 1.与服务器连接创建成功后被调用
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        //ctx 需要共享
        context = ctx;
    }

    /**
     * 2.设置参数
     *
     * @param para
     */
    public void setPara(String para) {
        this.para = para;
    }

    /**
     * 3.被代理对象调用,发送数据给服务器 -> wait ->等待被唤醒 ->返回结果
     *
     * @return
     * @throws Exception
     */
    @Override
    public synchronized Object call() throws Exception {
        context.writeAndFlush(para);
        //调用当前对象锁
        wait();
        return result;
    }

    /**
     * 4.读取服务器消息后唤醒call方法
     *
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    public synchronized void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        result = msg.toString();
        /*唤醒等待线程*/
        notify();
    }

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

  

public class NettyServer {
    private final static int PORT = 7000;
    private NioEventLoopGroup boosGroup;
    private NioEventLoopGroup workerGroup;
    private ServerBootstrap serverBootstrap;

    public NettyServer() {
        boosGroup = new NioEventLoopGroup(1);
        workerGroup = new NioEventLoopGroup();
        serverBootstrap = new ServerBootstrap().group(boosGroup, 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 StringDecoder())
                                .addLast(new StringEncoder())
                                .addLast(new NettyServerHandler());
                    }
                });
    }

    private void startServer0() throws InterruptedException {
        ChannelFuture future = serverBootstrap.bind(PORT).sync();
        System.out.println("provider is beginning to serving...");
        future.channel().closeFuture().sync();
    }

    /**
     * 方法重载
     *
     * @throws InterruptedException
     */
    public void startServer() throws InterruptedException {
        startServer0();
    }
}

  

public class NettyServerHandler extends ChannelInboundHandlerAdapter {
    HelloService helloService = new HelloServerImpl();

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("msg:" + msg.toString());
        /*自定义协议,客户端每次发送消息需要以某段字符串开头*/
        if (msg.toString().startsWith("###")) {
            String result = helloService.hello(msg.toString()
                    .substring(msg.toString().lastIndexOf("#") + 1));
            ctx.writeAndFlush(result);
        }
    }

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

  

provider

public class HelloServerImpl implements HelloService {
    @Override
    public String hello(String msg) {
        System.out.println("收到消费者消息" + msg);
        if (msg != null) {
            return "hello customer, I have received your message [" + msg + "]";
        } else {
            return "hello customer, I have received your message";
        }
    }
}

  

/**
 * 启动一个NettyServer
 */
public class ServerBootstrap {
    public static void main(String[] args) throws InterruptedException {
        new NettyServer().startServer();
    }
}

interface

/**
 * 公共接口
 */
public interface HelloService {
    String hello(String msg);
}

  

 

posted @ 2020-07-09 17:17  wavee_wyi  阅读(165)  评论(0)    收藏  举报