SOA如何設計傳輸模塊(二)-Netty的實現

上一遍介紹了設計傳輸模時接口的定義(http://blog.csdn.net/csujiangyu/article/details/51761479)

這裏用了主流的框架netty來實現。

首先設計NettyServer,主要功能是綁定端口,監聽連接。



public class NettyServer extends AbstractServer {
    private ServerBootstrap serverBootstrap;
    private EventLoopGroup bossGroup;
    private EventLoopGroup workerGroup;
    public NettyServer(ChannelHandler handler) {
        super(handler);
    }
    @Override
    public void bind(InetSocketAddress address) throws InterruptedException {
        bossGroup = new NioEventLoopGroup();
        workerGroup = new NioEventLoopGroup();
        serverBootstrap = new ServerBootstrap();
        serverBootstrap.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<SocketChannel>() { // (4)
                    @Override
                    public void initChannel(SocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new DecoderHandler());
                        ch.pipeline().addLast(new NettyServerHandler(channelHandler));
                        ch.pipeline().addLast(new EncoderHandler());
                    }
                })
                .option(ChannelOption.SO_BACKLOG, 128)
                .childOption(ChannelOption.SO_KEEPALIVE, true);
        serverBootstrap.bind(address).sync();
    }
    @Override
    public void close() {
        bossGroup.shutdownGracefully();
        workerGroup.shutdownGracefully();
    }
    @Override
    public boolean isClosed() {
        return false;
    }
}

這裏爲了簡單,有些方法沒有實現,比如isClosed,只是返回false。

NettyClient實現如下,主要是向服務端發起請求,建立連接。



public class NettyClient implements Client{
    private Bootstrap bootstrap;
    private NioEventLoopGroup workerGroup;
    private ChannelHandler channelHandler;
    public NettyClient(ChannelHandler channelHandler){
        this.channelHandler = channelHandler;
        init();
    }
    private void init(){
        workerGroup = new NioEventLoopGroup();
        bootstrap = new Bootstrap();
        bootstrap.group(workerGroup); // (2)
        bootstrap.channel(NioSocketChannel.class); // (3)
        bootstrap.option(ChannelOption.SO_KEEPALIVE, true); // (4)
        bootstrap.handler(new ChannelInitializer<SocketChannel>() {
            @Override
            public void initChannel(SocketChannel ch) throws Exception {
                ch.pipeline().addLast(new DecoderHandler());
                ch.pipeline().addLast(new NettyClientHandler(channelHandler));
                ch.pipeline().addLast(new EncoderHandler());
            }
        });
    }
    @Override
    public Channel connect(InetSocketAddress inetSocketAddress) throws Exception {
        ChannelFuture f = bootstrap.connect(inetSocketAddress).sync();
        io.netty.channel.Channel channel = f.channel();
        return new NettyChannel(channel, channelHandler);
    }
}

還需要實現Channel的實現


public class NettyChannel implements Channel {
    private static final ConcurrentHashMap<io.netty.channel.Channel, NettyChannel> channelMap = new ConcurrentHashMap<>();
    //包裝netty的client,用到了適配器模式
    private io.netty.channel.Channel channel;
    private ChannelHandler channelHandler;
    public NettyChannel(io.netty.channel.Channel channel, ChannelHandler channelHandler) {
        this.channel = channel;
        this.channelHandler = channelHandler;
    }
    static NettyChannel getOrAddChannel(io.netty.channel.Channel channel, ChannelHandler handler) {
        NettyChannel ret = channelMap.get(channel);
        //不存在則嘗試更新
        if (ret == null) {
            NettyChannel nettyChannel = new NettyChannel(channel, handler);
            //這裏處於線程安全考慮,如果存在則更新,直接返回
            ret = channelMap.putIfAbsent(channel, nettyChannel);
            if (ret == null) {
                ret = nettyChannel;
            }
        }
        return ret;
    }
    static void removeIfInactive(io.netty.channel.Channel channel) {
        if (channel != null && !channel.isActive()) {
            channelMap.remove(channel);
        }
    }
    @Override
    public void send(Object message) {
        this.channel.writeAndFlush(message);
    }
    @Override
    public ChannelHandler getChannelHandler() {
        return channelHandler;
    }
    @Override
    public void close() {
    }
    @Override
    public boolean isClosed() {
        return false;
    }
    @Override
    public InetSocketAddress getRemoteAddress() {
        return null;
    }
    @Override
    public boolean isConnnect() {
        return false;
    }
}

然後是NettyServerHandler的實現


