Ir al registro de aprendizaje de RPC de idiomas

Ir al registro de aprendizaje de RPC de idiomas

Concepto de RPC

RPC (Protocolo de llamada a procedimiento remoto) , es la abreviatura de llamada a procedimiento remoto, en términos simples, es llamar a una función remota. Correspondiente a ella está la llamada a la función local, primero echemos un vistazo a la llamada a la función local. Cuando escribimos el siguiente código:
reglas

result := Add(1,2)

Sabemos que pasamos los dos parámetros 1, 2 y llamamos a una función Agregar en el código local para obtener el valor de retorno del resultado. En este momento, los parámetros, el valor de retorno y el segmento de código están todos en un espacio de proceso, que es una llamada de función local.

¿Hay alguna forma de llamar a un proceso cruzado (llamado "remoto", un ejemplo típico, este proceso se implementa en otro servidor)?

Esta es también la función principal de RPC.

¿Por qué los microservicios necesitan RPC?

Una de las ventajas de nuestro uso de microservicios es que no limitamos la selección de tecnología utilizada por el proveedor de servicios y podemos lograr el desacoplamiento tecnológico entre los equipos de la empresa.

En este caso, si no hay un marco de servicio unificado y un marco RPC , los proveedores de servicios de cada equipo deben implementar un conjunto de serialización, deserialización, marco de red, grupo de conexiones, subprocesos de envío y recepción, procesamiento de tiempo de espera, máquina de estado, etc. La mano de obra técnica duplicada fuera de China ha provocado una ineficiencia generalizada. Por lo tanto, el marco unificado de RPC para hacer frente a la mano de obra técnica "fuera del negocio" antes mencionada es el problema principal que debe resolverse en el servicio.

Versión RPC de "hello world"

La ruta del paquete RPC del lenguaje Go es net / rpc, que se encuentra en el directorio del paquete net. Por lo tanto, podemos suponer que el paquete RPC se basa en el paquete net. Luego intentamos implementar un ejemplo similar basado en rpc. Primero construimos un tipo HelloService, donde el método Hello se usa para implementar la función de impresión:

type HelloService struct{}
func(p *HelloService)Hello(request string,reply *string)error{
*reply = "hello:" + request
return  nil
}

El método del método Hello debe cumplir con las reglas RPC del lenguaje Go: el método solo puede tener dos parámetros serializables, de los cuales el segundo parámetro es un tipo de puntero y devuelve un tipo de error, y debe ser un método público.

Los tipos en golang como: canal (canal), complejo (tipo complejo), func (función) no se pueden serializar

Luego, puede registrar un objeto de tipo HelloService como un servicio RPC:

func main(){
    //rpc注册服务  
    //注册rpc服务,维护一个hash表,key值是服务名称,value值是服务的地址
    rpc.RegisterName("HelloService",new(HelloService))

    //设置服务监听
    listener,err := net.Listen("tcp",":1234")
    if err != nil {
        panic(err)
    }

    //接受传输的数据
    conn,err := listener.Accept()
    if err != nil {
        panic(err)
    }

    //rpc调用,并返回执行后的数据
    //1.read,获取服务名称和方法名,获取请求数据
    //2.调用对应服务里面的方法,获取传出数据
    //3.write,把数据返回给client
    rpc.ServeConn(conn)

}

La llamada a la función rpc.Register registrará todos los métodos de objeto en el tipo de objeto que cumplen las reglas de RPC como funciones de RPC, y todos los métodos registrados se colocarán en el espacio de servicio "HelloService". Luego, establecemos un enlace TCP único y proporcionamos servicios RPC para la otra parte en el enlace TCP a través de la función rpc.ServeConn.

El siguiente es el código para que el cliente solicite el servicio HelloService:

func main(){
    //用rpc连接
    client,err := rpc.Dial("tcp","localhost:1234")
    if err != nil {
        panic(err)
    }

    var reply string
    //调用服务中的函数
    err = client.Call("HelloService.Hello","world",&reply)
    if err != nil {
        panic(err)
    }

    fmt.Println("收到的数据为,",reply)
}

