13-API风格(下):RPCAPI介绍

RPC在Go项目开发中用得也非常多,需要我们认真掌握。

RPC介绍

根据维基百科的定义,RPC(Remote Procedure Call),即远程过程调用,是一个计算机通信协议。该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员不用额外地为这个交互作用编程。

通俗来讲,就是服务端实现了一个函数,客户端使用RPC框架提供的接口,像调用本地函数一样调用这个函数,并获取返回值。 

RPC的调用过程如下图所示:

RPC调用具体流程如下:

  1. Client通过本地调用,调用Client Stub。
  2. Client Stub将参数打包(也叫Marshalling)成一个消息,然后发送这个消息。
  3. Client所在的OS将消息发送给Server。
  4. Server端接收到消息后,将消息传递给Server Stub。
  5. Server Stub将消息解包(也叫 Unmarshalling)得到参数。
  6. Server Stub调用服务端的子程序(函数),处理完后,将最终结果按照相反的步骤返回给 Client。

这里需要注意,Stub负责调用参数和返回值的流化(serialization)、参数的打包和解包,以及网络层的通信。Client端一般叫Stub,Server端一般叫Skeleton。

目前,业界有很多优秀的RPC协议,例如腾讯的Tars、阿里的Dubbo、微博的Motan、Facebook的Thrift、RPCX,等等。但使用最多的还是gRPC

gRPC介绍

gRPC是由Google开发的高性能、开源、跨多种编程语言的通用RPC框架,基于HTTP 2.0协议开发,默认采用Protocol Buffers数据序列化协议。gRPC具有如下特性:

  • 支持多种语言,例如 Go、Java、C、C++、C#、Node.js、PHP、Python、Ruby等。
  • 基于IDL(Interface Definition Language)文件定义服务,通过proto3工具生成指定语言的数据结构、服务端接口以及客户端Stub。通过这种方式,也可以将服务端和客户端解耦,使客户端和服务端可以并行开发。
  • 通信协议基于标准的HTTP/2设计,支持双向流、消息头压缩、单TCP的多路复用、服务端推送等特性。
  • 支持Protobuf和JSON序列化数据格式。Protobuf是一种语言无关的高性能序列化框架,可以减少网络传输流量,提高通信效率。

这里要注意的是,gRPC的全称不是golang Remote Procedure Call,而是google Remote Procedure Call。

gRPC的调用如下图所示:

 ,通过gRPC调用,我们可以非常容易地构建出一个分布式应用。

 

gRPC支持多种语言,比如我们可以用Go语言实现gRPC服务,并通过Java语言客户端调用gRPC服务所提供的方法。通过多语言支持,我们编写的gRPC服务能满足客户端多语言的需求。

gRPC API接口通常使用的数据传输格式是Protocol Buffers。接下来,我们就一起了解下Protocol Buffers。

Protocol Buffers介绍

Protocol Buffers(ProtocolBuffer/ protobuf)是Google开发的一套对数据结构进行序列化的方法,可用作(数据)通信协议、数据存储格式等,也是一种更加灵活、高效的数据格式,与XML、JSON类似。它的传输性能非常好,所以常被用在一些对数据传输性能要求比较高的系统中,作为数据传输格式。Protocol Buffers的主要特性有下面这几个。

  • 更快的数据传输速度:protobuf在传输时,会将数据序列化为二进制数据,和XML、JSON的文本传输格式相比,这可以节省大量的IO操作,从而提高数据传输速度。
  • 跨平台多语言:protobuf自带的编译工具 protoc 可以基于protobuf定义文件,编译出不同语言的客户端或者服务端,供程序直接调用,因此可以满足多语言需求的场景。
  • 具有非常好的扩展性和兼容性,可以更新已有的数据结构,而不破坏和影响原有的程序。
  • 基于IDL文件定义服务,通过proto3工具生成指定语言的数据结构、服务端和客户端接口

在gRPC的框架中,Protocol Buffers主要有三个作用。

第一,可以用来定义数据结构。举个例子,下面的代码定义了一个SecretInfo数据结构:

// SecretInfo contains secret details.
message SecretInfo {string name = 1;string secret_id  = 2;string username   = 3;string secret_key = 4;int64 expires = 5;string description = 6;string created_at = 7;string updated_at = 8;
}

