第十三章 golang RPC

RPC俗称远程过程调用,是发送tcp请求进行通信,是各个语言都有的,下面是一个最简单的例子,服务端启动服务,客户端通过rpc请求调用

服务端代码:

package main

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

type Person struct {
	Name string
	Age  int
}

type HelloService struct {
}

func (p *HelloService) Hello(msg string, person *Person) error {
	person.Name = "zhangsan"
	person.Age = 10
	return nil
}

func main() {
	rpc.RegisterName("HelloSer", new(HelloService))
	listener, err := net.Listen("tcp", ":1234")
	if err != nil {
		fmt.Println("err1 = ", err)
		return
	}
	conn, err := listener.Accept()
	if err != nil {
		fmt.Println("err2 = ", err)
		return
	}
	rpc.ServeConn(conn)
}

客户端代码:

package main

import (
	"fmt"
	"net/rpc"
)

type Person struct {
	Name string
	Age  int
}

func main() {
	client, err := rpc.Dial("tcp", "localhost:1234")
	if err != nil {
		fmt.Println("err1 = ", err)
		return
	}
	var person Person
	err = client.Call("HelloSer.Hello", "haha", &person)
	if err != nil {
		fmt.Println("err2 = ", err)
		return
	}
	fmt.Println("person = ", person)
}

启动服务端后,执行客户端代码结果:

person =  {zhangsan 10}

以上的例子是最简单的,只能连接一次,下面这个例子是稍微复杂一点,服务端启动后,可以支持多个客户端连接:

服务端代码

package main

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

const HelloServiceName = "path/HelloService"

type Person struct {
	Name string
	Age  int
}

type HelloService struct {
}

type HelloServiceInterface interface {
	Hello(msg string, person *Person) error
}

func (p *HelloService) Hello(msg string, person *Person) error {
	person.Name = "zhangsan"
	person.Age = 123
	return nil
}

func RegisterService(service HelloServiceInterface) error {
	return rpc.RegisterName(HelloServiceName, service)
}

func main() {
	RegisterService(new(HelloService))
	listener, err := net.Listen("tcp", ":1234")
	if err != nil {
		fmt.Println("err1 = ", err)
		return
	}

	for {
		conn, err := listener.Accept()
		if err != nil {
			fmt.Println("err2 = ", err)
			return
		}

		go rpc.ServeConn(conn)
	}
}

客户端代码

package main

import (
	"fmt"
	"net/rpc"
)

const HelloServiceName = "path/HelloService"

type Person struct {
	Name string
	Age  int
}

type HelloServiceInterface interface {
	Hello(msg string, person *Person) error
}

type HelloServiceClient struct {
	Client *rpc.Client
}

func DialHelloService(network, address string) (*HelloServiceClient, error) {
	client, err := rpc.Dial(network, address)
	if err != nil {
		return nil, err
	}
	return &HelloServiceClient{Client: client}, err
}

func (p *HelloServiceClient) Hello(msg string, person *Person) error {
	err := p.Client.Call(HelloServiceName+".Hello", "haha", person)
	return err
}

func main() {
	client, err := DialHelloService("tcp", "localhost:1234")
	if err != nil {
		fmt.Println("err1 = ", err)
		return
	}
	var person Person
	err = client.Hello("haha", &person)
	if err != nil {
		fmt.Println("err2 = ", err)
		return
	}

	fmt.Println("person1 = ", person)
}

结果:

person1 =  {zhangsan 123}

13.2 跨语言的RPC

和Http一样,RPC也支持跨语言,如下所示:

服务端代码

package main

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

type HelloService struct {
}
type Person struct {
	Name string
	Age  int
}

func (p *HelloService) Hello(msg string, person *Person) error {
	person.Name = "zhangsan"
	person.Age = 123
	return nil
}

func main() {
	rpc.RegisterName("HelloService", new(HelloService))

	listener, err := net.Listen("tcp", ":1234")
	if err != nil {
		fmt.Println("err1 = ", err)
		return
	}

	for {
		conn, err := listener.Accept()
		if err != nil {
			fmt.Println("err2 = ", err)
			return
		}

		go rpc.ServeCodec(jsonrpc.NewServerCodec(conn))
	}
}

服务端启动后,在命令行窗口输入echo -e '{"method":"HelloService.Hello","params":["hello"],"id":1}' | nc localhost 1234,可以查看返回值,如下所示:

{"id":1,"result":{"Name":"zhangsan","Age":123},"error":null}

13.3 使用protobuf进行参数约束

protobuf可以用来对RPC请求的参数和响应进行约束,使得客户端和服务端按照一定的约束来写代码,达到规范的目标,如下是例子

hello.proto文件

syntax = "proto3";
option go_package="./;hello";
package main;

message Person {
  string name = 1;
  int32  age = 2;
}

执行protoc --go_out=. hello.proto命令生成hello.pb.go代码

最重要的是上方图片的type Person struct {}结构体的代码

生成go代码后就可以用在RPC服务端和客户端端了

服务端代码

package main

import (
	"fmt"
	"google.golang.org/protobuf/runtime/protoimpl"
	"net"
	"net/rpc"
)

type HelloService struct{}
type Person struct {
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
	Age  int32  `protobuf:"varint,2,opt,name=age,proto3" json:"age,omitempty"`
}

func (p *HelloService) Hello(msg string, person *Person) error {
	person.Name = "zhangsan"
	person.Age = 123
	return nil
}

func main() {
	rpc.RegisterName("HelloService", new(HelloService))
	listener, err := net.Listen("tcp", ":1234")
	if err != nil {
		fmt.Println("err1 = ", err)
		return
	}

	for {
		conn, err := listener.Accept()
		if err != nil {
			fmt.Println("err2 = ", err)
			return
		}

		go rpc.ServeConn(conn)
	}
}

 客户端代码

package main

import (
	"fmt"
	"google.golang.org/protobuf/runtime/protoimpl"
	"net/rpc"
)

type Person struct {
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
	Age  int32  `protobuf:"varint,2,opt,name=age,proto3" json:"age,omitempty"`
}

func main() {
	client, err := rpc.Dial("tcp", "localhost:1234")
	if err != nil {
		fmt.Println("err1 = ", err)
		return
	}

	var person Person
	msg := "hahahaha"

	err = client.Call("HelloService.Hello", msg, &person)
	if err != nil {
		fmt.Println("err2 = ", err)
		return
	}

	fmt.Println("person = ", person.Name)
}

执行结果

person =  zhangsan

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值