Java による RPC フレームワークの手書き

Guanhui
リリース: 2020-06-17 17:31:03
転載
3327 人が閲覧しました

Java による RPC フレームワークの手書き

RPC フレームワークはリモート呼び出しフレームワークと呼ばれ、その実装の中心原則は、コンシューマが動的プロキシを使用してインターフェイスをプロキシすることです (JDK に基づく動的プロキシ、もちろん CGLib を使えばインターフェースクラスを使わずにメソッドを直接使うことも可能です) ネットワーク送信プログラミングを追加することで、送信呼び出しインターフェースのメソッド名とメソッドパラメータがプロバイダーによって取得され、リフレクションを通じてインターフェースのメソッドが取得されます。が実行され、その後リフレクションが実行され、その結果がネットワーク プログラミングを通じて消費者に返されます。

次に、これらの概念を順番に実装してみましょう。ここでは最も単純な実装を行います。ネットワーク プログラミングでは BIO が使用されます。Reactor モードで Netty を使用すると、パフォーマンスが向上する方法で書き直すことができます。ネットワーク送信で使用されるシリアル化とデシリアライゼーションも Java ネイティブであり、もちろん、このような送信バイトは比較的大きく、Google の protoBuffer または kryo を使用して処理できます。 これは原理を説明するための便宜上のものです。

pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.guanjian</groupId>
    <artifactId>rpc-framework</artifactId>
    <version>1.0-SNAPSHOT</version>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.7.0</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
ログイン後にコピー

まず、もちろん、リモートで呼び出すインターフェイスとインターフェイス メソッドです。

public interface HelloService {
    String sayHello(String content);}
ログイン後にコピー

インターフェイス実装クラス

public class HelloServiceImpl implements HelloService {    public String sayHello(String content) {        return "hello," + content;    }
}
ログイン後にコピー

コンシューマ側の動的プロキシ、プロバイダとコンシューマを2つのプロジェクトに記述する場合、プロバイダ側には上記のインターフェイスと実装クラスが必要ですが、コンシューマ側には上記のインターフェースのみが必要です。

public class ConsumerProxy {
    /**
     * 消费者端的动态代理
     * @param interfaceClass 代理的接口类
     * @param host 远程主机IP
     * @param port 远程主机端口
     * @param <T>
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> T consume(final Class<T> interfaceClass,final String host,final int port) {
        return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(),
                new Class[]{interfaceClass}, (proxy,method,args) -> {
                    //创建一个客户端套接字
                    Socket socket = new Socket(host, port);
                    try {
                        //创建一个对外传输的对象流,绑定套接字
                        ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
                        try {
                            //将动态代理的方法名写入对外传输的对象流中
                            output.writeUTF(method.getName());
                            //将动态代理的方法的参数写入对外传输的对象流中
                            output.writeObject(args);
                            //创建一个对内传输的对象流,绑定套接字
                            //这里是为了获取提供者端传回的结果
                            ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
                            try {
                                //从对内传输的对象流中获取结果
                                Object result = input.readObject();
                                if (result instanceof Throwable) {
                                    throw (Throwable) result;
                                }
                                return result;
                            } finally {
                                input.close();
                            }
                        } finally {
                            output.close();
                        }
                    } finally {
                        socket.close();
                    }
                }
        );
    }
}
ログイン後にコピー

JDK 動的プロキシについては、AOP の原則と自己実装を参照してください。BIO については、従来の IO と NIO の比較を参照してください。

プロバイダー側ネットワーク伝送およびリモート呼び出しサービス

public class ProviderReflect {
    private static final ExecutorService executorService = Executors.newCachedThreadPool();

    /**
     * RPC监听和远程方法调用
     * @param service RPC远程方法调用的接口实例
     * @param port 监听的端口
     * @throws Exception
     */
    public static void provider(final Object service,int port) throws Exception {
        //创建服务端的套接字,绑定端口port
        ServerSocket serverSocket = new ServerSocket(port);
        while (true) {
            //开始接收客户端的消息,并以此创建套接字
            final Socket socket = serverSocket.accept();
            //多线程执行,这里的问题是连接数过大,线程池的线程数会耗尽
            executorService.execute(() -> {
                try {
                    //创建呢一个对内传输的对象流,并绑定套接字
                    ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
                    try {
                        try {
                            //从对象流中读取接口方法的方法名
                            String methodName = input.readUTF();
                            //从对象流中读取接口方法的所有参数
                            Object[] args = (Object[]) input.readObject();
                            Class[] argsTypes = new Class[args.length];
                            for (int i = 0;i < args.length;i++) {
                                argsTypes[i] = args[i].getClass();

                            }
                            //创建一个对外传输的对象流,并绑定套接字
                            //这里是为了将反射执行结果传递回消费者端
                            ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
                            try {
                                Class<?>[] interfaces = service.getClass().getInterfaces();
                                Method method = null;
                                for (int i = 0;i < interfaces.length;i++) {
                                    method = interfaces[i].getDeclaredMethod(methodName,argsTypes);
                                    if (method != null) {
                                        break;
                                    }
                                }
                                Object result = method.invoke(service, args);
                                //将反射执行结果写入对外传输的对象流中
                                output.writeObject(result);
                            } catch (Throwable t) {
                                output.writeObject(t);
                            } finally {
                                output.close();
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        } finally {
                            input.close();
                        }
                    } finally {
                        socket.close();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
        }
    }
}
ログイン後にコピー

プロバイダー側​​でネットワーク リスニングとリモート呼び出しを開始

public class RPCProviderMain {
    public static void main(String[] args) throws Exception {
        HelloService service = new HelloServiceImpl();
        ProviderReflect.provider(service,8083);
    }
}
ログイン後にコピー

コンシューマの動的プロキシ呼び出しを開始

public class RPCConsumerMain {
    public static void main(String[] args) throws InterruptedException {
        HelloService service = ConsumerProxy.consume(HelloService.class,"127.0.0.1",8083);
        for (int i = 0;i < 1000;i++) {
            String hello = service.sayHello("你好_" + i);
            System.out.println(hello);
            Thread.sleep(1000);
        }
    }
}
ログイン後にコピー

実行結果

こんにちは、こんにちは _0
こんにちは、こんにちは_1
こんにちは、こんにちは_2
こんにちは、こんにちは_3
こんにちは、こんにちは_4
こんにちは、こんにちは_5

....

Netty ProtoBuffer を使用して高性能 RPC フレームワークに拡張したい場合は、Protobuffer を統合した Netty の関連記述メソッドを参照してください。

推奨チュートリアル:「PHP

以上がJava による RPC フレームワークの手書きの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:oschina.net
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート