grpc-教程(golang版)

目录

一、介绍

二、环境准备

三、Golang中使用grpc

1.编写protobuf文件

2.服务端

3.客户端

四、proto文件详解

1.proto语法

2.数据类型 

基本数据类型

数组类型

map类型

嵌套类型

编写风格

3.多服务 

4.多个proto文件

五、流式传输

1.普通rpc

2.服务器流式

3.客户端流式 

4.双向流

六、配套代码


一、介绍

RPCRemote Procedure Call的简称,中文叫远程过程调用,简单的说,就是调用远程方法和调用本地方法一样

那么grpc就是由 google开发的一个高性能、通用的开源RPC框架

官网地址:Introduction to gRPC | gRPC

  1. gRPC是一种现代开源高性能远程过程调用(RPC)框架,可在任何环境中运行。
  2. 它可以高效地连接数据中心内的服务,并支持负载平衡、跟踪、健康检查和身份验证等插件功能。
  3. 它适用于分布式计算的最后一英里,以连接设备、移动应用程序和浏览器到后端服务。
  4. 公司已使用gRPC连接其环境中的多个服务,从连接少数服务到跨多种语言的数据中心内数百种服务。
  5. gRPC最初由谷歌创建,用于连接在其数据中心内和跨越其数据中心的大量微服务。

二、环境准备

以Golang使用为例,只需要在windows上安装protoc转换工具

官网地址:Go | gRPC

​# 下载网址:

https://github.com/protocolbuffers/protobuf/releases/download/v3.9.0/protoc-3.9.0-win64.zip

# go语言需要安装的依赖
go get github.com/golang/protobuf/proto
go get google.golang.org/grpc
go install github.com/golang/protobuf/protoc-gen-go

安装好之后,需要将protoc的bin目录添加到环境变量中

还需要将protoc-gen-go.exe的目录添加到环境变量中

刚刚添加之后,可能需要重启电脑或者重启goland,才能在goland的terminal中使用

三、Golang中使用grpc

整体结构如图:

1.编写protobuf文件

现在还没有学过怎么编写,不用担心,先复制粘贴就行了,主要是用于测试环境是否正常

创建文件夹 /grpc_proto 在该文件夹中创建文件 hello.proto ,编写内容如下:

syntax = "proto3"; // 指定proto版本
package hello_grpc;     // 指定默认包名// 指定golang包名
option go_package = "/hello_grpc";//定义rpc服务
service HelloService {// 定义函数rpc SayHello (HelloRequest) returns (HelloResponse) {}
}// HelloRequest 请求内容
message HelloRequest {string name = 1;string message = 2;
}// HelloResponse 响应内容
message HelloResponse{string name = 1;string message = 2;
}

在文件夹 /grpc_proto 中执行命令:protoc -I . --go_out=plugins=grpc:. .\hello.proto

或者编写set.bat批处理文件,方便使用,内容如下:

protoc -I . --go_out=plugins=grpc:.\grpc_proto .\grpc_proto\hello.proto

2.服务端

package mainimport ("context""fmt""google.golang.org/grpc""google.golang.org/grpc/grpclog""net""oslee/grpc_study/1base/grpc_proto/hello_grpc"
)// HelloServer 得有一个结构体,需要实现这个服务的全部方法,叫什么名字不重要
type HelloServer struct {
}func (HelloServer) SayHello(ctx context.Context, request *hello_grpc.HelloRequest) (*hello_grpc.HelloResponse, error) {fmt.Println("入参:", request.Name, request.Message)return &hello_grpc.HelloResponse{Name:    "server",Message: "hello " + request.Name,}, nil
}func main() {// 监听端口listen, err := net.Listen("tcp", ":8080")if err != nil {grpclog.Fatalf("Failed to listen: %v", err)}// 创建一个gRPC服务器实例。s := grpc.NewServer()server := HelloServer{}// 将server结构体注册为gRPC服务。hello_grpc.RegisterHelloServiceServer(s, &server)fmt.Println("grpc server running :8080")// 开始处理客户端请求。err = s.Serve(listen)
}

3.客户端

