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

rpc入门笔记 0x02 protobuf的杂七杂八

syntax = "proto3"; // 这是个proto3的文件message HelloRequest{  // 创建数据对象string name = 1; // name表示名称,编号是1
}

生成python文件

安装grpcio和grpcio-tools库

pip install grpcio #安装grpc
pip install grpcio-tools #安装grpc tools

生成proto的python文件

python -m grpc_tools.protoc --python_out=. --grpc_python_out=. -I. helloworld.proto
  • python -m grpc_tools.protoc:使用grpc_tools包中的protoc命令进行代码生成。
  • --python_out=.:指定生成的Python代码的存放位置为当前目录。
  • --grpc_python_out=.:指定生成的gRPC代码的存放位置为当前目录。
  • -I.:指定搜索.proto文件的路径为当前目录

总结起来,该命令的作用是将当前目录下的helloworld.proto文件生成对应的Python代码,并将生成的代码存放在当前目录中

python使用grpc

syntax = "proto3";// The greeting service definition.
service Greeter {// Sends a greeting 下面就是暴露出来的一些方法rpc SayHello (HelloRequest) returns (HelloReply) {} // 定义返回什么类型就要返回什么类型rpc SayHelloStreamReply (HelloRequest) returns (stream HelloReply) {}rpc SayHelloBidiStream (stream HelloRequest) returns (stream HelloReply) {}
}// The request message containing the user's name.
message HelloRequest {string name = 1;
}// The response message containing the greetings
message HelloReply {string message = 1;
}

生成出来的文件就直接使用他

go使用grpc

生成指令:

#好像是版本不兼容了 protoc -I . helloworld.proto --go_out=plugins=grpc:.
protoc -I . --go_out=. --go-grpc_out=. ./hello.proto

样例:

syntax = "proto3";
option go_package = ".;proto"; // 这个是必须加的// The greeting service definition.
service Greeter {// Sends a greeting 下面就是暴露出来的一些方法rpc SayHello (HelloRequest) returns (HelloReply) {} // 定义返回什么类型就要返回什么类型
}// The request message containing the user's name.
message HelloRequest {string name = 1;
}// The response message containing the greetings
message HelloReply {string message = 1;
}

https://blog.csdn.net/neve_give_up_dan/article/details/126920398

https://github.com/grpc/grpc-go/issues/3794

https://blog.csdn.net/Mirale/article/details/122736894

UnimplementedXxxServer的作用:https://blog.csdn.net/Canon_in_D_Major/article/details/108135724

https://zhuanlan.zhihu.com/p/660634947

go_package

option go_package = "common/stream/proto/v1";

表示生成的Go代码文件应该位于common/stream/proto/v1这个包路径下。换句话说,生成的Go代码文件将被放置在common/stream/proto/v1目录下,并且其package声明会是v1

流模式

grpc的流模式主要有三种:

  • 客户端流模式
  • 服务端流模式
  • 双向流模式

proto文件:stream_proto.proto

syntax = "proto3";option go_package = ".;proto";service Greeter {// 服务端流模式:客户端是流,服务端不是流rpc GetStream(StreamReqData) returns (stream StreamResData); // 服务端流模式rpc PostStream(stream StreamReqData) returns (StreamResData); // 客户端流模式rpc AllStream(stream StreamReqData) returns (stream StreamResData); // 双向流模式
}message StreamReqData{string data = 1;
}message StreamResData{string data = 1;
}

server.go

