独自のRPCフレームワークを実装する(研究ノート)

1.基本的な概念

RPCは、Remote Procedure Call、つまりリモートプロシージャコールの略語です。最も基本的なRPCモデルを次の図に示します。
次の図では、サービスプロバイダーAサーバーとサービスコンシューマーBサーバーです。サービスコンシューマーは、インターフェイスを使用するだけで、サービスプロバイダーが提供する対応するインターフェイスの実装をリモートで呼び出すことができます。これにより、戻り値が取得され、対応する呼び出しプロセスが完了します。
ここに写真の説明を挿入

2.具体的な実現

次のコード例では、Socketを使用して独自のRPCフレームワークを実装しています。プロジェクトの構造は次のとおりです。
ここに写真の説明を挿入
プロジェクトは、rpcモジュール、パブリックインターフェイスモジュール、サービスプロバイダーモジュール、およびサービスコンシューマーモジュールの4つのモジュールに分かれています。

2.1rpcモジュール

rpcモジュールは、主にRPCリモートプロシージャ呼び出しの機能を実装します。このモジュールは、クライアント側、サーバー側、および通信プロトコルの3つの部分に分かれています。以下に示すように。
ここに写真の説明を挿入

2.1.1通信プロトコル

RPCフレームワークでは、クライアントメソッドは、インターフェイスを介してサーバー対応インターフェイスの実装クラスのメソッドを呼び出すために必要な情報を提供する必要があります。具体的には、インターフェイス名、メソッド名、パラメータタイプリスト、パラメータ値リストが含まれます。コードは次のとおりです。

package com.supger.rpc.core.protocol;

import java.io.Serializable;

/**
 * RPC框架请求协议类,由于这个类需要在网络中传输,需要实现序列化接口。
 */
public class RequestProtocol implements Serializable {
    
    
    /**
     * 接口全名称
     */
    private String interfaceClassName;
    /**
     * 方法名称
     */
    private String methodName;
    /**
     * 参数类型列表
     */
    private Class<?>[] parameterTypes;
    /**
     * 参数值列表
     */
    private Object[] parameterValues;


    public String getInterfaceClassName() {
    
    
        return interfaceClassName;
    }

    public void setInterfaceClassName(String interfaceClassName) {
    
    
        this.interfaceClassName = interfaceClassName;
    }

    public String getMethodName() {
    
    
        return methodName;
    }

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

    public Class<?>[] getParameterTypes() {
    
    
        return parameterTypes;
    }

    public void setParameterTypes(Class<?>[] parameterTypes) {
    
    
        this.parameterTypes = parameterTypes;
    }

    public Object[] getParameterValues() {
    
    
        return parameterValues;
    }

    public void setParameterValues(Object[] parameterValues) {
    
    
        this.parameterValues = parameterValues;
    }
}

2.1.2クライアント

RPCフレームワークでは、クライアントは動的プロキシを使用します。動的プロキシでは、呼び出す必要のあるサーバーインターフェイスの関連情報がシリアル化され、最後にSocketを介してサーバーに送信され、サーバーが呼び出しを完了するのを待って、最後に結果を書き込みます。動的エージェント内にソケットを入力し、返された結果を取得します。発信者に戻ります。コードは次のとおりです。

package com.supger.rpc.core.client;

import com.supger.rpc.core.protocol.RequestProtocol;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.InetSocketAddress;
import java.net.Socket;

/**
 * RPC框架客户端核心实现类
 */
