gRPC(狂神说)

news2024/11/27 7:26:09

gRPC(狂神说)

视频地址:【狂神说】gRPC最新超详细版教程通俗易懂 | Go语言全栈教程_哔哩哔哩_bilibili

1、gRPC介绍

单体架构

  • 一旦某个服务宕机,会引起整个应用不可用,隔离性差
  • 只能整体应用进行伸缩,浪费资源,可伸缩性差
  • 代码耦合在一起,可维护性差

微服务架构:

解决了单体架构的弊端,但同时引入了新的问题

  • 代码冗余
  • 服务和服务之间存在调用关系

服务拆分后,服务和服务之间发生的是进程和进程之间的调用,服务器和服务器之间的调用,那么就需要发起网络调用。

网络调用我们能立马想起的就是http,但是在微服务架构中,http虽然便捷方便,但性能较低,这时候就需要引入RPC(远程过程调用),通过自定义协议发起TCP调用,来加快传输效率。

文档:Documentation | gRPC

中文文档:gRPC 官方文档中文版_V1.0 (oschina.net)

RPC的全称是Remote Procedure Call,远程过程调用。这是一种协议,是用来屏蔽分布式计算中的各种调用细节,使得你可以像是本地调用一样直接调用一个远程的函数。

客户端服务端 沟通的过程

  1. 客户端 发送数据(以字节流的方式)
  2. 服务端 接受并解析。 根据约定知道要执行什么。然后把结果返回给客户

RPC:

  • RPC就是将上述过程封装,使其操作更加优化
  • 使用一些大家都认可的协议 使其规范化
  • 做成一些框架。直接或间接产生利益

而gRPC又是什么呢?用官方的话来说

A high performance, open source universal RPC framework

在gRPC中,我们称调用方为client,被调用方为server。跟其他的RPC框架一样,gRPC也是基于“服务定义“的思想。简单的来讲,就是我们通过某种方式来描述一个服务,这种描述方式是语言无关的。在这个"服务定义"的过程中,我们描述了我们提供的服务服务名是什么,有哪些方法可以被调用,这些方法有什么样的入参,有什么样的回参。

也就是说,在定义好了这些服务、这些方法之后,**gRPC会屏蔽底层的细节,client只需要直接调用定义好的方法,就能拿到预期的返回结果。对于server端来说,还需要实现我们定义的方法。**同样的,gRPC也会帮我们屏蔽底层的细节,我们只需要实现所定义的方法的具体逻辑即可。

你可以发现,在上面的描述过程中,所谓的”“服务定义",就跟定义接口的语义是很接近的。我更愿意理解为这是一种"约定”,双方约定好接口,然后server实现这个接口,client调用这个接口的代理对象。至于其他的细节,交给gRPC。

此外,gRPC还是语言无关的。你可以用C++作为服务端,使用Golang、java等作为客户端。为了实现这一点,我们在"定义服务“和在编码和解码的过程中,应该是做到语言无关的。

image-20240608162027789

因此,gRPC使用了Protocol Buffss。这是谷歌开源的一套成熟的数据结构序列化机制。

你可以把他当成一个代码生成工具以及序列化工具。这个工具可以把我们定义的方法,转换成特定语言的代码。比如你定义了一种类型的参数。他会帮你转换成Golang中的struct 结构体,你定义的方法,他会帮你转换成func 图数。此外,在发送请求和接受响应的时候,这个工具还会完成对应的编码和解码工作,将你即将发送的数据编码成gRPC能够传输的形式,又或者将即将接收到的数据解码为编程语言能够理解的数据格式。

**序列化:**将数据结构或对象转换成二进制串的过程
**反序列化:**将在序列化过程中所产生的二进制串转换成数据结构或者对象的过程

protobuf是谷歌开源的一种数据格式,适合高性能,对响应速度有要求的数据传输场累。因为protobuf是二进制数据格式,需要编码和解码。数据本身不具有可读性。因此只能反序列化之后得到真正可读的数据。

优势

  • 序列化后体积相比Json和XML很小,适合网络传输
  • 支持跨平台多语言
  • 消息格式升级和兼容性还不错
  • 序列化反序列化速度很快

2、安装Protobuf

安装地址:Releases · protocolbuffers/protobuf (github.com)