La primera opción es marcar el servicio RPC a través de rpc.Dial y luego llamar al método RPC específico a través de client.Call. Al llamar a client.Call, el primer parámetro es el nombre del servicio RPC y el nombre del método enlazados por puntos, y el segundo y tercer parámetros definen respectivamente los dos parámetros del método RPC.

RPC en varios idiomas

El RPC de la biblioteca estándar utiliza codificación gob exclusiva del idioma Go de forma predeterminada. Por lo tanto, será más difícil para otros idiomas llamar a los servicios RPC implementados en el idioma Go. El lenguaje cruzado es un requisito previo para RPC en la era de Internet. Aquí implementaremos un RPC entre lenguajes. Gracias al diseño del marco de trabajo RPC, RPC en el lenguaje Go es realmente muy fácil de implementar con soporte entre idiomas.

Aquí intentaremos implementar un RPC en varios idiomas a través de la extensión oficial net / rpc / jsonrpc.

El primero es volver a implementar el servicio RPC basado en la codificación json:

func main(){
    //注册rpc服务
    rpc.RegisterName("HelloService",new(HelloService))
    //设置监听
    listener,err := net.Listen("tcp",":1234")
    if err != nil {
        panic(err)
    }

    for{
        //接收连接
        conn,err := listener.Accept()
        if err != nil {
            panic(err)
        }
        //给当前连接提供针对json格式的rpc服务
        go rpc.ServeCodec(jsonrpc.NewServerCodec(conn))
    }
}

El mayor cambio en el código es reemplazar la función rpc.ServeConn con la función rpc.ServeCodec. Los parámetros pasados ​​son para el códec json en el lado del servidor.

Luego está el cliente que implementa la versión json:

func main(){
    //简历tcp连接
    conn,err := net.Dial("tcp","localhost:1234")
    if err !=nil{
        panic(err)
    }
    //简历基于json编解码的rpc服务
    client := rpc.NewClientWithCodec(jsonrpc.NewClientCodec(conn))

    var reply string
    //调用rpc服务方法
    err = client.Call("HelloService.Hello"," world",&reply)
    if err != nil {
        panic(err)
    }

    fmt.Println("收到的数据为:",reply)
}

Primero, llame manualmente a la función net.Dial para establecer un enlace TCP y luego establezca un códec json para el cliente basado en el enlace.

Después de asegurarnos de que el cliente normalmente puede llamar al método de servicio RPC, podemos usar comandos para verificar qué datos envía el cliente al servidor. Aquí usamos == nc -l 1234 == Este comando simula que el servidor monitorea los datos recibidos en el puerto 1234, y luego vuelve a ejecutar el cliente, encontrará que nc ha generado la siguiente información:

{"method":"HelloService.Hello","params":["hello"],"id":0}

Hay dos nc de uso común, uno es para conectarse a la ip y al puerto especificados

puerto de nombre de host nc

La otra es escuchar en el puerto y esperar la conexión.

puerto nc -l

Estos son datos codificados en json, donde la parte del método corresponde al nombre del servicio rpc y al método que se va a llamar, el primer elemento de la parte params es un parámetro y el id es un número de llamada único mantenido por la persona que llama.

El objeto de datos json solicitado corresponde a dos estructuras internas: el cliente es clientRequest y el servidor es serverRequest. Los contenidos de las estructuras clientRequest y serverRequest son básicamente los mismos:

type clientRequest struct {
    Method string         `json:"method"`
    Params []interface{}  `json:"params"`
    Id     uint64         `json:"id"`
}
type serverRequest struct {
    Method string           `json:"method"`
    Params *json.RawMessage `json:"params"`
    Id     *json.RawMessage `json:"id"`
}

Después de comprender qué datos necesita enviar el cliente, podemos ver qué datos devolverá el servidor después de recibir los datos transmitidos por el cliente, o usar nuestro comando nc. El funcionamiento es el siguiente:

echo -e '{"method":"HelloService.Hello","params":["hello"],"id":1}'| nc localhost 1234

Los datos devueltos son los siguientes:

Ir al registro de aprendizaje de RPC de idiomas

