grpc-教程(golang版)
目录
一、介绍
二、环境准备
三、Golang中使用grpc
1.编写protobuf文件
2.服务端
3.客户端
四、proto文件详解
1.proto语法
2.数据类型
基本数据类型
数组类型
map类型
嵌套类型
编写风格
3.多服务
4.多个proto文件
五、流式传输
1.普通rpc
2.服务器流式
3.客户端流式
4.双向流
六、配套代码
一、介绍
RPC
是Remote Procedure Call
的简称,中文叫远程过程调用,简单的说,就是调用远程方法和调用本地方法一样
那么grpc就是由 google
开发的一个高性能、通用的开源RPC
框架
官网地址:Introduction to gRPC | gRPC
- gRPC是一种现代开源高性能远程过程调用(RPC)框架,可在任何环境中运行。
- 它可以高效地连接数据中心内的服务,并支持负载平衡、跟踪、健康检查和身份验证等插件功能。
- 它适用于分布式计算的最后一英里,以连接设备、移动应用程序和浏览器到后端服务。
- 公司已使用gRPC连接其环境中的多个服务,从连接少数服务到跨多种语言的数据中心内数百种服务。
- 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.UnknownFieldsA1 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 |
---|---|---|
double | float64 | |
float | float32 | |
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 |
bool | bool | |
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教程
相关文章:
grpc-教程(golang版)
目录 一、介绍 二、环境准备 三、Golang中使用grpc 1.编写protobuf文件 2.服务端 3.客户端 四、proto文件详解 1.proto语法 2.数据类型 基本数据类型 数组类型 map类型 嵌套类型 编写风格 3.多服务 4.多个proto文件 五、流式传输 1.普通rpc 2.服务器流式 …...
Spring与Spring Boot的区别:从框架设计到应用开发
这是我自己开发的一款小程序,感兴趣的可以体验一下: 进入正题: 在Java开发领域,Spring和Spring Boot都是备受推崇的框架,它们为开发人员提供了丰富的功能和便捷的开发体验。然而,许多人对它们之间的区别仍…...
React Hooks 全解: 常用 Hooks 及使用场景详解
React Hooks 是 React 16.8 版本引入的一项重要特性,它极大地简化和优化了函数组件的开发过程。 React 中常用的 10 个 Hooks,包括 useState、useEffect、useContext、useReducer、useCallback、useMemo、useRef、useLayoutEffect、useImperativeHandle 和 useDebugValue。这些…...
第十三届蓝桥杯真题:x进制减法,数组切分,gcd,青蛙过河
目录 x进制减法 数组切分 gcd 青蛙过河 x进制减法 其实就是一道观察规律的题。你发现如果a这个位置上的数x,b这个位置上的数是y,那么此位置至少是max(x,y)1进制。一定要把位置找对啊 #include <bits/stdc.h> using namespace std; typedef l…...
JavaEE初阶Day 6:多线程(4)
目录 Day 6:多线程(4)1. 线程不安全的原因2. 锁3. synchronized Day 6:多线程(4) 前序:针对Day 5结尾的count 多线程的执行,是随机调度抢占式的执行模式,某个线程执行指…...
微信小程序 django+nodejs电影院票务售票选座系统324kd
小程序Android端运行软件 微信开发者工具/hbuiderx uni-app框架:使用Vue.js开发跨平台应用的前端框架,编写一套代码,可编译到Android、小程序等平台。 前端:HTML5,CSS3 VUE 后端:java(springbootssm)/python(flaskdja…...
基于springboot实现桂林旅游景点导游平台管理系统【项目源码+论文说明】计算机毕业设计
基于springboot实现桂林旅游景点导游平台管理系统演示 摘要 随着信息技术在管理上越来越深入而广泛的应用,管理信息系统的实施在技术上已逐步成熟。本文介绍了桂林旅游景点导游平台的开发全过程。通过分析桂林旅游景点导游平台管理的不足,创建了一个计算…...
idea 开发serlvet汽车租赁管理系统idea开发sqlserver数据库web结构计算机java编程layUI框架开发
一、源码特点 idea开发 java servlet 汽车租赁管理系统是一套完善的web设计系统sqlserver数据库 系统采用serlvetdaobean mvc 模式开发,对理解JSP java编程开发语言有帮助,系统具有完整的源代码和数据库,系统主要采用B/S模式开发。 java se…...
Unity之PUN实现多人联机射击游戏的优化(Section 3)
目录 💣一、准备工作 💣二、生成弹头脚本的编写 💣三、实现发射和伤害同步 手雷都加了在给狗剩加个火箭筒不过分吧。效果看GIF动图,分别是单机和联机的效果。 添加火箭筒依旧是在原有的基础上更改,我查看火箭筒模型…...
PDF锐化
PDF Shaper Ultimate(pdf转图片) 编辑->添加文件->选中一个要处理的pdf 操作->转换->PDF转为图片 ComicEnhancerPro设置(把图片锐化) PDF Shaper Ultimate(图片转pdf) 编辑-添加图片->选中所有锐化处理后的图片 转换->图片转为pdf(会把所有图…...
【python和java】
如何理解java和python的不同,在java中,先有类,类生出对象,对象承载数据。而python是直接数据,没有类的概念 理解 Java 和 Python 在面向对象编程(OOP)方面的不同,关键在于理解它们各…...
C盘满了怎么办,清理工具TreeSize
TreeSize是一款强大的磁盘空间分析工具,它可以帮助用户轻松地找出电脑中占用空间最多的文件和程序,从而让用户进行针对性地删除或卸载。 占用空间很小 下载链接:https://pan.quark.cn/s/bea23ed6b1d3...
【vue】watch 侦听器
watch:可监听值的变化,旧值和新值 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><titl…...
校招生如何准备软件测试、测试开发岗位的面试?
校招生如何准备软件测试、测试开发岗位的面试? 求职建议 大家都很困惑如何学习测试?如何准备测试方面的面试? 我有朋友是做研发的,他认为测试不用准备,直接用开发的简历就行。也有人认为要学习一些测试理论…...
蓝桥杯抱佛脚篇~
文章目录 基础语法输入输出集合(set)排序 基础语法 输入输出 # 输入一个数 nint(input())# 输入两、三个数,例如:1 2 或者 1 2 3 x,y map(int,input().split())# 输入数组 # ——— 1 —— nums[int(i) for i in input().split()] print(n…...
基于springboot的大学城水电管理系统源码数据库
基于springboot的大学城水电管理系统源码数据库 随着信息技术在管理上越来越深入而广泛的应用,管理信息系统的实施在技术上已逐步成熟。本文介绍了大学城水电管理系统的开发全过程。通过分析大学城水电管理系统管理的不足,创建了一个计算机管理大学城水…...
AI大模型探索之路-应用篇2:Langchain框架ModelIO模块—数据交互的秘密武器
目录 前言 一、概述 二、Model 三、Prompt 五、Output Parsers 总结 前言 随着人工智能技术的不断进步,大模型的应用场景越来越广泛。LangChain框架作为一个创新的解决方案,专为处理大型语言模型的输入输出而设计。其中,Model IO&#…...
【SSH】群晖开启ssh访问
群晖开启ssh访问 假设 你需要设置群晖 账号 test-user 开启ssh访问 设置 你的 test-user 为管理员权限 否则你无法通过cmd 面板 连接访问 群晖你需要哪个账号 就使用哪个账号终端 cmd连接 否则需要考虑后续创建 rsa 公密钥文件的 所属权 问题账号密码连接登录终端 ssh -p 端…...
Vue 移动端(H5)项目怎么实现页面缓存(即列表页面进入详情返回后列表页面缓存且还原页面滚动条位置)keep-alive缓存及清除keep-alive缓存
一、需求 产品要求:Vue移动端项目进入列表页,列表页需要刷新,而从详情页返回列表页,列表页则需要缓存并且还原页面滚动条位置 二、实现思路 1、使用Vue中的keep-alive组件,keep-alive提供了路由缓存功能 2、因为我项…...
【MVCC】深入浅出彻底理解MVCC
MVCC概述 MVCC(Multi-Version Concurrency Control)即多版本并发控制。主要是为了提高数据库的并发性能而提供的,采用了不加锁的方式处理读-写并发冲突,确保了任何时刻的读操作都是非阻塞的。只需要很小的开销,就可以…...
【问题解决】ubuntu安装新版vscode报code-insiders相关错误
问题 目前 vscode官网 最新的包为 insiders_1.89.0-1712297812_amd64.deb ,双击或者使用sudo dpkg -i code-insiders_1.89.0-1712297812_amd64.deb安装后报错,执行其他命令也报错。 安装环境:ubuntu18.04 dpkg: 处理软件包 code-insiders (…...
【Python】面向对象(专版提升2)
面向对象 1. 概述1.1面向过程1.2 面向对象 2. 类和对象2.1 语法2.1.1 定义类2.1.2 实例化对象 2.2 实例成员2.2.1 实例变量2.2.2 实例方法2.2.3 跨类调用 3. 三大特征3.1 封装3.1.1 数据角度3.1.2 行为角度3.1.3 案例:信息管理系统3.1.3.1 需求3.1.3.2 分析3.1.3.3 设计 3.2 继…...
Vscode设置滚轮进行字体大小的调节
Vscode设置滚轮进行字体大小的调节 正常的话按 ctrl 或者 ctrl - 进行字体的大小调节 1.打开Vscode,找打设置的图标,在点击设置,或者直接使用快捷键,【ctrl ,】 2. 在搜索框搜索Font Ligatures 3.双击进入settings.json ,找到如…...
【QT入门】Qt自定义控件与样式设计之控件提升与自定义控件
【QT入门】Qt自定义控件与样式设计之控件提升与自定义控件 往期回顾 【QT入门】Qt自定义控件与样式设计之QProgressBar用法及qss-CSDN博客 【QT入门】 Qt自定义控件与样式设计之QSlider用法及qss-CSDN博客 【QT入门】Qt自定义控件与样式设计之qss的加载方式-CSDN博客 一、最终…...
Spring Validation解决后端表单校验
NotNull:从前台传递过来的参数不能为null,如果为空,会在控制台日志中把message打印出来 Range:范围,最大多少,最小多少 Patten,标注的字段值必须符合定义的正则表达式(按照业务规则࿰…...
Harmony鸿蒙南向驱动开发-UART接口使用
功能简介 UART指异步收发传输器(Universal Asynchronous Receiver/Transmitter),是通用串行数据总线,用于异步通信。该总线双向通信,可以实现全双工传输。 两个UART设备的连接示意图如下,UART与其他模块一…...
【示例】MySQL-事务控制示例:账户转账-savepoint关键字
前言 本文讲述MySQL中的事务,以账户转账为例,体会事务的概念,并讲解事务相关的一个关键字用法:savepoint 示例 数据准备 drop table if exists account;create table account(id int primary key AUTO_INCREMENT comment ID,n…...
STM32使用标准版RT-Thread,移植bsp中的板文件后,想使用I/O设备模型,使用串口3或者串口4收发时,发现串口3或者串口4没反应
STM32移植RT-Thread出现的问题及解决办法 问题原因解决方法 问题 使用标准版RT-Thread,移植bsp中的板文件后,想使用I/O设备模型,使用串口3或者串口4收发时,发现串口3或者串口4没反应。出现问题:程序一直跑在 while (__HAL_UART_…...
MVCC(解决MySql中的并发事务的隔离性)
MVCC 如何保证事务的隔离性? 1.排他锁:如一个事务获取了一个数据行的排他锁,其他事务就不能再获取改行的其他锁。 2.MVCC:多版本并发控制。 MVCC: 1.隐藏字段 1.DB_TRX_ID:最近修改事务的id。默认值从0开…...
第四十八章 为 Web 应用程序实现 HTTP 身份验证 - 在处理请求之前在 CSP 中进行身份验证
文章目录 第四十八章 为 Web 应用程序实现 HTTP 身份验证 - 在处理请求之前在 CSP 中进行身份验证在处理请求之前在 CSP 中进行身份验证。 第四十八章 为 Web 应用程序实现 HTTP 身份验证 - 在处理请求之前在 CSP 中进行身份验证 在处理请求之前在 CSP 中进行身份验证。 这是…...
专业的网站制作中心/百度推广登陆网址
1 数据封装 所有数据对象如image或surface都包含在DataNode中,这些DataNode描述数据本身(mitk::BaseData及其派生类),如: - 以二维或三维的渲染方式呈现(一个mitk::Mapper列表);- 与…...
下载类网站做多久才有流量/福州网站优化
首先说一下 出于性能优化的角度 要尽量少用Activity,甚至只用1个,界面用碎片来承载,这样便于内存以及业务逻辑的管理single instance 适合整个安卓系统只有这样一个活动的情形,比如launcher,所以一般用不到single task…...
漳州做网站建设/网站设计公司北京
目录 环境 BUG/漏洞编码 症状 触发条件 解决方案 环境 系统平台:N/A 版本:4.3.4,4.3.4.2,4.3.4.3,4.3.4.4,4.3.4.5 BUG/漏洞编码 症状 安全版数据库admin工具自生成SQL语句会将NULL值转换为’NULL’ 导致用工具修改表数据报错。 触发条件 在…...
做网站论坛 前置许可/长沙疫情最新数据消息
场景一:类似于微博,实现关注和被关注功能。 思路: 对每个用户使用两个集合类型键,用来存储关注别人的用户和被该用户关注的用户。当用户A关注用户B的时候,执行两步操作: sadd user:A B sadd user:B A 问题1…...
百度权重查询方法/东莞百度推广优化排名
为什么80%的码农都做不了架构师?>>> 运行一下 python manage.py sycdb 报错: ImproperlyConfigured: Error loading MySQLdb module: No module named MySQLdb 运行: pip install mysql-python 报错:EnvironmentError…...
网站建设问题大全/平原县网站seo优化排名
今天给大家分享一个最新开发的Vue3自定义模拟滚动条组件v3scroll。V3Scroll 基于vue3.0开发的轻量级PC端虚拟滚动条组件。支持是否自动隐藏、自定义大小、颜色及层叠等功能。开发灵感来自于之前的vue2版自定义滚动条组件。借鉴了ElementPlus滚动条设计。vue2.x自定义桌面端滚动…...