Go语言grpc服务开发——Protocol Buffer

文章目录

    • 一、Protocol Buffer简介
    • 二、Protocol Buffer编译器安装
    • 三、proto3语言指南
    • 四、序列化与反序列化
    • 五、引入grpc-gateway
      • 1、插件安装
      • 2、定义proto文件
      • 3、生成go文件
      • 4、实现Service服务
      • 5、gRPC服务启动方法
      • 6、gateway服务启动方法
      • 7、main函数启动
      • 8、验证

相关参考链接:

protobuf官方文档

proto3语法指南

protoc下载地址

grpc-gateway官方文档

grpc-gateway readme

swagger安装文档

【当前代码Demo】

一、Protocol Buffer简介

作为一个后端开发,我们用的最多的传递信息的协议是http协议。性能需求的提高,要求我们转向性能更高的协议,这就不得不提到一个由Google公司推广的一个小而快的rpc协议——protocol buffer协议。它到底有着什么样的魔力?让大家觉得真香定律。

Protocol Buffer是一个由Google开发的协议,是一个可以对结构化数据的序列化和反序列化协议。谷歌开发它的目的是提供一种比XML更好的方式来使系统进行通信。因此,他们致力于使其比XML更简单、更小、更快、更易于维护。这个协议甚至超过了JSON,具有更好的性能、更好的可维护性和更小的体积。

另外protoc支持多语言,以及跨平台。

天然支持C、C++、Java、Python、PHP、Ruby、Kotlin等语言。
不支持Go、Dart等语言。所以基于Go语言需要额外安装插件,下面会说到。

  • Why Protocol Buffer?——简单来说它更小、因此也更快。举例如下:
// 当前代码位于 https://gitee.com/liuwen766/protobuf-demo.git
func TestSerialize(t *testing.T) {// 这个是基于proto生成的personFromProto := &pb.Person{Name:     "我是小明",Age:      18,PhoneNum: []string{"188", "120"},Pets: &pb.Pets{Type: "Cat",Name: "Tom",},}marshal1, _ := proto.Marshal(personFromProto)create, _ := os.Create(fileName)defer create.Close()n, err := create.Write(marshal1)if err != nil {log.Fatal("create.Write(marshal) has err:", err)return}log.Println("proto.Marshal——Serialize Success:", n)// 这个是Go结构体personFromStruct := &Person{Name:     "我是小明",Age:      18,PhoneNum: []string{"188", "120"},Pets: &Pets{Type: "Cat",Name: "Tom",},}marshal2, _ := json.Marshal(personFromStruct)create, _ = os.Create(fileName)defer create.Close()n, err = create.Write(marshal2)if err != nil {log.Fatal("create.Write(marshal) has err:", err)return}log.Println("json.Marshal——Serialize Success:", n)// 日志输出如下:【传递的Person信息一模一样,但是基于proto的长度只有38,它是二进制数据】//2024/01/22 18:13:16 proto.Marshal——Serialize Success: 38//2024/01/22 18:13:16 json.Marshal——Serialize Success: 107
}

二、Protocol Buffer编译器安装

编译proto文件前的环境准备工作只有简单的两个步骤:

  • step1、下载protoc 。 protoc下载地址
#将protoc.exe文件配置到环境变量,配置完之后验证
$ protoc --version
libprotoc 3.19.4
  • step2、安装go插件。

前面提到过protoc支持多语言,包括C、C++、Java、Python、PHP、Ruby、Kotlin等语言。
但是不支持Go、Dart等语言。所以基于Go语言需要额外安装插件。只需要下面一个简单的命令。

go get google.golang.org/protobuf/cmd/protoc-gen-go
go install google.golang.org/protobuf/cmd/protoc-gen-go

go get 命令会获取依赖包到go env的 GOMODCACHE 目录下。
go intall 命令会将对应的可执行文件安装到go env下的 GOPATH/bin 目录下。
因此需要配置GOPATH/bin为环境变量。否则会报错“‘protoc-gen-go’ 不是内部或外部命令,也不是可运行的程序或批处理文件。”

一个简单的proto3文件样例。

