RPC-远程调用_IDL

基础概念

RPC跨越了网络层和传输层,只要有数据连接就可以,比如spi和pcie

RPC是定义和实现相分离的设计,

采用客户端(服务调用方)和服务器端(服务提供方)模式

两方各自独立运行。

客户端只需引入要使用发接口,

接口的实现和运行在服务端。

RPC依赖的技术包括:序列化,反序列化,数据传输协议。

RPC主要是内部服务之间的调用

RPC解决方案是RMI,Hessian,Dubbo(阿里巴巴)

服务发现上:

HTTP建立连接要IP地址和端口,域名DNS解析拿到IP连接

RPC的服务自理是通过中间服务(分布式微服务的注册中心)去保存中间信息,比如: consul 和 etcd,nacos

底层连接:

HTTP建立底层TCP连接后,会保持keep-alive,后面的请求和响应都会附用

RPC通过建立TCP的长连接交互,它会建立一个连接池

发数据的时候,从连接池拿取一条连接出来,使用完在放回去。

RPC在传输数据时会采用体积更小的protobuf

GRPC底层用的http2

LDL(接口描述语言”,中立语言,能使软件组件(不同语言编写的)间相互通信)

通常用于 CORBA 或其他分布式系统中,提供跨平台的通信机制,使不同编程语言和系统能够互相交流。通过定义接口,IDL允许开发者指定调用远程对象的方法和传递数据。

一个IDL文件通常由多个接口定义组成,通常以.IDL为文件扩展名

module MyModule {  
    interface MyInterface {  
        void myMethod(in string param1, out long param2);  
    };  
};
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
module Example {  
    enum Color {  
        RED,  
        GREEN,  
        BLUE  
    };  

    struct Point {  
        long x;  
        long y;  
    };  

    interface Shape {  
        void setColor(in Color c);  
        Color getColor();  
        void setPosition(in Point p);  
    };  
};
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.

调用LDL要用编程语言及其IDL编译器,将你的IDL文件编译成编程代码

RPC完整流程

桩文件→序列化→TCP

1.定义IDL件,知道payment的service有哪些方法可以调用

具体的IDL实现:在protobuf(序列化协议)(bp)文件定义好每个接口,请求参数和返回值

编译工具把BP生成stub桩文件,相当于生成了静态库,通过静态代理的方式实现函数映射问题

整个过程屏蔽了网络层。

网络里传输的数据都是二进制数据, 需要把请求参数、返回结果进行序列化和反序列化

序列化是把结构体或对象转化成二进制数据流,反序列化相反。

序列化字节流通过TCP等协议封装,在网络传输数据

根据RPC协议约定数据头、元数据、消息体等,保证有ID能使请求和返回结果做到一一映射 基于成熟的网络库进行TCP/UDP传输

RPC开发

python-http

import requests  

url = 'https://api.example.com/data'  
response = requests.get(url)  

if response.status_code == 200:  
    print('响应内容:', response.json())  
else:  
    print('请求失败,状态码:', response.status_code)
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

go-http

client := http.Client{ CheckRedirect: redirectPolicyFunc, }
创建一个新的 HTTP 客户端实例 client,并设置重定向策略为 redirectPolicyFunc。

resp, err := client.Get("http://example.com")
使用 client 发送一个 GET 请求到 http://example.com,并将响应存储在 resp 中,错误信息存储在 err 中。

req, err := http.NewRequest("GET", "http://example.com")
创建一个新的 HTTP 请求 req,请求方法为 GET,目标 URL 

req.Header.Add("If-None-Match", "W/\"zyy\"")
向请求头中添加一个名为 If-None-Match 的字段,其值为 W/"zyy",通常用于缓存控制。

resp, err = client.Do(req)
使用 client 执行之前创建的请求 req,并将响应存储在 resp 中,错误信息存储在 err 中。
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.

RPC order模块注册一个payment的client,client.pay()建立一个请求RPC连接, 实现远程调用,在order的代码块里,看上去就像是在调用本地方法

go-rpc-client

package main  

import (  
    "context"  
    "fmt"  
    "log"  
    "time"  

    "google.golang.org/grpc"  
    pb "path/to/your/proto/package" // 替换为你的 proto 包路径  
)  

func main() {  
    // 连接到服务器  
    addr := "localhost:50051" // 替换为你的服务器地址  
    conn, err := grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials()))  //通过 grpc.Dial 函数连接到指定的地址 addr,使用传输凭据
    if err != nil {  
        log.Fatalf("did not connect: %v", err)  
    }  
    defer conn.Close()  

    // 创建客户端 
    c := pb.NewGreeterClient(conn)  

    // 设置上下文和超时  
    ctx, cancel := context.WithTimeout(context.Background(), time.Second)  
    defer cancel()  

    // 发送请求并打印响应  
    name := "world" // 替换为你想要的名字  
    r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name})  
    if err != nil {  
        log.Fatalf("could not greet: %v", err)  
    }   
    fmt.Printf("Greeting: %s\n", r.GetMessage())  
}//印出服务器返回的消息。
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.