package mainimport ("context""fmt""google.golang.org/grpc""google.golang.org/grpc/credentials/insecure""log""oslee/grpc_study/1base/grpc_proto/hello_grpc"
)func main() {addr := ":8080"// 使用 grpc.Dial 创建一个到指定地址的 gRPC 连接。// 此处使用不安全的证书来实现 SSL/TLS 连接conn, err := grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials()))if err != nil {log.Fatalf(fmt.Sprintf("grpc connect addr [%s] 连接失败 %s", addr, err))}defer conn.Close()// 初始化客户端client := hello_grpc.NewHelloServiceClient(conn)result, err := client.SayHello(context.Background(), &hello_grpc.HelloRequest{Name:    "client",Message: "hello",})fmt.Println(result, err)
}

调用结果如下:

四、proto文件详解

1.proto语法

  • service 对应的就是go里面的接口,可以作为服务端,客户端
  • rpc 对应的就是结构体中的方法
  • message对应的也是结构体

2.数据类型 

基本数据类型

message Request {
  double a1 = 1;
  float a2 = 2;
  int32 a3 = 3;
  uint32 a4 = 4;
  uint64 a5 = 5;
  sint32 a6 = 6;
  sint64 a7 = 7;
  fixed32 a8 = 8;
  fixed64 a9 = 9;
  sfixed32 a10 = 10;
  sfixed64 a11 = 11;
  bool a12 = 12;
  string a13 = 13;
  bytes a14 = 14;
}

对应go类型

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

  A1  float64 `protobuf:"fixed64,1,opt,name=a1,proto3" json:"a1,omitempty"`
  A2  float32 `protobuf:"fixed32,2,opt,name=a2,proto3" json:"a2,omitempty"`
  A3  int32   `protobuf:"varint,3,opt,name=a3,proto3" json:"a3,omitempty"`
  A4  uint32  `protobuf:"varint,4,opt,name=a4,proto3" json:"a4,omitempty"`
  A5  uint64  `protobuf:"varint,5,opt,name=a5,proto3" json:"a5,omitempty"`
  A6  int32   `protobuf:"zigzag32,6,opt,name=a6,proto3" json:"a6,omitempty"`
  A7  int64   `protobuf:"zigzag64,7,opt,name=a7,proto3" json:"a7,omitempty"`
  A8  uint32  `protobuf:"fixed32,8,opt,name=a8,proto3" json:"a8,omitempty"`
  A9  uint64  `protobuf:"fixed64,9,opt,name=a9,proto3" json:"a9,omitempty"`
  A10 int32   `protobuf:"fixed32,10,opt,name=a10,proto3" json:"a10,omitempty"`
  A11 int64   `protobuf:"fixed64,11,opt,name=a11,proto3" json:"a11,omitempty"`
  A12 bool    `protobuf:"varint,12,opt,name=a12,proto3" json:"a12,omitempty"`
  A13 string  `protobuf:"bytes,13,opt,name=a13,proto3" json:"a13,omitempty"`
  A14 []byte  `protobuf:"bytes,14,opt,name=a14,proto3" json:"a14,omitempty"`
}

标量类型

.proto Type解释Go Type
doublefloat64
floatfloat32
int32使用变长编码,对于负值的效率很低,如果你的域有可能有负值,请使用sint64替代int32
uint32使用变长编码uint32
uint64使用变长编码uint64
sint32使用变长编码,这些编码在负值时比int32高效的多int32
sint64使用变长编码,有符号的整型值。编码时比通常的int64高效int64
fixed32总是4个字节,如果数值总是比总是比228大的话,这个类型会比uint32高效。uint32
fixed64总是8个字节,如果数值总是比总是比256大的话,这个类型会比uint64高效。uint64
sfixed32总是4个字节int32
sfixed64总是8个字节int64
boolbool
string一个字符串必须是UTF-8编码或者7-bit ASCII编码的文本string
bytes可能包含任意顺序的字节数据[]byte

标量类型如果没有被赋值,则不会被序列化,解析时,会赋予默认值

  • strings:空字符串
  • bytes:空序列
  • bools:false
  • 数值类型:0

数组类型

message ArrayRequest {
  repeated int64 a1 = 1;
  repeated string a2 = 2;
  repeated Request request_list = 3;
}