// 指定proto语言版本
syntax = "proto3";
// 生成*.pb.go文件的包路径
option go_package = "/pb";
// proto包路径 
package protobuf.demo;message Person{string name = 1;int32 age = 2;
}

通过protoc生成 *.pb.go 文件:

protoc -I . --go_out=./proto ./proto/person.proto
# 这里的三个 . 都表示当前目录

三、proto3语言指南

通过上面编写的简单proto文件,可以发现,proto文件中定义message与我们创建一个Go语言中的struct结构体类似。针对Go语言中的一些复杂类型,例如:数组[]int、集合map、枚举enum、嵌套等,proto自然也有相对应的定义。

  • repeated

repeated关键字的作用是用来定义数组,使用方式是repeated 数组类型 属性名称 = 字段编号;

message Person {repeated string name = 1;
}
  • map

map类型的定义方式是map <键类型,值类型> 属性名称 = 字段编号; ,这里需要注意对于map的键类型,只能定义为基本数据类型,但是值的类型可以是任何支持的类型。

message Person {map <string, Pets> pets =1;
}
// 嵌套
message Pets{string  Type = 1;string  name = 2;
}
  • enum

对于枚举的定义,需要用到enum关键字。

message Person{Sex sex = 5;
}
enum Sex{Sex_MAN = 0;SEX_WOMAN = 1;
}

一个完整示例。

// 指定proto语言版本
syntax = "proto3";
// 生成*.pb.go文件的包路径
option go_package = "/pb";
// proto包路径
package protobuf.demo;message Person{string name = 1;int32 age = 2;bool marry = 3;repeated string phoneNum = 4;map<string, string> address = 5;Sex sex = 6;Pets pets = 7;
}message Pets{string  Type = 1;string  name = 2;
}enum Sex{Sex_MAN = 0;SEX_WOMAN = 1;
}

定义完proto文件后,通过protoc生成 *.pb.go 文件,执行如下命令:

protoc -I . --go_out=./proto ./proto/person.proto
# 这里的三个 . 都表示当前目录

四、序列化与反序列化

以序列化和反序列化为例,演示如何使用由proto编译生成的*.pb.go文件

package protoimport ("encoding/json""log""os""testing""google.golang.org/protobuf/proto""protobuf-demo/proto/pb"
)var fileName1 = "person-proto.txt"
var fileName2 = "person-json.txt"type Pets struct {Type stringName string
}type Person struct {Name     stringAge      int32PhoneNum []stringAddress  map[string]stringPets     *Pets
}// 序列化
func TestSerialize(t *testing.T) {personFromProto := &pb.Person{Name:     "我是小明",Age:      18,PhoneNum: []string{"188", "120"},//Sex:      pb.Sex_Sex_MAN,Pets: &pb.Pets{Type: "Cat",Name: "Tom",},}marshal1, _ := proto.Marshal(personFromProto)create, _ := os.Create(fileName1)defer create.Close()n, err := create.Write(marshal1)if err != nil {log.Fatal("create.Write(marshal) has err:", err)return}log.Println("proto.Marshal——Serialize Success:", n)// why proto?let's look look json.personFromStruct := &Person{Name:     "我是小明",Age:      18,PhoneNum: []string{"188", "120"},//Sex:      pb.Sex_Sex_MAN,Pets: &Pets{Type: "Cat",Name: "Tom",},}marshal2, _ := json.Marshal(personFromStruct)create, _ = os.Create(fileName2)defer create.Close()n, err = create.Write(marshal2)if err != nil {log.Fatal("create.Write(marshal) has err:", err)return}log.Println("json.Marshal——Serialize Success:", n)// 日志输出如下:所以说proto更小//2024/01/25 18:13:16 proto.Marshal——Serialize Success: 38//2024/01/25 18:13:16 json.Marshal——Serialize Success: 107
}// 反序列化
func TestDeserialize(t *testing.T) {read, _ := os.ReadFile(fileName1)person := pb.Person{}err := proto.Unmarshal(read, &person)if err != nil {log.Fatal("proto.Unmarshal(read, &person) has err:", err)return}log.Printf("Deserialize Success: %+v", person)
}

五、引入grpc-gateway

grpc-gateway是protoc的一个插件,它会读取proto文件中的grpc服务的定义并将其生成RestFul JSON API,并将其反向代理到我们的grpc服务中。

在这里插入图片描述

grpc-gatway是在grpc上做的一个拓展。但是grpc并不能很好的支持客户端,以及传统的RESTful API。因此grpc-gateway诞生了,该项目可以为grpc服务提供HTTP+JSON接口。

1、插件安装

首先,在项目中去创建一个tools的文件,然后执行go mod tidy

//go:build tools
// +build toolspackage toolsimport (_ "github.com/envoyproxy/protoc-gen-validate"_ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway"_ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2"_ "google.golang.org/grpc/cmd/protoc-gen-go-grpc"_ "google.golang.org/protobuf/cmd/protoc-gen-go"
)

