Skip to content

Latest commit

 

History

History
401 lines (300 loc) · 10.4 KB

File metadata and controls

401 lines (300 loc) · 10.4 KB

8.4 RPC

En las secciones anteriores hemos hablado de cómo escribir aplicaciones de red basado en sockets y HTTP, nos enteramos de que dos de ellos están utilizando el modelo "intercambio de información", que los clientes envían peticiones y servidores respuesta. Este tipo de intercambio de datos se basan en determinado formato para que ambas partes son capaces de entender. Sin embargo, muchas aplicaciones de independencia no utilizan este modelo, pero llaman a los servicios al igual que las funciones normales de llamada.

RPC se pretende lograr la puesta en red de modo de llamada de función. Clientes como llamar a las funciones nativas y, a continuación, envasados ​​estos parámetros después de pasar a través de la red al servidor, la ejecución del proceso sin envasar servidor, y ejecuta los resultados de vuelta al cliente.

En informática, una llamada a procedimiento remoto (RPC) es una comunicación entre procesos que permite que un programa de ordenador para hacer una subrutina o procedimiento para ejecutar en otro espacio de direcciones (comúnmente en otro ordenador en una red compartida) sin que el programador codificar explícitamente la detalles sobre la interacción a distancia. Es decir, el programador escribe esencialmente el mismo código si la subrutina es local para el programa en ejecución, o remoto. Cuando el software en cuestión utiliza los principios orientados a objetos, RPC se llama invocación remota o invocación de métodos remotos.

Principios del funcionamiento RPC

Figure 8.8 RPC working principle.

Normalmente, una llamada RPC desde el cliente al servidor tiene los siguientes diez pasos:

    1. Llama al manejador del cliente, ejecutar argumentos de transferencia.
    1. Llama núcleo del sistema local para enviar mensajes de red.
    1. Enviar mensajes a hosts remotos.
    1. El servidor recibe controladores y argumentos.
    1. Ejecutar procesos remotos.
    1. Vuelva ejecutar resultado al controlador correspondiente.
    1. El controlador del servidor llama núcleo del sistema remoto.
    1. Los mensajes enviados a kernel del sistema local.
    1. El controlador del cliente recibe los mensajes de kernel del sistema.
    1. El cliente recibe los resultados del controlador correspondiente.

Go RPC

Go tiene soporte oficial para RPC en la biblioteca estándar de tres niveles, que son TCP, HTTP y JSON RPC. Tenga en cuenta que Go RPC no es como otros sistemas RPC tradicionales, se requiere el uso de aplicaciones de Go en ambos lados de los clientes y los servidores, ya que codifica el contenido a través de Gob.

Funciones del Go RPC tienen que seguir las siguientes reglas para el acceso remoto, lo que corresponde por lo demás se ignorarán las llamadas.

  • Las funciones se exportan(capitalize).
  • Funciones deben tener dos argumentos con tipos exportados.
  • El primer argumento es para la recepción por parte del cliente, y la segunda tiene que ser un tipo de puntero y es para responder al cliente.
  • Funciones deben tener un valor de retorno de tipo de error.

For example:

func (t *T) MethodName(argType T1, replyType *T2) error

Donde T, T1 y T2 deben ser capaces de codificado por paquete encoding/gob.

Cualquier tipo de RPC tiene que a través de la red para transferir datos, Go RPC puede utilizar HTTP o TCP, los beneficios del uso de HTTP es que se puede volver a utilizar alguna función en el paquete net/http.

HTTP RPC

HTTP server side code:

package main

import (
	"errors"
	"fmt"
	"net/http"
	"net/rpc"
)

type Args struct {
	A, B int
}

type Quotient struct {
	Quo, Rem int
}

type Arith int

func (t *Arith) Multiply(args *Args, reply *int) error {
	*reply = args.A * args.B
	return nil
}

func (t *Arith) Divide(args *Args, quo *Quotient) error {
	if args.B == 0 {
		return errors.New("divide by zero")
	}
	quo.Quo = args.A / args.B
	quo.Rem = args.A % args.B
	return nil
}

