RPC与Protobuf(五)

RPC实现上下文信息

基于上下文可以针对不同的客户端提供定制化的RPC服务,我们可以通过为每个连接提供独立的RPC服务来实现对上下文的特性的支持,下面我们将针对每个RPC服务进行登陆验证,如果通过服务登陆就可以调用RPC,首先是proto目录下的proto文件:

syntax = "proto3";

package proto;

message String {
    string value = 1;
}

当然我们需要使用特殊的命令生成相应的接口文件: protoc --go_out=. hello.proto ;紧接着是服务端代码, ctx_service.go:

package main

import (
	"fmt"
	"log"
	"net"
	"net/rpc"
	"rpc_protobuf/proto"
)

type HelloServiceCtx struct {
	conn net.Conn
	isLogin bool
}

func (p *HelloServiceCtx) Login(request *proto.String, reply *proto.String) error {
	if request.GetValue() != "user:password" {
		return fmt.Errorf("auth failed")
	}
	log.Println("login OK")
	p.isLogin = true
	return nil
}

func (p *HelloServiceCtx) Hello(request *proto.String, reply *proto.String) error {
	if !p.isLogin {
		return fmt.Errorf("please login")
	}
	reply.Value = "hello :"+ reply.GetValue() + ", from " + p.conn.LocalAddr().String()
	return nil
}

func main() {
	listener, err := net.Listen("tcp",":1234")
	if err != nil {
		log.Fatal("tcp is error")
	}

	// 基于每个连接创建一个RPC服务
	for {
		conn, err:= listener.Accept()
		if err != nil {
			log.Fatal("accept error")
		}
		go func() {
			defer conn.Close()
			p := rpc.NewServer()
			p.Register(&HelloServiceCtx{conn:conn})
			p.ServeConn(conn)
		}()
	}
}

然后是客户端代码ctx_client.go:

package main

import (
	"fmt"
	"log"
	"net/rpc"
	"rpc_protobuf/proto"
)

func main() {
	// 拨号本端1234端口服务
	client, err := rpc.Dial("tcp", "localhost:1234")
	if err != nil {
		log.Fatal("TCP error:", err)
	}

	// 统一采用接口提供的参数,和请求参数
	var reply = &proto.String{}
	var param = &proto.String{
		Value: "user:password",
	}

	// 先登陆后在调服务
	err = client.Call("HelloServiceCtx.Login", &param, &reply)
	if err != nil {
		log.Fatal(err)
	}

	// 调用远程服务
	err = client.Call("HelloServiceCtx.Hello", &param, &reply)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(reply)
}

到处RPC和Protobuf相关知识就介绍到这了, 如果想深度研究可以访问官网,之后我们可能会介绍gRPC相关知识。

原文地址:https://www.cnblogs.com/double-W/p/12758491.html