public class RpcClient {
    
    
    /**
     * 通过动态代理获取调用接口对应实例
     * @param interfaceClass
     * @param address
     * @param <T>
     * @return
     */
    public static <T> T getRemoteProxy(Class<T> interfaceClass, InetSocketAddress address){
    
    
        return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(),
                new Class<?>[]{
    
    interfaceClass},
                new InvocationHandler() {
    
    
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
                        try(Socket socket = new Socket()){
    
    
                            // 通过网络连接服务端
                            socket.connect(address);
                            try(
                                    // 获取输出流
                                    ObjectOutputStream serializer = new ObjectOutputStream(socket.getOutputStream());
                                    // 获取输入流
                                    ObjectInputStream deSerializer = new ObjectInputStream(socket.getInputStream())
                                    ){
    
    
                                // 创建一个RPC框架中请求协议对象
                                RequestProtocol requestProtocol = new RequestProtocol();
                                // 填充属性
                                requestProtocol.setInterfaceClassName(interfaceClass.getName());
                                requestProtocol.setMethodName(method.getName());
                                requestProtocol.setParameterTypes(method.getParameterTypes());
                                requestProtocol.setParameterValues(args);
                                // 序列化协议对象(把数据放入到网络中)
                                serializer.writeObject(requestProtocol);
                                /**
                                 * -----------------------
                                 * 同步过程
                                 * -----------------------
                                 */
                                // 反序列化(从网络中获取服务端放入的数据)
                                Object result = deSerializer.readObject();
                                return  result;
                            }
                        }catch (Exception e){
    
    
                            e.printStackTrace();
                        }
                        return null;
                    }
                });
    }
}

2.1.3サーバー

RPCフレームワークでは、対応するポートを監視するためにサーバーを起動する必要があります。クライアントは対応するポートに接続します。モニタリング開始後、複数のクライアントリクエストが来た場合、前のリクエストが処理されないためにブロックされないようにするには、マルチスレッドメカニズムを導入する必要があります。サーバーがクライアント接続を監視するとき、サーバーはスレッドプールを使用して対応するタスクを処理します。取得した対応するソケット接続情報は、タスクに保存されます。タスク内のrunメソッドでは、クライアントの対応する呼び出し情報が取得され、対応する実装クラスがリフレクションによって呼び出されます。その前に、サーバーは対応するサービスリストを公開する必要があります。コードは次のとおりです。

package com.supger.rpc.core.server;

import com.supger.rpc.core.protocol.RequestProtocol;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * RPC框架服务端的核心实现类
 * 核心实现步骤:
 * 1、暴露需要调用的服务接口
 * 2、启动服务端
 */
public class RpcServer {
    
    

    /**
     * 定义存储暴露的服务列表
     */
    Map<String,Object> serverMap = new ConcurrentHashMap<>(32);
    /**
     * 定义一个线程池
     */
    ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(8,20,200, TimeUnit.MICROSECONDS, new ArrayBlockingQueue<>(10));

    /**
     * 暴露服务的方法
     * @param interfaceClass
     * @param instance
     */
    public void publishServiceAPI(Class<?> interfaceClass,Object instance){
    
    
        this.serverMap.put(interfaceClass.getName(),instance);
    }

