• go笔记--rpc和grpc使用


    go笔记--rpc和grpc使用


    rpc

    RPC(Remote Procedure Call,远程过程调用)是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络细节的应用程序通信协议。RPC协议构建于TCP或UDP,或者是HTTP上。允许开发者直接调用另一台服务器上的程序,而开发者无需另外的为这个调用过程编写网络通信相关代码,使得开发网络分布式程序在内的应用程序更加容易
    RPC采用客户端-服务器端的工作模式,请求程序就是一个客户端,而服务提供程序就是一个服务器端。当执行一个远程过程调用时,客户端程序首先先发送一个带有参数的调用信息到服务端,然后等待服务端响应。在服务端,服务进程保持睡眠状态直到客户端的调用信息到达。当一个调用信息到达时,服务端获得进程参数,计算出结果,并向客户端发送应答信息。然后等待下一个调用。

    在Go中,标准库提供的net/rpc包提供了通过网络或其他I/O连接对一个对象的导出方法的访问。服务端注册一个对象,使它作为一个服务被暴露,服务的名字是该对象的类型名。注册之后,对象的导出方法就可以被远程访问。服务端可以注册多个不同类型的对象(服务),但注册具有相同类型的多个对象是错误的。
    只有满足如下标准的方法才能用于远程访问,其余方法会被忽略:

    --

    • 方法是导出的
    • 方法有两个参数,都是导出类型或内建类型
    • 方法的第二个参数是指针
    • 方法只有一个error接口类型的返回值

    也就是说,方法必须看起来像这样:

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

    其中T、T1和T2都能被encoding/gob包序列化。这些限制即使使用不同的编解码器也适用。(未来,对定制的编解码器可能会使用较宽松一点的限制)
    方法的第一个参数代表调用者提供的参数;第二个参数代表返回给调用者的参数。方法的返回值,如果非nil,将被作为字符串回传,在客户端看来就和errors.New创建的一样。如果返回了错误,回复的参数将不会被发送给客户端。
    服务端可能会单个连接上调用ServeConn管理请求。更典型地,它会创建一个网络监听器然后调用Accept;或者,对于HTTP监听器,调用HandleHTTP和http.Serve。
    想要使用服务的客户端会创建一个连接,然后用该连接调用NewClient。
    更方便的函数Dial(DialHTTP)会在一个原始的连接(或HTTP连接)上依次执行这两个步骤。
    生成的Client类型值有两个方法,Call和Go,它们的参数为要调用的服务和方法、一个包含参数的指针、一个用于接收接个的指针。
    Call方法会等待远端调用完成,而Go方法异步的发送调用请求并使用返回的Call结构体类型的Done通道字段传递完成信号。
    除非设置了显式的编解码器,本包默认使用encoding/gob包来传输数据。

    一个简单的例子。一个服务端想要导出Arith类型的一个对象:

    server.go

    package main
    
    import (
        //"fmt"
        "log"
        "net"
        "net/http"
        "net/rpc"
        //"os"
    	//"time"
    	"errors"
    )
    
    
    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()
    
    	l, e := net.Listen("tcp", ":1234")
    
    	if e != nil {
    		log.Fatal("listen error:", e)
    	}
    	
    	http.Serve(l, nil)
    
    }
    

    client.go (sync)

    package main
    
    import (
        "fmt"
        "log"
        "net/rpc"
        //"time"
    )
    
    type Args struct {
        A, B int
    }
    
    func main() {
    
    	client, err := rpc.DialHTTP("tcp", "127.0.0.1:1234")
    	if err != nil {
    		log.Fatal("dialing:", err)
    	}
    
    	// Synchronous call
    	args := &Args{7,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", args.A, args.B, reply)
    
    }
    

    client.go (async)

    package main
    
    import (
        "fmt"
        "log"
        "net/rpc"
        //"time"
    )
    
    type Args struct {
        A, B int
    }
    
    type Quotient struct {
    	Quo, Rem int
    }
    
    func main() {
    
    	client, err := rpc.DialHTTP("tcp", "127.0.0.1:1234")
    	if err != nil {
    		log.Fatal("dialing:", err)
    	}
    
    	// Synchronous call
    	/*args := &Args{7,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", args.A, args.B, reply)*/
    
    	// Asynchronous call
    	args := &Args{10,3}
    	quotient := new(Quotient)
    	divCall := client.Go("Arith.Divide", args, quotient, nil)
    	replyCall := <-divCall.Done	// will be equal to divCall
    	// check errors, print, etc.
    	fmt.Println(replyCall.Reply)
    }
    

    grpc

    gRPC是一个可以在任何环境中运行的现代开源高性能RPC框架。增加了很多扩展接口,例如:可插拔的负载平衡、跟踪、健康体检和身份验证等等。

    gRPC默认使用protocol buffers,它是google开源的一套成熟的结构数据序列化机制(当然也可以使用其他数据格式如JSON),可以用proto files创建gRPC服务,用protocol buffers消息类型来定义方法参数和返回类型。

    在gRPC客户端可以直接调用不通服务器上的远程程序,就想调用本地程序一样,很容易构建分布式应用和服务。和很多RPC系统一样,服务负责实现定义好的接口并处理客户端请求,客户端根据接口描述直接调用需要的服务。客户端和服务器可以分别使用gRPC支持的不同语言实现。

    初次接触就先简单理解为 google rpc 吧。

    protobuf 本文就不讲了,直接上例子。

    protoc

    syntax = "proto3"; //proto版本
    
    package proto;
    
    //请求message
    message HelloReq{
        string req=1;
    }
    
    //响应message
    message HelloRep{
        string rep=1;
    }
    
    //Hello服务
    service Hello{
        //定义服务中的方法
        rpc SayHello(HelloReq)returns (HelloRep){}
    }
    

    生成 pb.go

    目录结构

    go mod 老找不到pb.go 就只好 放各自文件下了, 注意 正常是放在公共文件下的。

    go get -u google.golang.org/grpc

    server.go

    package main
    
    import (
        pb "serv/protoc"
        "net"
    	
    	"log"
        "golang.org/x/net/context"
        "google.golang.org/grpc"
    )
    
    
    //定义一个helloServer并实现约定的接口
    type helloService struct{}
    
    func (h helloService) SayHello(ctx context.Context, in *pb.HelloReq) (*pb.HelloRep, error) {
        resp := new(pb.HelloRep)
        resp.Rep = "reply : " + in.Req
        return resp, nil
    }
    
    var HelloServer = helloService{}
    
    func main() {
        listen, err := net.Listen("tcp", "127.0.0.1:1234")
        if err != nil {
            log.Fatal("failed to listen")
        }
        //实现gRPC Server
        serv := grpc.NewServer()
        //注册helloServer为客户端提供服务
        pb.RegisterHelloServer(serv, HelloServer) //内部调用了s.RegisterServer()
    
        serv.Serve(listen)
    
    }
    
    

    client.go

    package main
    
    import (
        "fmt"
    
        pb "cli/protoc"
    
        "golang.org/x/net/context"
        "google.golang.org/grpc"
    )
    
    const (
        Address = "127.0.0.1:50052"
    )
    
    func main() {
    
    	conn, err := grpc.Dial("127.0.0.1:1234", grpc.WithInsecure())
        if err != nil {
            fmt.Println(err)
        }
        defer conn.Close()
    
        //初始化客户端
        c := pb.NewHelloClient(conn)
    
        req := new(pb.HelloReq)
        req.Req = "hello"
        r, err := c.SayHello(context.Background(), req)
        if err != nil {
    		fmt.Println(err)
    		return 
        }
        fmt.Println(r.Rep)
    
    }
    

  • 相关阅读:
    linux_shell_入门
    Linux下安装jdk
    Linux杂记
    Linux常用命令
    Java 性能优化的五大技巧
    Java异常处理的9个最佳实践
    Java面试:投行的15个多线程和并发面试题
    敏捷持续集成详解
    gitlab系列详解
    git系列讲解
  • 原文地址:https://www.cnblogs.com/ailumiyana/p/12069658.html
Copyright © 2020-2023  润新知