public class NettyServerHandler extends ChannelInboundHandlerAdapter {
    //消息轉發到自定義可擴展的ChannelHandler
    private ChannelHandler channelHandler;
    public NettyServerHandler(ChannelHandler channelHandler){
        this.channelHandler = channelHandler;
    }
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        NettyChannel.getOrAddChannel(ctx.channel(), channelHandler);
    }
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object object) throws Exception {
        NettyChannel nettyChannel = NettyChannel.getOrAddChannel(ctx.channel(), channelHandler);
        channelHandler.receive(nettyChannel, object);
    }
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        NettyChannel.removeIfInactive(ctx.channel());
    }
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

NettyClientHandler:


public class NettyClientHandler extends ChannelInboundHandlerAdapter {
    private ChannelHandler channelHandler;
    public NettyClientHandler(ChannelHandler channelHandler) {
        this.channelHandler = channelHandler;
    }
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        NettyChannel.getOrAddChannel(ctx.channel(), channelHandler);
    }
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object object) throws Exception {
        NettyChannel nettyChannel = NettyChannel.getOrAddChannel(ctx.channel(), channelHandler);
        channelHandler.receive(nettyChannel, object);
    }
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

下面我們來驗證一下,看能否滿足SOA傳輸的需要。

定義服務提供者,用於暴露服務,這裏的服務是HelloService


public class RpcProvider {
    private static final Map<String, Invoker> invokers = new ConcurrentHashMap<>();
    public static Invoker getInvoker(String className) {
        return invokers.get(className);
    }
    public static void export(String interfaceName, Class<?> clazz) {
        Invoker invoker = new ServerInvoker(clazz);
        invokers.put(interfaceName, invoker);
    }
    private static final ChannelHandler requestHandler = new AbstractChannelHandler() {
        @Override
        public void receive(Channel channel, Object object) throws Exception {
            if (object instanceof Request) {
                Request request = (Request) object;
                Invocation invocation = request.getInvocation();
                Invoker invoker = getInvoker(invocation.getClazz().getSimpleName());
                Result result = invoker.invoke(invocation);
                Response response = new Response();
                response.setMsgId(request.getMsgId());
                response.setResult(result.getData());
                send(channel, response);
            }
        }
    };
    public static void main(String[] args) throws Exception {
        Server server = new NettyServer(requestHandler);
        server.bind(new InetSocketAddress(Constant.LOCAL_HOST, Constant.PORT));
        export(HelloService.class.getSimpleName(), HelloServiceImpl.class);
    }
}

然後是定義消費者,發起RPC請求,引用遠程服務HelloService


public class RpcConsumer {
    private static final ChannelHandler requestHandler = new AbstractChannelHandler() {
        @Override
        public void receive(Channel channel, Object message) throws Exception {
            if (message instanceof Response) {
                Response response = (Response) message;
                ResponseFuture future = ResponseFuture.getFuture(response.getMsgId());
                future.receive(response);
            }
        }
    };
    private static final Client client = new NettyClient(requestHandler);
    public static <T> T refer(final Class<T> clazz) throws Exception {
        final Channel channel = client.connect(new InetSocketAddress(Constant.LOCAL_HOST, Constant.PORT));
        ExchangeClient exchangeClient = new ExchangeClient();
        final ClientInvoker clientInvoker = new ClientInvoker(clazz);
        clientInvoker.setExchangeClient(exchangeClient);
        return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{clazz}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Invocation invocation = new Invocation();
                invocation.setClazz(clazz);
                invocation.setMethodName(method.getName());
                invocation.setParameterTypes(method.getParameterTypes());
                invocation.setArguments(args);
                Request request = new Request();
                request.setInvocation(invocation);
                ResponseFuture responseFuture = new ResponseFuture(request.getMsgId());
                channel.send(request);
                Response response = responseFuture.get();
                return response.getResult();
            }
        });
    }
    public static void main(String[] args) throws Exception {
        HelloService helloService = refer(HelloService.class);
        for (int i = 0; i < 100; i++) {
            String string = helloService.hello("Foo");
            System.out.println(string);
            Thread.sleep(1000);
        }
    }
}

分別啓動上面兩個程序,RpcConsumer控制檯不斷打印“Hello Foo”,說明調用成功。


Hello Foo
Hello Foo
Hello Foo
Hello Foo

源代碼在https://github.com/Jdoing/example/tree/master/example-transport/src/main/java/example/transport/netty

https://github.com/Jdoing/example/tree/master/example-soa/src/main/java/rpc3

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章