解压、为bin目录配置环境变量

image-20240608162808975

验证:

cmd 输入:protoc

C:\Users\27184>protoc
Usage: protoc [OPTION] PROTO_FILES
Parse PROTO_FILES and generate output based on the options given:
  -IPATH, --proto_path=PATH   Specify the directory in which to search for
                              imports.  May be specified multiple times;
                              directories will be searched in order.  If not
                              given, the current working directory is used.
                              If not found in any of the these directories,
                              the --descriptor_set_in descriptors will be
                              checked for required proto file.
  --version                   Show version info and exit.
  -h, --help                  Show this text and exit.
----------------------------------------------------------------------  

安装gRPC核心库

go get google.golang.org/grpc

如果下载不下来,可能是没配置代理:

go env -w GOPROXY="https://goproxy.cn,direct"

image-20240608163206201

go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

3、Proto文件编写

//这是在说明我们使用的是proto3语法。
syntax ="proto3";

//这部分的内容是关于最后生成的go文件是处在哪个目录哪个包中,.代表在当前目录生成,service代表了生成的go文件的包名是service。
option go_package =".;service";

//然后我们需要定义一个服务,在这个服务中需要有一个方法,这个方法可以接受客户端的参数,再返回服务端的响应。
//其实很容易可以看出,我仰定义了一个service,称为SayHe11o,这个服务中有一个rpc方法,名为SayHe1lo。
//这个方法会发送-个HelloRequest,然后返回一个HelloResponse。
service SayHello{
  rpc SayHello(HelloRequest)returns (HelloResponse){}
}
//message关键字,其实你可以理解为Go1ang中的结构体。
//这里比较特别的是变量后面的“赋值”。注意,这里并不是赋值,而是在定义这个变量在这个message中的位置。
message HelloRequest{
  string requestName = 1;
  // int64 age = 2;
}
message HelloResponse{
  string responseMsg = 1;
}

在编写完上面的内容后,在hello-server/proto目录下执行如下命令:

protoc --go_out=. hello.proto
protoc --go-grpc_out=. hello.proto

(如果出现了 --go_out: hello.pb.go: unparsable Go source: 1:3: illegal UTF-8 encoding (and 18 more errors) 的错误,请删除上述文件中的中文!!)

4、Proto文件介绍

message

message: protobuf中定义一个消息类型是通过关键字message字段指定的。消息就是需要传输的数据格式的定义

message 关键字类似于C++中的class,JAVA中的class,go中的struct

在消息中承载的数据分别对应于每一个字段,其中每个字段都有一个名字和一种类型

一个proto文件中可以定义多个消息类型

字段规则

required: 消息体中必填字段,不设置会导致编码异常。在protobuf2中使用,在protobuf3中被删去

optional: 消息体中可选字段。protobuf3没有了required,optional等说明关键字,都默认为optional

repeated: 消息体中可重复字段,重复的值的顺序会被保留在go中重复的会被定义为切片。

message HelloRequest{
  string requestName = 1;
  int64 age = 2;
  repeated string name = 3;
}

image-20240608181450016

消息号

在消息体的定义中,每个字段都必须要有一个唯一的标识号,标识号是[1,2^29-1]范围内的一个整数

嵌套消息

可以在其他消息类型中定义、使用消息类型,在下面的例子中,person消息就定义在Personlnfo消息内如

message PersonInfo{
  message Person{
    string name = 1;
    int32 height = 2;
  }
  repeated Person info = 1;
}

image-20240608181810694

服务定义

如果想要将消息类型用在RPC系统中,可以在.proto文件中定义一个RPC服务接口,protocol buffer编译器将会根据所选择的不同语言生成服务接口代码及存根。

service searchService{
  rpc Search(searchRequest)returns (searchResponse){}
}

上述代表表示,定义了一个RPC服务,该方法接收 searchRequest 返回 searchResponse

5、服务端代码编写

package main

import (
	"context"
	"fmt"
	pb "gRPC/hello-server/proto"
	"google.golang.org/grpc"
	"net"
)

type server struct {
	pb.UnimplementedSayHelloServer
}

func (s *server) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloResponse, error) {
	return &pb.HelloResponse{ResponseMsg: "hello" + " " + req.RequestName}, nil
}

