当前位置: 首页 > news >正文

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教程

相关文章:

grpc-教程(golang版)

目录 一、介绍 二、环境准备 三、Golang中使用grpc 1.编写protobuf文件 2.服务端 3.客户端 四、proto文件详解 1.proto语法 2.数据类型 基本数据类型 数组类型 map类型 嵌套类型 编写风格 3.多服务 4.多个proto文件 五、流式传输 1.普通rpc 2.服务器流式 …...

Spring与Spring Boot的区别:从框架设计到应用开发

这是我自己开发的一款小程序&#xff0c;感兴趣的可以体验一下&#xff1a; 进入正题&#xff1a; 在Java开发领域&#xff0c;Spring和Spring Boot都是备受推崇的框架&#xff0c;它们为开发人员提供了丰富的功能和便捷的开发体验。然而&#xff0c;许多人对它们之间的区别仍…...

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&#xff0c;b这个位置上的数是y&#xff0c;那么此位置至少是max(x,y)1进制。一定要把位置找对啊 #include <bits/stdc.h> using namespace std; typedef l…...

JavaEE初阶Day 6:多线程(4)

目录 Day 6&#xff1a;多线程&#xff08;4&#xff09;1. 线程不安全的原因2. 锁3. synchronized Day 6&#xff1a;多线程&#xff08;4&#xff09; 前序&#xff1a;针对Day 5结尾的count 多线程的执行&#xff0c;是随机调度抢占式的执行模式&#xff0c;某个线程执行指…...

微信小程序 django+nodejs电影院票务售票选座系统324kd

小程序Android端运行软件 微信开发者工具/hbuiderx uni-app框架&#xff1a;使用Vue.js开发跨平台应用的前端框架&#xff0c;编写一套代码&#xff0c;可编译到Android、小程序等平台。 前端&#xff1a;HTML5,CSS3 VUE 后端&#xff1a;java(springbootssm)/python(flaskdja…...

基于springboot实现桂林旅游景点导游平台管理系统【项目源码+论文说明】计算机毕业设计

基于springboot实现桂林旅游景点导游平台管理系统演示 摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了桂林旅游景点导游平台的开发全过程。通过分析桂林旅游景点导游平台管理的不足&#xff0c;创建了一个计算…...

idea 开发serlvet汽车租赁管理系统idea开发sqlserver数据库web结构计算机java编程layUI框架开发

一、源码特点 idea开发 java servlet 汽车租赁管理系统是一套完善的web设计系统sqlserver数据库 系统采用serlvetdaobean mvc 模式开发&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。 java se…...

Unity之PUN实现多人联机射击游戏的优化(Section 3)

目录 &#x1f4a3;一、准备工作 &#x1f4a3;二、生成弹头脚本的编写 &#x1f4a3;三、实现发射和伤害同步 手雷都加了在给狗剩加个火箭筒不过分吧。效果看GIF动图&#xff0c;分别是单机和联机的效果。 添加火箭筒依旧是在原有的基础上更改&#xff0c;我查看火箭筒模型…...

PDF锐化

PDF Shaper Ultimate(pdf转图片) 编辑->添加文件->选中一个要处理的pdf 操作->转换->PDF转为图片 ComicEnhancerPro设置(把图片锐化) PDF Shaper Ultimate(图片转pdf) 编辑-添加图片->选中所有锐化处理后的图片 转换->图片转为pdf&#xff08;会把所有图…...

【python和java】

如何理解java和python的不同&#xff0c;在java中&#xff0c;先有类&#xff0c;类生出对象&#xff0c;对象承载数据。而python是直接数据&#xff0c;没有类的概念 理解 Java 和 Python 在面向对象编程&#xff08;OOP&#xff09;方面的不同&#xff0c;关键在于理解它们各…...

C盘满了怎么办,清理工具TreeSize

TreeSize是一款强大的磁盘空间分析工具&#xff0c;它可以帮助用户轻松地找出电脑中占用空间最多的文件和程序&#xff0c;从而让用户进行针对性地删除或卸载。 占用空间很小 下载链接&#xff1a;https://pan.quark.cn/s/bea23ed6b1d3...

