golang grpc进阶
protobuf
官方文档
基本数据类型
.proto Type | Notes | 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 |
- 在java中,无符号32位和64位整型被表示成他们的整型对应形似,最高位被储存在标志位中。
- 对于所有的情况,设定值会执行类型检查以确保此值是有效。
- 64位或者无符号32位整型在解码时被表示成为ilong,但是在设置时可以使用int型值设定,在所有的情况下,值必须符合其设置其类型的要求。
- python中string被表示成在解码时表示成unicode。但是一个ASCIIstring可以被表示成str类型。
- Integer在64位的机器上使用,string在32位机器上使用
当一个消息被解析的时候,如果被编码的信息不包含一个特定的singular元素,被解析的对象锁对应的域被设置位一个默认值,对于不同类型指定如下:
● 对于strings,默认是一个空string
● 对于bytes,默认是一个空的bytes
● 对于bools,默认是false
● 对于数值类型,默认是0
● 对于枚举,默认是第一个定义的枚举值,必须为0;
● 对于消息类型(message),域没有被设置,确切的消息是根据语言确定的,详见generated code guide
对于可重复域的默认值是空(通常情况下是对应语言中空列表)。
注:对于标量消息域,一旦消息被解析,就无法判断域释放被设置为默认值(例如,例如boolean值是否被设置为false)还是根本没有被设置。你应该在定义你的消息类型时非常注意。例如,比如你不应该定义boolean的默认值false作为任何行为的触发方式。也应该注意如果一个标量消息域被设置为标志位,这个值不应该被序列化传输。
查看generated code guide选择你的语言的默认值的工作细节。
proto文件中引入另一个proto文件
1.被引用的proto要先转成go代码
2.使用protobuf提供的proto引用格式与自定义的proto引用格式不同
base.proto
syntax = "proto3";
// 生成proto文件所在包路径
package protos;
// 影响go文件生成位置和包名
//.是当前文件夹
option go_package = ".;proto";
message Empty{}
message Pong{string id=1;
}
转成go代码
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative base.proto
hello.proto
syntax = "proto3";
// 生成proto文件所在包路径
package protos;
// 影响go文件生成位置和包名
//.是当前文件夹
option go_package = ".;proto";
import "base.proto";
import "google/protobuf/empty.proto";
service Greeter{rpc SayHello(HelloRquest)returns(HelloReply);//hello接口rpc Ping(google.protobuf.Empty)returns (Pong);
}
message HelloRquest{string name=1;//1是编号不是值string url=2;
}
message HelloReply{string message=1;
}
转成go代码
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative hello.proto
message嵌套
目录结构
hello.proto
syntax = "proto3";
// 生成proto文件所在包路径
package protos;
// 影响go文件生成位置和包名
//.是当前文件夹
option go_package = ".;proto";
import "base.proto";
import "google/protobuf/empty.proto";
service Greeter{rpc SayHello(HelloRquest)returns(HelloReply);//hello接口rpc Ping(google.protobuf.Empty)returns (Pong);
}
message HelloRquest{string name=1;//1是编号不是值string url=2;
}message HelloReply{string message=1;repeated Result data=2;message Result{string name=1;string url=2;}
}
转换
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative hello.proto
使用
package mainimport "GolangStudy/Introduction/grpc/example4/proto"func main() {_ = proto.HelloReply_Result{}
}
枚举类型
目录结构
hello2.proto
syntax = "proto3";// 生成proto文件所在包路径
package protos;
// 影响go文件生成位置和包名
//.是当前文件夹
option go_package = ".;proto1";
service Greeter{rpc SayHello(HelloRquest)returns(HelloReply);//hello接口
}
enum Gender{MALE=0;FEMALE=1;
}
message HelloRquest{string name=1;//1是编号不是值Gender g=3;
}
message HelloReply{string message=1;
}
转换
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative hello2.proto
调用
package mainimport (_ "GolangStudy/Introduction/grpc/example4/proto""GolangStudy/Introduction/grpc/example4/proto1""context""fmt""google.golang.org/grpc"
)func main() {// _ = proto.HelloReply_Result{}conn, err := grpc.Dial("127.0.0.1:8080", grpc.WithInsecure())if err != nil {panic(err)}defer conn.Close()c := proto1.NewGreeterClient(conn)r, err := c.SayHello(context.Background(), &proto1.HelloRquest{Name: "bobby",G: proto1.Gender_FEMALE,})if err != nil {panic(err)}fmt.Println(r.Message)
}
map类型
hello2.proto
syntax = "proto3";// 生成proto文件所在包路径
package protos;
// 影响go文件生成位置和包名
//.是当前文件夹
option go_package = ".;proto1";
service Greeter{rpc SayHello(HelloRquest)returns(HelloReply);//hello接口
}
enum Gender{MALE=0;FEMALE=1;
}
message HelloRquest{string name=1;//1是编号不是值Gender g=2;map<string,string> mp=3;
}
message HelloReply{string message=1;
}
转换
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative hello2.proto
使用
package mainimport (_ "GolangStudy/Introduction/grpc/example4/proto""GolangStudy/Introduction/grpc/example4/proto1""context""fmt""google.golang.org/grpc"
)func main() {// _ = proto.HelloReply_Result{}conn, err := grpc.Dial("127.0.0.1:8080", grpc.WithInsecure())if err != nil {panic(err)}defer conn.Close()c := proto1.NewGreeterClient(conn)r, err := c.SayHello(context.Background(), &proto1.HelloRquest{Name: "bobby",G: proto1.Gender_FEMALE,Mp: map[string]string{"name": "bobby","company": "mooc",},})if err != nil {panic(err)}fmt.Println(r.Message)
}
timestamp类型
hello2.proto
syntax = "proto3";// 生成proto文件所在包路径
package protos;
// 影响go文件生成位置和包名
//.是当前文件夹
option go_package = ".;proto1";
import "google/protobuf/timestamp.proto";
service Greeter{rpc SayHello(HelloRquest)returns(HelloReply);//hello接口
}
enum Gender{MALE=0;FEMALE=1;
}
message HelloRquest{string name=1;//1是编号不是值Gender g=2;map<string,string> mp=3;google.protobuf.Timestamp addTime=4;
}
message HelloReply{string message=1;
}
转换
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative hello2.proto
使用
package mainimport (_ "GolangStudy/Introduction/grpc/example4/proto""GolangStudy/Introduction/grpc/example4/proto1""context""fmt""time""google.golang.org/grpc""google.golang.org/protobuf/types/known/timestamppb"
)func main() {// _ = proto.HelloReply_Result{}conn, err := grpc.Dial("127.0.0.1:8080", grpc.WithInsecure())if err != nil {panic(err)}defer conn.Close()c := proto1.NewGreeterClient(conn)r, err := c.SayHello(context.Background(), &proto1.HelloRquest{Name: "bobby",G: proto1.Gender_FEMALE,Mp: map[string]string{"name": "bobby","company": "mooc",},AddTime: timestamppb.New(time.Now()),})if err != nil {panic(err)}fmt.Println(r.Message)
}
grpc
metadata
grpc让我们可以像本地调用一样实现远程调用,对于每一次的RPC调用中,都可能会有一些有用的数据,而这些数据就可以通过metadata来传递。metadata是以key-value的形式存储数据的,其中key是string类型,而value是[]string,即一个字符串数组类型。metadata使得client和server能够为对方提供关于本次调用的一些信息,就像一次http请求的RequestHeader和ResponseHeader一样。http中header的生命周周期是一次http请求,那么metadata的生命周期就是一次rpc调用。
实例化
//第一种方式
md := metadata.New(map[string]string{"key1": "val1", "key2": "val2"})
//第二种方式 key不区分大小写,会被统一转成小写。
md := metadata.Pairs("key1", "val1","key1", "val1-2", // "key1" will have map value []string{"val1", "val1-2"}"key2", "val2",
)
使用
//发送md := metadata.Pairs("key", "val")// 新建一个有 metadata 的 context
ctx := metadata.NewOutgoingContext(context.Background(), md)// 单向 RPC
response, err := client.SomeRPC(ctx, someRequest)
//接收
func (s *server) SomeRPC(ctx context.Context, in *pb.SomeRequest) (*pb.SomeResponse, err) {md, ok := metadata.FromIncomingContext(ctx)// do something with metadata
}
使用例子
拦截器
grpc Go支持“拦截器”,即在将请求传递到用户的应用程序逻辑之前在 grpc服务器上执行的中间件,或者在用户调用时在 grpc 客户端上执行的中间件。它是实现常见模式的完美方式:身份验证、日志记录、跟踪、指标、验证、重试、速率限制等,它们可以成为出色的通用构建块,让您轻松构建多个微服务。
实例
目录结构
hello.proto
syntax = "proto3";
option go_package = ".;proto";
service Greeter {rpc SayHello (HelloRequest) returns (HelloReply);
}
//将 sessionid放入 放入cookie中 http协议
message HelloRequest {string name = 1;
}message HelloReply {string message = 1;
}
生成
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative hello2.proto
server
package mainimport ("context""fmt""net""google.golang.org/grpc""GolangStudy/Introduction/grpc/interpretor/proto"
)type Server struct{}func (s *Server) SayHello(ctx context.Context, request *proto.HelloRequest) (*proto.HelloReply,error) {return &proto.HelloReply{Message: "hello " + request.Name,}, nil
}func main() {var interceptor grpc.UnaryServerInterceptorinterceptor = func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {// 继续处理请求fmt.Println("接收到新请求")res, err := handler(ctx, req)fmt.Println("请求处理完成")return res, err}var opts []grpc.ServerOptionopts = append(opts, grpc.UnaryInterceptor(interceptor))g := grpc.NewServer(opts...)proto.RegisterGreeterServer(g, &Server{})lis, err := net.Listen("tcp", "0.0.0.0:50051")if err != nil {panic("failed to listen:" + err.Error())}err = g.Serve(lis)if err != nil {panic("failed to start grpc:" + err.Error())}
}
client
package mainimport ("context""fmt""time""google.golang.org/grpc""GolangStudy/Introduction/grpc/interpretor/proto"
)func interceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {start := time.Now()err := invoker(ctx, method, req, reply, cc, opts...)fmt.Printf("method=%s req=%v rep=%v duration=%s error=%v\n", method, req, reply, time.Since(start), err)return err
}func main() {//streamvar opts []grpc.DialOptionopts = append(opts, grpc.WithInsecure())// 指定客户端interceptoropts = append(opts, grpc.WithUnaryInterceptor(interceptor))conn, err := grpc.Dial("localhost:50051", opts...)if err != nil {panic(err)}defer conn.Close()c := proto.NewGreeterClient(conn)r, err := c.SayHello(context.Background(), &proto.HelloRequest{Name: "bobby"})if err != nil {panic(err)}fmt.Println(r.Message)
}
拦截器框架
auth认证
hello.proto
syntax = "proto3";
option go_package = ".;proto";
service Greeter {rpc SayHello (HelloRequest) returns (HelloReply);
}message HelloRequest {string name = 1;
}message HelloReply {string message = 1;
}
生成
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative hello2.proto
server
package mainimport ("context""fmt""net""google.golang.org/grpc""google.golang.org/grpc/codes""google.golang.org/grpc/metadata""google.golang.org/grpc/status""GolangStudy/Introduction/grpc/token_auth/proto"
)type Server struct{}func (s *Server) SayHello(ctx context.Context, request *proto.HelloRequest) (*proto.HelloReply,error) {return &proto.HelloReply{Message: "hello " + request.Name,}, nil
}func main() {var interceptor grpc.UnaryServerInterceptorinterceptor = func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {// 继续处理请求fmt.Println("接收到新请求")md, ok := metadata.FromIncomingContext(ctx)if !ok {return resp, status.Error(codes.Unauthenticated, "无token认证信息")}var (appid stringappkey string)if va1, ok := md["appid"]; ok {appid = va1[0]}if va1, ok := md["appkey"]; ok {appkey = va1[0]}fmt.Println(appid, appkey)if appid != "101010" || appkey != "i am key" {return resp, status.Error(codes.Unauthenticated, "无token认证信息")}res, err := handler(ctx, req)fmt.Println("请求处理完成")return res, err}var opts []grpc.ServerOptionopts = append(opts, grpc.UnaryInterceptor(interceptor))g := grpc.NewServer(opts...)proto.RegisterGreeterServer(g, &Server{})lis, err := net.Listen("tcp", "0.0.0.0:50051")if err != nil {panic("failed to listen:" + err.Error())}err = g.Serve(lis)if err != nil {panic("failed to start grpc:" + err.Error())}
}
client
package mainimport ("context""fmt""GolangStudy/Introduction/grpc/token_auth/proto""google.golang.org/grpc"
)type customCredential struct{}func (cc *customCredential) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {return map[string]string{"appid": "101010","appkey": "i am key",}, nil
}// RequireTransportSecurity indicates whether the credentials requires
// transport security.
func (cc *customCredential) RequireTransportSecurity() bool {return false
}
func main() {// interceptor := func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {// start := time.Now()// md := metadata.New(map[string]string{// "appid": "101010",// "appkey": "i am key",// })// ctx = metadata.NewOutgoingContext(context.Background(), md)// err := invoker(ctx, method, req, reply, cc, opts...)// fmt.Printf("method=%s req=%v rep=%v duration=%s error=%v\n", method, req, reply, time.Since(start), err)// return err// }//streamvar opts []grpc.DialOptionopts = append(opts, grpc.WithInsecure())// 指定客户端interceptoropts = append(opts, grpc.WithPerRPCCredentials(&customCredential{}))conn, err := grpc.Dial("127.0.0.1:50051", opts...)if err != nil {panic(err)}defer conn.Close()c := proto.NewGreeterClient(conn)r, err := c.SayHello(context.Background(), &proto.HelloRequest{Name: "bobby"})if err != nil {panic(err)}fmt.Println(r.Message)
}
验证器
实例
Protocol Buffer Validation
使用的mac电脑,使用官网的第二种第三种都试过了不可以,第二种在$GOPATH:bin找不到protoc-gen-validate文件,第三种能找到但是将proto转换成go文件时会一直报如下错误,最后使用了第一种才成功。
protoc-gen-validate: program not found or is not executable
Please specify a program using absolute path or make sure the program is available in your PATH system variable
--validate_out: protoc-gen-validate: Plugin failed with status code 1.
//第一种
go install github.com/envoyproxy/protoc-gen-validate@latest//第二种
go get -d github.com/envoyproxy/protoc-gen-validate//第三种
git clone https://github.com/bufbuild/protoc-gen-validate.git
cd $GOPATH/bin
cd protoc-gen-validate
make build
hello.proto
syntax = "proto3";option go_package=".;proto";
import "validate.proto";service Greeter {rpc SayHello (Person) returns (Person);
}message Person {uint64 id = 1 [(validate.rules).uint64.gt = 999];string email = 2 [(validate.rules).string.email = true];string mobile = 3 [(validate.rules).string = {pattern: "^1[3456789]\\d{9}$"}];}
转换
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative --validate_out="lang=go:." hello.proto
client
package mainimport ("GolangStudy/Introduction/grpc/validate/proto""context""fmt""google.golang.org/grpc"
)type customCredential struct{}func main() {var opts []grpc.DialOption//opts = append(opts, grpc.WithUnaryInterceptor(interceptor))opts = append(opts, grpc.WithInsecure())conn, err := grpc.Dial("localhost:50051", opts...)if err != nil {panic(err)}defer conn.Close()c := proto.NewGreeterClient(conn)//rsp, _ := c.Search(context.Background(), &empty.Empty{})rsp, err := c.SayHello(context.Background(), &proto.Person{Id: 9999,Email: "bobby@qq.com",Mobile: "19999999999",})if err != nil {panic(err)}fmt.Println(rsp.Id)
}
server
package mainimport ("context""net""google.golang.org/grpc/codes""google.golang.org/grpc/status""google.golang.org/grpc""GolangStudy/Introduction/grpc/validate/proto"
)type Server struct{}func (s *Server) SayHello(ctx context.Context, request *proto.Person) (*proto.Person,error) {return &proto.Person{Id: 32,}, nil
}type Validator interface {Validate() error
}func main() {var interceptor grpc.UnaryServerInterceptorinterceptor = func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {// 继续处理请求if r, ok := req.(Validator); ok {if err := r.Validate(); err != nil {return nil, status.Error(codes.InvalidArgument, err.Error())}}return handler(ctx, req)}var opts []grpc.ServerOptionopts = append(opts, grpc.UnaryInterceptor(interceptor))g := grpc.NewServer(opts...)proto.RegisterGreeterServer(g, &Server{})lis, err := net.Listen("tcp", "0.0.0.0:50051")if err != nil {panic("failed to listen:" + err.Error())}err = g.Serve(lis)if err != nil {panic("failed to start grpc:" + err.Error())}
}
错误码
Code | Number | Description |
---|---|---|
OK | 0 | Not an error; returned on success. |
CANCELLED | 1 | 操作被取消,通常是由调用者取消的。 |
UNKNOWN | 2 | 未知错误。例如,当从另一个地址空间接收到的状态值属于该地址空间中未知的错误空间时,可能会返回此错误。此外,由未返回足够错误信息的 API 引发的错误也可能会转换为此错误。 |
INVALID_ARGUMENT | 3 | 客户端指定了无效参数。请注意,这与 FAILED_PRECONDITION 不同。 |
DEADLINE_EXCEEDED | 4 | 操作完成之前截止日期已过。对于更改系统状态的操作,即使操作已成功完成,也可能会返回此错误。例如,服务器的成功响应可能会延迟很长时间 |
NOT_FOUND | 5 | 未找到某些请求的实体(例如文件或目录)。服务器开发人员请注意:如果整个用户类别的请求被拒绝,例如逐步推出功能或未记录的许可名单,则可以使用 NOT_FOUND。如果拒绝一类用户中的某些用户的请求,例如基于用户的访问控制,则必须使用 PERMISSION_DENIED。 |
ALREADY_EXISTS | 6 | 客户端尝试创建的实体(例如文件或目录)已存在 |
PERMISSION_DENIED | 7 | 调用者没有执行指定操作的权限。 PERMISSION_DENIED 不得用于因耗尽某些资源而导致的拒绝(对于这些错误,请使用 RESOURCE_EXHAUSTED 代替)。如果无法识别调用者,则不得使用 PERMISSION_DENIED(对于这些错误,请使用 UNAUTHENTICATED)。此错误代码并不意味着请求有效或请求的实体存在或满足其他先决条件 |
RESOURCE_EXHAUSTED | 8 | 某些资源已耗尽,可能是每个用户的配额,或者可能是整个文件系统空间不足。 |
FAILED_PRECONDITION | 9 | 操作被拒绝,因为系统未处于操作执行所需的状态。例如,要删除的目录非空、rmdir 操作应用于非目录等。服务实现者可以使用以下准则来决定 FAILED_PRECONDITION、ABORTED 和 UNAVAILABLE: (a) 如果客户端可以仅重试失败的调用。 (b) 如果客户端应在更高级别重试(例如,当客户端指定的测试和设置失败时,指示客户端应重新启动读取-修改-写入序列),则使用 ABORTED。 © 如果客户端在系统状态明确修复之前不应重试,则使用 FAILED_PRECONDITION。例如,如果“rmdir”由于目录非空而失败,则应返回 FAILED_PRECONDITION,因为除非从目录中删除文件,否则客户端不应重试。 |
ABORTED | 10 | 操作被中止,通常是由于并发问题,例如定序器检查失败或事务中止。请参阅上面的指南来决定 FAILED_PRECONDITION、ABORTED 和 UNAVAILABLE。 |
OUT_OF_RANGE | 11 | 尝试的操作超出了有效范围。例如,查找或读取文件末尾之后的内容。与 INVALID_ARGUMENT 不同,此错误指示如果系统状态发生更改则可以修复的问题。例如,如果要求读取不在 [0,2^32-1] 范围内的偏移量,32 位文件系统将生成 INVALID_ARGUMENT,但如果要求读取超过当前偏移量的偏移量,则会生成 OUT_OF_RANGE文件大小。 FAILED_PRECONDITION 和 OUT_OF_RANGE 之间有相当多的重叠。我们建议在应用时使用 OUT_OF_RANGE(更具体的错误),以便迭代空间的调用者可以轻松查找 OUT_OF_RANGE 错误以检测何时完成。 |
UNIMPLEMENTED | 12 | 此服务未实现或不支持/启用该操作。 |
INTERNAL | 13 | 内部错误。这意味着底层系统所期望的一些不变量已经被打破。该错误代码是为严重错误保留的。 |
UNAVAILABLE | 14 | 该服务目前不可用。这很可能是一种瞬态情况,可以通过后退重试来纠正。请注意,重试非幂等操作并不总是安全的。 |
DATA_LOSS | 15 | 不可恢复的数据丢失或损坏。 |
UNAUTHENTICATED | 16 | 该请求没有该操作的有效身份验证凭据 |
实例
服务端
status.New(codes.InvalidArgument, "invalid username")
客户端
st, ok := status.FromError(err)
if !ok {// Error was not a status error
}
st.Message()
st.Code()
grpc的超时机制
ctx, cancel := context.WithTimeout(context.TODO(), time.Second*3)
defer cancel()r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name})
实例
相关文章:
golang grpc进阶
protobuf 官方文档 基本数据类型 .proto TypeNotesGo Typedoublefloat64floatfloat32int32使用变长编码,对于负值的效率很低,如果你的域有可能有负值,请使用sint64替代int32uint32使用变长编码uint32uint64使用变长编码uint64sint32使用变长…...
Java JUC(三) AQS与同步工具详解
Java JUC(三) AQS与同步工具详解 一. ReentrantLock 概述 ReentrantLock 是 java.util.concurrent.locks 包下的一个同步工具类,它实现了 Lock 接口,提供了一种相比synchronized关键字更灵活的锁机制。ReentrantLock 是一种独占…...
使用rust写一个Web服务器——async-std版本
文章目录 实现异步代码并发地处理连接使用多线程提升性能 使用rust实现一个异步运行时是async-std的单线程Web服务器。 仓库地址: 1037827920/web-server: 使用rust编写的简单web服务器 (github.com) 在之前的单线程版本的Web服务器代码上进行修改,具体…...
C语言复习概要(一)
本文 C语言入门详解:从基础概念到分支与循环1. C语言常见概念1.1 程序的基本结构1.2 变量作用域和存储类1.3 输入输出1.4 编译与运行 2. C语言中的数据类型和变量2.1 基本数据类型2.2 变量的声明与初始化2.3 常量与枚举 3. C语言的分支结构3.1 if语句3.2 if-else语句…...
二、kafka生产与消费全流程
一、使用java代码生产、消费消息 1、生产者 package com.allwe.client.simple;import lombok.extern.slf4j.Slf4j; import org.apache.kafka.clients.producer.KafkaProducer; import org.apache.kafka.clients.producer.ProducerConfig; import org.apache.kafka.clients.pr…...
本地搭建OnlyOffice在线文档编辑器结合内网穿透实现远程协作
文章目录 前言1. 安装Docker2. 本地安装部署ONLYOFFICE3. 安装cpolar内网穿透4. 固定OnlyOffice公网地址 前言 本篇文章讲解如何使用Docker在本地Linux服务器上安装ONLYOFFICE,并结合cpolar内网穿透实现公网访问本地部署的文档编辑器与远程协作。 Community Editi…...
ScrapeGraphAI 大模型增强的网络爬虫
在数据驱动的动态领域,从在线资源中提取有价值的见解至关重要。从市场分析到学术研究,对特定数据的需求推动了对强大的网络抓取工具的需求。 NSDT工具推荐: Three.js AI纹理开发包 - YOLO合成数据生成器 - GLTF/GLB在线编辑 - 3D模型格式在线…...
PDF转换为TIF,JPG的一个简易工具(含下载链接)
目录 0.前言: 1.工具目录 2.工具功能(效果),如何运行 效果 PDF转换为JPG(带颜色) PDF转换为TIF(LZW形式压缩,可以显示子的深浅) PDF转换为TIF(CCITT形…...
Wireshark 解析QQ、微信的通信协议|TCP|UDP
写在前面 QQ,微信这样的聊天软件。我们一般称为im,Instant Messaging,即时通讯系统。那大家会不会有疑问,自己聊天内容会不会被黑客或者不法分子知道?这种体量的im是基于tcp还是udp呢?这篇文章我们就来探索…...
网络编程(5)——模拟伪闭包实现连接的安全回收
六、day6 今天学习如何利用C11模拟伪闭包实现连接的安全回收,之前的异步服务器为echo模式,但存在安全隐患,在极端情况下客户端关闭可能会导致触发写和读回调函数,二者都进入错误处理逻辑,进而造成二次析构。今天学习如…...
C#绘制动态曲线
前言 用于实时显示数据动态曲线,比如:SOC。 //用于绘制动态曲线,可置于定时函数中,定时更新数据曲线 void DrawSocGraph() {double f (double)MainForm.readData[12]; //display datachart1.Series[0].Points.Add(f);if (ch…...
用Python实现运筹学——Day 10: 线性规划的计算机求解
一、学习内容 1. 使用 Python 的 scipy.optimize.linprog 进行线性规划求解 scipy.optimize.linprog 是 Python 中用于求解线性规划问题的函数。它实现了单纯形法、内点法等算法,能够处理求解最大化或最小化问题,同时满足线性约束条件。 线性规划问题的…...
[C++]使用C++部署yolov11目标检测的tensorrt模型支持图片视频推理windows测试通过
官方框架: https://github.com/ultralytics/ultralytics yolov8官方最近推出yolov11框架,标志着目标检测又多了一个检测利器,于是尝试在windows下部署yolov11的tensorrt模型,并最终成功。 重要说明:安装环境视为最基…...
霍夫曼树及其与B树和决策树的异同
霍夫曼树是一种用于数据压缩的二叉树结构,通常应用于霍夫曼编码算法中。它的主要作用是通过对符号进行高效编码,减少数据的存储空间。霍夫曼树在压缩领域扮演着重要角色,与B树、决策树等数据结构都有一些相似之处,但又在应用场景和…...
CompletableFuture常用方法
一、获得结果和触发计算 1.获取结果 (1)public T get() public class CompletableFutureAPIDemo{public static void main(String[] args) throws ExecutionException, InterruptedException{CompletableFuture<String> completableFuture Com…...
本地化测试对游戏漏洞修复的影响
本地化测试在游戏开发的质量保证过程中起着至关重要的作用,尤其是在修复bug方面。当游戏为全球市场做准备时,它们通常会被翻译和改编成各种语言和文化背景。这种本地化带来了新的挑战,例如潜在的语言错误、文化误解,甚至是不同地区…...
使用rust实现rtsp码流截图
中文互联网上的rust示例程序源码还是太稀少,找资料很是麻烦,下面是自己用业余时间开发实现的一个对批量rtsp码流源进行关键帧截图并存盘的rust demo源码记录。 要编译这个源码需要先安装vcpkg,然后用vcpkg install ffmpeg安装最新版本的ffmpe…...
Cpp::STL—string类的模拟实现(12)
文章目录 前言一、string类各函数接口总览二、默认构造函数string(const char* str "");string(const string& str);传统拷贝写法现代拷贝写法 string& operator(const string& str);传统赋值构造现代赋值构造 ~string(); 三、迭代器相关函数begin &…...
一文搞懂SentencePiece的使用
目录 1. 什么是 SentencePiece?2. SentencePiece 基础概念2.1 SentencePiece 的工作原理2.2 SentencePiece 的优点 3. SentencePiece 的使用3.1 安装 SentencePiece3.2 训练模型与加载模型3.3 encode(高频)3.4 decode(高频&#x…...
一个简单的摄像头应用程序1
这个Python脚本实现了一个基于OpenCV的简单摄像头应用,我们在原有的基础上增加了录制视频等功能,用户可以通过该应用进行拍照、录制视频,并查看已拍摄的照片。以下是该脚本的主要功能和一些使用时需要注意的事项: 功能 拍照: 用户可以通过点击界面上的“拍照”按钮或按…...
通过PHP获取商品详情
在电子商务的浪潮中,数据的重要性不言而喻。商品详情信息对于电商运营者来说尤为宝贵。PHP,作为一种广泛应用的服务器端脚本语言,为我们提供了获取商品详情的便捷途径。 了解API接口文档 开放平台提供了详细的API接口文档。你需要熟悉商品详…...
【Android】获取备案所需的公钥以及签名MD5值
目录 重要前提 获取签名MD5值 获取公钥 重要前提 生成jks文件以及gradle配置应用该文件。具体步骤请参考我这篇文章:【Android】配置Gradle打包apk的环境_generate signed bundle or apk-CSDN博客 你只需要从头看到该文章的配置build.gradle(app&…...
看480p、720p、1080p、2k、4k、视频一般需要多大带宽呢?
看视频都喜欢看高清,那么一般来说看电影不卡顿需要多大带宽呢? 以4K为例,这里引用一位网友的回答:“视频分辨率4092*2160,每个像素用红蓝绿三个256色(8bit)的数据表示,视频帧数为60fps,那么一秒钟画面的数据量是:4096*2160*3*8*60≈11.9Gbps。此外声音大概是视频数据量…...
解决IDEA中@Autowired红色报错的实用指南:原因与解决方案
前言: 在使用Spring Boot开发时,Autowired注解是实现依赖注入的常用方式。然而,许多开发者在IDEA中使用Autowired时,可能会遇到红色报错,导致代码的可读性降低。本文将探讨导致这种现象的原因,并提供几种解…...
408知识点自检(一)
一、细节题 虚电路是面向连接的吗?虚电路线路上会不会有其他虚电路通过?虚电路适合什么类型的数据交换?虚电路的可靠性靠其他协议还是自己?固态硬盘的优势体现在什么存取方式?中断向量地址是谁的地址?多播…...
负载均衡--相关面试题(六)
在负载均衡的面试中,可能会遇到一系列涉及概念、原理、实践应用以及技术细节的问题。以下是一些常见的负载均衡面试题及其详细解答: 一、什么是负载均衡? 回答:负载均衡是一种将网络请求或数据传输工作分配给多个服务器或网络资源…...
【Unity踩坑】Unity更新Google Play结算库
一、问题描述: 在Google Play上提交了app bundle后,提示如下错误。 我使用的是Unity 2022.01.20f1,看来用的Play结算库版本是4.0 查了一下文档,Google Play结算库的维护周期是两年。现在需要更新到至少6.0。 二、更新过程 1. 下…...
Redis:hash类型
Redis:hash类型 hash命令设置与读取HSETHGETHMGET 哈希操作HEXISTSHDELHKEYSHVALSHGETALLHLENHSETNXHINCRBYHINCRBYFLOAT 内部编码ziplisthashtable 目前主流的编程语言中,几乎都提供了哈希表相关的容器,Redis自然也会支持对应的内容…...
力扣9.30
1749. 任意子数组和的绝对值的最大值 给你一个整数数组 nums 。一个子数组 [numsl, numsl1, ..., numsr-1, numsr] 的 和的绝对值 为 abs(numsl numsl1 ... numsr-1 numsr) 。 请你找出 nums 中 和的绝对值 最大的任意子数组(可能为空),…...
kafka下载配置
下载安装 参开kafka社区 zookeeperkafka消息队列群集部署https://apache.csdn.net/66c958fb10164416336632c3.html 下载 kafka_2.12-3.2.0安装包快速下载地址分享 官网下载链接地址: 官网下载地址:https://kafka.apache.org/downloads 官网呢下载慢…...
湖北建设工程注册中心网站/深圳疫情防控最新消息
错误观点:编程研究到一定程度归根结底是数学问题。正确观点:编程研究到一定程度归根结底是管理和人类发展的问题。一群水平一般的Enginners 一个优秀、经验丰富的Manager 一群拔尖的Enginners 。中国恰好在管理上缺乏人优秀的人才,尽量国内…...
最新的新闻/百度关键词优化是什么意思
一、回答下述问题 1、阐述常见的Web安全测试有几种类型?参考教材P173 常见的Web安全测试有7种,如下: 1、数据加密:某些数据需要进行信息加密和过滤后才能在客户端和服务器之间进行传输,包括用户登录密码、信用卡信息等…...
永康市建设局网站为什么打不开/百度指数怎么刷指数方法
前言 soapui...
一品威客网怎么接单/seo关键词如何布局
咱先来聊聊Redis 像Redis的基础入门,掌握下图这几个列出来的知识点足以了。 进阶的话,就得下点功夫了,事务、主从复制、哨兵、集群等等之类的搞不明白你就上不去呀。 再看美团亿级流量Redis实战,Redis分布式锁、session、缓存与数…...
网站建设技术服务的方式是什么意思/广州seo优化电话
初次注册博客 还不太熟练,现在慢慢上手了,很感谢有这样一个平台 , 可以跟很多技术大牛们学习 提高自己,也能记录自己平时生活里的点点滴滴 ,真心不错。 转载于:https://www.cnblogs.com/wyz937386163/p/5252383.html...
企业网站设计需求文档/网络营销教学网站
如飞机和鸟,它没有共同属性,但是他们有共同的行为——飞,这个时候我们可以用接口。而民用飞机、战斗机等他们都是飞机一种,这个时候我们可以将飞机座位一个抽象类。...