第二,可以用来定义服务接口。下面的代码定义了一个Cache服务,服务包含了ListSecrets和ListPolicies 两个API接口。

// Cache implements a cache rpc service.
service Cache{rpc ListSecrets(ListSecretsRequest) returns (ListSecretsResponse) {}rpc ListPolicies(ListPoliciesRequest) returns (ListPoliciesResponse) {}
}

第三,可以通过protobuf序列化和反序列化,提升传输效率。

gRPC示例

 运行本示例需要在Linux服务器上安装Go编译器、Protocol buffer编译器(protoc,v3)和 protoc 的Go语言插件,在 02讲 中我们已经安装过,这里不再讲具体的安装方法。

这个示例分为下面几个步骤:

  1. 定义gRPC服务。
  2. 生成客户端和服务器代码。
  3. 实现gRPC服务。
  4. 实现gRPC客户端。

示例代码存放在gopractise-demo/apistyle/greeter目录下。代码结构如下:

$ tree
├── client
│   └── main.go
├── helloworld
│   ├── helloworld.pb.go
│   └── helloworld.proto
└── server└── main.go

client目录存放Client端的代码,helloworld目录用来存放服务的IDL定义,server目录用来存放Server端的代码。

下面我具体介绍下这个示例的四个步骤。

  1. 定义gRPC服务。

首先,需要定义我们的服务。进入helloworld目录,新建文件helloworld.proto:

$ cd helloworld
$ vi helloworld.proto

内容如下:

syntax = "proto3";option go_package = "github.com/marmotedu/gopractise-demo/apistyle/greeter/helloworld";package helloworld;// The greeting service definition.
service Greeter {// Sends a greetingrpc 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;
}

在helloworld.proto定义文件中,option关键字用来对.proto文件进行一些设置,其中go_package是必需的设置,而且go_package的值必须是包导入的路径。package关键字指定生成的.pb.go文件所在的包名。我们通过service关键字定义服务,然后再指定该服务拥有的RPC方法,并定义方法的请求和返回的结构体类型:

service Greeter {// Sends a greetingrpc SayHello (HelloRequest) returns (HelloReply) {}
}

gRPC支持定义4种类型的服务方法,分别是简单模式、服务端数据流模式、客户端数据流模式和双向数据流模式。

  • 简单模式(Simple RPC):是最简单的gRPC模式。客户端发起一次请求,服务端响应一个数据。定义格式为rpc SayHello (HelloRequest) returns (HelloReply) {}。

  • 服务端数据流模式(Server-side streaming RPC):客户端发送一个请求,服务器返回数据流响应,客户端从流中读取数据直到为空。定义格式为rpc SayHello (HelloRequest) returns (stream HelloReply) {}。

  • 客户端数据流模式(Client-side streaming RPC):客户端将消息以流的方式发送给服务器,服务器全部处理完成之后返回一次响应。定义格式为rpc SayHello (stream HelloRequest) returns (HelloReply) {}。

  • 双向数据流模式(Bidirectional streaming RPC):客户端和服务端都可以向对方发送数据流,这个时候双方的数据可以同时互相发送,也就是可以实现实时交互RPC框架原理。定义格式为rpc SayHello (stream HelloRequest) returns (stream HelloReply) {}。

本示例使用了简单模式。.proto文件也包含了Protocol Buffers 消息的定义,包括请求消息和返回消息。例如请求消息:

// The request message containing the user's name.
message HelloRequest {string name = 1;
}
  1. 生成客户端和服务器代码。

接下来,我们需要根据.proto服务定义生成gRPC客户端和服务器接口。我们可以使用protoc编译工具,并指定使用其Go语言插件来生成:

$ protoc -I. --go_out=plugins=grpc:$GOPATH/src helloworld.proto
$ ls
helloworld.pb.go  helloworld.proto

你可以看到,新增了一个helloworld.pb.go文件。

  1. 实现gRPC服务。

接着,我们就可以实现gRPC服务了。进入server目录,新建main.go文件:

$ cd ../server
$ vi main.go

main.go内容如下:

// Package main implements a server for Greeter service.
package mainimport ("context""log""net"pb "github.com/marmotedu/gopractise-demo/apistyle/greeter/helloworld""google.golang.org/grpc"
)const (port = ":50051"
)// server is used to implement helloworld.GreeterServer.
type server struct {pb.UnimplementedGreeterServer
}// SayHello implements helloworld.GreeterServer
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {log.Printf("Received: %v", in.GetName())return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil
}func main() {lis, err := net.Listen("tcp", port)if err != nil {log.Fatalf("failed to listen: %v", err)}s := grpc.NewServer()pb.RegisterGreeterServer(s, &server{})if err := s.Serve(lis); err != nil {log.Fatalf("failed to serve: %v", err)}
}

上面的代码实现了我们上一步根据服务定义生成的Go接口。

我们先定义了一个Go结构体server,并为server结构体添加SayHello(context.Context, pb.HelloRequest) (pb.HelloReply, error)方法,也就是说server是GreeterServer接口(位于helloworld.pb.go文件中)的一个实现。

在我们实现了gRPC服务所定义的方法之后,就可以通过 net.Listen(...) 指定监听客户端请求的端口;接着,通过 grpc.NewServer() 创建一个gRPC Server实例,并通过 pb.RegisterGreeterServer(s, &server{}) 将该服务注册到gRPC框架中;最后,通过 s.Serve(lis) 启动gRPC服务。

创建完main.go文件后,在当前目录下执行 go run main.go ,启动gRPC服务。

  1. 实现gRPC客户端。

打开一个新的Linux终端,进入client目录,新建main.go文件:

$ cd ../client
$ vi main.go

main.go内容如下:

// Package main implements a client for Greeter service.
package mainimport ("context""log""os""time"pb "github.com/marmotedu/gopractise-demo/apistyle/greeter/helloworld""google.golang.org/grpc"
)const (address     = "localhost:50051"defaultName = "world"
)func main() {// Set up a connection to the server.conn, err := grpc.Dial(address, grpc.WithInsecure(), grpc.WithBlock())if err != nil {log.Fatalf("did not connect: %v", err)}defer conn.Close()c := pb.NewGreeterClient(conn)// Contact the server and print out its response.name := defaultNameif len(os.Args) > 1 {name = os.Args[1]}ctx, cancel := context.WithTimeout(context.Background(), time.Second)defer cancel()r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name})if err != nil {log.Fatalf("could not greet: %v", err)}log.Printf("Greeting: %s", r.Message)
}

在上面的代码中,我们通过如下代码创建了一个gRPC连接,用来跟服务端进行通信:

// Set up a connection to the server.
conn, err := grpc.Dial(address, grpc.WithInsecure(), grpc.WithBlock())
if err != nil {log.Fatalf("did not connect: %v", err)
}
defer conn.Close()

在创建连接时,我们可以指定不同的选项,用来控制创建连接的方式,例如grpc.WithInsecure()、grpc.WithBlock()等。gRPC支持很多选项,更多的选项可以参考grpc仓库下dialoptions.go文件中以With开头的函数。

连接建立起来之后,我们需要创建一个客户端stub,用来执行RPC请求c := pb.NewGreeterClient(conn)。创建完成之后,我们就可以像调用本地函数一样,调用远程的方法了。例如,下面一段代码通过 c.SayHello 这种本地式调用方式调用了远端的SayHello接口:

r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name})
if err != nil {log.Fatalf("could not greet: %v", err)
}
log.Printf("Greeting: %s", r.Message)

从上面的调用格式中,我们可以看到RPC调用具有下面两个特点。

  • 调用方便:RPC屏蔽了底层的网络通信细节,使得调用RPC就像调用本地方法一样方便,调用方式跟大家所熟知的调用类的方法一致:ClassName.ClassFuc(params)
  • 不需要打包和解包:RPC调用的入参和返回的结果都是Go的结构体,不需要对传入参数进行打包操作,也不需要对返回参数进行解包操作,简化了调用步骤。

最后,创建完main.go文件后,在当前目录下,执行go run main.go发起RPC调用:

$ go run main.go
2020/10/17 07:55:00 Greeting: Hello world

至此,我们用四个步骤,创建并调用了一个gRPC服务。接下来我再给大家讲解一个在具体场景中的注意事项。