func main() {
	// 1. 开启端口
	listen, err := net.Listen("tcp", ":9090")
	if err != nil {
		fmt.Printf("listen error: %v", err)
		fmt.Println()
	}

	// 2. 创建grpc服务,在grpc服务端中注册
	grpcServer := grpc.NewServer()
	pb.RegisterSayHelloServer(grpcServer, &server{})

	// 3.启动服务
	err = grpcServer.Serve(listen)
	if err != nil {
		fmt.Printf("failed to serve: %v", err)
		return
	}

}

6、客户端代码编写

package main

import (
	"context"
	"fmt"
	pb "gRPC/hello-server/proto"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	"log"
)

func main() {
	// 1. 连接到server端,此处禁用安全传输,没有加密和验证
	conn, err := grpc.Dial("127.0.0.1:9090", grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		log.Fatalf("did not connect: %v", err)
	}
	defer conn.Close()

	// 2. 建立连接
	client := pb.NewSayHelloClient(conn)

	// 3. 执行rpc调用
	resp, _ := client.SayHello(context.Background(), &pb.HelloRequest{RequestName: "zwj"})
	fmt.Printf(resp.ResponseMsg)
}

依次启动服务端、客户端即可。

7、认证及安全传输

gRPC 是一个典型的C/S模型,需要开发客户端和服务端,客户端与服务端需要达成协议,使用某一个确认的传输协议来传输数据,
gRPC默认是使用protobuf来作为传输协议,当然也是可以使用其他自定义的协议。

image-20240608184326020

那么,客户端与服务端要通信之前,客户端如何知道自己的数据是发给哪一个明确的服务端呢?反过来,服务端是不是也需要有一种方式来弄清楚自己的数据要返回给谁呢?

那么就不得不提gRPC的认证,此处说到的认证,不是用户的身份认证,而是指多个server 和多个client之间,如何识别对方是谁,并且可以安全的进行数据传输

  • SSL/TLS认证方式(采用http2协议)
  • 基于Token的认证方式(基于安全连接)
  • 不采用任何措施的连接,这是不安全的连接(默认采用http1)
  • 自定义的身份认证

客户端和服务端之间调用,我们可以通过加入证书的方式,实现调用的安全性

TLS (Transport Laver Security,安全传输层),TLS是建立在传输层TCP协议之上的协议,服务于应用层,它的前身是SSL(Secure SocketLaver 安全套接字层),它实现了将应用层的报文进行加密后再交由TCP进行传输的功能。

TLS协议主要解决如下三个网络安全问题:

  1. 保密(message privacy),保密通过加密encryption实现,所有信息都加密传输,第三方无法嗅探
  2. 完整性(message integrity),通过MAC校验机制,一旦被篡改,通信双方会立刻发现;
  3. 认证(mutual authentication),双方认证,双方都可以配备证书,防止身份被冒充:

生产环境可以购买证书或者使用一些平台发放的免费证书

key:服务器上的私钥文件,用于对发送给客户端数据的加密,以及对从客户端接收到数据的解密。

csr:证书签名请求文件,用于提交给证书颁发机构(CA)对证书签名

crt:由证书颁发机构(CA)签名后的证书,或者是开发者自签名的证书,包含证书持有人的信息,持有人的公钥,以及签署者的签名等信息。

pem:是基于Base64编码的证书格式,扩展名包括PEM、CRT和CER。

一些名词解释:一次把http、https、tcp、udp、tls、ssl聊明白 - 二柒的博客 - 博客园 (cnblogs.com)

8、TLS认证实现

首先通过openssl生成证书和私钥

  1. 官网下载:https://www.openssl.org/source/
    • 其他人做的便捷版安装包 Win32/Win64 OpenSSL Installer for Windows - Shining Light Productions (slproweb.com)
  2. 我们使用 便捷版安装包,一直下一步即可
  3. 配置环境变量 D:\Environment\OpenSSL-Win64\bin
  4. 命令行测试 openssl

生成证书

#1、生成私钥
openssl genrsa -out server.key 2048
#2、生成证书全部回车即可,可以不填
openssl req -new -x509 -key server.key -out server.crt -days 36500

#国家名称
Country Name (2 letter code)[AU]:CN

#省名称
State or Province Name (full name)[Some-State]:GuangDong

