在写代码之前我们先要想清楚几个问题。

  1. 我们的框架到底要实现什么功能?

    我们要实现一个远程调用的 RPC 协议。
  2. 最终实现效果是什么样的?

    我们能像调用本地服务一样调用远程的服务。
  3. 怎样实现上面的效果?

    前面几章已经给大家说了,使用动态代理,在客户端生成接口代理类使用,在代理类的 invoke 方法里面将方法参数等信息组装成 request 发给服务端,服务端需要起一个服务器一直等待接收这种消息,接收之后使用反射调

    用对应接口的实现类。

首先我们需要实现底层的通信的服务端和客户端,可以有一下几种实现:

  1. 基于 Socket 的客户端和服务端(同步阻塞式,不推荐),大家可以当作一个编程练习,整个和系统没有进行整合,纯粹练习使用。

    基于 Socket 的服务端。

    启动一个阻塞式的 socket server,加入一个线程池实现伪异步。

    public class SocketServer {
    
         private static SocketServer INSTANCE = new SocketServer();
    
         private SocketServer(){};
    
         public static SocketServer getInstance() {
    return INSTANCE;
    } //没有核心线程数量控制的线程池,最大线程数是 Integer 的最大值,多线程实现伪异步
    ExecutorService executorService = Executors.newCachedThreadPool(); /**
    * 发布服务,bio 模型
    * @param service
    * @param port
    */
    public void publiser(int port){
    try (ServerSocket serverSocket = new ServerSocket(port);)
    {
    while (true){
    Socket socket = serverSocket.accept();//接收请求
    executorService.execute(new SocketHandler(socket));
    }
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }

    对应的 hanlder,使用反射调用对应的服务,并通过 sokcet 写回结果。

    public class SocketHandler implements Runnable{
    
         private Socket socket;
    
         public SocketHandler(Socket socket) {
    this.socket = socket;
    } @Override
    public void run() {
    try (ObjectInputStream inputStream = new ObjectInputStream(socket.getInputStream());
    ObjectOutputStream outputStream = new ObjectOutputStream(socket.getOutputStream());)
    {
    Object o = inputStream.readObject(); //readObject 是 java 反序列化的过程
    System.out.println(o);
    Object result = invoke((RpcRequest) o);
    //写回结果
    outputStream.writeObject(result);
    outputStream.flush();
    } catch (IOException e) {
    e.printStackTrace();
    } catch (ClassNotFoundException e) {
    e.printStackTrace();
    }
    } private Object invoke(RpcRequest invocation){ //根据方法名和参数类型在 service 里获取方法
    try {
    String interFaceName = invocation.getInterfaceName();
    Class impClass = Class.forName(invocation.getImpl()); Method method = impClass.getMethod(invocation.getMethodName(),invocation.getParamTypes());
    String result = (String)method.invoke(impClass.newInstance(),invocation.getParams());
    return result;
    } catch (NoSuchMethodException e) {
    e.printStackTrace();
    } catch (IllegalAccessException e) {
    e.printStackTrace();
    } catch (InvocationTargetException e) {
    e.printStackTrace();
    } catch (InstantiationException e) {
    e.printStackTrace();
    } catch (ClassNotFoundException e) {
    e.printStackTrace();
    } return null;
    }
    }

    在看客户端,拼装参数,发送给 socket 服务端。

    public class SocketClient {
    
         private static SocketClient INSTANCE = new SocketClient();
    
         private SocketClient(){};
    
         public static SocketClient getInstance() {
    return INSTANCE;
    } private Socket newSocket(String host, Integer port) {
    System.out.println("创建一个新的 socket 连接");
    try {
    Socket socket = new Socket(host, port);
    return socket;
    } catch (IOException e) {
    System.out.println("建立连接失败");
    e.printStackTrace();
    }
    return null;
    } public Object sendRequest(String host, Integer port,RpcRequest rpcRequest) {
    Socket socket = newSocket(host,port);
    try (
    ObjectOutputStream outputStream = new ObjectOutputStream(socket.getOutputStream());
    ObjectInputStream inputStream = new ObjectInputStream(socket.getInputStream());)
    {
    outputStream.writeObject(rpcRequest);
    outputStream.flush(); Object result = inputStream.readObject(); inputStream.close();
    outputStream.close();
    return result; } catch (Exception e) {
    e.printStackTrace();
    }
    return null;
    }
    }

    通过上面的代码相信大家已经明白了这个流程了,就是一个客户端与服务端通信的过程,将需要调用的方法的参数传到服务端,服务端通过反射完成调用,最后返回结果给客户端。

    下面正式开始。

  2. 基于 Http 请求的客户端和基于 Tomcat 的服务端。

    基于 Tomcat 的服务端,单例模式,只有一个启动服务的 start 方法,监听到的请求通过 DispatcherServlet 处理。

    public class HttpServer {
    
     	private static HttpServer INSTANCE = new HttpServer();
    
     	private HttpServer(){}
    
     	public static HttpServer getInstance(){
    return INSTANCE;
    } /**
    *
    * servlet 容器,tomcat
    * @param hostname
    * @param port
    */ public void start(String hostname,Integer port){ Tomcat tomcat = new Tomcat();
    Server server = tomcat.getServer();
    Service service = server.findService("Tomcat"); Connector connector = new Connector();
    connector.setPort(port); Engine engine = new StandardEngine();
    engine.setDefaultHost(hostname); Host host = new StandardHost();
    host.setName(hostname); String contextPath = "";
    Context context = new StandardContext();
    context.setPath(contextPath);
    context.addLifecycleListener(new Tomcat.FixContextListener()); //声明周期监听器 host.addChild(context);
    engine.addChild(host); service.setContainer(engine);
    service.addConnector(connector); tomcat.addServlet(contextPath,"dispatcher", new DispatcherServlet());
    context.addServletMappingDecoded("/*","dispatcher"); try {
    tomcat.start();
    tomcat.getServer().await();
    } catch (LifecycleException e) {
    e.printStackTrace();
    }
    }
    }

    下面来看请求分发器 DispatcherServlet 的实现,将请求派发给 HttpServletHandler 实现。

    /**
    * tomcat 是 servlet 容器,写一个 servlet
    *
    */
    public class DispatcherServlet extends HttpServlet { @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    new HttpServletHandler().handler(req,resp);
    }
    }

    HttpServletHandler 的实现其实就是解析 request,通过反射调用最后返回结果。

    public class HttpServletHandler{
    
         public void handler(HttpServletRequest req, HttpServletResponse resp) {
    
             try(InputStream inputStream = req.getInputStream();
    OutputStream outputStream =resp.getOutputStream();){
    ObjectInputStream ois = new ObjectInputStream(inputStream);
    RpcRequest invocation = (RpcRequest) ois.readObject(); // 从注册中心根据接口,找接口的实现类
    String interFaceName = invocation.getInterfaceName();
    Class impClass = Class.forName(invocation.getImpl()); Method method = impClass.getMethod(invocation.getMethodName(),invocation.getParamTypes());
    Object result = method.invoke(impClass.newInstance(),invocation.getParams()); RpcResponse rpcResponse = new RpcResponse();
    rpcResponse.setResponseId(invocation.getRequestId());
    rpcResponse.setData(result);
    IOUtils.write(toByteArray(rpcResponse),outputStream);
    }catch (IOException e){
    e.printStackTrace();
    } catch (ClassNotFoundException e) {
    e.printStackTrace();
    } catch (NoSuchMethodException e) {
    e.printStackTrace();
    } catch (IllegalAccessException e) {
    e.printStackTrace();
    } catch (InvocationTargetException e) {
    e.printStackTrace();
    } catch (InstantiationException e) {
    e.printStackTrace();
    }
    } public byte[] toByteArray (Object obj) {
    byte[] bytes = null;
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    try {
    ObjectOutputStream oos = new ObjectOutputStream(bos);
    oos.writeObject(obj);
    oos.flush();
    bytes = bos.toByteArray ();
    oos.close();
    bos.close();
    } catch (IOException ex) {
    ex.printStackTrace();
    }
    return bytes;
    } }

    最后来看客户端的实现,通过 post 方法发送数据,最后解析服务端返回的结果。

    public class HttpClient {
    
     	private static HttpClient INSTANCE = new HttpClient();
    
     	private HttpClient(){}
    
     	public static HttpClient getInstance(){
    return INSTANCE;
    } public Object post(String hostname, Integer port, RpcRequest invocation){ try{
    URL url = new URL("http",hostname,port,"/");
    HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection(); httpURLConnection.setRequestMethod("POST");
    httpURLConnection.setDoOutput(true); OutputStream outputStream = httpURLConnection.getOutputStream();
    ObjectOutputStream oos = new ObjectOutputStream(outputStream);
    oos.writeObject(invocation);
    oos.flush();
    oos.close(); InputStream inputStream = httpURLConnection.getInputStream();
    RpcResponse rpcResponse = (RpcResponse)toObject(IOUtils.toByteArray(inputStream));
    return rpcResponse.getData(); } catch (MalformedURLException e) {
    e.printStackTrace();
    } catch (IOException e) {
    e.printStackTrace();
    }
    return null; } public Object toObject (byte[] bytes) {
    Object obj = null;
    try {
    ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
    ObjectInputStream ois = new ObjectInputStream (bis);
    obj = ois.readObject();
    ois.close();
    bis.close();
    } catch (IOException ex) {
    ex.printStackTrace();
    } catch (ClassNotFoundException ex) {
    ex.printStackTrace();
    }
    return obj;
    }
    }
  3. Netty 模型的客户端和服务端。

    基于 Netty 的服务端,里面的编码器和解码器是我们自己实现的,大家可以先用我注释掉的那部分,等我们写到编码解码器的时候再替换。

    public class NettyServer {
    
     	private static NettyServer INSTANCE = new NettyServer();
    
     	private static Executor executor = Executors.newCachedThreadPool();
    
         private final static int MESSAGE_LENGTH = 4;
    
         private NettyServer(){};
    
         public static NettyServer getInstance(){
    return INSTANCE;
    } private SerializeType serializeType = SerializeType.queryByType(Configuration.getInstance().getSerialize()); public static void submit(Runnable t){
    executor.execute(t);
    } public void start(String host, Integer port){
    EventLoopGroup bossGroup = new NioEventLoopGroup(1);
    EventLoopGroup workerGroup = new NioEventLoopGroup(); try{
    final 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 arg0) throws Exception {
    ChannelPipeline pipeline = arg0.pipeline();
    //ObjectDecoder的基类半包解码器LengthFieldBasedFrameDecoder的报文格式保持兼容。因为底层的父类LengthFieldBasedFrameDecoder
    //的初始化参数即为super(maxObjectSize, 0, 4, 0, 4);
    // pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, NettyServer.MESSAGE_LENGTH, 0, NettyServer.MESSAGE_LENGTH));
    //利用LengthFieldPrepender回填补充ObjectDecoder消息报文头
    // pipeline.addLast(new LengthFieldPrepender(NettyServer.MESSAGE_LENGTH));
    // pipeline.addLast(new ObjectEncoder());
    //考虑到并发性能,采用weakCachingConcurrentResolver缓存策略。一般情况使用:cacheDisabled即可
    // pipeline.addLast(new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.weakCachingConcurrentResolver(this.getClass().getClassLoader())));
    //注册解码器NettyDecoderHandler
    pipeline.addLast(new NettyDecoderHandler(RpcRequest.class, serializeType));
    //注册编码器NettyEncoderHandler
    pipeline.addLast(new NettyEncoderHandler(serializeType));
    pipeline.addLast("handler", new NettyServerHandler()); } });
    Channel channel = bootstrap.bind(host, port).sync().channel();
    System.out.println("Server start listen at " + port);
    }catch(Exception e){
    bossGroup.shutdownGracefully();
    workerGroup.shutdownGracefully();
    }
    } }

    服务端对应的 handler,netty 都是这种 handler 模式,handler 里面也是将这个接收的 request 放入线程池中处理。

        public class NettyServerHandler extends SimpleChannelInboundHandler<RpcRequest> {
    
     	private ChannelHandlerContext context;
    
     	@Override
    protected void channelRead0(ChannelHandlerContext ctx, RpcRequest rpcRequest) throws Exception {
    System.out.println("server channelRead...");
    System.out.println(ctx.channel().remoteAddress() + "->server:" + rpcRequest.toString());
    InvokeTask it = new InvokeTask(rpcRequest,ctx);
    NettyServer.submit(it);
    } @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception{
    this.context = ctx;
    } }

    给出 InvokeTask 的对应实现。

    public class InvokeTask implements Runnable{
    
     	private RpcRequest invocation;
    private ChannelHandlerContext ctx; public InvokeTask(RpcRequest invocation,ChannelHandlerContext ctx) {
    super();
    this.invocation = invocation;
    this.ctx = ctx;
    } @Override
    public void run() { // 从注册中心根据接口,找接口的实现类
    String interFaceName = invocation.getInterfaceName();
    Class impClass = null;
    try {
    impClass = Class.forName(invocation.getImpl());
    } catch (ClassNotFoundException e) {
    e.printStackTrace();
    } Method method;
    Object result = null;
    try {
    method = impClass.getMethod(invocation.getMethodName(),invocation.getParamTypes());
    //这块考虑实现类,是不是应该在 spring 里面拿
    result = method.invoke(impClass.newInstance(),invocation.getParams());
    } catch (Exception e) {
    e.printStackTrace();
    }
    RpcResponse rpcResponse = new RpcResponse();
    rpcResponse.setResponseId(invocation.getRequestId());
    rpcResponse.setData(result);
    ctx.writeAndFlush(rpcResponse).addListener(new ChannelFutureListener() {
    public void operationComplete(ChannelFuture channelFuture) throws Exception {
    System.out.println("RPC Server Send message-id respone:" + invocation.getRequestId());
    }
    }); } }

    再来看客户端,客户端有两种实现,一种是不能复用 handler(可以立即为 connection)的模式,这种模式并发不太高,另一种是能够复用 handler 的 handlerPool 模式。

    不能复用的模式。

    public class NettyClient {
    private static NettyClient INSTANCE = new NettyClient(); private final static int parallel = Runtime.getRuntime().availableProcessors() * 2; private NettyClient(){}; public static NettyClient getInstance(){
    return INSTANCE;
    } private SerializeType serializeType = SerializeType.queryByType(Configuration.getInstance().getSerialize()); public void start(String host,Integer port){ Bootstrap bootstrap = new Bootstrap();
    EventLoopGroup group = new NioEventLoopGroup(parallel); try{
    bootstrap.group(group)
    .channel(NioSocketChannel.class)
    .handler(new ChannelInitializer<SocketChannel>(){ @Override
    protected void initChannel(SocketChannel arg0) throws Exception {
    ChannelPipeline pipeline = arg0.pipeline();
    //ObjectDecoder的基类半包解码器LengthFieldBasedFrameDecoder的报文格式保持兼容。因为底层的父类LengthFieldBasedFrameDecoder
    //的初始化参数即为super(maxObjectSize, 0, 4, 0, 4);
    // pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4));
    //利用LengthFieldPrepender回填补充ObjectDecoder消息报文头
    // pipeline.addLast(new LengthFieldPrepender(4));
    // pipeline.addLast(new ObjectEncoder());
    //考虑到并发性能,采用weakCachingConcurrentResolver缓存策略。一般情况使用:cacheDisabled即可
    // pipeline.addLast(new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.weakCachingConcurrentResolver(this.getClass().getClassLoader())));
    //注册Netty编码器
    System.out.println("11111111:"+serializeType.getSerializeType());
    pipeline.addLast(new NettyEncoderHandler(serializeType));
    //注册Netty解码器
    pipeline.addLast(new NettyDecoderHandler(RpcResponse.class, serializeType));
    pipeline.addLast("handler", new NettyClientHandler()); } });
    ChannelFuture future = bootstrap.connect(host,port).sync();
    }catch(Exception e){
    group.shutdownGracefully();
    } }
    }

    在看可复用的模式,固定 handler 数量,目前框架中使用的是可复用模式,上面的不可复用的没用上,为了给大家理解,没有删除。

    public class NettyChannelPoolFactory {
    
         //初始化Netty Channel阻塞队列的长度,该值为可配置信息
    private static final int channelConnectSize = 10; //Key为服务提供者地址,value为Netty Channel阻塞队列
    private static final Map<URL, ArrayBlockingQueue<Channel>> channelPoolMap = new ConcurrentHashMap<>(); private static NettyChannelPoolFactory INSTANCE = new NettyChannelPoolFactory(); private NettyChannelPoolFactory(){}; public static NettyChannelPoolFactory getInstance(){
    return INSTANCE;
    } private List<ServiceProvider> serviceMetaDataList = new ArrayList<>(); //根据配置文件里面需要调用的接口信息来初始化 channel
    public void initNettyChannelPoolFactory(Map<String, List<ServiceProvider>> providerMap){ //将服务提供者信息存入serviceMetaDataList列表
    Collection<List<ServiceProvider>> collectionServiceMetaDataList = providerMap.values();
    for (List<ServiceProvider> serviceMetaDataModels : collectionServiceMetaDataList) {
    if (CollectionUtils.isEmpty(serviceMetaDataModels)) {
    continue;
    }
    serviceMetaDataList.addAll(serviceMetaDataModels);
    } //获取服务提供者地址列表
    Set<URL> set = new HashSet<>();
    for (ServiceProvider serviceMetaData : serviceMetaDataList) {
    String serviceIp = serviceMetaData.getIp();
    int servicePort = serviceMetaData.getPort();
    URL url = new URL(serviceIp,servicePort);
    set.add(url);
    } for(URL url:set){
    //为每个 ip端口 建立多个 channel,并且放入阻塞队列中
    int channelSize = 0;
    while(channelSize < channelConnectSize){
    Channel channel = null;
    while(channel == null){
    channel = registerChannel(url);
    } channelSize ++; ArrayBlockingQueue<Channel> queue = channelPoolMap.get(url);
    if(queue == null){
    queue = new ArrayBlockingQueue<Channel>(channelConnectSize);
    channelPoolMap.put(url, queue);
    }
    queue.offer(channel); }
    } } public Channel registerChannel(URL url) {
    final SerializeType serializeType = SerializeType.queryByType(Configuration.getInstance().getSerialize());
    Bootstrap bootstrap = new Bootstrap();
    EventLoopGroup group = new NioEventLoopGroup(10); try{
    bootstrap.group(group)
    .channel(NioSocketChannel.class)
    .handler(new ChannelInitializer<SocketChannel>(){ @Override
    protected void initChannel(SocketChannel arg0) throws Exception {
    ChannelPipeline pipeline = arg0.pipeline();
    //ObjectDecoder的基类半包解码器LengthFieldBasedFrameDecoder的报文格式保持兼容。因为底层的父类LengthFieldBasedFrameDecoder
    //的初始化参数即为super(maxObjectSize, 0, 4, 0, 4);
    // pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4));
    //利用LengthFieldPrepender回填补充ObjectDecoder消息报文头
    // pipeline.addLast(new LengthFieldPrepender(4));
    // pipeline.addLast(new ObjectEncoder());
    //考虑到并发性能,采用weakCachingConcurrentResolver缓存策略。一般情况使用:cacheDisabled即可
    // pipeline.addLast(new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.weakCachingConcurrentResolver(this.getClass().getClassLoader())));
    pipeline.addLast(new NettyEncoderHandler(serializeType));
    //注册Netty解码器
    pipeline.addLast(new NettyDecoderHandler(RpcResponse.class, serializeType));
    pipeline.addLast("handler", new NettyClientHandler()); } });
    ChannelFuture future = bootstrap.connect(url.getHost(),url.getPort()).sync();
    Channel channel = future.channel();
    //等待Netty服务端链路建立通知信号
    final CountDownLatch connectedLatch = new CountDownLatch(1); final List<Boolean> isSuccess = new ArrayList<>(1);
    future.addListener(new ChannelFutureListener(){ @Override
    public void operationComplete(ChannelFuture future)
    throws Exception {
    if(future.isSuccess()){
    isSuccess.add(true);
    }else{
    isSuccess.add(false);
    }
    connectedLatch.countDown();
    } });
    connectedLatch.await();
    if(isSuccess.get(0)){
    return channel;
    }
    }catch(Exception e){
    group.shutdownGracefully();
    e.printStackTrace();
    }
    return null;
    }
    //根据 url 获取阻塞队列
    public ArrayBlockingQueue<Channel> acqiure(URL url){
    System.out.println(channelPoolMap.toString());
    return channelPoolMap.get(url);
    } //channel 使用完毕后进行回收
    public void release(ArrayBlockingQueue<Channel> queue, Channel channel, URL url){
    if(queue == null){
    return;
    }
    //需要检查 channel 是否可用,如果不可用,重新注册一个放入阻塞队列中
    if(channel == null || !channel.isActive() || !channel.isOpen()|| !channel.isWritable()){
    if (channel != null) {
    channel.deregister().syncUninterruptibly().awaitUninterruptibly();
    channel.closeFuture().syncUninterruptibly().awaitUninterruptibly();
    }
    Channel c = null;
    while(c == null){
    c = registerChannel(url);
    }
    queue.offer(c);
    return;
    }
    queue.offer(channel);
    } }

    给出对应的 handler 实现,在 channelread0 里面读取 server 端返回的信息,因为 netty 是异步的,所以需要 MessageCallBack 来实现我们的同步调用。

    public class NettyClientHandler extends SimpleChannelInboundHandler<RpcResponse> {
    
         private ChannelHandlerContext context;
    
         @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
    throws Exception {
    cause.printStackTrace();
    ctx.close();
    } @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
    System.out.println("停止时间是:"+new Date());
    System.out.println("HeartBeatClientHandler channelInactive");
    } @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
    this.context = ctx;
    System.out.println("激活时间是:"+ctx.channel().id());
    } @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, RpcResponse rpcResponse) throws Exception {
    // String res = (String)msg;
    //RpcResponse rpcResponse = (RpcResponse)msg;
    String responseId = rpcResponse.getResponseId();
    MessageCallBack callBack = ResponseHolder.getInstance().mapCallBack.get(responseId);
    if(callBack != null){
    ResponseHolder.getInstance().mapCallBack.remove(responseId);
    callBack.over(rpcResponse);
    }
    }
    }

    MessageCallBack 的实现。

    public class MessageCallBack {
    
     	private RpcRequest rpcRequest;
    
     	private RpcResponse rpcResponse;
    
     	private Lock lock = new ReentrantLock();
    
     	private Condition finish = lock.newCondition();
    
     	public MessageCallBack(RpcRequest request) {
    this.rpcRequest = request;
    } public Object start() throws InterruptedException {
    try {
    lock.lock();
    //设定一下超时时间,rpc服务器太久没有相应的话,就默认返回空吧。
    finish.await(10*1000, TimeUnit.MILLISECONDS);
    if (this.rpcResponse != null) {
    return this.rpcResponse.getData();
    } else {
    return null;
    }
    } finally {
    lock.unlock();
    }
    } public void over(RpcResponse reponse) {
    try {
    lock.lock();
    this.rpcResponse = reponse;
    finish.signal();
    } finally {
    lock.unlock();
    }
    } }

    既然是可插拔式框架,那么底层协议一定要是可选择的,所以我们定义一个顶层接口来支持我们选择协议。

    start 方法是启动服务端,send 方法是客户端发送数据。

    public interface Procotol {
    
         void start(URL url);
    Object send(URL url, RpcRequest invocation);
    }

    对应的三个协议的接口实现。

    Netty 的实现

    public class DubboProcotol implements Procotol {
    @Override
    public void start(URL url) {
    NettyServer nettyServer = NettyServer.getInstance();
    nettyServer.start(url.getHost(),url.getPort());
    } @Override
    public Object send(URL url, RpcRequest invocation) {
    ArrayBlockingQueue<Channel> queue = NettyChannelPoolFactory.getInstance().acqiure(url);
    Channel channel = null;
    try {
    channel = queue.poll(invocation.getTimeout(), TimeUnit.MILLISECONDS);
    if(channel == null || !channel.isActive() || !channel.isOpen()|| !channel.isWritable()){
    channel = queue.poll(invocation.getTimeout(), TimeUnit.MILLISECONDS);
    if(channel == null){
    channel = NettyChannelPoolFactory.getInstance().registerChannel(url);
    }
    }
    //将本次调用的信息写入Netty通道,发起异步调用
    ChannelFuture channelFuture = channel.writeAndFlush(invocation);
    channelFuture.syncUninterruptibly();
    MessageCallBack callback = new MessageCallBack(invocation);
    ResponseHolder.getInstance().mapCallBack.put(invocation.getRequestId(), callback);
    try {
    return callback.start();
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    return null;
    } catch (InterruptedException e1) {
    e1.printStackTrace();
    }finally{
    System.out.println("release:"+channel.id());
    NettyChannelPoolFactory.getInstance().release(queue, channel, url);
    }
    return null;
    }
    }

    http 的实现

    public class HttpProcotol implements Procotol {
    @Override
    public void start(URL url) {
    HttpServer httpServer = HttpServer.getInstance();
    httpServer.start(url.getHost(),url.getPort());
    } @Override
    public Object send(URL url, RpcRequest invocation) {
    HttpClient httpClient = HttpClient.getInstance();
    return httpClient.post(url.getHost(),url.getPort(),invocation);
    }
    }

    Socket 的实现

    public class SocketProcotol implements Procotol {
    @Override
    public void start(URL url) {
    SocketServer socketServer = SocketServer.getInstance();
    socketServer.publiser(url.getPort());
    } @Override
    public Object send(URL url, RpcRequest invocation) {
    SocketClient socketClient = SocketClient.getInstance();
    return socketClient.sendRequest(url.getHost(),url.getPort(),invocation);
    }
    }

    这样一个可选择协议的模型就实现了,我们可已通过这个模块选择协议,并且与服务端通信。

带你手写基于 Spring 的可插拔式 RPC 框架(三)通信协议模块的更多相关文章

  1. 带你手写基于 Spring 的可插拔式 RPC 框架(一)介绍

    概述 首先这篇文章是要带大家来实现一个框架,听到框架大家可能会觉得非常高大上,其实这和我们平时写业务员代码没什么区别,但是框架是要给别人使用的,所以我们要换位思考,怎么才能让别人用着舒服,怎么样才能让 ...

  2. 带你手写基于 Spring 的可插拔式 RPC 框架(二)整体结构

    前言 上一篇文章中我们已经知道了什么是 RPC 框架和为什么要做一个 RPC 框架了,这一章我们来从宏观上分析,怎么来实现一个 RPC 框架,这个框架都有那些模块以及这些模块的作用. 总体设计 在我们 ...

  3. 带你手写基于 Spring 的可插拔式 RPC 框架(五)注册中心

    注册中心代码使用 zookeeper 实现,我们通过图片来看看我们注册中心的架构. 首先说明, zookeeper 的实现思路和代码是参考架构探险这本书上的,另外在 github 和我前面配置文件中的 ...

  4. 带你手写基于 Spring 的可插拔式 RPC 框架(四)代理类的注入与服务启动

    上一章节我们已经实现了从客户端往服务端发送数据并且通过反射方法调用服务端的实现类最后返回给客户端的底层协议. 这一章节我们来实现客户端代理类的注入. 承接上一章,我们实现了多个底层协议,procoto ...

  5. C基础 带你手写 redis sds

    前言 - Simple Dynamic Strings  antirez 想统一 Redis,Disque,Hiredis 项目中 SDS 代码, 因此构建了这个项目 https://github.c ...

  6. C基础 带你手写 redis adlist 双向链表

    引言 - 导航栏目 有些朋友可能对 redis 充满着数不尽的求知欲, 也许是 redis 属于工作, 交流(面试)的大头戏, 不得不 ... 而自己当下对于 redis 只是停留在会用层面, 细节层 ...

  7. 基于Spring MVC的Web应用开发(三) - Resources

    基于Spring MVC的Web应用开发(3) - Resources 上一篇介绍了在基于Spring MVC的Web项目中加入日志,本文介绍Spring MVC如何处理资源文件. 注意到本项目的we ...

  8. 第二篇 基于.net搭建热插拔式web框架(沙箱的构建)

    上周五写了一个实现原理篇,在评论中看到有朋友也遇到了我的问题,真的是有种他乡遇知己的感觉,整个系列我一定会坚持写完,并在最后把代码开源到git中.上一篇文章很多人看了以后,都表示不解,觉得不知道我到底 ...

  9. 第三篇 基于.net搭建热插拔式web框架(重造Controller)

    由于.net MVC 的controller 依赖于HttpContext,而我们在上一篇中的沙箱模式已经把一次http请求转换为反射调用,并且http上下文不支持跨域,所以我们要重造一个contro ...

随机推荐

  1. P4136 谁能赢呢? 脑子

    思路:脑子(教练说是博弈论?) 提交:1次 题解: 结论:若\(n\)为奇数后手胜,若\(n\)为偶数先手胜. 大致证明: 我们发现,若我们把棋盘黑白染色并设左上角为黑色,那么显然有:若\(n\)为奇 ...

  2. Axios的params与data的

    Axios发送请求时params和data的区别  https://www.cnblogs.com/cwzqianduan/p/8675356.html(copy 在使用axios时,注意到配置选项中 ...

  3. learning express step(十一)

    learning express.Router() code: const express = require('express'); const app = express(); var route ...

  4. [Luogu] 最大收益

    题面:https://www.luogu.org/problemnew/show/P2647 题解:https://www.zybuluo.com/wsndy-xx/note/1142685

  5. linux crontab 防止周期内为执行完成重复执行

    问题的背景: 我们常常需要通过crontab部署某个脚本运行某些定时任务,但在实际的过程中,一旦处理不好可能导致在同一时刻出现脚本的多个运行副本,比如crontab的调度是每5 分钟运行一次脚本,如果 ...

  6. Scarily interesting! (URAL - 2021)

    Problem This year at Monsters University it is decided to arrange Scare Games. At the Games all camp ...

  7. ICP、MRR、BKA等特性

    一.Index Condition Pushdown(ICP) Index Condition Pushdown (ICP)是 mysql 使用索引从表中检索行数据的一种优化方式,从mysql5.6开 ...

  8. Irrlicht引擎剖析一

    代码风格:  1.接口以I开头,实现以C开头,保存数据的结构体以S开头  2.函数名以小写字母开头,变量以大字母开头  3.接口的公共函数,其参数大部分给了默认值  4.采用名字空间    名字空间i ...

  9. 关于在eclipse中Undefined attribute name (role).解决办法

    方案一: 只需要在jsp表头添加一句: <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c&q ...

  10. java试题复盘——11月13日

    上: 10. AccessViolationException异常触发后,下列程序的输出结果为(   A   ) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 AccessViol ...