在做服务开发时,我们经常会遇到一种场景:定义一个接口,接口会通过判断是否传入某个参数,决定接口行为。例如,我们想提供一个GetUser接口,期望GetUser接口在传入username参数时,根据username查询用户的信息,如果没有传入username,则默认根据userId查询用户信息。

这时候,我们需要判断客户端有没有传入username参数。我们不能根据username是否为空值来判断,因为我们不能区分客户端传的是空值,还是没有传username参数。这是由Go语言的语法特性决定的:如果客户端没有传入username参数,Go会默认赋值为所在类型的零值,而字符串类型的零值就是空字符串。

那我们怎么判断客户端有没有传入username参数呢?最好的方法是通过指针来判断,如果是nil指针就说明没有传入,非nil指针就说明传入,具体实现步骤如下:

  1. 编写protobuf定义文件。

新建user.proto文件,内容如下:

syntax = "proto3";package proto;
option go_package = "github.com/marmotedu/gopractise-demo/protobuf/user";//go:generate protoc -I. --experimental_allow_proto3_optional --go_out=plugins=grpc:.service User {rpc GetUser(GetUserRequest) returns (GetUserResponse) {}
}message GetUserRequest {string class = 1;optional string username = 2;optional string user_id = 3;
}message GetUserResponse {string class = 1;string user_id = 2;string username = 3;string address = 4;string sex = 5;string phone = 6;
}

你需要注意,这里我们在需要设置为可选字段的前面添加了optional标识。

  1. 使用protoc工具编译protobuf文件。

在执行protoc命令时,需要传入--experimental_allow_proto3_optional参数以打开optional选项,编译命令如下:

$ protoc --experimental_allow_proto3_optional --go_out=plugins=grpc:. user.proto

上述编译命令会生成user.pb.go文件,其中的GetUserRequest结构体定义如下:

type GetUserRequest struct {state         protoimpl.MessageStatesizeCache     protoimpl.SizeCacheunknownFields protoimpl.UnknownFieldsClass    string  `protobuf:"bytes,1,opt,name=class,proto3" json:"class,omitempty"`Username *string `protobuf:"bytes,2,opt,name=username,proto3,oneof" json:"username,omitempty"`UserId   *string `protobuf:"bytes,3,opt,name=user_id,json=userId,proto3,oneof" json:"user_id,omitempty"`
}

通过 optional + --experimental_allow_proto3_optional 组合,我们可以将一个字段编译为指针类型。

  1. 编写gRPC接口实现。

新建一个user.go文件,内容如下:

package userimport ("context"pb "github.com/marmotedu/api/proto/apiserver/v1""github.com/marmotedu/iam/internal/apiserver/store"
)type User struct {
}func (c *User) GetUser(ctx context.Context, r *pb.GetUserRequest) (*pb.GetUserResponse, error) {if r.Username != nil {return store.Client().Users().GetUserByName(r.Class, r.Username)}return store.Client().Users().GetUserByID(r.Class, r.UserId)
}

总之,在GetUser方法中,我们可以通过判断r.Username是否为nil,来判断客户端是否传入了Username参数。

RESTful VS gRPC

到这里,今天我们已经介绍完了gRPC API。回想一下我们昨天学习的RESTful API,你可能想问:这两种API风格分别有什么优缺点,适用于什么场景呢?我把这个问题的答案放在了下面这张表中,你可以对照着它,根据自己的需求在实际应用时进行选择。

当然,更多的时候,RESTful API 和gRPC API是一种合作的关系,对内业务使用gRPC API,对外业务使用RESTful API,如下图所示:

总结

在Go项目开发中,我们可以选择使用 RESTful API 风格和 RPC API 风格,这两种服务都用得很多。其中,RESTful API风格因为规范、易理解、易用,所以适合用在需要对外提供API接口的场景中而RPC API因为性能比较高、调用方便,更适合用在内部业务中

RESTful API使用的是HTTP协议,而RPC API使用的是RPC协议。目前,有很多RPC协议可供你选择,而我推荐你使用gRPC,因为它很轻量,同时性能很高、很稳定,是一个优秀的RPC框架。所以目前业界用的最多的还是gRPC协议,腾讯、阿里等大厂内部很多核心的线上服务用的就是gRPC。

