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'
相关文章:
Go语言grpc服务开发——Protocol Buffer
文章目录 一、Protocol Buffer简介二、Protocol Buffer编译器安装三、proto3语言指南四、序列化与反序列化五、引入grpc-gateway1、插件安装2、定义proto文件3、生成go文件4、实现Service服务5、gRPC服务启动方法6、gateway服务启动方法7、main函数启动8、验证 相关参考链接&am…...
【开源】基于JAVA语言的实验室耗材管理系统
目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 耗材档案模块2.2 耗材入库模块2.3 耗材出库模块2.4 耗材申请模块2.5 耗材审核模块 三、系统展示四、核心代码4.1 查询耗材品类4.2 查询资产出库清单4.3 资产出库4.4 查询入库单4.5 资产入库 五、免责说明 一、摘要 1.1…...
金智易表通构建学生缴费数据查询+帆软构建缴费大数据报表并整合到微服务
使用金智易表通挂接外部数据,快速建设查询类服务,本次构建学生欠费数据查询,共有3块设计,规划如下: 1、欠费明细查询:学校领导和财务处等部门可查询全校欠费学生明细数据;各二级学院教职工可查询本二级学院欠费学生明细数据。 2、大数据统计报表:从应收总额、欠费总额…...
MySQL复合索引
复合索引是指在数据库表上同时包含两个或更多列的索引。它们对于优化涉及这些列的查询非常有效,特别是当这些列常常在查询条件(如WHERE子句)、排序(ORDER BY子句)和连接(JOIN条件)中使用时。 复…...
Web3 游戏开发者的数据分析指南
作者:lesleyfootprint.network 在竞争激烈的 Web3 游戏行业中,成功不仅仅取决于游戏的发布,还需要在游戏运营过程中有高度的敏锐性,以应对下一次牛市的来临。 人们对 2024 年的游戏行业充满信心。A16Z GAMES 和 GAMES FUND ONE …...
temu跨境电商怎么样?做temu蓝海项目有哪些优势?
在全球电商市场激烈的竞争中,Temu跨境电商平台以其独特的优势和策略,逐渐崭露头角。对于许多想要拓展海外市场的商家来说,Temu的蓝海项目提供了一个充满机遇的新平台。本文将深入探讨Temu跨境电商的优势以及在蓝海市场中的发展前景。 全球化市…...
C#使用RabbitMQ-1_Docker部署并在c#中实现简单模式消息代理
介绍 RabbitMQ是一个开源的消息队列系统,实现了高级消息队列协议(AMQP)。 🍀RabbitMQ起源于金融系统,现在广泛应用于各种分布式系统中。它的主要功能是在应用程序之间提供异步消息传递,实现系统间的解耦和…...
EasyExcel中自定义拦截器的运用
在EasyExcel中自定义拦截器不仅可以帮助我们不止步于数据的填充,而且可以对样式、单元格合并等带来便捷的功能。下面直接开始 我们定义一个MergeWriteHandler的类继承AbstractMergeStrategy实现CellWriteHandler public class MergeLastWriteHandler extends Abst…...
shell编程-7
shell学习第7天 sed的学习1.sed是什么2.sed有两个空间pattern hold3.sed的语法4. sed里单引号和双引号的区别:5.sed的查找方式6.sed的命令sed的标签用法sed的a命令:追加sed的i命令:根据行号插入sed的c命令:整行替换sed的r命令sed的s命令:替换sed的d命令:删除sed中的&符号 7…...
工业智能网关储能物联网应用实现能源的高效利用及远程管理
储能电力物联网是指利用物联网技术和储能技术相结合,实现对电力系统中各种储能设备的智能管理和优化控制。随着可再生能源的不断发展和应用,电力系统面临着越来越大的电力调度和储能需求而储能电力物联网的出现可以有效解决这一问题,提高电力…...
虹科数字化与AR部门升级为安宝特AR子公司
致关心虹科AR的朋友们: 感谢您一直以来对虹科数字化与AR的支持和信任,为了更好地满足市场需求和公司发展的需要,虹科数字化与AR部门现已升级为虹科旗下独立子公司,并正式更名为“安宝特AR”。 ”虹科数字化与AR“自成立以来&…...
服务器是什么?(四种服务器类型)
服务器 服务器定义广义: 专门给其他机器提供服务的计算机。狭义:一台高性能的计算机,通过网络提供外部计算机一些业务服务 个人PC内存大概8G,服务器内存128G起步 服务器是什么 服务器指的是 网络中能对其他机器提供某些服务的计算机系统 ,相对…...
09-微服务Sentinel整合GateWay
一、概述 在微服务系统中,网关提供了微服务系统的统一入口,所以我们在做限流的时候,肯定是要在网关层面做一个流量的控制,Sentinel 支持对 Spring Cloud Gateway、Zuul 等主流的 API Gateway 进行限流。 1.1 总览 Sentinel 1.6.…...
python基础学习-03 安装
python3 可应用于多平台包括 Windows、Linux 和 Mac OS X。 Unix (Solaris, Linux, FreeBSD, AIX, HP/UX, SunOS, IRIX, 等等。)Win 9x/NT/2000Macintosh (Intel, PPC, 68K)OS/2DOS (多个DOS版本)PalmOSNokia 移动手机Windows CEAcorn/RISC OSBeOSAmigaVMS/OpenVMSQNXVxWorksP…...
HTML — 区块元素
HTML 通过各种标签将元素组合起来。 一. 区块元素 大多数 HTML 元素被定义为块级元素或内联元素。块级元素在浏览器显示时,通常会以新的行开始。例如:<div>、<h1>、<p>、<ul>等。 它们在使用时会独自占据一行,称为块…...
《PCI Express体系结构导读》随记 —— 第I篇 第3章 PCI总线的数据交换(4)
接前一篇文章:《PCI Express体系结构导读》随记 —— 第I篇 第3章 PCI总线的数据交换(3) 3.2 PCI设备的数据传递 PCI设备的数据传递使用地址译码方式,当一个存储器读写总线事务到达PCI总线时,在这条总线上的所有PCI设…...
力扣0083——删除排序链表中的重复元素
删除排序链表中的重复元素 难度:简单 题目描述 给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。 示例1 输入:head [1,1,2] 输出:[1,2]示例2 输入:…...
MySQL数据库的一些缩写含义
DDL Data Definition Language,数据定义语言,用来定义数据库对象(数据库,表,字段) DML DML英文全称是Data Manipulation Language(数据操作语言),用来对数据库中表的数据记录进 行增、删、改操作。 添加数据&#x…...
解决 ssh: connect to host github.com port 22: Connection timed out
问题 今天使用git克隆github上的代码时,一直报错 原以为是公钥过期了,就尝试修改配置公钥,但是尝试了几次都不行,最终在博客上找到了解决方案,在次记录一下,以备不时之需 解决ssh-connect-to-host-github…...
【iOS ARKit】同时开启前后摄像头BlendShapes
在上一节中已经了解了 iOS ARkit 进行BlendShapes的基本操作,这一小节继续实践同时开启前后摄像头进行人脸捕捉和世界追踪。 iOS设备配备了前后两个摄像头,在运行AR 应用时,需要选择使用哪个摄像头作为图像输人。最常见的AR 体验使用设备后置…...
地震勘探——干扰波识别、井中地震时距曲线特点
目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波:可以用来解决所提出的地质任务的波;干扰波:所有妨碍辨认、追踪有效波的其他波。 地震勘探中,有效波和干扰波是相对的。例如,在反射波…...
Admin.Net中的消息通信SignalR解释
定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...
条件运算符
C中的三目运算符(也称条件运算符,英文:ternary operator)是一种简洁的条件选择语句,语法如下: 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true,则整个表达式的结果为“表达式1”…...
转转集团旗下首家二手多品类循环仓店“超级转转”开业
6月9日,国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解,“超级…...
leetcodeSQL解题:3564. 季节性销售分析
leetcodeSQL解题:3564. 季节性销售分析 题目: 表:sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...
NLP学习路线图(二十三):长短期记忆网络(LSTM)
在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...
DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”
目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...
#Uniapp篇:chrome调试unapp适配
chrome调试设备----使用Android模拟机开发调试移动端页面 Chrome://inspect/#devices MuMu模拟器Edge浏览器:Android原生APP嵌入的H5页面元素定位 chrome://inspect/#devices uniapp单位适配 根路径下 postcss.config.js 需要装这些插件 “postcss”: “^8.5.…...
Redis:现代应用开发的高效内存数据存储利器
一、Redis的起源与发展 Redis最初由意大利程序员Salvatore Sanfilippo在2009年开发,其初衷是为了满足他自己的一个项目需求,即需要一个高性能的键值存储系统来解决传统数据库在高并发场景下的性能瓶颈。随着项目的开源,Redis凭借其简单易用、…...
Python+ZeroMQ实战:智能车辆状态监控与模拟模式自动切换
目录 关键点 技术实现1 技术实现2 摘要: 本文将介绍如何利用Python和ZeroMQ消息队列构建一个智能车辆状态监控系统。系统能够根据时间策略自动切换驾驶模式(自动驾驶、人工驾驶、远程驾驶、主动安全),并通过实时消息推送更新车…...