通过执行go install将这些可执行文件安装在GOPATH/bin目录下。

go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway
go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2
go install google.golang.org/protobuf/cmd/protoc-gen-go
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc

2、定义proto文件

// 指定proto语言版本
syntax = "proto3";
// 生成*.pb.go文件的包路径
option go_package = "/pb";package protobuf.demo;// 导入api注释依赖【注意将这里的注释依赖包放在当前项目的根目录下】
import "google/api/annotations.proto";message Book {int32 id = 1;string name = 2;
}message CreateBookRequest {string name = 1;
}message CreateBookResponse{string code = 1;string message = 2;Book data = 3;
}// 定义服务
service BookService {// 创建Bookrpc CreateBook (CreateBookRequest) returns (CreateBookResponse) {option (google.api.http) = {// POST  /v1/bookspost: "/v1/books"body: "*"};};
}

注意这里需要导入api注释依赖。

由于项目依赖了google的proto文件,所以在使用protoc生成go文件的时候,需要将依赖的proto文件复制到项目中,依赖的proto文件仓库 google/api 和 google/protobuf 。下载下来,放在当前项目中的根目录下。注释依赖一般可以自动检测,不行就手动配置依赖。

3、生成go文件

执行命令

protoc -I . --grpc-gateway_out=./proto/gen --grpc-gateway_opt logtostderr=true --grpc-gateway_opt paths=source_relative --go_out=./proto/gen --go_opt paths=source_relative --go-grpc_out=./proto/gen --go-grpc_opt paths=source_relative ./proto/book.proto
# 如果报错没有目录,则手动创建目录

不要被这么长的命令唬住了,记住 . 表示当前目录就行。
几个命令分别对应前面安装的三个插件:protoc-gen-go、protoc-gen-go-grpc和protoc-gen-grpc-gateway
protoc -I .
–grpc-gateway_out=./proto/gen
–grpc-gateway_opt logtostderr=true --grpc-gateway_opt paths=source_relative
–go_out=./proto/gen
–go_opt paths=source_relative
–go-grpc_out=./proto/gen
–go-grpc_opt paths=source_relative
./proto/book.proto

4、实现Service服务

package serviceimport ("context""log""protobuf-demo/db"pb "protobuf-demo/proto/gen/proto"
)type BookService struct {// 这里是要实现的服务pb.UnimplementedBookServiceServer
}// 这里是实现的方法
func (b *BookService) CreateBook(ctx context.Context, req *pb.CreateBookRequest) (*pb.CreateBookResponse, error) {resp := &pb.CreateBookResponse{}db.Db.Mux.Lock()defer db.Db.Mux.Unlock()id := db.Db.GetId()book := pb.Book{Name: req.GetName(),Id:   id,}err := db.Db.Save(&book)if err != nil {return resp, err}resp.Data = &booklog.Printf("user %s create a book, %+v", db.GetUserId(ctx), &book)return resp, nil
}

5、gRPC服务启动方法