【vue】watch 侦听器

watch&#xff1a;可监听值的变化&#xff0c;旧值和新值 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><titl…...

校招生如何准备软件测试、测试开发岗位的面试?

校招生如何准备软件测试、测试开发岗位的面试&#xff1f; 求职建议 大家都很困惑如何学习测试&#xff1f;如何准备测试方面的面试&#xff1f; 我有朋友是做研发的&#xff0c;他认为测试不用准备&#xff0c;直接用开发的简历就行。也有人认为要学习一些测试理论&#xf…...

蓝桥杯抱佛脚篇~

文章目录 基础语法输入输出集合(set&#xff09;排序 基础语法 输入输出 # 输入一个数 nint(input())# 输入两、三个数&#xff0c;例如&#xff1a;1 2 或者 1 2 3 x,y map(int,input().split())# 输入数组 # ——— 1 —— nums[int(i) for i in input().split()] print(n…...

基于springboot的大学城水电管理系统源码数据库

基于springboot的大学城水电管理系统源码数据库 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了大学城水电管理系统的开发全过程。通过分析大学城水电管理系统管理的不足&#xff0c;创建了一个计算机管理大学城水…...

AI大模型探索之路-应用篇2:Langchain框架ModelIO模块—数据交互的秘密武器

目录 前言 一、概述 二、Model 三、Prompt 五、Output Parsers 总结 前言 随着人工智能技术的不断进步&#xff0c;大模型的应用场景越来越广泛。LangChain框架作为一个创新的解决方案&#xff0c;专为处理大型语言模型的输入输出而设计。其中&#xff0c;Model IO&#…...

【SSH】群晖开启ssh访问

群晖开启ssh访问 假设 你需要设置群晖 账号 test-user 开启ssh访问 设置 你的 test-user 为管理员权限 否则你无法通过cmd 面板 连接访问 群晖你需要哪个账号 就使用哪个账号终端 cmd连接 否则需要考虑后续创建 rsa 公密钥文件的 所属权 问题账号密码连接登录终端 ssh -p 端…...

Vue 移动端(H5)项目怎么实现页面缓存(即列表页面进入详情返回后列表页面缓存且还原页面滚动条位置)keep-alive缓存及清除keep-alive缓存

一、需求 产品要求&#xff1a;Vue移动端项目进入列表页&#xff0c;列表页需要刷新&#xff0c;而从详情页返回列表页&#xff0c;列表页则需要缓存并且还原页面滚动条位置 二、实现思路 1、使用Vue中的keep-alive组件&#xff0c;keep-alive提供了路由缓存功能 2、因为我项…...

【MVCC】深入浅出彻底理解MVCC

MVCC概述 MVCC&#xff08;Multi-Version Concurrency Control&#xff09;即多版本并发控制。主要是为了提高数据库的并发性能而提供的&#xff0c;采用了不加锁的方式处理读-写并发冲突&#xff0c;确保了任何时刻的读操作都是非阻塞的。只需要很小的开销&#xff0c;就可以…...

【问题解决】ubuntu安装新版vscode报code-insiders相关错误

问题 目前 vscode官网 最新的包为 insiders_1.89.0-1712297812_amd64.deb &#xff0c;双击或者使用sudo dpkg -i code-insiders_1.89.0-1712297812_amd64.deb安装后报错&#xff0c;执行其他命令也报错。 安装环境&#xff1a;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&#xff0c;找打设置的图标&#xff0c;在点击设置&#xff0c;或者直接使用快捷键&#xff0c;【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&#xff1a;从前台传递过来的参数不能为null,如果为空&#xff0c;会在控制台日志中把message打印出来 Range&#xff1a;范围&#xff0c;最大多少&#xff0c;最小多少 Patten&#xff0c;标注的字段值必须符合定义的正则表达式&#xff08;按照业务规则&#xff0…...

Harmony鸿蒙南向驱动开发-UART接口使用

功能简介 UART指异步收发传输器&#xff08;Universal Asynchronous Receiver/Transmitter&#xff09;&#xff0c;是通用串行数据总线&#xff0c;用于异步通信。该总线双向通信&#xff0c;可以实现全双工传输。 两个UART设备的连接示意图如下&#xff0c;UART与其他模块一…...

【示例】MySQL-事务控制示例:账户转账-savepoint关键字

前言 本文讲述MySQL中的事务&#xff0c;以账户转账为例&#xff0c;体会事务的概念&#xff0c;并讲解事务相关的一个关键字用法&#xff1a;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中的板文件后&#xff0c;想使用I/O设备模型&#xff0c;使用串口3或者串口4收发时&#xff0c;发现串口3或者串口4没反应。出现问题&#xff1a;程序一直跑在 while (__HAL_UART_…...

MVCC(解决MySql中的并发事务的隔离性)

MVCC 如何保证事务的隔离性&#xff1f; 1.排他锁&#xff1a;如一个事务获取了一个数据行的排他锁&#xff0c;其他事务就不能再获取改行的其他锁。 2.MVCC&#xff1a;多版本并发控制。 MVCC&#xff1a; 1.隐藏字段 1.DB_TRX_ID&#xff1a;最近修改事务的id。默认值从0开…...

第四十八章 为 Web 应用程序实现 HTTP 身份验证 - 在处理请求之前在 CSP 中进行身份验证

文章目录 第四十八章 为 Web 应用程序实现 HTTP 身份验证 - 在处理请求之前在 CSP 中进行身份验证在处理请求之前在 CSP 中进行身份验证。 第四十八章 为 Web 应用程序实现 HTTP 身份验证 - 在处理请求之前在 CSP 中进行身份验证 在处理请求之前在 CSP 中进行身份验证。 这是…...

专业的网站制作中心/百度推广登陆网址

1 数据封装 所有数据对象如image或surface都包含在DataNode中&#xff0c;这些DataNode描述数据本身&#xff08;mitk::BaseData及其派生类&#xff09;&#xff0c;如&#xff1a; - 以二维或三维的渲染方式呈现&#xff08;一个mitk::Mapper列表&#xff09;&#xff1b;- 与…...

下载类网站做多久才有流量/福州网站优化

首先说一下 出于性能优化的角度 要尽量少用Activity&#xff0c;甚至只用1个&#xff0c;界面用碎片来承载&#xff0c;这样便于内存以及业务逻辑的管理single instance 适合整个安卓系统只有这样一个活动的情形&#xff0c;比如launcher&#xff0c;所以一般用不到single task…...

漳州做网站建设/网站设计公司北京

目录 环境 BUG/漏洞编码 症状 触发条件 解决方案 环境 系统平台&#xff1a;N/A 版本&#xff1a;4.3.4,4.3.4.2,4.3.4.3,4.3.4.4,4.3.4.5 BUG/漏洞编码 症状 安全版数据库admin工具自生成SQL语句会将NULL值转换为’NULL’ 导致用工具修改表数据报错。 触发条件 在…...

做网站论坛 前置许可/长沙疫情最新数据消息

场景一&#xff1a;类似于微博&#xff0c;实现关注和被关注功能。 思路&#xff1a; 对每个用户使用两个集合类型键&#xff0c;用来存储关注别人的用户和被该用户关注的用户。当用户A关注用户B的时候&#xff0c;执行两步操作&#xff1a; sadd user:A B sadd user:B A 问题1…...

百度权重查询方法/东莞百度推广优化排名

为什么80%的码农都做不了架构师&#xff1f;>>> 运行一下 python manage.py sycdb 报错&#xff1a; ImproperlyConfigured: Error loading MySQLdb module: No module named MySQLdb 运行&#xff1a; pip install mysql-python 报错&#xff1a;EnvironmentError…...

网站建设问题大全/平原县网站seo优化排名

今天给大家分享一个最新开发的Vue3自定义模拟滚动条组件v3scroll。V3Scroll 基于vue3.0开发的轻量级PC端虚拟滚动条组件。支持是否自动隐藏、自定义大小、颜色及层叠等功能。开发灵感来自于之前的vue2版自定义滚动条组件。借鉴了ElementPlus滚动条设计。vue2.x自定义桌面端滚动…...