func main() {

	arith := new(Arith)
	rpc.Register(arith)
	rpc.HandleHTTP()

	err := http.ListenAndServe(":1234", nil)
	if err != nil {
		fmt.Println(err.Error())
	}
}

Registramos un servicio RPC de Arith, entonces registrado en este servicio HTTP a través rpc.HandleHTTP . Después de eso, somos capaces de transferir datos a través de HTTP.

Client side code:

package main

import (
	"fmt"
	"log"
	"net/rpc"
	"os"
)

type Args struct {
	A, B int
}

type Quotient struct {
	Quo, Rem int
}


func main() {
	if len(os.Args) != 2 {
		fmt.Println("Usage: ", os.Args[0], "server")
		os.Exit(1)
	}
	serverAddress := os.Args[1]

	client, err := rpc.DialHTTP("tcp", serverAddress+":1234")
	if err != nil {
		log.Fatal("dialing:", err)
	}
	// Synchronous call
	args := Args{17, 8}
	var reply int
	err = client.Call("Arith.Multiply", args, &reply)
	if err != nil {
		log.Fatal("arith error:", err)
	}
	fmt.Printf("Arith: %d*%d=%d\n", args.A, args.B, reply)

	var quot Quotient
	err = client.Call("Arith.Divide", args, &quot)
	if err != nil {
		log.Fatal("arith error:", err)
	}
	fmt.Printf("Arith: %d/%d=%d remainder %d\n", args.A, args.B, quot.Quo, quot.Rem)

}

Compilamos el cliente y el código del lado del servidor por separado, iniciar el servidor e iniciar el cliente, entonces tendrás algo similar de la siguiente manera después de ingresar algunos datos.

$ ./http_c localhost
Arith: 17*8=136
Arith: 17/8=2 remainder 1

Como puede ver, hemos definido una estructura de tipo de retorno, lo usamos como el tipo de argumento de la función en el lado del servidor, y utilizar como el tipo de los argumentos segundo y tercero en el cliente client.Call . Esta llamada es muy importante, tiene tres argumentos, donde el primero el nombre de la función que se va a llamar, y el segundo es el argumento que desea pasar, el último es el valor de retorno (tipo de puntero). Hasta ahora vemos que es fácil de implementar RPC en Go.

TCP RPC

Vamos a tratar de la RPC que se basa en TCP, aquí está el código del lado del Server:

package main

import (
	"errors"
	"fmt"
	"net"
	"net/rpc"
	"os"
)

type Args struct {
	A, B int
}

type Quotient struct {
	Quo, Rem int
}

type Arith int

func (t *Arith) Multiply(args *Args, reply *int) error {
	*reply = args.A * args.B
	return nil
}

func (t *Arith) Divide(args *Args, quo *Quotient) error {
	if args.B == 0 {
		return errors.New("divide by zero")
	}
	quo.Quo = args.A / args.B
	quo.Rem = args.A % args.B
	return nil
}

func main() {

	arith := new(Arith)
	rpc.Register(arith)

	tcpAddr, err := net.ResolveTCPAddr("tcp", ":1234")
	checkError(err)

	listener, err := net.ListenTCP("tcp", tcpAddr)
	checkError(err)

	for {
		conn, err := listener.Accept()
		if err != nil {
			continue
		}
		rpc.ServeConn(conn)
	}

}

func checkError(err error) {
	if err != nil {
		fmt.Println("Fatal error ", err.Error())
		os.Exit(1)
	}
}

La diferencia entre RPC HTTP y TCP RPC es que tenemos para controlar las conexiones por nosotros mismos si utilizamos RPC TCP, a continuación, pasar a las conexiones RPC para su procesamiento.

Como se puede adivinar, se trata de una aplicación de patrón de bloqueo, usted es libre de utilizar goroutine extender esta aplicación para el experimento más avanzado.

El código del lado del cliente:

