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

【Go】五、Grpc 的入门使用

grpc 与 protobuf

grpc 使用的是 protobuf 协议,其是一个通用的 rpc 框架,基本支持主流的所有语言、其底层使用 http/2 进行网络通信,具有较高的效率

protobuf 是一种序列化格式,这种格式具有 序列化以及解码速度快(对比json、xml 速度快 2 - 100 倍),压缩率高等优点,是一个性能炸弹

基础环境配置

我们使用之前,要先安装 protobuf 的相关环境:

在 github 上下载 protoc 并配置好环境变量(环境变量配置到 bin 这一级)(用来生成源码)

记得在编译器中安装 protobuf support 插件

protobuf尝试

对 protobuf 进行尝试:创建一个.proto 文件:

syntax = "proto3";message HelloRequest {string name = 1;  // 注意这里不是赋值,而是指定编号
}

之后使用 protoc 生成源码

protoc -I . --go_out=. --go-grpc_out=require_unimplemented_servers=false:. ./helloworld.proto

测试proto的使用:

func main() {req := HelloWorld.HelloRequest{Name:    "Chen",Age:     16,Courses: []string{"C", "Go"},}rsp, _ := proto.Marshal(&req)       // 编码newReq := HelloWorld.HelloRequest{} // 创建一个空的proto_ = proto.Unmarshal(rsp, &newReq)   // 解码,解码存储在 newReq 中fmt.Println(newReq.Name, newReq.Age, newReq.Courses)
}

实际开发尝试

创建目录结构:

grpc_test

server

server.go

client

proto

helloworld.proto

helloworld.pb.go(自动生成)

helloworld_grpc.pb.go(自动生成)

首先自己编写 helloworld.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 -I . --go_out=. --go-grpc_out=require_unimplemented_servers=false:. ./helloworld.proto

生成helloworld.pb.go 文件:

// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// 	protoc-gen-go v1.32.0
// 	protoc        v4.25.3
// source: helloworld.protopackage protoimport (protoreflect "google.golang.org/protobuf/reflect/protoreflect"protoimpl "google.golang.org/protobuf/runtime/protoimpl"reflect "reflect"sync "sync"
)const (// Verify that this generated code is sufficiently up-to-date._ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)// Verify that runtime/protoimpl is sufficiently up-to-date._ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)type HelloRequest struct {state         protoimpl.MessageStatesizeCache     protoimpl.SizeCacheunknownFields protoimpl.UnknownFieldsName string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
}func (x *HelloRequest) Reset() {*x = HelloRequest{}if protoimpl.UnsafeEnabled {mi := &file_helloworld_proto_msgTypes[0]ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))ms.StoreMessageInfo(mi)}
}func (x *HelloRequest) String() string {return protoimpl.X.MessageStringOf(x)
}func (*HelloRequest) ProtoMessage() {}func (x *HelloRequest) ProtoReflect() protoreflect.Message {mi := &file_helloworld_proto_msgTypes[0]if protoimpl.UnsafeEnabled && x != nil {ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))if ms.LoadMessageInfo() == nil {ms.StoreMessageInfo(mi)}return ms}return mi.MessageOf(x)
}// Deprecated: Use HelloRequest.ProtoReflect.Descriptor instead.
func (*HelloRequest) Descriptor() ([]byte, []int) {return file_helloworld_proto_rawDescGZIP(), []int{0}
}func (x *HelloRequest) GetName() string {if x != nil {return x.Name}return ""
}type HelloReply struct {state         protoimpl.MessageStatesizeCache     protoimpl.SizeCacheunknownFields protoimpl.UnknownFieldsMessage string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
}func (x *HelloReply) Reset() {*x = HelloReply{}if protoimpl.UnsafeEnabled {mi := &file_helloworld_proto_msgTypes[1]ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))ms.StoreMessageInfo(mi)}
}func (x *HelloReply) String() string {return protoimpl.X.MessageStringOf(x)
}func (*HelloReply) ProtoMessage() {}func (x *HelloReply) ProtoReflect() protoreflect.Message {mi := &file_helloworld_proto_msgTypes[1]if protoimpl.UnsafeEnabled && x != nil {ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))if ms.LoadMessageInfo() == nil {ms.StoreMessageInfo(mi)}return ms}return mi.MessageOf(x)
}// Deprecated: Use HelloReply.ProtoReflect.Descriptor instead.
func (*HelloReply) Descriptor() ([]byte, []int) {return file_helloworld_proto_rawDescGZIP(), []int{1}
}func (x *HelloReply) GetMessage() string {if x != nil {return x.Message}return ""
}var File_helloworld_proto protoreflect.FileDescriptorvar file_helloworld_proto_rawDesc = []byte{0x0a, 0x10, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x70, 0x72, 0x6f,0x74, 0x6f, 0x22, 0x22, 0x0a, 0x0c, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65,0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x26, 0x0a, 0x0a, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52,0x65, 0x70, 0x6c, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18,0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x32, 0x31,0x0a, 0x07, 0x47, 0x72, 0x65, 0x65, 0x74, 0x65, 0x72, 0x12, 0x26, 0x0a, 0x08, 0x53, 0x61, 0x79,0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x0d, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71,0x75, 0x65, 0x73, 0x74, 0x1a, 0x0b, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c,0x79, 0x42, 0x09, 0x5a, 0x07, 0x2e, 0x3b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72,0x6f, 0x74, 0x6f, 0x33,
}var (file_helloworld_proto_rawDescOnce sync.Oncefile_helloworld_proto_rawDescData = file_helloworld_proto_rawDesc
)func file_helloworld_proto_rawDescGZIP() []byte {file_helloworld_proto_rawDescOnce.Do(func() {file_helloworld_proto_rawDescData = protoimpl.X.CompressGZIP(file_helloworld_proto_rawDescData)})return file_helloworld_proto_rawDescData
}var file_helloworld_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_helloworld_proto_goTypes = []interface{}{(*HelloRequest)(nil), // 0: HelloRequest(*HelloReply)(nil),   // 1: HelloReply
}
var file_helloworld_proto_depIdxs = []int32{0, // 0: Greeter.SayHello:input_type -> HelloRequest1, // 1: Greeter.SayHello:output_type -> HelloReply1, // [1:2] is the sub-list for method output_type0, // [0:1] is the sub-list for method input_type0, // [0:0] is the sub-list for extension type_name0, // [0:0] is the sub-list for extension extendee0, // [0:0] is the sub-list for field type_name
}func init() { file_helloworld_proto_init() }
func file_helloworld_proto_init() {if File_helloworld_proto != nil {return}if !protoimpl.UnsafeEnabled {file_helloworld_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {switch v := v.(*HelloRequest); i {case 0:return &v.statecase 1:return &v.sizeCachecase 2:return &v.unknownFieldsdefault:return nil}}file_helloworld_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {switch v := v.(*HelloReply); i {case 0:return &v.statecase 1:return &v.sizeCachecase 2:return &v.unknownFieldsdefault:return nil}}}type x struct{}out := protoimpl.TypeBuilder{File: protoimpl.DescBuilder{GoPackagePath: reflect.TypeOf(x{}).PkgPath(),RawDescriptor: file_helloworld_proto_rawDesc,NumEnums:      0,NumMessages:   2,NumExtensions: 0,NumServices:   1,},GoTypes:           file_helloworld_proto_goTypes,DependencyIndexes: file_helloworld_proto_depIdxs,MessageInfos:      file_helloworld_proto_msgTypes,}.Build()File_helloworld_proto = out.Filefile_helloworld_proto_rawDesc = nilfile_helloworld_proto_goTypes = nilfile_helloworld_proto_depIdxs = nil
}

以及:

helloworld_grpc.pb.go

// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.3.0
// - protoc             v4.25.3
// source: helloworld.protopackage protoimport (context "context"grpc "google.golang.org/grpc"codes "google.golang.org/grpc/codes"status "google.golang.org/grpc/status"
)// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7const (Greeter_SayHello_FullMethodName = "/Greeter/SayHello"
)// GreeterClient is the client API for Greeter service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type GreeterClient interface {SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error)
}type greeterClient struct {cc grpc.ClientConnInterface
}func NewGreeterClient(cc grpc.ClientConnInterface) GreeterClient {return &greeterClient{cc}
}func (c *greeterClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) {out := new(HelloReply)err := c.cc.Invoke(ctx, Greeter_SayHello_FullMethodName, in, out, opts...)if err != nil {return nil, err}return out, nil
}// GreeterServer is the server API for Greeter service.
// All implementations should embed UnimplementedGreeterServer
// for forward compatibility
type GreeterServer interface {SayHello(context.Context, *HelloRequest) (*HelloReply, error)
}// UnimplementedGreeterServer should be embedded to have forward compatible implementations.
type UnimplementedGreeterServer struct {
}func (UnimplementedGreeterServer) SayHello(context.Context, *HelloRequest) (*HelloReply, error) {return nil, status.Errorf(codes.Unimplemented, "method SayHello not implemented")
}// UnsafeGreeterServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to GreeterServer will
// result in compilation errors.
type UnsafeGreeterServer interface {mustEmbedUnimplementedGreeterServer()
}func RegisterGreeterServer(s grpc.ServiceRegistrar, srv GreeterServer) {s.RegisterService(&Greeter_ServiceDesc, srv)
}func _Greeter_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {in := new(HelloRequest)if err := dec(in); err != nil {return nil, err}if interceptor == nil {return srv.(GreeterServer).SayHello(ctx, in)}info := &grpc.UnaryServerInfo{Server:     srv,FullMethod: Greeter_SayHello_FullMethodName,}handler := func(ctx context.Context, req interface{}) (interface{}, error) {return srv.(GreeterServer).SayHello(ctx, req.(*HelloRequest))}return interceptor(ctx, in, info, handler)
}// Greeter_ServiceDesc is the grpc.ServiceDesc for Greeter service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var Greeter_ServiceDesc = grpc.ServiceDesc{ServiceName: "Greeter",HandlerType: (*GreeterServer)(nil),Methods: []grpc.MethodDesc{{MethodName: "SayHello",Handler:    _Greeter_SayHello_Handler,},},Streams:  []grpc.StreamDesc{},Metadata: "helloworld.proto",
}

尝试编写业务逻辑(这里应该写在handler中,由于业务过于简单,先写在server中):

server.go:

注意这里 grpc 帮我们处理了服务器不能连续被访问的问题,不需要我们手动通过一个死循环进行处理

package mainimport ("context""net""google.golang.org/grpc""FirstGo/goon/grpc_test/proto"
)type Server struct{}// 业务逻辑
// 第一个参数必须是context,error必须加
func (s *Server) SayHello(ctx context.Context, request *proto.HelloRequest) (*proto.HelloReply, error) {return &proto.HelloReply{Message: "hello " + request.Name,}, nil
}func main() {g := grpc.NewServer()proto.RegisterGreeterServer(g, &Server{})lis, err := net.Listen("tcp", "0.0.0.0:8080")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 ("FirstGo/goon/grpc_test/proto""context""fmt""google.golang.org/grpc"
)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)// 调用对应的方法r, err := c.SayHello(context.Background(), &proto.HelloRequest{Name: "Chen"})if err != nil {panic(err)}fmt.Println(r.Message)
}

GRPC的四种数据传输模式

RPC 还具有四种数据模式,其分别是:

  1. 简单模式(上述模式)

    客户端发起一次请求,服务器返回一次响应

  2. 服务端数据流模式

    客户发起一次请求,服务器返回一段连续的数据流,最典型的例子是:客户发送一段股票代码,服务端实时将股票的数据源源不断的返回给客户端

  3. 客户端数据流模式

    与服务端数据流模式相反,这种是由客户端源源不断的向服务端发送数据流,在发送结束后,由服务端发送一个响应,这种的典型例子是:物联网终端向服务器报送数据

  4. 双向数据流模式

    这种是客户端和服务端都可以向双方发送数据流,这个时候双方的数据都可以相互发送,也就是可以实时交互,这种最典型的例子就是聊天机器人

实际测试:
建立文件结构:

stream_grpc_test

server

server.go

client

client.go

proto

stream.pb.gp

stream.proto

stream_grpc.pb.go

stream.proto:

