RPCとは何ですか?
RPC(リモートプロシージャコール)リモートプロシージャコール
RPCの起源
なぜrpcのようなものがあるのですか?また、Webサービステクノロジアーキテクチャの進化によるものです。初期のWebアプリケーションはモノリシックアーキテクチャでした。アクションでサービスサービスを使用する場合は、新しいサービスオブジェクトを追加することで、サービスオブジェクトの属性を直接取得できます。アクションコードと呼び出しサービスで。オブジェクトメソッド。
しかし、時代は変わりました。モノリシックアーキテクチャは増加するトラフィックをサポートできないため、モノリシックアーキテクチャをサービス指向アーキテクチャ(SOA)に分解する必要があります。このようなトスの後、アクションクラスとサービスクラスが1つのJVMに含まれない場合があります。もうサーバー上に。しかし、変更されていないのは、アクションがサービス内のメソッドを呼び出す必要があるということです。どうすればよいですか?現時点では、ネットワークを介して別のサーバー上のサービスメソッドをリモートで呼び出すことができるツールが必要です。このツールはRPCフレームワークです。簡単な理解は、ノードが別のノードによって提供されるサービスを要求することです。
Httpプロトコルを使用してデータを送信してみませんか?
HTTPはアプリケーション層で完了するため、通信全体のコストが比較的高くなります。リモートプロシージャコールでは、リモートコールはTCPに直接基づいており、データ送信はトランスポート層のTCP層で完了します。 RPCは主に顧客に依存し、サーバーとサーバー間でソケット接続が確立され、基盤となる実装はRESTよりも複雑です。
RPCリモート呼び出しプロセス
- クライアントは、呼び出す必要のある関数IDをサーバーに通知します(関数とプロセスIDの間にはマッピングがあり、マップに格納されています)。
- 呼び出し元の関数のIDと入力パラメーター(シリアル化され、バイトストリームに変換されたもの)をサーバーに転送します
- サーバーはシリアル化されたデータを受け入れ、それをオブジェクトに逆シリアル化します
- サーバーは、論理処理用の関数IDに従って、対応する関数を実行します。
- サーバーは返された結果をシリアル化し、クライアントに送信します
- クライアントの処理がブラウザに戻ります
リモート呼び出しによって引き起こされる問題
リモートで呼び出す場合、実行する必要のある関数の本体はリモートマシン上にあり、いくつかの新しい問題が発生します。
- コールIDマッピング。どのメソッドを呼び出すかをリモートマシンにどのように伝えるのですか?ローカル呼び出しでは、関数本体は関数ポインターによって直接指定されます。関数を呼び出すと、コンパイラーは対応する関数ポインターを自動的に呼び出します。ただし、リモート呼び出しでは、2つのプロセスのアドレス空間が完全に異なるため、関数ポインターは受け入れられません。したがって、RPCでは、すべての関数に独自のIDが必要です。このIDは、すべてのプロセスで一意に決定されます。クライアントは、リモートプロシージャコールを行うときにこのIDを添付する必要があります。次に、クライアントとサーバーでそれぞれ{function <–> CallID}対応テーブルを維持する必要があります。2つのテーブルは必ずしも完全に同じである必要はありませんが、同じ機能に対応するコールIDは同じである必要があります。クライアントがリモート呼び出しを行う必要がある場合、クライアントはこのテーブルをチェックして対応する呼び出しIDを見つけ、それをサーバーに渡します。サーバーはテーブルもチェックして、クライアントが呼び出す必要のある関数を判別してから、対応する関数のコード。
- シリアル化と逆シリアル化。クライアントはどのようにしてパラメータ値をリモート関数に渡しますか?ローカル呼び出しでは、パラメーターをスタックにプッシュし、関数にスタックから読み取らせるだけです。ただし、リモートプロシージャコールでは、クライアントとサーバーは異なるプロセスであり、パラメータをメモリに渡すことはできません。クライアントとサーバーが同じ言語を使用していない場合もあります(たとえば、サーバーはC ++を使用し、クライアントはJavaまたはPythonを使用します)。このとき、クライアントは最初にパラメータをバイトストリームに変換してから、バイトストリームを自分で読み取れる形式に転送する必要があります。このプロセスは、シリアル化および逆シリアル化と呼ばれます。同様に、サーバーから返される値もシリアル化および逆シリアル化する必要があります。
- ネットワーク伝送。ネットワーク上ではリモートコールがよく使用され、クライアントとサーバーはネットワークを介して接続されます。すべてのデータはネットワークを介して送信する必要があるため、ネットワーク送信層が必要です。ネットワークトランスポート層は、呼び出しIDとシリアル化されたパラメーターバイトをサーバーにストリーミングしてから、シリアル化された呼び出し結果をクライアントに送り返す必要があります。両方を実現できる限り、トランスポート層として使用できます。したがって、送信が完了できる限り、使用するプロトコルは実際には無制限です。ほとんどのRPCフレームワークはTCPプロトコルを使用しますが、UDPも使用できますが、gRPCは単にHTTP2を使用します。JavaのNettyもこのレイヤーに属しています。
// Client端
// Student student = Call(ServerAddr, addAge, student)
4. 将这个调用映射为Call ID。
5. 将Call ID,student(params)序列化,以二进制形式打包
6. 把2中得到的数据包发送给ServerAddr,这需要使用网络传输层
7. 等待服务器返回结果
8. 如果服务器调用成功,那么就将结果反序列化,并赋给student,年龄更新
// Server端
9. 在本地维护一个Call ID到函数指针的映射call_id_map,可以用Map<String, Method> callIdMap
10. 等待服务端请求
11. 得到一个请求后,将其数据包反序列化,得到Call ID
12. 通过在callIdMap中查找,得到相应的函数指针
13. 将student(params)反序列化后,在本地调用study()函数,得到结果
14. 将student结果序列化后通过网络返回给Client
RPC通信を実装するためのコード
最初にディレクトリ構造を見てください
- order-apiは、マイクロサービス間の共通のインターフェース定義です。
- order-provideは注文システムのマイクロサービスです
- user-serviceは、ユーザーシステムのマイクロサービスです
ここで、ユーザーサービスは注文サービスのメソッドを呼び出す必要があります。コードは次のように示しています。
パブリックインターフェイス
IOrderService
public interface IOrderService {
String queryOrderList();
String orderById(String id);
}
RpcRequest
package com.xhc.example;
import java.io.Serializable;
public class RpcRequest implements Serializable{
private String className;
private String methodName;
private Object[] args;
private Class[] types;
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String getMethodName() {
return methodName;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public Object[] getArgs() {
return args;
}
public void setArgs(Object[] args) {
this.args = args;
}
public Class[] getTypes() {
return types;
}
public void setTypes(Class[] types) {
this.types = types;
}
}
オーダーセンター
ブートストラップ
package com.xhc.example;
public class Bootstrap {
public static void main(String[] args) {
//SPRING BOOT
IOrderService orderService=new OrderServiceImpl();
RpcProxyServer rpcProxyServer=new RpcProxyServer();
rpcProxyServer.publisher(orderService,8080);
}
}
OrderServiceImpl
package com.xhc.example;
public class OrderServiceImpl implements IOrderService{
@Override
public String queryOrderList() {
return "我是 queryOrderList 方法,是的没错!";
}
@Override
public String orderById(String id) {
return "我是 orderById 方法,是的没错!";
}
}
ProcessorHandler
package com.xhc.example;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.Socket;
public class ProcessorHandler implements Runnable{
private Socket socket;
private Object service;
public ProcessorHandler(Socket socket, Object service) {
this.socket = socket;
this.service = service;
}
@Override
public void run() {
ObjectInputStream inputStream=null;
ObjectOutputStream outputStream=null;
try {
inputStream=new ObjectInputStream(socket.getInputStream());//?
RpcRequest request=(RpcRequest)inputStream.readObject(); //反序列化
Object rs=invoke(request);
System.out.println("服务端的执行结果:"+rs);
outputStream=new ObjectOutputStream(socket.getOutputStream());
outputStream.writeObject(rs);
outputStream.flush();
} catch (Exception e) {
e.printStackTrace();
}finally {
//TODO 关闭流
if(inputStream!=null){
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (outputStream!=null){
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private Object invoke(RpcRequest request) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
//通过反射进行服务的调用
Class clazz=Class.forName(request.getClassName());
//找到目标方法
Method method=clazz.getMethod(request.getMethodName(),request.getTypes());
return method.invoke(service,request.getArgs());
}
}
RpcProxyServer
package com.xhc.example;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class RpcProxyServer {
private final ExecutorService executorService= Executors.newCachedThreadPool();
public void publisher(Object service,int port){
ServerSocket serverSocket=null;
try {
serverSocket=new ServerSocket(port);
while(true){
Socket socket=serverSocket.accept(); //监听客户端请求
executorService.execute(new ProcessorHandler(socket,service));
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if(serverSocket!=null){
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
ユーザーセンター
アプリ
package com.xhc.example.rcp;
import com.xhc.example.IOrderService;
/**
* Hello world!
*
*/
public class App
{
public static void main( String[] args ){
RpcProxyClient rpcProxyClient=new RpcProxyClient();
IOrderService orderService=rpcProxyClient.clientProxy(IOrderService.class,"localhost",8080);
System.out.println(orderService.queryOrderList());
System.out.println(orderService.orderById("Mic"));
}
}
RemoteInvocationHandler
package com.xhc.example.rcp;
import com.xhc.example.RpcRequest;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class RemoteInvocationHandler implements InvocationHandler{
private String host;
private int port;
public RemoteInvocationHandler(String host, int port) {
this.host = host;
this.port = port;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//先建立远程连接
RpcNetTransport rpcNetTransport=new RpcNetTransport(host,port);
//传递数据了?
// 调用哪个接口、 哪个方法、方法的参数?
RpcRequest request=new RpcRequest();
request.setArgs(args);
request.setClassName(method.getDeclaringClass().getName());
request.setTypes(method.getParameterTypes()); //参数的类型
request.setMethodName(method.getName());
return rpcNetTransport.send(request);
}
}
RpcNetTransport
package com.xhc.example.rcp;
import com.xhc.example.RpcRequest;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
public class RpcNetTransport {
private String host;
private int port;
public RpcNetTransport(String host, int port) {
this.host = host;
this.port = port;
}
public Socket newSocket() throws IOException {
Socket socket=new Socket(host,port);
return socket;
}
public Object send(RpcRequest request){
ObjectOutputStream outputStream=null;
ObjectInputStream inputStream=null;
try {
Socket socket=newSocket();
//IO操作
outputStream=new ObjectOutputStream(socket.getOutputStream());
outputStream.writeObject(request);
outputStream.flush();
inputStream=new ObjectInputStream(socket.getInputStream());
return inputStream.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
}
return null;
}
}
RpcProxyClient
package com.xhc.example.rcp;
import java.lang.reflect.Proxy;
public class RpcProxyClient {
public <T> T clientProxy(final Class<T> interfaceCls,final String host,final int port){
return (T) Proxy.newProxyInstance(interfaceCls.getClassLoader(), new Class<?>[]{
interfaceCls},new RemoteInvocationHandler(host,port));
}
}