package main

import (
	"fmt"
	"log"
	"net/rpc"
	"os"
)

type Args struct {
	A, B int
}

type Quotient struct {
	Quo, Rem int
}

func main() {
	if len(os.Args) != 2 {
		fmt.Println("Usage: ", os.Args[0], "server:port")
		os.Exit(1)
	}
	service := os.Args[1]

	client, err := rpc.Dial("tcp", service)
	if err != nil {
		log.Fatal("dialing:", err)
	}
	// Synchronous call
	args := Args{17, 8}
	var reply int
	err = client.Call("Arith.Multiply", args, &reply)
	if err != nil {
		log.Fatal("arith error:", err)
	}
	fmt.Printf("Arith: %d*%d=%d\n", args.A, args.B, reply)

	var quot Quotient
	err = client.Call("Arith.Divide", args, &quot)
	if err != nil {
		log.Fatal("arith error:", err)
	}
	fmt.Printf("Arith: %d/%d=%d remainder %d\n", args.A, args.B, quot.Quo, quot.Rem)

}

La única diferencia en el código del lado del cliente es que el cliente HTTP utiliza DialHTTP donde el cliente TCP utiliza marcación (TCP).

JSON RPC

JSON RPC codifica los datos de JSON en lugar de gob, vamos a ver un ejemplo de Go JSON ejemplo de código en el servidor RPC:

package main

import (
	"errors"
	"fmt"
	"net"
	"net/rpc"
	"net/rpc/jsonrpc"
	"os"
)

type Args struct {
	A, B int
}

type Quotient struct {
	Quo, Rem int
}

type Arith int

func (t *Arith) Multiply(args *Args, reply *int) error {
	*reply = args.A * args.B
	return nil
}

func (t *Arith) Divide(args *Args, quo *Quotient) error {
	if args.B == 0 {
		return errors.New("divide by zero")
	}
	quo.Quo = args.A / args.B
	quo.Rem = args.A % args.B
	return nil
}

func main() {

	arith := new(Arith)
	rpc.Register(arith)

	tcpAddr, err := net.ResolveTCPAddr("tcp", ":1234")
	checkError(err)

	listener, err := net.ListenTCP("tcp", tcpAddr)
	checkError(err)

	for {
		conn, err := listener.Accept()
		if err != nil {
			continue
		}
		jsonrpc.ServeConn(conn)
	}

}

func checkError(err error) {
	if err != nil {
		fmt.Println("Fatal error ", err.Error())
		os.Exit(1)
	}
}

JSON RPC is based on TCP, it hasn't support HTTP yet.

The client side code:

package main

import (
	"fmt"
	"log"
	"net/rpc/jsonrpc"
	"os"
)

type Args struct {
	A, B int
}

type Quotient struct {
	Quo, Rem int
}

func main() {
	if len(os.Args) != 2 {
		fmt.Println("Usage: ", os.Args[0], "server:port")
		log.Fatal(1)
	}
	service := os.Args[1]

	client, err := jsonrpc.Dial("tcp", service)
	if err != nil {
		log.Fatal("dialing:", err)
	}
	// Synchronous call
	args := Args{17, 8}
	var reply int
	err = client.Call("Arith.Multiply", args, &reply)
	if err != nil {
		log.Fatal("arith error:", err)
	}
	fmt.Printf("Arith: %d*%d=%d\n", args.A, args.B, reply)

	var quot Quotient
	err = client.Call("Arith.Divide", args, &quot)
	if err != nil {
		log.Fatal("arith error:", err)
	}
	fmt.Printf("Arith: %d/%d=%d remainder %d\n", args.A, args.B, quot.Quo, quot.Rem)

}

Resumen

Go tiene un buen soporte de HTTP, TPC, implementación RPC JSON, podemos desarrollar fácilmente aplicaciones web distribuidas; Sin embargo, es lamentable que Go no tiene soporte para SOAP RPC que algunos paquetes de terceros lo hicieron en código abierto.

Links