GRPC入门(Go语言实现)

说明

  • 文章涉及的内容较为简单,适合初学者
  • 本人也是初学者,记录自己的理解,难免有疏忽错误的地方

主要内容

在介绍GRPC之前,我会先介绍一下什么是微服务,然后,我会介绍RPC的相关概念,比如什么是RPC、什么是服务治理、网关以及在其中起到关键作用的protocol buffer等。接着我会介绍什么是GRPC,最后我会通过一个实际的过程来体会RPC是如何进行工作的。

相关概念

什么是微服务?

以前的传统可开发采用的单体架构,就是所有的服务都部署在同一台机器上,当用户来进行访问时,不管用户需要哪一个服务,请求都要到这台机器上。当用户增多时,虽然可以通过Nginx做负载均衡,通过在多台机器上部署相同的服务,但是仍然避免不了当一台机器上的一个服务出现问题,那么该机器上的其他服务都将不可用的事实。

基于上述事实,提出微服务架构,对原来的服务进行拆分,通过将不同的服务部署在不同的机器上,当一个服务不可用时,其他的服务仍然可以正常工作,但是这样仍然还有一些问题,在用户进行访问时,不管访问什么服务,系统都需要对用户进行认证授权,所以会造成代码的冗余,此时就把公共的部分提出来,做成一个网关,作为统一的入口。

网关还有一个重要的作用就是保证对外接口的一致性,不可能一个网站要访问很多个域名来访问吧。网关可以根据不同的请求路由到不同的机器上提供服务。

什么是RPC?

RPC,全称是 Remote Procedure Call,翻译过来是远程过程调用。此处的远程是指位于另外一台机器或者另一个独立的服务上,过程是指某一段逻辑代码,所以顾名思义,就是值在本地调用位于另一个服务上的函数,就像调用本地的函数一样。RPC的目的就是为了掩盖这一事实。所以RPC更多的是指一种协议,就像其他的网络协议一样,如果双方都遵循某一个协议,则不管对方是什么语言,双方都是可以实现互相调用。

RPC底层采用TCP传输,加快信息的传输效率。

服务治理

暂时理解为一套管理标准,可能不同的企业都不一样。

服务发现与注册中心

如果想在别的服务中调用另一个服务,那么另一个服务首先需要在注册中心注册自己,就像定位一样,首先对方得开启GPS设备,你才能知道对方在哪。而注册服务的过程就是服务发现。

protocol buffer

一种消息格式,由Google开发,用来RPC调用过程中的序列化与反序列化,RPC采用的消息格式有很多种,不止ProtoBuf,XML、JSON等也是可以的,只是RPC默认采用这种格式的消息。

语法格式

这里详细可以参考官网Protocol Buffer,或者找几篇博客参考一下。

什么是GRPC

上面说的RPC是一种协议标准,而GRPC可以说是RPC的一个具体实现,是一种通用的RPC框架,GRPC采用C/S通信模式进行通信,为了满足通用性,它的底层采用HTTP2

GRPC实例

环境搭建

  • 安装GPRC
    下载地址
    根据自己的硬件平台选择下载,下载解压后:
    在这里插入图片描述
    将其中的bin加入的环境变量,以便在全局中使用。里边有个protoc.exe,该文件的目的是编译proto文件,根据我们定义的消息格式生成相应的代码。
  • 下载grpc包

go get -u github.com/golang/protobuf/protoc-gen-go

安装完这个包后,检查一下GoPath下的bin目录是否有protoc-gen-go.exe这个文件,如果没有,进入到..\GoPath\pkg\mod\github.com\golang\protobuf@v1.5.3\protoc-gen-go
在这个目录重新生成一下:

go install .

目录结构

在这里插入图片描述
这里直接在同一台机器上进行编写。

编写proto文件

// 文件位置:pbfile/studnet.proto
// 指定使用的语法格式,根据自己下载的protoc的版本选择
syntax = "proto3";
// 指定代码生成在哪个位置
option go_package = "../service";
// 指定包名
package service;

message Student{
	string name = 1;
	int32 age = 2;
}
// 定义请求体结构
message StudentRequest{
	int32 number = 1; //请求中传入学生的学号,1表示占位符
}

// 定义响应体结构
message StudentResponse{
	Student student = 1;
}

//定义一个服务,根据学生的学号查询学生的信息
service StudentService{
	rpc GetStudentByStuNumber(StudentRequest) returnns(StudentResponse);
} 