La identificación corresponde al parámetro de identificación de entrada, el resultado es el resultado devuelto y la parte de error indica información de error cuando algo sale mal. Para llamadas secuenciales, id no es necesario. Sin embargo, el marco RPC del lenguaje Go admite llamadas asincrónicas. Cuando el orden de los resultados devueltos no es coherente con el orden de las llamadas, la llamada correspondiente se puede identificar mediante id.

Los datos json devueltos también corresponden a las dos estructuras internas: el cliente es clientResponse y el servidor es serverResponse. Los contenidos de las dos estructuras también son similares:

type clientResponse struct {
    Id     uint64           `json:"id"`
    Result *json.RawMessage `json:"result"`
    Error  interface{}      `json:"error"`
}
type serverResponse struct {
    Id     *json.RawMessage `json:"id"`
    Result interface{}      `json:"result"`
    Error  interface{}      `json:"error"`
}

Por lo tanto, no importa qué idioma se use, siempre que siga la misma estructura json y el mismo proceso, puede comunicarse con el servicio RPC escrito en el idioma Go. De esta manera, simplemente podemos implementar RPC en varios idiomas con json.

Sin embargo, además de utilizar json para servicios RPC en varios idiomas durante el desarrollo, muchas empresas ahora eligen protobuf para servicios RPC en varios idiomas. Entonces, ¿qué es ProtoBuf ? A continuación, echemos un vistazo más de cerca .

Paquete de protocolo RPC

El nombre del servicio del código anterior está codificado y no es lo suficientemente flexible (es fácil escribir mal). Aquí encapsulamos el servidor y el cliente de RPC nuevamente para bloquear el nombre del servicio. El código específico es el siguiente

Encapsulación del lado del servidor
//抽离服务名称
var serverName = "LoginService"

//定义一个父类
type RPCDesign interface {
    Hello(string,*string)error
}

//实现工厂函数
func RegisterRPCServer(srv RPCDesign)error{
    return rpc.RegisterName(serverName,srv)
}

El servidor encapsulado se implementa de la siguiente manera:

type RpcServer struct{}

//5 + 3i    chan   func    complex
func (this *RpcServer) Hello(req string, resp *string) error {
    *resp += req + "你好"
    return nil
}

func main() {
    //设置监听
    listener, err := net.Listen("tcp", ":8899")
    if err != nil {
        fmt.Println("设置监听错误")
        return
    }
    defer listener.Close()

    fmt.Println("开始监听....")
    for {
        //接收链接
        conn, err := listener.Accept()
        if err != nil {
            fmt.Println("获取连接失败")
            return
        }
        defer conn.Close()

        fmt.Println(conn.RemoteAddr().String() + "连接成功")

        //rpc表  注册rpc服务

        if err = RegisterRPCServer(new(RpcServer)); err != nil {
            fmt.Println("注册rpc服务失败")
            return
        }

        //把rpc服务和套接字绑定
        //rpc.ServeConn(conn)
        rpc.ServeCodec(jsonrpc.NewServerCodec(conn))
    }

}
Paquete de cliente
type RPCClient struct {
    rpcClient *rpc.Client
}

func NewRpcClient(addr string)(RPCClient){
    conn,err := net.Dial("tcp",addr)
    if err != nil {
        fmt.Println("链接服务器失败")
        return RPCClient{}
    }
    defer conn.Close()

    //套接字和rpc服务绑定
    client := rpc.NewClientWithCodec(jsonrpc.NewClientCodec(conn))
    return RPCClient{rpcClient:client}
}

func (this*RPCClient)CallFunc(req string,resp*string)error{
    return this.rpcClient.Call(serverName+".Hello",req,resp)
}

Implementación del cliente después de la encapsulación

func main()  {
    //初始化对象  与服务名有关的内容完全封装起来了
    client := NewRpcClient("127.0.0.1:8899")

    //调用成员函数
    var temp string
    client.CallFunc("xiaoming",&temp)

    fmt.Println(temp)
}

Supongo que te gusta

Origin blog.51cto.com/13380838/2674839
Recomendado
Clasificación