#城市名称
Locality Name
(eg,city)[]Meizhou

#公司组织名称
organization Name (eg,company)[Internet widgits Pty Ltd]:Xuexiangban

#部门名称
organizational Unit Name (eg,section)[]go

#服务器or网站名称
Common Name (e.g.server FQDN or YOUR name)[]kuangstudy

#邮件
Email Address []:24736743@qq.com
#3、生成csr
openssl req -new -key server.key -out server.csr

更改openssl文件

#1)复制一份你安装的openss1的bin目录里面的openss1.cnf 文件到你项目所在的目录
#2)找到[ CA_default],打开 copy_extensions = copy (就是把前面的#去掉)
#3)找到[ req ],打开 req_extensions = v3_reg # The extensions to add to a certificate request
#4)找到[v3_req],添加 subjectAltName = @alt_names
#5)添加新的标签[ alt_names ],和标签字段
DNS.1 =*.kuangstudy.com

生成私钥信息

#生成证书私钥test.key
openssl genpkey -algorithm RSA -out test.key
#通过私钥test.key生成证书请求文件test.csr(注意cfg和cnf)
openssl req -new -nodes -key test.key -out test.csr -days 3650 -subj "/C=cn/OU=myorg/O=mycomp/CN=myname" -config ./openssl.cfg -extensions v3_req
#test.csr是上面生成的证书请求文件。ca.crt/server.key是CA证书文件和key,用来对test.csr进行签名认证。这两个文件在第一部分生成。

#上述一般会有警告信息:Ignoring -days without -x509; not generating a certificate,这个警告表明,在没有指定 -x509 选项的情况下,-days 选项将被忽略。-days 选项通常用于指定自签名证书(self-signed certificate)的有效期限,而不是 CSR。因此,如果你想生成一个自签名证书,你需要添加 -x509 选项,但是这样做下面的生成SAN证书会出现错误!!
openssl req -new -nodes -key test.key -out test.csr -x509 -days 3650 -subj "/C=cn/OU=myorg/O=mycomp/CN=myname" -config ./openssl.cfg -extensions v3_req
#生成SAN证书pem
openssl x509 -req -days 365 -in test.csr -out test.pem -CA server.crt -CAkey server.key -CAcreateserial -extfile ./openssl.cfg -extensions v3_req

服务端代码

package main

import (
	"context"
	"fmt"
	pb "gRPC/hello-server/proto"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"net"
	"os"
)

type server struct {
	pb.UnimplementedSayHelloServer
}

func (s *server) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloResponse, error) {
	return &pb.HelloResponse{ResponseMsg: "hello" + " " + req.RequestName}, nil
}

func main() {

	cwd, err := os.Getwd()
	if err != nil {
		fmt.Println("Error getting current working directory:", err)
		return
	}
	fmt.Println("Current working directory:", cwd)

	cred, err := credentials.NewServerTLSFromFile("./key/test.pem", "./key/test.key")
	if err != nil {
		fmt.Println(err)
		fmt.Println("NewServerTLSFromFile error")
	}

	// 1. 开启端口
	listen, err := net.Listen("tcp", ":9090")
	if err != nil {
		fmt.Printf("listen error: %v", err)
		fmt.Println()
	}

	// 2. 创建grpc服务,在grpc服务端中注册
	grpcServer := grpc.NewServer(grpc.Creds(cred))
	pb.RegisterSayHelloServer(grpcServer, &server{})

	// 3.启动服务
	err = grpcServer.Serve(listen)
	if err != nil {
		fmt.Printf("failed to serve: %v", err)
		return
	}

}

客户端代码

package main

import (
	"context"
	"fmt"
	pb "gRPC/hello-server/proto"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"log"
)

func main() {

	cred, err := credentials.NewClientTLSFromFile("./key/test.pem",
		"*.kuangstudy.com")
	if err != nil {
		fmt.Println(err)
		fmt.Println("NewClientTLSFromFile error")
	}

	// 1. 连接到server端,此处禁用安全传输,没有加密和验证
	conn, err := grpc.Dial("127.0.0.1:9090", grpc.WithTransportCredentials(cred))
	if err != nil {
		log.Fatalf("did not connect: %v", err)
	}
	defer conn.Close()

	// 2. 建立连接
	client := pb.NewSayHelloClient(conn)

	// 3. 执行rpc调用
	resp, _ := client.SayHello(context.Background(), &pb.HelloRequest{RequestName: "zwj"})
	fmt.Printf(resp.ResponseMsg)
}