syntax = "proto3";option go_package = ".;proto";service Greeter {rpc GetStream(StreamReqData) returns (stream StreamResData);  //  服务端流模式,返回的响应数据是流rpc PutStream(stream StreamReqData) returns (StreamResData);  // 客户端流模式,传给服务器的数据是流rpc AllStream(stream StreamReqData) returns (stream StreamResData);   // 双向流模式
}message StreamReqData {string data = 1;
}message StreamResData {string data = 1;
}

服务端流模式简单使用

只写了 服务端流模式的 server,go:

const PORT = ":50052"type server struct {
}// 对于服务端数据传输模式,参考以下内容:
// 没有 context 参数,而是将返回作为入参传入
func (s *server) GetStream(req *proto.StreamReqData, res proto.Greeter_GetStreamServer) error {i := 0for {i++_ = res.Send(&proto.StreamResData{Data: fmt.Sprintf("%v", time.Now().Unix()),})time.Sleep(time.Second)if i > 10 {break}}return nil
}// 客户端数据流模式
// 只有一个用来不断接收的入参
func (s *server) PutStream(cliStr proto.Greeter_PutStreamServer) error {return nil
}// 双向数据流模式,和客户端模式相同,只有一个入参
func (s *server) AllStream(allStr proto.Greeter_AllStreamServer) error {return nil
}func main() {lis, err := net.Listen("tcp", PORT)if err != nil {panic(err)}s := grpc.NewServer()proto.RegisterGreeterServer(s, &server{})s.Serve(lis)}

只针对于服务端流的client.go

func main() {// 拨号conn, err := grpc.Dial("localhost:50052", grpc.WithInsecure())if err != nil {panic(err)}defer conn.Close()c := proto.NewGreeterClient(conn)// 这里要注意:我们在进行调用的时候使用的仍让是 simple 的模式,而不是服务端的函数模式res, _ := c.GetStream(context.Background(), &proto.StreamReqData{Data: "Chen"})// 使用一个死循环来接收客户端传进来的数据for {a, err := res.Recv()if err != nil {fmt.Println(err)break}fmt.Println(a)}
}

客户端流模式的简单使用

server.go:

package mainimport ("FirstGo/goon/stream_grpc_test/proto""fmt""google.golang.org/grpc""net""time"
)const PORT = ":50052"type server struct {
}// 对于服务端数据传输模式,参考以下内容:
// 没有 context 参数,而是将返回作为入参传入
func (s *server) GetStream(req *proto.StreamReqData, res proto.Greeter_GetStreamServer) error {i := 0for {i++_ = res.Send(&proto.StreamResData{Data: fmt.Sprintf("%v", time.Now().Unix()),})time.Sleep(time.Second)if i > 10 {break}}return nil
}// 客户端数据流模式
// 只有一个用来不断接收的入参
func (s *server) PutStream(cliStr proto.Greeter_PutStreamServer) error {// 客户端流模式,客户端不断的发送数据给服务器for {if a, err := cliStr.Recv(); err != nil {fmt.Println(err)} else {fmt.Println(a.Data)}}return nil
}// 双向数据流模式,和客户端模式相同,只有一个入参
func (s *server) AllStream(allStr proto.Greeter_AllStreamServer) error {return nil
}func main() {lis, err := net.Listen("tcp", PORT)if err != nil {panic(err)}s := grpc.NewServer()proto.RegisterGreeterServer(s, &server{})s.Serve(lis)}

client.go:

package mainimport ("FirstGo/goon/stream_grpc_test/proto""context""fmt""google.golang.org/grpc""time"
)func main() {// 拨号conn, err := grpc.Dial("localhost:50052", grpc.WithInsecure())if err != nil {panic(err)}defer conn.Close()c := proto.NewGreeterClient(conn)putS, _ := c.PutStream(context.Background())i := 0for {i++putS.Send(&proto.StreamReqData{Data: fmt.Sprintf("Chen %d", i)})time.Sleep(time.Second)if i > 10 {break}}
}

双向流模式的简单使用

server.go:

package mainimport ("FirstGo/goon/stream_grpc_test/proto""fmt""google.golang.org/grpc""net""sync""time"
)const PORT = ":50052"type server struct {
}// 对于服务端数据传输模式,参考以下内容:
// 没有 context 参数,而是将返回作为入参传入
func (s *server) GetStream(req *proto.StreamReqData, res proto.Greeter_GetStreamServer) error {i := 0for {i++_ = res.Send(&proto.StreamResData{Data: fmt.Sprintf("%v", time.Now().Unix()),})time.Sleep(time.Second)if i > 10 {break}}return nil
}// 客户端数据流模式
// 只有一个用来不断接收的入参
func (s *server) PutStream(cliStr proto.Greeter_PutStreamServer) error {// 客户端流模式,客户端不断的发送数据给服务器for {if a, err := cliStr.Recv(); err != nil {fmt.Println(err)} else {fmt.Println(a.Data)}}return nil
}// 双向数据流模式,和客户端模式相同,只有一个入参
func (s *server) AllStream(allStr proto.Greeter_AllStreamServer) error {// 不可以像下面这样简单使用,因为 Recv() 会阻塞主线程//allStr.Recv()//allStr.Send()wg := sync.WaitGroup{}wg.Add(2) // 添加两个等待线程go func() {defer wg.Done()for {data, _ := allStr.Recv()fmt.Println("收到客户端消息:" + data.Data)}}()go func() {defer wg.Done()for {_ = allStr.Send(&proto.StreamResData{Data: "我是服务器"})time.Sleep(time.Second)}}()wg.Wait()return nil
}func main() {lis, err := net.Listen("tcp", PORT)if err != nil {panic(err)}s := grpc.NewServer()proto.RegisterGreeterServer(s, &server{})s.Serve(lis)}

client.go:

package mainimport ("FirstGo/goon/stream_grpc_test/proto""context""fmt""google.golang.org/grpc""sync""time"
)func main() {// 拨号conn, err := grpc.Dial("localhost:50052", grpc.WithInsecure())if err != nil {panic(err)}defer conn.Close()c := proto.NewGreeterClient(conn)// // 这里要注意:我们在进行调用的时候使用的仍让是 simple 的模式,而不是服务端的函数模式// 服务端模式的客户端//res, _ := c.GetStream(context.Background(), &proto.StreamReqData{Data: "Chen"})// // 使用一个死循环来接收客户端传进来的数据//for {//	a, err := res.Recv()//	if err != nil {//		fmt.Println(err)//		break//	}//	fmt.Println(a)//}// 客户端数据流模式//putS, _ := c.PutStream(context.Background())//i := 0//for {//	i++//	putS.Send(&proto.StreamReqData{Data: fmt.Sprintf("Chen %d", i)})//	time.Sleep(time.Second)//	if i > 10 {//		break//	}//}// 双向流模式allStr, _ := c.AllStream(context.Background())wg := sync.WaitGroup{}wg.Add(2) // 添加两个等待线程go func() {defer wg.Done()for {data, _ := allStr.Recv()fmt.Println("收到客户端消息:" + data.Data)}}()go func() {defer wg.Done()for {_ = allStr.Send(&proto.StreamReqData{Data: "我是Chen (客户端)"})time.Sleep(time.Second)}}()wg.Wait()
}

相关文章:

【Go】五、Grpc 的入门使用