除了使用gRPC协议,在进行Go项目开发前,你也可以了解业界一些其他的优秀Go RPC框架,比如腾讯的tars-go、阿里的dubbo-go、Facebook的thrift、rpcx等,你可以在项目开发之前一并调研,根据实际情况进行选择。

课后练习

  1. 使用gRPC包,快速实现一个RPC API服务,并实现PrintHello接口,该接口会返回“Hello World”字符串。
  2. 请你思考这个场景:你有一个gRPC服务,但是却希望该服务同时也能提供RESTful API接口,这该如何实现?

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

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

相关文章

1.5-数组-059. 螺旋矩阵 II★★

59. 螺旋矩阵II ★★ 力扣题目链接&#xff0c;给你一个正整数 n &#xff0c;生成一个包含 1 到 n 2 n^2 n2 所有元素&#xff0c;且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。1 < n < 20 示例 1&#xff1a; 输入&#xff1a;n 3 输出&#xff1a;[[1,…

mp4转rmvb怎么转?超快的方法分享!

RMVB&#xff08;RealMediaVariableBitrate&#xff09;是由RealNetworks公司推出的一种视频文件格式。相较于其他视频格式&#xff0c;RMVB以其高度压缩和可变比特率的特性而著称。它常被用于网络视频传输&#xff0c;适用于带宽有限或网络环境较差的情况。 RMVB文件格式的特性…

arp 协议

数据链路层 我们之前学习到的 IP 协议解决的是数据跨网络传输的问题。 数据链路层解决的是&#xff1a;直接相连的主机&#xff0c;进行数据交付的问题&#xff01; 直接相连的设备包括我们的电脑&#xff0c;路由器等等哈&#xff01; 我们在网络基础那篇文章中讲过什么是以…

音频怎么转换成二维码?1分钟在线生成音频二维码

音频是现在很常见一种内容展现方式&#xff0c;很多人会通过音频文件来做推广传播&#xff0c;在很多的应用场景中&#xff0c;音频用来为用户提供内容&#xff0c;从而提升用户体验效果。 为了方便用户能够快速的获取音频文件&#xff0c;通过将音频生成二维码之后&#xff0…

基于单片机的便携式瓦斯检测仪系统设计

**单片机设计介绍&#xff0c;基于单片机的便携式瓦斯检测仪系统设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机的便携式瓦斯检测仪系统设计是一个针对煤矿等工业环境中瓦斯气体浓度检测的重要项目。以下是该设计…

【单调栈】力扣84.柱状图中最大的矩形

上篇文章我们介绍了使用 无重复值 单调栈代码解决 含有重复值 的问题&#xff0c;在文章的最后&#xff0c;留下了一道考察相同思想的题目&#xff0c;今天我们来看看如何套路解决该题。 &#xff08;还没看过前几篇介绍的小伙伴赶快关注&#xff0c;在 「单调栈」 集合里查看…

启信宝商业大数据助力全国经济普查

近日&#xff0c;合合信息旗下启信宝收到中国青年创业就业基金会感谢信&#xff0c;对启信宝协同助力全国经济普查和服务青年创业就业研究表达感谢。 第五次全国经济普查是新时代新征程上一次重大国情国力调查&#xff0c;是对国民经济“全面体检”和“集中盘点”&#xff0c;…

2024年最受欢迎的视频号小店,怎么做才能月入过万?两步教会你

大家好&#xff0c;我是电商花花。 要说现在最火的项目&#xff0c;我觉得是视频号小店&#xff0c;现在视频号小店经过两年时间的沉淀&#xff0c;让更多人看到了视频号强大私域转化和高利润空间。 抖音小店我们应该也都知道&#xff0c;现在现在的抖音小店利润是不是变小了…

ESCTF-密码赛题WP

*小学生的爱情* Base64解码获得flag *中学生的爱情* 社会主义核心价值观在线解码得到flag http://www.atoolbox.net/Tool.php?Id850 *高中生的爱情* U2FsdG开头为rabbit密码,又提示你密钥为love。本地toolfx密码工具箱解密。不知道为什么在线解密不行。 *大学生的爱情* …

lua脚本在redis集群中哈希槽分片问题

上文说到&#xff0c;通过用redis lua脚本实现时间窗分布式限流 可以操作redis lua脚本来实现时间窗限流&#xff0c;在执行lua脚本的时候&#xff0c;参数中有个keys列表&#xff0c;当lua脚本中如果有操作多个key的情况&#xff0c;就可以传个key列表了。通常情况下&#xff…