9、自定义Token

我们先看一个gRPC提供我们的一个接口,这个接口中有两个方法,接口位于credentials 包下,这个接口需要客户端来实现

// PerRPCCredentials defines the common interface for the credentials which need to
// attach security information to every RPC (e.g., oauth2).
type PerRPCCredentials interface {
	// GetRequestMetadata gets the current request metadata, refreshing tokens
	// if required. This should be called by the transport layer on each
	// request, and the data should be populated in headers or other
	// context. If a status code is returned, it will be used as the status for
	// the RPC (restricted to an allowable set of codes as defined by gRFC
	// A54). uri is the URI of the entry point for the request.  When supported
	// by the underlying implementation, ctx can be used for timeout and
	// cancellation. Additionally, RequestInfo data will be available via ctx
	// to this call.  TODO(zhaoq): Define the set of the qualified keys instead
	// of leaving it as an arbitrary string.
	GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error)
	// RequireTransportSecurity indicates whether the credentials requires
	// transport security.
	RequireTransportSecurity() bool
}

第一个方法作用是获取元数据信息,也就是客户端提供的key,alue对,context用于控制超时和取消,uri是请求入口处的uri

第二个方法的作用是否需要基于 TLS 认证进行安全传输,如果返回值是true,则必须加上TLS验证,返回值是false则不用。

客户端代码

package main

import (
	"context"
	"fmt"
	pb "gRPC/hello-server/proto"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	"log"
)

type ClientTokenAuth struct {
}

func (c ClientTokenAuth) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
	return map[string]string{
		"appId":  "kuangshen",
		"appKey": "123123",
	}, nil
}

func (c ClientTokenAuth) RequireTransportSecurity() bool {
	return false
}

func main() {

	var dials []grpc.DialOption
	dials = append(dials, grpc.WithTransportCredentials(insecure.NewCredentials()))
	dials = append(dials, grpc.WithPerRPCCredentials(new(ClientTokenAuth)))

	//cred, err := credentials.NewClientTLSFromFile("./key/test.pem",
	//	"*.kuangstudy.com")
	//if err != nil {
	//	fmt.Println(err)
	//	fmt.Println("NewClientTLSFromFile error")
	//}

	// 1. 连接到server端,此处禁用安全传输,没有加密和验证
	conn, err := grpc.Dial("127.0.0.1:9090", dials...)
	if err != nil {
		log.Fatalf("did not connect: %v", err)
	}
	defer conn.Close()

	// 2. 建立连接
	client := pb.NewSayHelloClient(conn)

	// 3. 执行rpc调用
	resp, _ := client.SayHello(context.Background(), &pb.HelloRequest{RequestName: "zwj"})
	fmt.Printf(resp.ResponseMsg)
}

服务端代码

package main

import (
	"context"
	"errors"
	"fmt"
	pb "gRPC/hello-server/proto"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	"google.golang.org/grpc/metadata"
	"net"
)

type server struct {
	pb.UnimplementedSayHelloServer
}

// SayHello 业务代码
func (s *server) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloResponse, error) {
	md, ok := metadata.FromIncomingContext(ctx)
	if !ok {
		return nil, errors.New("not found token")
	}
	var appId string
	var appKey string
	if v, ok := md["appid"]; ok {
		appId = v[0]
	}
	if v, ok := md["appkey"]; ok {
		appKey = v[0]
	}
	fmt.Printf("appId:%s,appKey:%s", appId, appKey)
	fmt.Println()
	if appId != "kuangshen" || appKey != "123123" {
		return nil, errors.New("token is not valid")
	}
	return &pb.HelloResponse{ResponseMsg: "hello" + " " + req.RequestName + " " + appId + " " + appKey}, nil
}