package grpcimport ("log""net""protobuf-demo/config""protobuf-demo/grpc/middle"pb "protobuf-demo/proto/gen/proto""protobuf-demo/service""google.golang.org/grpc"
)// Run grpc的启动方法
func Run() error {//Step1:监听端口,用于提供grpc服务grpcAddr := config.GetRpcAddr()listen, err := net.Listen("tcp", grpcAddr)if err != nil {log.Fatalf("tcp listen failed: %v", err)return err}defer listen.Close()// Step2:可以为这个grpc服务加一些定制化的特性option := []grpc.ServerOption{// 这里可以加一些middleware中间件grpc.UnaryInterceptor(middle.AuthInterceptor),}// Step3:创建一个grpc服务,它是空的服务,还不能接收/处理任何请求server := grpc.NewServer(option...)// Step4:进行服务注册registerServer(server)log.Printf("Serving gRPC on %s", listen.Addr())// Step5:grpc服务接收从监听端口过来的流量请求,对外提供服务return server.Serve(listen)
}func registerServer(server *grpc.Server) {// 注册 BookService 服务pb.RegisterBookServiceServer(server, service.NewBookService())
}

6、gateway服务启动方法

package gatewayimport ("context""log""net/http""protobuf-demo/config""protobuf-demo/gateway/middle""protobuf-demo/handler"pb "protobuf-demo/proto/gen/proto""github.com/grpc-ecosystem/grpc-gateway/v2/runtime""google.golang.org/grpc""google.golang.org/grpc/credentials/insecure"
)// Run gateway的启动方法
func Run() error {// Step1:创建一个客户端,连接grpc服务ctx := context.Background()option := []grpc.DialOption{// 这里可以加一些middleware中间件grpc.WithChainUnaryInterceptor(middle.AuthInterceptor),grpc.WithTransportCredentials(insecure.NewCredentials()),}// 创建grpc连接  连接到127.0.0.1:8001conn, err := grpc.DialContext(ctx, config.GetRpcAddr(), option...)if err != nil {log.Fatalf("dial failed: %v", err)return err}// Step2:创建一个ServeMux,它是 grpc-gateway 的请求多路复用器。serveMuxOption := []runtime.ServeMuxOption{// 响应拦截runtime.WithForwardResponseOption(middle.Interceptor),// 错误页自定义runtime.WithRoutingErrorHandler(middle.RoutingErrorHandler),// 自定义保留哪些请求头信息到整个上下文中runtime.WithIncomingHeaderMatcher(func(s string) (string, bool) {if s == "User-Id" {return s, true}return runtime.DefaultHeaderMatcher(s)}),}mux := runtime.NewServeMux(serveMuxOption...)// 自定义一些不好用proto编译的接口,比如这里的上传/下载接口{if err = mux.HandlePath(http.MethodPost, "/v1/objects", handler.Upload); err != nil {return err}if err = mux.HandlePath(http.MethodGet, "/v1/objects/{name}", handler.Download); err != nil {return err}}// Step3:将http路由注册到前面创建的ServeMux,通过grpc-gateway反向代理,从而提供http服务err = newGateway(ctx, conn, mux)if err != nil {log.Fatalf("register handler failed: %v", err)return err}// Step4:创建一个http服务server := http.Server{Addr: config.GetHttpAddr(), // 127.0.0.1:8002// http服务需要处理的ServeMuxHandler: mux,}log.Printf("Serving Http on %s", server.Addr)// Step5:进行监听并提供服务err = server.ListenAndServe()if err != nil {return err}return nil
}func newGateway(ctx context.Context, conn *grpc.ClientConn, mux *runtime.ServeMux) error {// 注册 BookService 服务,进行反向代理err := pb.RegisterBookServiceHandler(ctx, mux, conn)if err != nil {return err}return nil
}

7、main函数启动

package mainimport ("log""os""protobuf-demo/gateway""protobuf-demo/grpc"
)func main() {// 启动grpc服务go func() {err := grpc.Run()if err != nil {log.Fatal(os.Stderr, err)os.Exit(1)}}()//	启动gateway服务err := gateway.Run()log.Fatal(os.Stderr, err)os.Exit(1)
}

8、验证

启动服务之后,进行接口验证。

# 新增接口
curl --request POST \--url http://localhost:8002/v1/books \--header 'X-User-Id: ' \--header 'content-type: application/json' \--data '{"name": "三国演义"
}'

接下来,完成所有的增删改查接口。如下:

# 查询接口
curl --request GET \--url http://localhost:8002/v1/books/1 \--header 'content-type: application/json'# 更新接口
curl --request POST \--url http://localhost:8002/v1/books/1 \--header 'content-type: application/json' \--data '{"id": 1,"name": "三国演义2"
}'# 删除接口
curl --request DELETE \--url http://localhost:8002/v1/books/1 \--header 'content-type: application/json'

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/650552.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Go、容器以及Linux调度器