java 抠取红色印章(透明背景)

一个亲戚让我帮他把照片里的红色印章抠出来&#xff0c;&#xff0c;&#xff0c;记录下处理过程&#xff0c;代码如下&#xff0c;可直接用&#xff1a; public static void signatureProcess(String sourceImagePath, String targetImagePath) {Graphics2D graphics2D null…

Nginx_简介 + Linux系统下详细安装教程指路

安装教程指路 可参看该视频【尚硅谷Nginx教程&#xff08;亿级流量nginx架构设计&#xff09;】 https://www.bilibili.com/video/BV1yS4y1N76R/?p2&share_sourcecopy_web&vd_source4c2f33f3ba1a0dd45bfdf574befd0069 的p2-p7。从安装centos虚拟机到在虚拟机上安装ng…

JVM(一)——内存结构

一. 前言 1、什么是 JVM? 1&#xff09;定义&#xff1a; Java Virtual Machine - java 程序的运行环境&#xff08;java 二进制字节码的运行环境&#xff09; 2&#xff09;好处&#xff1a; 一次编写&#xff0c;到处运行自动内存管理&#xff0c;垃圾回收功能数组下标越…

C++从入门到精通——函数重载

函数重载 前言一、函数重载概念二、函数重载的分类参数类型不同的函数重载参数个数不同的函数重载参数类型顺序不同的函数重载 三、函数重载的具体代码展示main.cpp 四、为什么为什么C支持函数重载&#xff0c;而C语言不支持函数重载呢 前言 函数重载是指在同一个作用域内&…

.NET CORE 分布式事务(三) DTM实现Saga及高并发下的解决方案

目录(结尾附加项目代码资源地址) 引言&#xff1a; 1. SAGA事务模式 2. 拆分为子事务 3. 失败回滚 4. 如何做补偿 4.1 失败的分支是否需要补偿 5. 异常 6. 异常与子事务屏障 6.1 NPC的挑战 6.2 现有方案的问题 6.3 子事务屏障 6.4 原理 7. 更多高级场景 7.1 部分…

30-3 越权漏洞 - 水平越权(横向越权)

环境准备:构建完善的安全渗透测试环境:推荐工具、资源和下载链接_渗透测试靶机下载-CSDN博客 一、定义 攻击者可以访问和操作与其拥有同级权限的用户资源。 示例: 学生A在教务系统上正常只能修改自己的作业内容,但由于不合理的权限校验规则等原因,学生A可以修改学生B的内…

element-ui autocomplete 组件源码分享

紧接着 input 组件的源码&#xff0c;分享带输入建议的 autocomplete 组件&#xff0c;在 element-ui 官方文档上&#xff0c;没有这个组件的 api 目录&#xff0c;它的 api 是和 input 组件的 api 在一起的&#xff0c;看完源码之后发现&#xff0c;源码当中 autocomplete 组件…

Linux部分命令

目录 1.文件介绍 2.ls命令 3.目录命令 4.相对路径以及绝对路径 5.命令创建目录&#xff08;文件夹&#xff09; 6.which命令 7.find命令 8.grep命令 9.wc命令 10.echo、tail、重定向符 1.文件介绍 和window不同&#xff0c;Linux没有盘路径&#xff0c;所有的文件都存…

Linux部署Kafka2.8.1

安装Jdk 首先确保你的机器上安装了Jdk&#xff0c;Kafka需要Java运行环境&#xff0c;低版本的Kafka还需要Zookeeper&#xff0c;我此次要安装的Kafka版本为2.8.1&#xff0c;已经内置了一个Zookeeper环境&#xff0c;所以我们可以不部署Zookeeper直接使用。 1、解压Jdk包 t…

OSPF GTSM(通用TTL安全保护机制)

目录 GTSM的定义 使用GTSM的目的 GTSM的原理 配置OSPF GTSM实例 组网需求 配置思路 操作步骤 1. 配置各接口的IP地址 2.配置OSPF基本功能 3.配置OSPF GTSM 4. 验证配置结果 GTSM的定义 GTSM&#xff08;Generalized TTL Security Mechanism&#xff09;&#xff0c;…