package mainimport ("GoRpc_quick/stream_grpc_test/proto""fmt""google.golang.org/grpc""net""sync""time"
)const PORT = ":8080"type server struct {proto.UnimplementedGreeterServer
}// 服务端流模式
func (s *server) GetStream(req *proto.StreamReqData, streamServer proto.Greeter_GetStreamServer) error {i := 0for true {streamServer.Send(&proto.StreamResData{Data: fmt.Sprintf("%v\n + %v", time.Now().Unix(), req.Data),})time.Sleep(time.Second)if i++; i > 10 {break}}return nil
}// 客户端流模式
func (s *server) PostStream(streamServer proto.Greeter_PostStreamServer) error {for {recv, err := streamServer.Recv()if err != nil {fmt.Println(err)break}fmt.Println(recv.Data)}return nil
}// 双向流模式
func (s *server) AllStream(streamServer proto.Greeter_AllStreamServer) error {wg := sync.WaitGroup{}wg.Add(2)go func() { // 负责receivedefer wg.Done()for {recv, _ := streamServer.Recv()fmt.Println("收到客户端消息:", recv.Data)}}()go func() {defer wg.Done()for i := 0; i < 10; i++ {streamServer.Send(&proto.StreamResData{Data: "我是服务器"})time.Sleep(time.Second)}}()wg.Wait()return nil
}func main() {listener, err := net.Listen("tcp", PORT)if err != nil {panic(err)}s := grpc.NewServer()proto.RegisterGreeterServer(s, &server{})err = s.Serve(listener)if err != nil {panic("failed to start grpc")}
}

client.go

package mainimport ("GoRpc_quick/stream_grpc_test/proto""context""fmt""google.golang.org/grpc""sync""time"
)func main() {conn, err := grpc.Dial("localhost:8080", grpc.WithInsecure())if err != nil {panic(err)}defer conn.Close()// 服务端流模式(客户端接收流)c := proto.NewGreeterClient(conn)stream, _ := c.GetStream(context.Background(), &proto.StreamReqData{Data: "alice"})for {recv, err := stream.Recv() // 实际上就是socket编程if err != nil {fmt.Println(err.Error())break}fmt.Println(recv)}// 客户端流模式,客户端发送流postStream, err := c.PostStream(context.Background())for i := 0; i < 10; i++ {_ = postStream.Send(&proto.StreamReqData{Data: fmt.Sprintf("客户端流模式 + %d", i)})time.Sleep(time.Second)}// 双向流模式allStream, _ := c.AllStream(context.Background())wg := sync.WaitGroup{}wg.Add(2)go func() { // 负责receivedefer wg.Done()for {recv, _ := allStream.Recv()fmt.Println("收到服务器消息:", recv.Data)}}()go func() {defer wg.Done()for i := 0; i < 10; i++ {allStream.Send(&proto.StreamReqData{Data: "我是客户端"})time.Sleep(time.Second)}}()wg.Wait()}

protobuf知识汇总

基本类型会有对应

一个标量消息字段可以含有一个如下的类型——该表格展示了定义于.proto文件中的类型,以及与之对应的、在自动生成的访问类中定义的类型:

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

protobuf是会有一个默认值,即使不传,也会有默认值

当一个消息被解析的时候,如果被编码的信息不包含一个特定的singular元素,被解析的对象锁对应的域被设置位一个默认值,对于不同类型指定如下:

  • 对于strings,默认是一个空string
  • 对于bytes,默认是一个空的bytes
  • 对于bools,默认是false
  • 对于数值类型,默认是0
  • 对于枚举,默认是第一个定义的枚举值,必须为0;
  • 对于消息类型(message),域没有被设置,确切的消息是根据语言确定的,详见generated code guide

使用repeated创建数组并使用python传递

client.py

# 方式1
with grpc.insecure_channel("localhost:8080") as channel:stub = helloword_pb2_grpc.GreeterStub(channel)hello_request = helloword_pb2.HelloRequest(id=[1, 2, 3,])  # 先实例化不赋值hello_request.name = "bob"res: helloword_pb2.HelloReply = stub.SayHello(hello_request)print(res.message)# 方式2
with grpc.insecure_channel("localhost:8080") as channel:stub = helloword_pb2_grpc.GreeterStub(channel)hello_request = helloword_pb2.HelloRequest()  # 先实例化不赋值hello_request.name = "bob"hello_request.id.extend([1, 2])  # 这个对象已经有默认值创建好了,我们把他当做一个list来进行操作hello_request.id.append(4)res: helloword_pb2.HelloReply = stub.SayHello(hello_request)print(res.message)

嵌套message定义的使用

// The response message containing the greetings
message HelloReply {string message = 1;message Result { // 在里面定义messagestring name = 1;string url = 2;}repeated Result data = 2; // 定义一个message数组
}

py中的嵌套调用:

from grpc_hello.proto.helloword_pb2 import HelloReply  #  使用嵌套里面的数据res = HelloReply.Result()
# 从哪嵌套的从哪调用

go中的嵌套调用:

import "GoRpc_quick/grpc_test/proto"proto.Pong{} // 直接调用

map与enum枚举类型等其他类型

// 使用枚举类型,使得类型只能是下面的某一个
enum Gender{MALE = 0;FEMALE = 1;
}// The request message containing the user's name.
message HelloRequest {string name = 1;string url = 2;Gender g = 3;map<string, string> mp = 4;google.protobuf.Timestamp addTime = 5; // 时间戳
}

python grpc配合asynio使用

现在已经有官方的api了https://grpc.github.io/grpc/python/grpc_asyncio.html

这里使用grpclib库来实现:

安装依赖包

pip install grpclib# 生成对应文件
python -m grpc_tools.protoc -I. --python_out=. --grpclib_python_out=. helloworld.proto

go-metadata

go中使用metadata

metadata使得client和server能够为对方提供关于本次调用的一些信息,就像一次http请求的RequestHeader和ResponseHeader一样。http中header的生命周周期是一次http请求,那么metadata的生命周期就是一次RPC调用

1. 新建metadata

MD 类型实际上是map,key是string,value是string类型的slice。

type MD map[string][]string

创建的时候可以像创建普通的map类型一样使用new关键字进行创建:

//第一种方式
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", // 使用逗号
)
2. 发送metadata
md := metadata.Pairs("key", "val")// 新建一个有 metadata 的 context
ctx := metadata.NewOutgoingContext(context.Background(), md)// 单向 RPC
response, err := client.SomeRPC(ctx, someRequest)
3. 接收metadata
func (s *server) SomeRPC(ctx context.Context, in *pb.SomeRequest) (*pb.SomeResponse, err) {md, ok := metadata.FromIncomingContext(ctx)// do something with metadata
}

2. grpc中使用metadata

proro

syntax = "proto3";
option go_package = ".;proto";// The greeting service definition.
service Greeter {// Sends a greeting 下面就是暴露出来的一些方法rpc SayHello (HelloRequest) returns (HelloReply) {} // 定义返回什么类型就要返回什么类型
}// The request message containing the user's name.
message HelloRequest {string name = 1;
}// The response message containing the greetings
message HelloReply {string message = 1;
}

server.go

type Server struct {*proto.UnimplementedGreeterServer
}// 参数设置是硬性规定的
func (s *Server) SayHello(ctx context.Context, req *proto.HelloRequest) (*proto.HelloReply, error) {md, ok := metadata.FromIncomingContext(ctx)if !ok {fmt.Println("get metadata error")}// 遍历一下这个string的mapfor k, v := range md {fmt.Println(k, v)}if nameSlice, ok := md["name"]; ok { // 判断map中是否含有元素fmt.Println(nameSlice)for i, e := range nameSlice {fmt.Println(i, e)}}return &proto.HelloReply{Message: "Go hello " + req.Name,}, nil
}func main() {// 一样的新建serverg := grpc.NewServer()// 注册服务proto.RegisterGreeterServer(g, &Server{})listen, err := net.Listen("tcp", "0.0.0.0:8080")if err != nil {panic("failed to listen: " + err.Error())}err = g.Serve(listen)if err != nil {panic("failed to start grpc: " + err.Error())}
}

client.go

func main() {conn, err := grpc.Dial("127.0.0.1:8080", grpc.WithInsecure())if err != nil {panic(err)}defer conn.Close()c := proto_bak.NewGreeterClient(conn)//md := metadata.Pairs("timestamp", time.Now().Format(times))md := metadata.New(map[string]string{"name": "alice","pwd":  "12345",})ctx := metadata.NewOutgoingContext(context.Background(), md)r, err := c.SayHello(ctx, &proto_bak.HelloRequest{Name: "bob",})if err != nil {panic(err)}fmt.Println(r.Message)}

python-meta

server.py

import grpcfrom grpc_metadata_test.proto import helloword_pb2, helloword_pb2_grpcif __name__ == '__main__':with grpc.insecure_channel("localhost:50051") as channel:stub = helloword_pb2_grpc.GreeterStub(channel)hello_request = helloword_pb2.HelloRequest()  # 先实例化不赋值hello_request.name = "bob"rsp, call = stub.SayHello.with_call(hello_request,metadata=(('name', 'bobby'),('pwd', '123456')))# call 能拿到服务器发回来的东西print(rsp.message)

client.py

import grpc
from concurrent import futures
from grpc_metadata_test.proto import helloword_pb2, helloword_pb2_grpcclass Greeter(helloword_pb2_grpc.GreeterServicer):def SayHello(self, request, context):# 一样的遍历metadata的mapfor k, v in context.invocation_metadata():print(k, v)return helloword_pb2.HelloReply(message=f'Python 你好{request.name}')if __name__ == '__main__':# 1. 实例化serverserver = grpc.server(futures.ThreadPoolExecutor(max_workers=10))  # 设置十个线程# 2. 注册逻辑到server中helloword_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server)# 3. 启动serverserver.add_insecure_port('[::]:50051')server.start()server.wait_for_termination()

拦截器

go拦截器

server.go

type Server struct {*proto.UnimplementedGreeterServer
}// 参数设置是硬性规定的
func (s *Server) SayHello(ctx context.Context, req *proto.HelloRequest) (*proto.HelloReply, error) {return &proto.HelloReply{Message: "Go hello " + req.Name,}, nil
}func main() {// 定义拦截器的函数interceptor := func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {fmt.Println("接收到了一个新的清秀")res, err := handler(ctx, req) // handler是原本的调用逻辑fmt.Println("请求已经完成")return res, err}opt := grpc.UnaryInterceptor(interceptor) // 参数是一个函数,所以参数必须一致(go认参数来区分函数// 一样的新建serverg := grpc.NewServer(opt) // 放进去// 注册服务proto.RegisterGreeterServer(g, &Server{})listen, err := net.Listen("tcp", "0.0.0.0:8080")if err != nil {panic("failed to listen: " + err.Error())}err = g.Serve(listen)if err != nil {panic("failed to start grpc: " + err.Error())}
}

client.go

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()err := invoker(ctx, method, req, reply, cc, opts...)fmt.Printf("耗时: %s\n", time.Since(start))return err} // 拦截器逻辑opt := grpc.WithUnaryInterceptor(interceptor) // 需要在dial(拨号)的时候传入参数conn, err := grpc.Dial("127.0.0.1:8080", grpc.WithInsecure(), opt)if err != nil {panic(err)}defer conn.Close()c := proto.NewGreeterClient(conn)r, err := c.SayHello(context.Background(), &proto.HelloRequest{Name: "bob",})if err != nil {panic(err)}fmt.Println(r.Message)}

python拦截器

server.py

class Greeter(helloword_pb2_grpc.GreeterServicer):def SayHello(self, request, context):for k, v in context.invocation_metadata():print(k, v)return helloword_pb2.HelloReply(message=f'Python 你好{request.name}')# 调用拦截器逻辑需要继承该抽象类,并且实现这个抽象类的方法
class LogInterceptors(grpc.ServerInterceptor):def intercept_service(self, continuation, handler_call_details):print("请求开始")print(type(handler_call_details))rsp = continuation(handler_call_details)  # 相当于handlerprint("请求结束")return rspif __name__ == '__main__':# 实例化一个interinterceptor = LogInterceptors()# 1. 实例化serverserver = grpc.server(futures.ThreadPoolExecutor(max_workers=10), interceptors=(interceptor,))  # 设置十个线程# 并传进去interceptor的tuple或者list, 最后一定要带个,# 2. 注册逻辑到server中helloword_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server)# 3. 启动serverserver.add_insecure_port('[::]:50051')server.start()server.wait_for_termination()

client.py

客户端的拦截器

# 第二个参数需要继承重写
class DefaultInterceptor(grpc.UnaryUnaryClientInterceptor):def intercept_unary_unary(self, continuation, client_call_details, request):# 拦截器业务逻辑start = datetime.now()rsp = continuation(client_call_details, request)print((datetime.now() - start).microseconds / 1000, "ms")return rspif __name__ == '__main__':# 实例化对象default_intercept = DefaultInterceptor()with grpc.insecure_channel("localhost:50051") as channel:# 需要对channel进行操作intercept_channel = grpc.intercept_channel(channel, default_intercept)stub = helloword_pb2_grpc.GreeterStub(intercept_channel)hello_request = helloword_pb2.HelloRequest()  # 先实例化不赋值hello_request.name = "bob"rsp, call = stub.SayHello.with_call(hello_request,metadata=(('name', 'bobby'),('pwd', '123456')))# call 能拿到服务器发回来的东西print(rsp.message)

验证器

docs:protoc-gen-validate/docs.md at main · bufbuild/protoc-gen-validate · GitHub

每个字段要满足一个验证规则

编译命令:

protoc \-I . \-I path/to/validate/ \--go_out=":../generated" \--validate_out="lang=go:../generated" \example.protoprotoc -I . --go_out=. --go-grpc_out=. --validate_out="lang=go:." ./hello.proto

需要去拷贝一份validate.proto放到文件中:protoc-gen-validate/validate/validate.proto at main · bufbuild/protoc-gen-validate · GitHub

proto

syntax = "proto3";import "validate.proto";
option go_package=".;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:   "^(?:(?:\\+|00)86)?1[3-9]\\d{9}$",max_bytes: 256,}];}

server.go

package mainimport ("context""google.golang.org/grpc/codes""google.golang.org/grpc/status""net""google.golang.org/grpc""GoRpc_quick/grpc_validate_test/proto"
)type Server struct {*proto.UnimplementedGreeterServer
}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() {//p := new(proto.Person)//p.Id = 1000//err := p.Validate() // 会返回一个error来判断合法性//if err != nil {// panic(err)//}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 { // 实例化为Validator,用多态的方法调用Validate方法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())}
}

client.go

package mainimport ("GoRpc_quick/grpc_validate_test/proto""context""fmt""google.golang.org/grpc"
)// 本质上封装了metadata的封装
type customCredential struct {
}func main() {var opts []grpc.DialOptionopts = append(opts, grpc.WithInsecure())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.Person{Id:     1000,Email:  "1234abc@qq.com",Mobile: "18888888888",})if err != nil {panic(err)}fmt.Println(r.Id)}

错误处理与超时设定

python

客户端处理:

import grpc
from grpc_error_test.proto import helloword_pb2, helloword_pb2_grpcif __name__ == '__main__':with grpc.insecure_channel("localhost:8080") as channel:stub = helloword_pb2_grpc.GreeterStub(channel)hello_request = helloword_pb2.HelloRequest()  # 先实例化不赋值hello_request.name = "bob"try:res: helloword_pb2.HelloReply = stub.SayHello(hello_request, timeout=3)except grpc.RpcError as e:d = e.details()print(d)status_code = e.code() # 获取codeprint(status_code.name)print(status_code.value)print(res.message)

服务端处理

import grpc
from concurrent import futures
from grpc_error_test.proto import helloword_pb2, helloword_pb2_grpcclass Greeter(helloword_pb2_grpc.GreeterServicer):def SayHello(self, request, context):context.set_code(grpc.StatusCode.NOT_FOUND)context.set_details("记录不存在")return helloword_pb2.HelloReply(message=f'Python 你好{request.name}')if __name__ == '__main__':# 1. 实例化serverserver = grpc.server(futures.ThreadPoolExecutor(max_workers=10))  # 设置十个线程# 2. 注册逻辑到server中helloword_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server)# 3. 启动serverserver.add_insecure_port('[::]:8080')server.start()server.wait_for_termination()

Go

客户端处理

func main() {conn, err := grpc.Dial("127.0.0.1:8080", grpc.WithInsecure())if err != nil {panic(err)}defer conn.Close()c := proto.NewGreeterClient(conn)// 设置3超时ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)defer cancel()_, err = c.SayHello(ctx, &proto.HelloRequest{Name: "bob",})if err != nil { // 捕捉并解析错误st, ok := status.FromError(err)if !ok {panic("error解析失败")}fmt.Println(st.Message())fmt.Println(st.Code())}//fmt.Println(r.Message)}

服务端处理:

package mainimport ("GoRpc_quick/grpc_error_test/proto""context""google.golang.org/grpc""google.golang.org/grpc/codes""google.golang.org/grpc/status""net"
)type Server struct {*proto.UnimplementedGreeterServer
}// 参数设置是硬性规定的
func (s *Server) SayHello(ctx context.Context, req *proto.HelloRequest) (*proto.HelloReply, error) {return &proto.HelloReply{Message: "Go hello " + req.Name,}, status.Error(codes.InvalidArgument, "未实现") // 返回错误码和描述
}func main() {// 一样的新建serverg := grpc.NewServer()// 注册服务proto.RegisterGreeterServer(g, &Server{})listen, err := net.Listen("tcp", "0.0.0.0:8080")if err != nil {panic("failed to listen: " + err.Error())}err = g.Serve(listen)if err != nil {panic("failed to start grpc: " + err.Error())}
}

相关文章:

rpc入门笔记 0x02 protobuf的杂七杂八

syntax "proto3"; // 这是个proto3的文件message HelloRequest{ // 创建数据对象string name 1; // name表示名称&#xff0c;编号是1 }生成python文件 安装grpcio和grpcio-tools库 pip install grpcio #安装grpc pip install grpcio-tools #安装grpc tools生成…...

keepalived与nginx与MySQL

keepalived VRRP介绍 集群&#xff08;cluster&#xff09;技术是一种较新的技术&#xff0c;通过集群技术&#xff0c;可以在付出较低成本的情况下获得在性能、可靠性、灵活性方面的相对较高的收益&#xff0c;其任务调度则是集群系统中的核心技术。 集群组成后&#xff0c;可…...

Pod基础概念

Pod是kubernetes中最小的资源管理组件&#xff0c;Pod也是最小化运行容器化应用的资源对象。一个Pod代表着集群中运行的一个进程。kubernetes中其他大多数组件都是围绕着Pod来进行支撑和扩展Pod功能的&#xff0c;例如&#xff0c;用于管理Pod运行的StatefulSet和Deployment等控…...

WebDAV之π-Disk派盘 + 一叶日记

推荐一款操作方便、界面简洁,记录生活点滴与心情,具有诗情画意的日记软件。 一叶日记是一款记录日记的手机软件,在这款软件中它里面有着各种不同的工具,可以方便用户去随时随地的记录日记,同时里面还有着各种不同的主题背景,可以供用户去选择使用各种功能,给用户记录带…...

在IDEA运行spark程序(搭建Spark开发环境)

建议大家写在Linux上搭建好Hadoop的完全分布式集群环境和Spark集群环境&#xff0c;以下在IDEA中搭建的环境仅仅是在window系统上进行spark程序的开发学习&#xff0c;在window系统上可以不用安装hadoop和spark&#xff0c;spark程序可以通过pom.xml的文件配置&#xff0c;添加…...

无穷级数例子

计算 lim ⁡ x → ∞ ( 1 n 1 1 n 2 1 n 3 . . . 1 n 2 n − 1 1 n 2 n ) 计算\lim _{x\to \infty} (\frac{1}{n1} \frac{1}{n2}\frac{1}{n3} ... \frac{1}{n2n-1} \frac{1}{n2n} ) 计算x→∞lim​(n11​n21​n31​...n2n−11​n2n1​) 解&#xff1a; lim ⁡ x …...

C++构造函数和析构函数详解

一、构造函数 1、概念 构造函数是特殊的成员函数&#xff0c;需要注意的是&#xff0c;构造函数虽然名叫做构造&#xff0c;但是构造函数的主要任务并不是开空间创建对象&#xff0c;而是初始化对象。 2、特征 函数名与类名相同。无返回值对象实例化时编译器自动调用对应的…...

MySQL数据库干货_16—— SQL99标准中的查询

SQL99标准中的查询 MySQL5.7 支持部分的SQL99 标准。 SQL99中的交叉连接(CROSS JOIN) 示例&#xff1a; 使用交叉连接查询 employees 表与 departments 表。 select * from employees cross join departments;SQL99中的自然连接(NATURAL JOIN) 自然连接 连接只能发生在两…...

LLM大语言模型训练中常见的技术:微调与嵌入

微调&#xff08;Fine-Tuning&#xff09;&#xff1a; 微调是一种用于预训练语言模型的技术。在预训练阶段&#xff0c;语言模型&#xff08;如GPT-3.5&#xff09;通过大规模的文本数据集进行训练&#xff0c;从而学会了语言的语法、语义和世界知识。然后&#xff0c;在微调阶…...

每日一练 | 网络工程师软考真题Day47

阅读以下关于Linux文件系统和Samba效劳的说明&#xff0c;答复以下【问题1】至【问题3】。 【说明】 Linux系统采用了树型多级目录来管理文件&#xff0c;树型结构的最上层是根目录&#xff0c;其他的所有目录都是从根目录生成的。通过Samba可以实现基于Linux操作系统的效劳器和…...

Kafka - 监控工具 Kafka Eagle:实时洞察Kafka集群的利器

文章目录 引言Kafka Eagle简介Kafka Eagle的特点Kafka Eagle的优势使用Kafka Eagle的步骤结论 引言 在现代大数据架构中&#xff0c;Apache Kafka已成为一个不可或缺的组件&#xff0c;用于可靠地处理和传输大规模的数据流。然而&#xff0c;随着Kafka集群规模的不断增长&…...

infercnv hpc东南服务器 .libpath 最终使用monocle2环境安装

安装不成功就用conda安装 conda install -c bioconda bioconductor-infercnv Installing infercnv There are several options for installing inferCNV. Choose whichever you prefer: Option A: Install infercnv from BioConductor (preferred) From within R, run the…...

【音视频 | Ogg】RFC3533 :Ogg封装格式版本 0(The Ogg Encapsulation Format Version 0)

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; &#x1f923;本文内容&#x1f923;&a…...

Hadoop时代落幕,开源大数据将何去何从?

Hadoop时代落幕&#xff0c;谁是大数据的新宠儿&#xff1f; 1、 1、...

作为一名程序员面临哪些挑战?应该如何应对?

在现今互联网失业潮的大环境下&#xff0c;每一位程序员都面临着被淘汰的风险&#xff0c;但逃避没有用&#xff0c;今天我们就来总结这些挑战与风险&#xff0c;找准自己的方向与定位&#xff0c;做好职业规划&#xff0c;希望这些信息能对大家有所帮助。 一、面临的挑战 老…...

flink的安装与使用(ubuntu)

组件版本 虚拟机&#xff1a;ubuntu-20.04.6-live-server-amd64.iso flink&#xff1a;flink-1.18.0-bin-scala_2.12.tgz jdk&#xff1a;jdk-8u291-linux-x64.tar flink 下载 1、官网&#xff1a;https://flink.apache.org/downloads/ 2、清华镜像&#xff1a;https://mirr…...

容器:软件性能测试的最佳环境

容器总体上提供了一种经济的和可扩展的方法来测试产品在实际情况下的性能&#xff0c;同时还能保持较低的资源成本和开销成本。 软件性能和可伸缩性是我们谈论应用程序开发时经常遇到的话题。一个很大的原因是应用程序的性能和可伸缩性直接影响其在市场上的成功。一个应用程序…...

【Qt控件之QMovie】详解

Qt控件之QMovies 概述公共类型属性公共函数公共槽函数信号静态公共成员示例使用场景 概述 QMovie类是一个方便的类&#xff0c;用于播放具有QImageReader的动画。此类用于显示没有声音的简单动画。如果您想显示视频和媒体内容&#xff0c;请改用Qt多媒体框架Qt Multimedia mul…...

Star History 九月开源精选 |开源 GitHub Copilot 替代

虽然大火了近一年&#xff0c;但是截至目前 AI 唯一破圈的场景是帮助写代码&#xff08;谷歌云旗下的 DORA 年度报告也给 AI 泼了盆冷水&#xff09;。不过对于软件开发来说&#xff0c;生成式人工智能绝对已经是新的标配。 本期 Star History 收集了一些开源 GitHub Copilot …...

【Rabbit MQ】Rabbit MQ 消息的可靠性 —— 生产者和消费者消息的确认,消息的持久化以及消费失败的重试机制

文章目录 前言&#xff1a;消息的可靠性问题一、生产者消息的确认1.1 生产者确认机制1.2 实现生产者消息的确认1.3 验证生产者消息的确认 二、消息的持久化2.1 演示消息的丢失2.2 声明持久化的交换机和队列2.3 发送持久化的消息 三、消费者消息的确认3.1 配置消费者消息确认3.2…...

Chapter03-Authentication vulnerabilities

文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...

Python:操作 Excel 折叠

💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...

通过Wrangler CLI在worker中创建数据库和表

官方使用文档&#xff1a;Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后&#xff0c;会在本地和远程创建数据库&#xff1a; npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库&#xff1a; 现在&#xff0c;您的Cloudfla…...

el-switch文字内置

el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...

DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI

前一阵子在百度 AI 开发者大会上&#xff0c;看到基于小智 AI DIY 玩具的演示&#xff0c;感觉有点意思&#xff0c;想着自己也来试试。 如果只是想烧录现成的固件&#xff0c;乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外&#xff0c;还提供了基于网页版的 ESP LA…...

python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)

更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...

Linux云原生安全:零信任架构与机密计算

Linux云原生安全&#xff1a;零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言&#xff1a;云原生安全的范式革命 随着云原生技术的普及&#xff0c;安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测&#xff0c;到2025年&#xff0c;零信任架构将成为超…...

从零实现STL哈希容器:unordered_map/unordered_set封装详解

本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说&#xff0c;直接开始吧&#xff01; 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...

拉力测试cuda pytorch 把 4070显卡拉满

import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试&#xff0c;通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小&#xff0c;增大可提高计算复杂度duration: 测试持续时间&#xff08;秒&…...

鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/

使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题&#xff1a;docker pull 失败 网络不同&#xff0c;需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...