在容器中运行Go应用程序时&#xff0c;需要设置合理的GOMAXPROCS&#xff0c;从而避免调度中因为资源不足而造成STW。原文: Go, Containers, and the Linux Scheduler Go开发的应用程序通常部署在容器中。在容器中运行时&#xff0c;重要的一点是要设置CPU限制以确保容器不会耗…

Python 潮流周刊第 37 期(摘要)

本周刊由 Python猫 出品&#xff0c;精心筛选国内外的 250 信息源&#xff0c;为你挑选最值得分享的文章、教程、开源项目、软件工具、播客和视频、热门话题等内容。愿景&#xff1a;帮助所有读者精进 Python 技术&#xff0c;并增长职业和副业的收入。 周刊全文&#xff1a;h…

Python无人驾驶系统的重要性

Python无人驾驶系统的重要性。无人驾驶系统是一个复杂而庞大的项目&#xff0c;涉及到多个模块和算法&#xff0c;代码量非常庞大。 一个完整的无人驾驶系统通常由以下几个主要模块组成&#xff1a; 感知模块 - 通过传感器&#xff08;如相机、激光雷达等&#xff09;获取周围…

YOLOv8-Seg改进:轻量化改进 | RepGhost,通过重参数化实现硬件高效的Ghost模块

🚀🚀🚀本文改进:RepGhost,通过重参数化实现硬件高效的Ghost模块,性能优于GhostNet、MobileNetV3等,在移动设备上具有更少的参数和可比的延迟。 🚀🚀🚀YOLOv8-seg创新专栏:http://t.csdnimg.cn/KLSdv 学姐带你学习YOLOv8,从入门到创新,轻轻松松搞定科研; 1…

CMU15-445 Project0

CMU14445 Task #1 - Copy-On-Write Trie Get()思路&#xff1a; 获取根节点指针&#xff0c;顺着key逐字符往下找节点&#xff0c;最后根据题意可以使用dynamic_cast检查是否是TrieNodeWithValue&#xff08;dynamic_pointer_cast也可以)&#xff0c;以下为两者用法&#xff1…

Making Large Language Models Perform Better in Knowledge Graph Completion论文阅读

文章目录 摘要1.问题的提出引出当前研究的不足与问题KGC方法LLM幻觉现象解决方案 2.数据集和模型构建数据集模型方法基线方法任务模型方法基于LLM的KGC的知识前缀适配器知识前缀适配器 与其他结构信息引入方法对比 3.实验结果与分析结果分析&#xff1a;可移植性实验&#xff1…

SQL 关键字参考手册(二)

目录 SQL 关键字 SQL CREATE TABLE 关键字 CREATE TABLE 使用另一个表创建表 SQL CREATE PROCEDURE 关键字 CREATE PROCEDURE SQL CREATE UNIQUE INDEX 关键字 CREATE UNIQUE INDEX SQL CREATE VIEW 关键字 CREATE VIEW 查询视图 SQL DATABASE 关键字 CREATE DATA…

Web3 游戏开发者的数据分析指南

作者&#xff1a;lesleyfootprint.network 在竞争激烈的 Web3 游戏行业中&#xff0c;成功不仅仅取决于游戏的发布&#xff0c;还需要在游戏运营过程中有高度的敏锐性&#xff0c;以应对下一次牛市的来临。 人们对 2024 年的游戏行业充满信心。A16Z GAMES 和 GAMES FUND ONE …

【VUE】v-if 和 v-show 大详解(多角度分析+面试简答版)

多角度分析+面试简答版 一、`v-if` 和 `v-show` 的区别之多角度分析控制手段:编译过程:编译条件:性能消耗:总结使用场景二、 `v-if`、`v-show`、`display:none` 和`visibility: hidden` 的区别三、简洁版回答:`v-show` 与 `v-if` 比较一、v-if 和 v-show 的区别之多角度分…

网络安全热门岗位大盘点