对应go类型 

type ArrayRequest struct {
  A1          []int64 
  A2          []string   
  RequestList []*Request
}

map类型

键只能是基本类型

message MapRequest {
  map<int64, string> m_i_s = 1;
  map<string, bool> m_i_b = 2;
  map<string, ArrayRequest> m_i_arr = 3;
}

对应go类型 

type MapRequest struct {

  MIS   map[int64]string
  MIB   map[string]bool
  MIArr map[string]*ArrayRequest
}

嵌套类型

message Q1 {
  message Q2{
    string name2 = 2;
  }
  string name1 = 1;
  Q2 q2 = 2;
}

对应go类型

type Q1 struct {
  state         protoimpl.MessageState
  sizeCache     protoimpl.SizeCache
  unknownFields protoimpl.UnknownFields
  Name1 string `protobuf:"bytes,1,opt,name=name1,proto3" json:"name1,omitempty"`
  Q2    *Q1_Q2 `protobuf:"bytes,2,opt,name=q2,proto3" json:"q2,omitempty"`
}

个人习惯不嵌套,分开写

编写风格

  • 文件名建议下划线,例如:my_student.proto
  • 包名和目录名对应
  • 服务名、方法名、消息名均为大驼峰
  • 字段名为下划线

3.多服务 

proto文件

syntax = "proto3"; // 指定proto版本
// 指定golang包名
option go_package = "/duo_server";service VideoService {rpc Look(Request)returns(Response){}
}service OrderService {rpc Buy(Request)returns(Response){}
}message Request{string name = 1;
}
message Response{string name = 1;
}

服务端

package mainimport ("context""fmt""google.golang.org/grpc""log""net""oslee/grpc_study/2duo_server/grpc_proto/duo_server"
)type VideoServer struct {
}func (VideoServer) Look(ctx context.Context, request *duo_server.Request) (res *duo_server.Response, err error) {fmt.Println("video:", request)return &duo_server.Response{Name: "server",}, nil
}type OrderServer struct {
}func (OrderServer) Buy(ctx context.Context, request *duo_server.Request) (res *duo_server.Response, err error) {fmt.Println("order:", request)return &duo_server.Response{Name: "server",}, nil
}func main() {listen, err := net.Listen("tcp", ":8080")if err != nil {log.Fatal(err)}s := grpc.NewServer()duo_server.RegisterVideoServiceServer(s, &VideoServer{})duo_server.RegisterOrderServiceServer(s, &OrderServer{})fmt.Println("grpc server程序运行在:8080")err = s.Serve(listen)
}

客户端