func main() {

	//cred, err := credentials.NewServerTLSFromFile("./key/test.pem", "./key/test.key")
	//if err != nil {
	//	fmt.Println(err)
	//	fmt.Println("NewServerTLSFromFile error")
	//}

	// 1. 开启端口
	listen, err := net.Listen("tcp", ":9090")
	if err != nil {
		fmt.Printf("listen error: %v", err)
		fmt.Println()
	}

	// 2. 创建grpc服务,在grpc服务端中注册
	grpcServer := grpc.NewServer(grpc.Creds(insecure.NewCredentials()))
	pb.RegisterSayHelloServer(grpcServer, &server{})

	// 3.启动服务
	err = grpcServer.Serve(listen)
	if err != nil {
		fmt.Printf("failed to serve: %v", err)
		return
	}

}

gRPC将各种认证方式浓缩统一到一个凭证(credentials)上,可以单独使用一种凭证,比如只使用TLS凭证或者只使用自定义凭证,也可以多种凭证组合,gRPC提供统一的API验证机制,使研发人员使用方便,这也是gRPC设计的巧妙之处。

10、小结

image-20240609110709927

期待kubernetes呜呜呜

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1805286.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于小波多分辨分析的一维时间序列信号趋势检测与去除(MATLAB R2018a)

小波最开始是数学上提出的概念,并且在纯数学的王国里存在了一个世纪之久。最开始是为了弥补傅里叶分析的缺陷,即傅里叶级数发散的问题,并寻找出能够代替傅里叶分析的方法。从最早的一些艰难的探索开始直到慢慢发展成为一套完整系统的小波分析…

Flutter Image源码分析

