分别以google.golang.org/grpc和net/rpc简单理解RPC - Go语言中文社区

分别以google.golang.org/grpc和net/rpc简单理解RPC


                    筚路蓝缕,以启山林。抚有蛮夷,以属华夏。不鸣则已,一鸣惊人。
                                                                                                          ——《左传`宣公十二年》

 

rpc:远程过程调用,是一个思想,一个概念。核心是分布式应用间通信,屏蔽不同语言,解耦(个人认为)。

net/rpc包是一个go自带的rpc实现方式,可以基于tcp、http、json rpc等方式进行调用。

gRpc基于Netty HTTP/2协议栈封装底层通信,启动时启动一个Netty server。

特别提醒(踩坑后可以返回来再看):如果没有用go module模式,那么需要在src下手动创建google.golang.org目录并进入该目录手动执行git clone https://e.coding.net/robinqiwei/googleprotobuf.git protobuf,否则包不能正确拉取(报找不到google.golang.org/protobuf/proto...等一堆错);如果用的go module模式,那么这个是自动完成的,无需手动创建。

目录

net/rpc

google.golang.org/grpc


net/rpc

实践目标:server端与client端可以实现通信。此实例采用TCP方式调用。项目目录结构:

pub.go

package base1_public

import (
	"fmt"
	"net/rpc"
)

const HelloServiceName = "pkg.HelloService"

type HelloServiceInterface interface {
	HelloS(request string, reply *string) error // 之所以命名为HelloS,是为了验证与其对应之处
}

func RegisterHelloService(svc HelloServiceInterface) error {
	return rpc.RegisterName(HelloServiceName, svc)
}

type HelloService struct{}

func (p *HelloService) HelloS(request string, reply *string) error {
	*reply = "hello:" + request
	fmt.Println("服务端回复:", *reply)
	return nil
}

服务端

package main

import (
	"log"
	"net"
	"net/rpc"
	"rpc-test/base1_public"
)

func main() {
	base1_public.RegisterHelloService(new(base1_public.HelloService))

	listener, err := net.Listen("tcp", ":3234")
	if err != nil {
		log.Fatal("ListenTCP error:", err)
	}

	for {
		conn, err := listener.Accept()
		if err != nil {
			log.Fatal("Accept error:", err)
		}

		go rpc.ServeConn(conn) // 此次server被终止时才会执行下次循环继续开启新的server,否则一直阻塞等待
	}
}

客户端

package main

import (
	"fmt"
	"log"
	"net/rpc"
	"rpc-test/base1_public"
)

type HelloServiceClient struct {
	*rpc.Client
}


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

func (p *HelloServiceClient) start(request string, reply *string) error {
	// 方式1
	//return p.Client.Call(base1_public.HelloServiceName+".HelloS", request, reply) // HelloS与接口的方法名对应即可

	// 方式2
	helloCall := p.Client.Go(base1_public.HelloServiceName+".HelloS", request, reply, nil)
	//helloCall = <-helloCall.Done
	return (<-helloCall.Done).Error
	//if err := helloCall.Error; err != nil {
	//	log.Fatal(err)
	//}

}

func main() {
	client, err := DialHelloService("tcp", "localhost:3234")
	if err != nil {
		log.Fatal("dialing:", err)
	}

	var reply string
	err = client.start("哈哈哈", &reply)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("收到的回复:", reply)
}

分别启动服务端和客户端,控制台分别如下:

服务端回复: hello:哈哈哈

收到的回复: hello:哈哈哈

http、json-rpc等方式的调用可移步:https://blog.csdn.net/ffzhihua/article/details/83270771

google.golang.org/grpc

实践目标:服务端有两个服务,客户端我要使用(调用)这两个服务。

背景设计:远端某个地方有两个服务:分别是加密服务和解密服务,client调用时只需传明文或密文作为参数即可。

项目结构如下:

通过.proto文件约定好服务接口、参数等,通过工具protoc-gen-go生成客户端和服务端共用的对照表。

secret.proto

syntax = "proto3";

package proto;

service SecretService {
  rpc Encrypt(SecretRequest) returns (SecretResponse) {}
  rpc Decrypt(SecretRequest) returns (SecretResponse) {}
}

message SecretRequest {
  string request = 1;
}

message SecretResponse {
  string response = 1;
}

生成的secret.pb.go挺长,这里就不贴了。生成方式及环境搭建可移步https://blog.csdn.net/HYZX_9987/article/details/106462026

服务端

package main

import (
	"context"
	"encoding/base64"
	"fmt"
	pt "gRpc-demo/proto"
	"google.golang.org/grpc"
	"log"
	"net"
)

const PORT = "9001"

type SecretServiceStruct struct{}

// 加密服务
func (s *SecretServiceStruct) Encrypt(ctx context.Context, r *pt.SecretRequest) (*pt.SecretResponse, error) {
	log.Print("调用了加密服务:", r.Request)
	secret := base64.StdEncoding.EncodeToString([]byte(r.Request))
	return &pt.SecretResponse{Response: r.GetRequest() + "加密成功:" + secret}, nil
}

// 解密服务
func (s *SecretServiceStruct) Decrypt(ctx context.Context, r *pt.SecretRequest) (*pt.SecretResponse, error) {
	log.Print("调用了解密服务:", r.Request)
	str, err := base64.StdEncoding.DecodeString(r.Request)
	if err != nil {
		log.Fatalf("服务端解密失败: %v", err.Error())
		return &pt.SecretResponse{Response: ""}, err
	}
	return &pt.SecretResponse{Response: r.GetRequest() + "解密成功:" + string(str)}, nil
}

func main() {
	server := grpc.NewServer()
	pt.RegisterSecretServiceServer(server, &SecretServiceStruct{})
	lis, err := net.Listen("tcp", ":"+PORT)
	if err != nil {
		log.Fatalf("net.Listen err: %v", err)
	}

	addr := fmt.Sprintf("%s", lis.Addr())
	log.Printf("Listening on %v", addr)
	log.Fatal(server.Serve(lis))
}

客户端

package main

import (
	"context"
	pt "gRpc-demo/proto"
	"google.golang.org/grpc"
	"log"
	"strings"
)

const PORT = "9001"

var str = `this is gRpc`

func main() {
	// 上面gRPC的服务没有提供证书支持,因此客户端在连接服务器中通过grpc.WithInsecure()选项跳过对服务器证书的验证
	conn, err := grpc.Dial(":"+PORT, grpc.WithInsecure())
	if err != nil {
		log.Fatalf("grpc.Dial err: %v", err)
	}
	defer conn.Close()

	client := pt.NewSecretServiceClient(conn)
	resp0, err := client.Encrypt(context.Background(), &pt.SecretRequest{
		Request: str,
	})
	if err != nil {
		log.Fatalf("client.Encrypt err: %v", err)
	}
	secretStr := strings.Split(resp0.GetResponse(), ":")[1]
	log.Printf("请求加密服务: %s", secretStr)

	resp1, err := client.Decrypt(context.Background(), &pt.SecretRequest{
		Request: secretStr,
	})
	if err != nil {
		log.Fatalf("client.Decrypt err: %v", err)
	}

	resStr := strings.Split(resp1.GetResponse(), ":")[1]
	log.Printf("请求解密服务: %s", resStr)
}

分别启动服务端、客户端,控制台分别如下:

服务端:
2020/06/01 09:41:20 Listening on [::]:9001
2020/06/01 09:41:37 调用了加密服务:this is gRpc
2020/06/01 09:41:37 调用了解密服务:dGhpcyBpcyBnUnBj

客户端:
2020/06/01 09:41:37 请求加密服务: dGhpcyBpcyBnUnBj
2020/06/01 09:41:37 请求解密服务: this is gRpc

 

 

 

 

 

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/HYZX_9987/article/details/106466755
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2020-06-30 11:08:04
  • 阅读 ( 3302 )
  • 分类:

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