网络安全已成为当今社会不可或缺的重要领域&#xff0c;国家和企业越来越重视网络安全&#xff0c;众多厂商也纷纷加大网络安全岗位的招聘力度。如果你对网络安全感兴趣&#xff0c;不妨了解一下这些热门岗位&#xff01; &#x1f3af;首席信息官&#xff08;CISO&#xff09;…

【iOS ARKit】同时开启前后摄像头BlendShapes

在上一节中已经了解了 iOS ARkit 进行BlendShapes的基本操作&#xff0c;这一小节继续实践同时开启前后摄像头进行人脸捕捉和世界追踪。 iOS设备配备了前后两个摄像头&#xff0c;在运行AR 应用时&#xff0c;需要选择使用哪个摄像头作为图像输人。最常见的AR 体验使用设备后置…

vue项目中使用Element多个Form表单同时验证

一、项目需求 在项目中一个页面中需要实现多个Form表单&#xff0c;并在页面提交时需要对多个Form表单进行校验&#xff0c;多个表单都校验成功时才能提交。 二、实现效果 三、多个表单验证 注意项&#xff1a;多个form表单&#xff0c;每个表单上都设置单独的model和ref&am…

单片机学习笔记---矩阵键盘密码锁

目录 一&#xff0c;设置密码按键 1.设置密码区域 2.设置输入的数字左移 3.设置记录按键的次数 二&#xff0c;设置确认键 1.密码正确时显示OK 2.密码错误时显示ERR 3.密码错误恢复初始状态重输 三&#xff0c;设置取消键 学了这么久&#xff0c;迫不及待想要做一个密…

bind函数的认识与基本使用

目录 测试代码 使用测试 预留参数测试 关于细节可以参考文献 第六节 std::bind 绑定器 - 知乎 (zhihu.com) 为了后续工作&#xff0c;需要了解bind的使用 简单来说就是为一个函数绑定一个参数&#xff0c;以方便后续调用函数的时候不需要传参进去 测试代码 #include <…

uni-app 接口封装,token过期,自动获取最新的token

一、文件路径截图 2、新建一个文件app.js let hosthttp://172.16.192.40:8083/jeecg-boot/ //本地接口 let myApi {login: ${host}wx/wxUser/login, //登录 } module.exports myApi 3、新建一个文件request.js import myApi from /utils/app.js; export const r…

HbuilderX报错“Error: Fail to open IDE“,以及运行之后没有打开微信开发者,或者运行没有反应的解决办法

开始 问题:HbuilderX启动时,打开微信开发者工具报错"Error: Fail to open IDE",以及运行之后没有打开微信开发者,或者运行没有反应的解决办法! 解决办法: 按照步骤一步一步完成分析,除非代码报错,否则都是可以启动的 第一步:检查HbuildX是否登录账号 第二步:检查微信…

运行adprep /forestprep扩展Active Directory架构

运行 adprep /forestprep 是为了扩展Active Directory架构&#xff0c;以便为整个林添加新版本Windows Server所支持的新类、属性和其他目录对象。在升级到更高版本的Windows Server并提升林功能级别之前&#xff0c;通常需要执行此操作。 以下是详细步骤&#xff1a; 确认环境…

C语言第十弹---函数(上)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】 函数 1、函数的概念 2、库函数 2.1、标准库和头文件 2.2、库函数的使用方法 2.2.1、功能 2.2.2、头文件包含 2.2.3、实践 2.2.4、库函数文档的⼀般格式 …

SpringBoot之分页查询的使用

背景 在业务中我们在前端总是需要展示数据&#xff0c;将后端得到的数据进行分页处理&#xff0c;通过pagehelper实现动态的分页查询&#xff0c;将查询页数和分页数通过前端发送到后端&#xff0c;后端使用pagehelper&#xff0c;底层是封装threadlocal得到页数和分页数并动态…

1.22ABM仿真(netlogo),A*(简要)

NETLOGO ABM建模 A* &#xff0c;体现了当前的步数成本 H为启发值&#xff0c;由启发性公式决定&#xff0c; 就是成本F由两个要素确定&#xff0c;一个是实际位置的成本G&#xff0c;由其自身固定的地理位置决定&#xff0c;另一个是启发值H&#xff0c;由下一个位置与目标…