本文用于记录分析Imge图片加载流程源码分析学习笔记 切入点是Image.network,加载网络图片 构造方法会创建NetworkImage,加载图片的实现类,父类是ImageProvider 加载本地图片等等都是类似 下面进入_ImageState类 void resolveStreamForKey(ImageConfiguration configurat…

【WEB前端2024】3D智体编程:乔布斯3D纪念馆-第37课-自动切换纹理

【WEB前端2024】3D智体编程:乔布斯3D纪念馆-第37课-自动切换纹理 使用dtns.network德塔世界(开源的智体世界引擎),策划和设计《乔布斯超大型的开源3D纪念馆》的系列教程。dtns.network是一款主要由JavaScript编写的智体世界引擎&…

【iOS】JSONModel源码阅读笔记

文章目录 前言一、JSONModel使用二、JSONModel其他方法转换属性名称 三、源码分析- (instancetype)initWithDictionary:(NSDictionary*)dict error:(NSError **)err[self init]__setup____inspectProperties - (BOOL)__doesDictionary:(NSDictionary*)dict matchModelWithKeyMa…

Day25 首页待办事项及备忘录添加功能

​ 本章节,完成首页待办事项及备忘录添加功能 一.修改待办事项和备忘录逻辑处理类,即AddMemoViewModel和AddTodoViewModel 在 AddMemoViewModel逻辑处理类中,为了支持与其关联的View视图文件的数据绑定,需要定义一个与视图文件相匹配的实体类 Model。这个Model将包含 View中…

Java开发-面试题-0005-==和String的equals()和String的intern()方法的区别

Java开发-面试题-0005-和String的equals()和String的intern()方法的区别 更多内容欢迎关注我(持续更新中,欢迎Star✨) Github:CodeZeng1998/Java-Developer-Work-Note 技术公众号:CodeZeng1998(纯纯技术…

算法:101. 对称二叉树

对称二叉树 给你一个二叉树的根节点 root , 检查它是否轴对称。 示例 1: 输入:root [1,2,2,3,4,4,3] 输出:true示例 2: 输入:root [1,2,2,null,3,null,3] 输出:false提示: 树中节…

蓝桥杯--跑步计划

问题描述 小蓝计划在某天的日期中出现 11 时跑 55 千米,否则只跑 11 千米。注意日期中出现 11 不仅指年月日也指星期。 请问按照小蓝的计划,20232023 年小蓝总共会跑步锻炼多少千米?例如,55 月 11 日、11 月 1313 日、1111 月 55 日、44 月…

Dubbo 3.x源码(20)—Dubbo服务引用源码(3)

基于Dubbo 3.1,详细介绍了Dubbo服务的发布与引用的源码。 此前我们学习了调用createProxy方法,根据服务引用参数map创建服务接口代理引用对象的整体流程,我们知道会调用createInvokerForRemote方法创建远程引用Invoker,这是Dubbo …

开启数字化校园解决方案,实现教育智能化

现代社会的教育面临诸多挑战,如何提高教育质量,实现教育智能化成为了当务之急。数字化校园解决方案应运而生,为学校提供了全新的教学模式和管理方式。本文将介绍数字化校园解决方案的重要性,以及如何开启数字化校园,实…

客户案例|Zilliz Cloud 助力点石科技转型 AI 智能服务商

福建点石科技网络科技有限公司成立于2010年,是国家高新技术企业,阿里云、蚂蚁金服等大厂海内外生态合作伙伴ISV。在餐饮、零售、酒店、旅游、商圈的行业定制化服务化上有深厚积累,在境内外做了大量标杆性软件项目,如东南亚RWS圣淘…

《python程序语言设计》2018版第5章第46题均值和标准方差-上部(我又一次被作者的出题击倒)

第N次被作者打倒了&#xff0c;第5章46题解题上集的记录 计算均值的代码段 step_num 0num_c 0 pow_c 0 while step_num < 10:a eval(input("Enter number is: "))num_c apow_c pow(a, 2)step_num 1 t2 num_c / 10这个结果和书里的答案差一点。书里写的是…

容器中运行ping提示bash: ping: command not found【笔记】

容器中运行ping提示bash: ping: command not found 原因是容器中没有安装ping命令 在容器中安装ping命令&#xff0c;可以使用以下命令&#xff1a; 对于基于Debian/Ubuntu的容器&#xff0c;使用以下命令&#xff1a; apt-get update apt-get install -y iputils-ping对于基…

Docker run 命令常用参数详解

Docker run 命令提供了丰富的参数选项&#xff0c;用于配置容器的各种设置。以下是docker run命令的主要参数详解&#xff0c; 主要参数详解 后台运行与前台交互 -d, --detach: 在后台运行容器&#xff0c;并返回容器ID。-it: 分配一个伪终端&#xff08;pseudo-TTY&#xff0…

如何做好电子内窥镜的网络安全管理?

电子内窥镜作为一种常用的医疗器械&#xff0c;其网络安全管理对于保护患者隐私和医疗数据的安全至关重要。以下是一些基本原则和步骤&#xff0c;用于确保电子内窥镜的网络安全&#xff1a; 1. 数据加密 为了防止数据泄露&#xff0c;电子内窥镜在传输患者图像数据时应采取有…

解决!word转pdf时,怎样保持图片不失真

#今天用word写了期末设计报告&#xff0c;里面有很多过程的截图&#xff0c;要打印出来&#xff0c;想到pdf图片不会错位&#xff0c;就转成了pdf&#xff0c;发现图片都成高糊了&#xff0c;找了好多方法&#xff0c;再不下载其他软件和插件的情况下&#xff0c;导出拥有清晰的…

cf 欧几里得距离

说明&#xff1a;欧几里得距离本质就是两点间距离 distancesqrt( sum(ai-bi)2 ) Problem - F - Codeforces 代码

下载安装Thonny并烧录MicroPython固件至ESP32

Thonny介绍 一、Thonny的基本特点 面向初学者&#xff1a;Thonny的设计初衷是为了帮助Python初学者更轻松、更快速地入门编程。它提供了直观易懂的用户界面和丰富的功能&#xff0c;降低了编程的门槛。轻量级&#xff1a;作为一款轻量级的IDE&#xff0c;Thonny不会占用过多的…

Zookeeper高频面试题整理(入门到精通)

文章目录 1、什么是Zookeeper&#xff1f;2、ZooKeeper的基本数据结构是什么&#xff1f;3、Zookeeper的节点类型有哪些&#xff1f;4、Zookeeper的特点5、ZooKeeper如何保证数据一致性&#xff1f;6、什么是ZAB协议&#xff1f;7、Zookeeper的ACL机制是什么&#xff1f;8、Zoo…

读书笔记:左耳听风

程序员如何用技术变现 我完全没有必要通过打工听人安排而活着&#xff0c;而是反过来通过在公司工作提高自己的技能&#xff0c;让自己可以更为独立和自由地生活。 因而&#xff0c;在工作当中&#xff0c;对于那些没什么技术含量的工作&#xff0c;我基本上就像是在学生时代那…