grpc 与 protobuf grpc 使用的是 protobuf 协议,其是一个通用的 rpc 框架,基本支持主流的所有语言、其底层使用 http/2 进行网络通信,具有较高的效率 protobuf 是一种序列化格式,这种格式具有 序列化以及解码速度快(…...

PDF加粗内容重复读取解决方案

文章目录 前言发现问题解决方案问题分析大致逻辑 show my code 前言 在使用pdfplumber读取PDF的过程中,由于加黑的内容会被莫名其妙的读取两次,带来了很大的困扰。这篇文章将给出解决方案。 发现问题 在在使用pdfplumber读取PDF的过程中,读…...

Golang 并发 Channel的用法

目录 Golang 并发 Channel的用法1. channel 的创建2. nil channel读写阻塞示例close示例 3. channel 的读写4. channel 只读只写5. 关闭channelchannel关闭后,剩余的数据能否取到读取关闭的channel,将获取零值使用ok判断,是否关闭使用for-ran…...

cfa复习资料介绍之二:notes(SchweserNotes)

什么是CFA notes? CFA资料Study Notes都是外国一些出版机构针对CFA考试提供的复习资料,而其中Schweser在国内的名气最大,用的人也最多。内容详尽并且突出重点,并且CFA Notes的内容相比于官方curriculum教材更加符合中国CFA考生的心态&#x…...

FITC Palmitate Conjugate,FITC-棕榈酸酯缀合物,可以用标准 FITC 滤光片组进行成像

FITC Palmitate Conjugate,FITC-棕榈酸酯缀合物,可以用标准 FITC 滤光片组进行成像 您好,欢迎来到新研之家 文章关键词:FITC Palmitate Conjugate,FITC-棕榈酸酯缀合物,FITC 棕榈酸酯缀合物,F…...

本机防攻击简介

定义 在网络中,存在着大量针对CPU(Central Processing Unit)的恶意攻击报文以及需要正常上送CPU的各类报文。针对CPU的恶意攻击报文会导致CPU长时间繁忙的处理攻击报文,从而引发其他业务的中断甚至系统的中断;大量正常…...

Python 进阶语法:JSON

1 什么是 JSON? 1.1 JSON 的定义 JSON 是 JavaScript Object Notation 的简写,字面上的意思是 JavaScript 对象标记。本质上,JSON 是轻量级的文本数据交换格式。轻量级,是拿它与另一种数据交换格式XML进行比较,相当轻…...

mescroll 在uni-app 运行的下拉刷新和上拉加载的组件

官网传送门: https://www.mescroll.com/uni.html 最近使用到了mescroll 但是一直都是整个页面的滚动, 最近需求有需要局部滚动, 收藏了一个博主的文章觉得写的还挺好, 传送门: https://blog.csdn.net/Minions_Fatman/article/details/134754926?spm1001.2014.3001.5506 使用…...

netty的TCP服务端和客户端实现

第一步&#xff1a;引入依赖 <dependencies><dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId><version>4.1.90.Final</version></dependency></dependencies> 第二步&#xff1a;实…...

合纵连横 – 以 Flink 和 Amazon MSK 构建 Amazon DocumentDB 之间的实时数据同步

在大数据时代&#xff0c;实时数据同步已经有很多地方应用&#xff0c;包括从在线数据库构建实时数据仓库&#xff0c;跨区域数据复制。行业落地场景众多&#xff0c;例如&#xff0c;电商 GMV 数据实时统计&#xff0c;用户行为分析&#xff0c;广告投放效果实时追踪&#xff…...

HBase 进阶

参考来源: B站尚硅谷HBase2.x 目录 Master 架构RegionServer 架构写流程MemStore Flush读流程HFile 结构读流程合并读取数据优化 StoreFile CompactionRegion Split预分区&#xff08;自定义分区&#xff09;系统拆分 Master 架构 Master详细架构 1&#xff09;Meta 表格介…...

一周学会Django5 Python Web开发-Django5路由命名与反向解析reverse与resolve

锋哥原创的Python Web开发 Django5视频教程&#xff1a; 2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~共计25条视频&#xff0c;包括&#xff1a;2024版 Django5 Python we…...

好奇!为什么gateway和springMVC之间依赖冲突?

Gateway和SpringMVC之间存在冲突&#xff0c;可能是因为它们分别基于不同的技术栈。具体来说&#xff1a; 技术栈差异&#xff1a;Spring Cloud Gateway 是建立在 Spring Boot 2.x 和 Spring WebFlux 基础之上的&#xff0c;它使用的是非阻塞式的 Netty 服务器。而 Spring MVC…...

一些内网渗透总结

windows命令收集 信息收集: 查看系统版本和补丁信息: systeminfo 查看系统开放端口: netstat -ano 查看系统进程: tasklist /svc 列出详细进程: tasklist /V /FO CSV 查看ip地址和dns信息: ipconfig /all 查看当前用户: whoami /user 查看计算机用户列表: net user 查看计算机…...

C#版字节跳动SDK - SKIT.FlurlHttpClient.ByteDance

前言 在我们日常开发工作中对接第三方开放平台&#xff0c;找一款封装完善且全面的SDK能够大大的简化我们的开发难度和提高工作效率。今天给大家推荐一款C#开源、功能完善的字节跳动SDK&#xff1a;SKIT.FlurlHttpClient.ByteDance。 项目官方介绍 可能是全网唯一的 C# 版字…...

深度学习系列60: 大模型文本理解和生成概述

参考网络课程&#xff1a;https://www.bilibili.com/video/BV1UG411p7zv/?p98&spm_id_frompageDriver&vd_source3eeaf9c562508b013fa950114d4b0990 1. 概述 包含理解和分类两大类问题&#xff0c;对应的就是BERT和GPT两大类模型&#xff1b;而交叉领域则对应T5 2.…...

SpringBoot 使用 JWT 保护 Rest Api 接口

用 spring-boot 开发 RESTful API 非常的方便&#xff0c;在生产环境中&#xff0c;对发布的 API 增加授权保护是非常必要的。现在我们来看如何利用 JWT 技术为 API 增加授权保护&#xff0c;保证只有获得授权的用户才能够访问 API。 一、Jwt 介绍 JSON Web Token (JWT)是一个开…...

大蟒蛇(Python)笔记(总结,摘要,概括)——第10章 文件和异常

目录 10.1 读取文件 10.1.1 读取文件的全部内容 10.1.2 相对文件路径和绝对文件路径 10.1.3 访问文件中的各行 10.1.4 使用文件的内容 10.1.5 包含100万位的大型文件 10.1.6 圆周率中包含你的生日吗 10.2 写入文件 10.2.1 写入一行 10.2.2 写入多行 10.3 异常 10.3.1 处理Ze…...

使用JDBC操作数据库(IDEA编译器)

目录 JDBC的本质 ​ JDBC好处 JDBC操作MySQL数据库 1.创建工程导入驱动jar包 2.编写测试代码 ​相关问题 JDBC的本质 官方(sun公司) 定义的一套操作所有关系型数据库的规则&#xff0c;即接口各个数据库厂商去实现这套接口&#xff0c;提供数据库驱动jar包我们可以使用这…...

Vue图片浏览组件v-viewer,支持旋转、缩放、翻转等操作

Vue图片浏览组件v-viewer&#xff0c;支持旋转、缩放、翻转等操作 之前用过viewer.js&#xff0c;算是市场上用过最全面的图片预览。v-viewer&#xff0c;是基于viewer.js的一个图片浏览的Vue组件&#xff0c;支持旋转、缩放、翻转等操作。 基本使用 安装&#xff1a;npm安装…...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…...

R语言AI模型部署方案:精准离线运行详解

R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...

Admin.Net中的消息通信SignalR解释

定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...

iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版​分享

平时用 iPhone 的时候&#xff0c;难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵&#xff0c;或者买了二手 iPhone 却被原来的 iCloud 账号锁住&#xff0c;这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...

OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 在 GPU 上对图像执行 均值漂移滤波&#xff08;Mean Shift Filtering&#xff09;&#xff0c;用于图像分割或平滑处理。 该函数将输入图像中的…...

AI书签管理工具开发全记录(十九):嵌入资源处理

1.前言 &#x1f4dd; 在上一篇文章中&#xff0c;我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源&#xff0c;方便后续将资源打包到一个可执行文件中。 2.embed介绍 &#x1f3af; Go 1.16 引入了革命性的 embed 包&#xff0c;彻底改变了静态资源管理的…...

处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的

修改bug思路&#xff1a; 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑&#xff1a;async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...

Mysql8 忘记密码重置,以及问题解决

1.使用免密登录 找到配置MySQL文件&#xff0c;我的文件路径是/etc/mysql/my.cnf&#xff0c;有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...

2025年渗透测试面试题总结-腾讯[实习]科恩实验室-安全工程师(题目+回答)

安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 腾讯[实习]科恩实验室-安全工程师 一、网络与协议 1. TCP三次握手 2. SYN扫描原理 3. HTTPS证书机制 二…...

Bean 作用域有哪些?如何答出技术深度?

导语&#xff1a; Spring 面试绕不开 Bean 的作用域问题&#xff0c;这是面试官考察候选人对 Spring 框架理解深度的常见方式。本文将围绕“Spring 中的 Bean 作用域”展开&#xff0c;结合典型面试题及实战场景&#xff0c;帮你厘清重点&#xff0c;打破模板式回答&#xff0c…...