在控制台执行以下命令,编译proto文件:

protoc --go_out=plugins=grpc:./ student.proto

此时会在service目录下生成一个Go文件student.pb.go,不要修改里面的东西。

编写服务端代码

实现一个接口

// StudentServiceServer is the server API for StudentService service.
type StudentServiceServer interface {
	GetStudentByStuNumber(context.Context, *StudentRequest) (*StudentResponse, error)
}

定义自己的服务类,实现其中的方法即可:

// student.go
package service

import "context"

var (
	data = map[int32]*Student{
		1: {
			Name: "ERIC",
			Age:  18,
		},
		2: {
			Name: "JOHN",
			Age:  19,
		},
	}
)

type StudentService struct {
}

func (s *StudentService) GetStudentByStuNumber(context context.Context, request *StudentRequest) (*StudentResponse, error) {
	student := s.GetStudentInfo(request.Number)
	return &StudentResponse{
		Student: student,
	}, nil
}

func (*StudentService) GetStudentInfo(number int32) *Student {
	//应该查询数据库查询数据

	//模拟数据
	if _, ok := data[number]; ok {
		return data[number]
	} else {
		return nil
	}
}

编写服务端server.go

package main

import (
	"google.golang.org/grpc"
	"grpc/service"
	"log"
	"net"
)

func main() {
	server := grpc.NewServer()
	studentService := service.StudentService{}

	//注册服务
	service.RegisterStudentServiceServer(server, &studentService)
	//启动监听程序
	listener, err := net.Listen("tcp", ":8000")
	if err != nil {
		log.Fatal("启动监听失败", err)
	}
	err = server.Serve(listener)
	if err != nil {
		log.Fatal("启动服务失败", err)
	}
}

编译server.go

go build -o ./out/server.exe server.go

编写客户端代码

copy一份

首先将刚刚生成代码复制到在client目录下的service目录

编写client.go

package main

import (
	"context"
	"fmt"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	"grpc/service"
	"log"
)

func main() {
	conn, err := grpc.Dial(":8000", grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		log.Fatal("连接服务端失败", err)
	}
	defer conn.Close()
	client := service.NewStudentServiceClient(conn)
	studentInfo, err := client.GetStudentByStuNumber(context.Background(), &service.StudentRequest{
		Number: 1,
	})
	if err != nil {
		log.Fatal("调用GRPC失败", err)
	}
	fmt.Printf("RPC调用成功, 学生信息:%s\n", studentInfo.String())
}

是不是就像调用本地代码一样呐?底层的细节不需要了解,只需要调用方法即可。

编译client.go

将编译生成的exe放在刚刚的out中,具体路径自己定,可能不同,就是为了等会启动方便

go build -o …/out/client.exe client.go

启动客户端和服务端,查看效果

在这里插入图片描述
可以看到,成功实现了RPC

总结

通过上面的简单介绍和实例编码,体会到了GRPC的实际效果,不过关于RPC还有很多知识,上面知识简单了解和体会,持续学习。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,让我来回答你的问题:Java如何调用远程gRPC服务? 首先,你需要定义一个.proto文件来描述你的服务和消息。proto文件是gRPC的核心文件之一,它定义了服务的接口和消息的格式。 接下来,你需要使用gRPC的代码生成工具来生成客户端和服务端的代码。你可以选择使用不同的编程语言,例如Java、C++或Python等。 在Java中,你需要使用gRPC的Java库来编写客户端代码。你可以使用Maven或Gradle来管理你的依赖项。在你的Java代码中,你需要实现一个Stub来调用远程服务。Stub是一个自动生成的类,它提供了一个简单的API来调用远程服务。 下面是一个简单的Java gRPC客户端的示例代码: ``` ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 50051) .usePlaintext() .build(); HelloWorldGrpc.HelloWorldBlockingStub stub = HelloWorldGrpc.newBlockingStub(channel); HelloRequest request = HelloRequest.newBuilder().setName("World").build(); HelloResponse response = stub.sayHello(request); System.out.println(response.getMessage()); channel.shutdown(); ``` 在这个例子中,我们创建了一个ManagedChannel来连接到远程服务。然后,我们创建了一个HelloWorldBlockingStub来调用远程服务。最后,我们使用这个Stub来发送一个HelloRequest消息并接收一个HelloResponse消息。 最后,你需要启动你的gRPC服务,并在客户端代码中使用正确的地址和端口来连接到它。 希望这个简单的指南能够帮助你入门gRPC

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值