    /**
     * 定义发布服务的方法
     * @param port
     */
    public void start(int port){
    
    
        // 创建网络服务端
        try {
    
    
            ServerSocket serverSocket = new ServerSocket();
            // 绑定指定的端口
            serverSocket.bind(new InetSocketAddress(port));
            System.out.println("=============Supger RPC Server Starting ...... ================");
            while (true){
    
    
                poolExecutor.execute(new ServerTask(serverSocket.accept()));
            }
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
    }

    /**
     * 创建客户端请求处理的线程类
     */
    private class ServerTask implements Runnable{
    
    

        private final Socket socket;

        private ServerTask(Socket socket) {
    
    
            this.socket = socket;
        }

        @Override
        public void run() {
    
    
            try(
                    ObjectInputStream deSerializer = new ObjectInputStream(socket.getInputStream());
                    ObjectOutputStream serializer = new ObjectOutputStream(socket.getOutputStream())
                    ){
    
    
                // 反序列化获取客户端传入的数据
                RequestProtocol requestProtocol = (RequestProtocol) deSerializer.readObject();
                // 获取接口全名称
                String interfaceClassName = requestProtocol.getInterfaceClassName();
                Object instance = serverMap.get(interfaceClassName);
                if (null == instance){
    
    
                    return;
                }
                // 创建一个方法对象(反射方式)
                Method method = instance.getClass()
                        .getDeclaredMethod(requestProtocol.getMethodName(), requestProtocol.getParameterTypes());
                // 调用方法
                Object result = method.invoke(instance, requestProtocol.getParameterValues());
                //  序列化调用结果
                serializer.writeObject(result);

            }catch (Exception e){
    
    
                e.printStackTrace();
            }
        }
    }
}

2.2ユーザーAPI

RPCシステムでは、リモートプロシージャ呼び出しは、統合されたAPIモジュールと統合されたjava-beanに従う必要があります。したがって、対応するapiとjavabeanが抽出されて、別個のモジュール(プロジェクト)が形成されます。リモートプロシージャコールは、このような一連の標準インターフェイスに基づいています。これは、RPCの原理と実装を理解するのに役立つ簡単な例です。コードは次のとおりです。

package service;

/**
 * 定义暴露给客户端的服务接口
 */
public interface UserService {
    
    
    String addUserName(String name);
}

2.3ユーザープロバイダー

RPCシステムでは、サービスプロバイダーはサービスプロバイダーです。サービスを提供するには、対応するインターフェイスを実装する必要があります。これは、2.2user-apiでUserServiceインターフェイスを実現することです。実装クラスコードを以下に示します。

package com.supger.service;

import service.UserService;

public class UserServiceImpl implements UserService {
    
    
    @Override
    public String addUserName(String name) {
    
    
        return "添加的姓名为:"+name;
    }
}

ここで実装されるインターフェースは、パブリックインターフェースを導入する必要があることを強調しておく必要があります。
インターフェイスを実装した後、対応するサービスを開始する必要があります。対応するコードを以下に示します。

package com.supger;

import com.supger.rpc.core.server.RpcServer;
import com.supger.service.UserServiceImpl;
import service.UserService;

public class App {
    
    
    public static void main(String[] args) {
    
    
        // 创建一个RPC的服务端
        RpcServer rpcServer = new RpcServer();
        // 发布暴露服务
        rpcServer.publishServiceAPI(UserService.class,new UserServiceImpl());
        // 启动服务
        rpcServer.start(12345);
    }
}

上記のコードでは、最初にRPCサーバーを作成します。次に、対応するサービスを公開および公開します。最後にサービスを開始します。

2.4ユーザー-消費者

上記の手順を完了したら、RPCフレームワークを呼び出して、最終的にコンシューマー内にサービスコンシューマーを作成します。対応するリモートプロシージャ呼び出しの完了は、ローカルメソッドを呼び出すのと同じくらい簡単です。対応するコードを以下に示します。

package com.supger;

import com.supger.rpc.core.client.RpcClient;
import service.UserService;

import java.net.InetSocketAddress;

public class App {
    
    
    public static void main(String[] args) {
    
    
        // 在客户端调用RPC框架

        UserService userService = RpcClient.getRemoteProxy(UserService.class, new InetSocketAddress("127.0.0.1", 12345));

        String result = userService.addUserName("supger");

        System.out.println(result);
    }
}

この時点で、独自のRPCフレームワークを実装するプロセスが完了します。

3.テストを実行します

次の図に示すように、サービス提供モジュールを開始して、サービスが正常に開始されたことを示します。
ここに写真の説明を挿入
次の図に示すように、サービス消費モジュールを開始します。
ここに写真の説明を挿入
実行結果から、上記のプロセスを通じて、独自のRPCフレームワークを実現していることがわかります。

4.結論

このプロセスには、リフレクション、シリアル化フロー、動的プロキシ、マルチスレッド、スレッドプールなど、Javaの基本的な知識が含まれます。関連する概念の詳細な要約はフォローアップで作成されます。一方で、目的は基本的なスキルを上手に活用し、学習とコミュニケーションを促進することです。
上記の実現プロセスは、RPCの原理を理解するための予備的なスキームにすぎません。後で、RPCによって実装される関連フレームワークであるDubboなどについて学習します。その際、RPCシステムで考慮する必要のあるさまざまな要素についての議論をさらに深めます。
RPCディスカッションに関する記事はたくさんありますが、いくつかの参考記事を以下に示します。さまざまな記事から始めて、さまざまな利益があります。RPCの理解を深めるために。(この記事は参照リソース1の照合に基づいており、ここに参照ソースを示します)

5.参照リソース

1.元のRPCフレームワークの原則は非常に学習されており、完全に一種の楽しみです。
2.RPCの最初の知り合い3.RPCの
基本原則

おすすめ

転載: blog.csdn.net/fanjianglin/article/details/112213296