package mainimport ("context""fmt""google.golang.org/grpc""google.golang.org/grpc/credentials/insecure""log""oslee/grpc_study/2duo_server/grpc_proto/duo_server"
)func main() {addr := ":8080"// 使用 grpc.Dial 创建一个到指定地址的 gRPC 连接。// 此处使用不安全的证书来实现 SSL/TLS 连接conn, err := grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials()))if err != nil {log.Fatalf(fmt.Sprintf("grpc connect addr [%s] 连接失败 %s", addr, err))}defer conn.Close()orderClient := duo_server.NewOrderServiceClient(conn)res, err := orderClient.Buy(context.Background(), &duo_server.Request{Name: "client",})fmt.Println(res, err)videoClient := duo_server.NewVideoServiceClient(conn)res, err = videoClient.Look(context.Background(), &duo_server.Request{Name: "client",})fmt.Println(res, err)}

4.多个proto文件

当项目大起来之后,会有很多个service,rpc,message

我们会将不同服务放在不同的proto文件中,还可以放一些公共的proto文件

本质就是生成go文件,需要在一个包内

proto文件 

common.proto

syntax = "proto3";
package proto;
option go_package = "/proto";message Request{string name = 1;
}
message Response{string name = 1;
}

order.proto

syntax = "proto3";
package proto;
option go_package = "/proto";
import "common.proto";service OrderService {rpc Look(Request)returns(Response){}
}

video.proto

syntax = "proto3";
package proto;
option go_package = "/proto";
import "common.proto";service VideoService {rpc Look(Request)returns(Response){}
}

服务端

package mainimport ("context""fmt""google.golang.org/grpc""log""net""oslee/grpc_study/3duo_proto/grpc_proto/proto"
)type VideoServer struct {
}func (VideoServer) Look(ctx context.Context, request *proto.Request) (res *proto.Response, err error) {fmt.Println("video:", request)return &proto.Response{Name: "server",}, nil
}type OrderServer struct {
}func (OrderServer) Look(ctx context.Context, request *proto.Request) (res *proto.Response, err error) {fmt.Println("order:", request)return &proto.Response{Name: "server",}, nil
}func main() {listen, err := net.Listen("tcp", ":8080")if err != nil {log.Fatal(err)}s := grpc.NewServer()proto.RegisterVideoServiceServer(s, &VideoServer{})proto.RegisterOrderServiceServer(s, &OrderServer{})fmt.Println("grpc server程序运行在:8080")err = s.Serve(listen)
}

客户端

package mainimport ("context""fmt""google.golang.org/grpc""google.golang.org/grpc/credentials/insecure""log""oslee/grpc_study/3duo_proto/grpc_proto/proto"
)func main() {addr := ":8080"// 使用 grpc.Dial 创建一个到指定地址的 gRPC 连接。// 此处使用不安全的证书来实现 SSL/TLS 连接conn, err := grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials()))if err != nil {log.Fatalf(fmt.Sprintf("grpc connect addr [%s] 连接失败 %s", addr, err))}defer conn.Close()orderClient := proto.NewOrderServiceClient(conn)res, err := orderClient.Look(context.Background(), &proto.Request{Name: "client",})fmt.Println(res, err)videoClient := proto.NewVideoServiceClient(conn)res, err = videoClient.Look(context.Background(), &proto.Request{Name: "client",})fmt.Println(res, err)
}

五、流式传输

1.普通rpc

一问一答式,文章前面已讲

2.服务器流式

建立连接后,不知道服务端什么时候发送完毕,使用场景:下载文件 

 proto文件

syntax = "proto3";
option go_package = "/proto";message Request {string name = 1;
}message FileResponse{string file_name = 1;bytes content = 2;
}
service ServiceStream{rpc DownLoadFile(Request)returns(stream FileResponse){}
}

服务端

package mainimport ("fmt""google.golang.org/grpc""io""log""net""os""oslee/grpc_study/4download/grpc_proto/proto"
)type ServiceStream struct{}func (ServiceStream) DownLoadFile(request *proto.Request, stream proto.ServiceStream_DownLoadFileServer) error {fmt.Println(request)file, err := os.Open("F:\\yckj\\workspace_gitee\\1own\\os_lee\\go_grpc_study\\res\\protoc-3.9.0-win64.zip")if err != nil {return err}defer file.Close()for {buf := make([]byte, 2048)_, err = file.Read(buf)if err == io.EOF {break}if err != nil {break}stream.Send(&proto.FileResponse{Content: buf,})}return nil
}func main() {listen, err := net.Listen("tcp", ":8080")if err != nil {log.Fatal(err)}server := grpc.NewServer()proto.RegisterServiceStreamServer(server, &ServiceStream{})server.Serve(listen)
}

 客户端

package mainimport ("bufio""context""fmt""google.golang.org/grpc""google.golang.org/grpc/credentials/insecure""io""log""os""oslee/grpc_study/4download/grpc_proto/proto"
)func main() {addr := ":8080"// 使用 grpc.Dial 创建一个到指定地址的 gRPC 连接。// 此处使用不安全的证书来实现 SSL/TLS 连接conn, err := grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials()))if err != nil {log.Fatalf(fmt.Sprintf("grpc connect addr [%s] 连接失败 %s", addr, err))}defer conn.Close()// 初始化客户端client := proto.NewServiceStreamClient(conn)stream, err := client.DownLoadFile(context.Background(), &proto.Request{Name: "张三",})file, err := os.OpenFile("F:\\yckj\\workspace_gitee\\1own\\os_lee\\go_grpc_study\\res\\protoc-3.9.0-win64_down.zip", os.O_CREATE|os.O_WRONLY, 0600)if err != nil {log.Fatalln(err)}defer file.Close()writer := bufio.NewWriter(file)var index intfor {index++response, err := stream.Recv()if err == io.EOF {break}fmt.Printf("第%d 次, 写入 %d 数据\n", index, len(response.Content))writer.Write(response.Content)}writer.Flush()
}

3.客户端流式 

建立连接后,不知道客户端什么时候发送完毕,使用场景:上传文件

proto文件

syntax = "proto3";
option go_package = "/proto";
message Response {string Text = 1;
}
message FileRequest{string file_name = 1;bytes content = 2;
}
service ClientStream{rpc UploadFile(stream FileRequest)returns(Response){}
}

服务端

package mainimport ("bufio""fmt""google.golang.org/grpc""io""log""net""os""oslee/grpc_study/5upload/grpc_proto/proto"
)type ClientStream struct{}func (ClientStream) UploadFile(stream proto.ClientStream_UploadFileServer) error {file, err := os.OpenFile("F:\\yckj\\workspace_gitee\\1own\\os_lee\\go_grpc_study\\res\\protoc-3.9.0-win64_up.zip", os.O_CREATE|os.O_WRONLY, 0600)if err != nil {log.Fatalln(err)}defer file.Close()writer := bufio.NewWriter(file)var index intfor {index++response, err := stream.Recv()if err == io.EOF {break}writer.Write(response.Content)fmt.Printf("第%d次", index)}writer.Flush()stream.SendAndClose(&proto.Response{Text: "完毕了"})return nil
}func main() {listen, err := net.Listen("tcp", ":8080")if err != nil {log.Fatal(err)}server := grpc.NewServer()proto.RegisterClientStreamServer(server, &ClientStream{})server.Serve(listen)
}

客户端

package mainimport ("context""fmt""google.golang.org/grpc""google.golang.org/grpc/credentials/insecure""io""log""os""oslee/grpc_study/5upload/grpc_proto/proto"
)func main() {addr := ":8080"// 使用 grpc.Dial 创建一个到指定地址的 gRPC 连接。// 此处使用不安全的证书来实现 SSL/TLS 连接conn, err := grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials()))if err != nil {log.Fatalf(fmt.Sprintf("grpc connect addr [%s] 连接失败 %s", addr, err))}defer conn.Close()// 初始化客户端client := proto.NewClientStreamClient(conn)stream, err := client.UploadFile(context.Background())file, err := os.Open("F:\\yckj\\workspace_gitee\\1own\\os_lee\\go_grpc_study\\res\\protoc-3.9.0-win64.zip")if err != nil {log.Fatalln(err)}defer file.Close()for {buf := make([]byte, 2048)_, err = file.Read(buf)if err == io.EOF {break}if err != nil {break}stream.Send(&proto.FileRequest{FileName: "x.png",Content:  buf,})}response, err := stream.CloseAndRecv()fmt.Println(response, err)
}

4.双向流

使用场景:聊天室

proto文件

syntax = "proto3";
option go_package = "/proto";message Request {string name = 1;
}
message Response {string Text = 1;
}service BothStream{rpc Chat(stream Request)returns(stream Response){}
}

服务端

package mainimport ("fmt""google.golang.org/grpc""log""net""oslee/grpc_study/6chat/grpc_proto/proto"
)type BothStream struct{}func (BothStream) Chat(stream proto.BothStream_ChatServer) error {for i := 0; i < 10; i++ {request, _ := stream.Recv()fmt.Println(request)stream.Send(&proto.Response{Text: "你好",})}return nil
}func main() {listen, err := net.Listen("tcp", ":8080")if err != nil {log.Fatal(err)}server := grpc.NewServer()proto.RegisterBothStreamServer(server, &BothStream{})server.Serve(listen)
}

客户端 

package mainimport ("context""fmt""google.golang.org/grpc""google.golang.org/grpc/credentials/insecure""log""oslee/grpc_study/6chat/grpc_proto/proto"
)func main() {addr := ":8080"// 使用 grpc.Dial 创建一个到指定地址的 gRPC 连接。// 此处使用不安全的证书来实现 SSL/TLS 连接conn, err := grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials()))if err != nil {log.Fatalf(fmt.Sprintf("grpc connect addr [%s] 连接失败 %s", addr, err))}defer conn.Close()// 初始化客户端client := proto.NewBothStreamClient(conn)stream, err := client.Chat(context.Background())for i := 0; i < 10; i++ {stream.Send(&proto.Request{Name: fmt.Sprintf("第%d次", i),})response, err := stream.Recv()fmt.Println(response, err)}
}

六、配套代码

代码下载地址:

go_grpc_study: Golang使用grpc教程

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

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

相关文章

03-JAVA设计模式-责任链模式

责任链模式 什么是责任链模式 责任链模式&#xff08;Chain of Responsibility Pattern&#xff09;是一种行为设计模式&#xff0c;允许你将请求沿着处理者链进行传递。每个处理者均对请求进行某些处理&#xff0c;并可决定是否将请求沿着链传递下去。这种模式给予请求的处理…

使用 Meltano 将数据从 Snowflake 导入到 Elasticsearch:开发者之旅

作者&#xff1a;来自 Elastic Dmitrii Burlutskii 在 Elastic 的搜索团队中&#xff0c;我们一直在探索不同的 ETL 工具以及如何利用它们将数据传输到 Elasticsearch&#xff0c;并在传输的数据上实现 AI 助力搜索。今天&#xff0c;我想与大家分享我们与 Meltano 生态系统以及…

ThreadX:怎么确定一个线程应该开多少内存

ThreadX&#xff1a;如何确定线程的大小 在实时操作系统&#xff08;RTOS&#xff09;ThreadX中&#xff0c;线程的大小是一个重要的参数。这个参数决定了线程的堆栈大小&#xff0c;也就是线程可以使用的内存空间。那么&#xff0c;我们应该如何确定一个线程需要多大的字节呢…

应用实战|从头开始开发记账本2:基于模板快速开始

上期视频我们创建好了BaaS服务的后端应用。从这期视频开始&#xff0c;我们将从头开发一个互联网记账本应用。本期视频我们介绍一下如何使用模板快速开启我们的应用开发之旅。 应用实战&#xff5c;从头开始开发记账本2&#xff1a;基于模板快速开始 相关代码 本期视频我们介绍…

37% 的公开共享文件暴露个人信息

许多存储在 Google Drive、Slack 和其他协作工作应用程序等平台上的敏感文档几个月甚至几年都无人看管。根据 Metomic 的“金融服务数据安全状况”报告&#xff0c;这给公司带来了数据蔓延的挑战&#xff0c;也给个人及其雇主带来了重大的数据安全威胁。 86% 的文件在 90 天内…

问题汇总

一、TCP的粘包和拆包问题&#xff1f; TCP在发送和接受数据的时候&#xff0c;有一个滑动窗口来控制接受数据的大小&#xff0c;这个滑动窗口你就可以理解为一个缓冲区的大小。缓冲区满了就会把数据发送&#xff0c;数据包的大小是不固定的&#xff0c;有时候比缓冲区大有时候…

Git分布式版本控制系统——Git常用命令(二)

五、Git常用命令————分支操作 同一个仓库可以有多个分支&#xff0c;各个分支相互独立&#xff0c;互不干扰 分支的相关命令&#xff0c;具体如下&#xff1a; git branch 查看分支 git branch [name] 创建分支&#x…

04异常Lambda算法正则

异常 异常是什么&#xff1f; 异常是代码在编译或者执行的过程中可能出现的错误。避免异常的出现&#xff0c;同时处理可能出现的异常&#xff0c;让代码更稳健。 异常分为几类&#xff1f; 编译时异常、运行时异常。编译时异常&#xff1a;没有继承RuntimeExcpetion的异常…

Android适配平板屏幕尺寸

一、划分手机和平板 人为判断方法: 大于6英寸的就是平板。小于6英寸的都是手机 平板尺寸&#xff1a; 6英寸、7英寸、10英寸、14英寸… Android系统支持多配置资源文件&#xff0c;我们可以追加新的资源目录到你的Android项目中。命名规范&#xff1a; 资源名字-限制符 l…

c++调python接口

1. 新建run.py文件&#xff0c;并定义相关接口&#xff1a; import numpy as np from scipy.fftpack import fftdef str_add(str1,str2):return int(str1) int(str2)def my_sort(data):data.sort()return datadef aw_fft(data, Fs):N len(data)result np.abs(fft(xdata, n…

Git 解决分支冲突

一、前言 一直习惯于 add commit push 的三步走&#xff0c;偶然间看到了一个评论说在 push 之前还有一个 pull&#xff0c;小小的疑问就埋在了我的心里。于是我就先了解了 pull 的工作原理&#xff0c;就是先拉取代码&#xff08;fetch&#xff09;再合并分支&#xff08;mer…

C语言-----结构体详解

前面已经向大家介绍过一点结构体的知识了&#xff0c;这次我们再来深度了解一下结构体。结构体是能够方便表示一个物体具有多种属性的一种结构。物体的属性可以转换为结构体中的变量。 1.结构体类型的声明 1.1 结构体的声明 struct tag {member-list;//结构体成员变量 }vari…

数字化时代:IT界的致富秘籍 —— 策略×态度×机遇

&#x1f31f; 数字化时代&#xff1a;IT界的致富秘籍&#x1f680; —— 策略态度机遇 在数字化浪潮中冲浪&#x1f3c4;‍♂️&#xff0c;IT行业犹如一片无限可能的蓝海。想要在这片汹涌的海洋中捕获财富&#xff0c;不是一件轻而易举的事。让我们一起深入探索&#xff0c;如…

(学习日记)2024.04.16:UCOSIII第四十四节:内存管理

写在前面&#xff1a; 由于时间的不足与学习的碎片化&#xff0c;写博客变得有些奢侈。 但是对于记录学习&#xff08;忘了以后能快速复习&#xff09;的渴望一天天变得强烈。 既然如此 不如以天为单位&#xff0c;以时间为顺序&#xff0c;仅仅将博客当做一个知识学习的目录&a…

Elastic 线下 Meetup 将于 2024 年 4 月 27 号在重庆举办

2024 Elastic Meetup 重庆站活动&#xff0c;由 Elastic、新智锦绣联合举办&#xff0c;现诚邀广大技术爱好者及开发者参加。 活动时间 2024年4月27日 13:30-18:00 活动地点 中国重庆 沙坪坝区学城大道62-1号研发楼一期b3栋1楼(瑞幸咖啡旁&#xff09; 活动流程 14:00-14:50…

【电控笔记4】拉普拉斯-传递函数-pid

数据标幺化 拉普拉斯变换 欧拉公式 常见s变换 s变换性质

《QT实用小工具·二十一》鼠标十字线

1、概述 源码放在文章末尾 该项目实现了界面绘制十字线并跟随鼠标移动的过程&#xff0c;下面是demo演示&#xff1a; 项目部分代码如下&#xff1a; #ifndef WIDGET_H #define WIDGET_H#include <QWidget>namespace Ui { class Widget; }class Widget : public QWidg…

PE文件的分析和构造超详细过程

本文详细讲述如何从0构造一个PE文件&#xff0c;运行该文件会弹出一个HelloPE的窗口 目录 预备知识 1. 构造DOS头IMAGE_DOS_HEADER 1.1 构造DOS_MZ头 1.2 构造DOS_STUB 2、构造PE头IMAGE_NT_HEADERS 248字节 2.1 signature 2.2 IMAGE_FILE_HEADER 2.3 IMAGE_OPTI…

Jenkins UI 自动化持续化集成测试

一&#xff1a;安装jenkins 环境 在官网下载msi 直接安装即可 二&#xff1a;设置全局变量 设置allure 路径 三&#xff1a;创建项目 1、创建自由风格项目 2、如果项目在本地&#xff0c;且本地服务器是windows &#xff0c;找到Jenkins安装根目录&#xff0c;寻找config…

Excel从零基础到高手【办公】

第1课 - 快速制作目录【上篇】第1课 - 快速制作目录【下篇】第2课 - 快速定位到工作表的天涯海角第3课 - 如何最大化显示工作表的界面第4课 - 给你的表格做个瘦身第5课 - 快速定位目标区域所在位置第6课 - 快速批量填充序号第7课 - 按自定义的序列排序第8课 - 快速删除空白行第…