Go语言grpc服务开发——Protocol Buffer

news2024/11/18 1:48:30

文章目录

    • 一、Protocol Buffer简介
    • 二、Protocol Buffer编译器安装
    • 三、proto3语言指南
    • 四、序列化与反序列化
    • 五、引入grpc-gateway
      • 1、插件安装
      • 2、定义proto文件
      • 3、生成go文件
      • 4、实现Service服务
      • 5、gRPC服务启动方法
      • 6、gateway服务启动方法
      • 7、main函数启动
      • 8、验证

相关参考链接:

protobuf官方文档

proto3语法指南

protoc下载地址

grpc-gateway官方文档

grpc-gateway readme

swagger安装文档

【当前代码Demo】

一、Protocol Buffer简介

作为一个后端开发,我们用的最多的传递信息的协议是http协议。性能需求的提高,要求我们转向性能更高的协议,这就不得不提到一个由Google公司推广的一个小而快的rpc协议——protocol buffer协议。它到底有着什么样的魔力?让大家觉得真香定律。

Protocol Buffer是一个由Google开发的协议,是一个可以对结构化数据的序列化和反序列化协议。谷歌开发它的目的是提供一种比XML更好的方式来使系统进行通信。因此,他们致力于使其比XML更简单、更小、更快、更易于维护。这个协议甚至超过了JSON,具有更好的性能、更好的可维护性和更小的体积。

另外protoc支持多语言,以及跨平台。

天然支持C、C++、Java、Python、PHP、Ruby、Kotlin等语言。
不支持Go、Dart等语言。所以基于Go语言需要额外安装插件,下面会说到。

  • Why Protocol Buffer?——简单来说它更小、因此也更快。举例如下:
// 当前代码位于 https://gitee.com/liuwen766/protobuf-demo.git
func TestSerialize(t *testing.T) {
    // 这个是基于proto生成的
	personFromProto := &pb.Person{
		Name:     "我是小明",
		Age:      18,
		PhoneNum: []string{"188", "120"},
		Pets: &pb.Pets{
			Type: "Cat",
			Name: "Tom",
		},
	}
	marshal1, _ := proto.Marshal(personFromProto)
	create, _ := os.Create(fileName)
	defer create.Close()
	n, err := create.Write(marshal1)
	if err != nil {
		log.Fatal("create.Write(marshal) has err:", err)
		return
	}
	log.Println("proto.Marshal——Serialize Success:", n)

	// 这个是Go结构体
	personFromStruct := &Person{
		Name:     "我是小明",
		Age:      18,
		PhoneNum: []string{"188", "120"},
		Pets: &Pets{
			Type: "Cat",
			Name: "Tom",
		},
	}
	marshal2, _ := json.Marshal(personFromStruct)
	create, _ = os.Create(fileName)
	defer create.Close()
	n, err = create.Write(marshal2)
	if err != nil {
		log.Fatal("create.Write(marshal) has err:", err)
		return
	}
	log.Println("json.Marshal——Serialize Success:", n)
	// 日志输出如下:【传递的Person信息一模一样,但是基于proto的长度只有38,它是二进制数据】
	//2024/01/22 18:13:16 proto.Marshal——Serialize Success: 38
	//2024/01/22 18:13:16 json.Marshal——Serialize Success: 107
}

二、Protocol Buffer编译器安装

编译proto文件前的环境准备工作只有简单的两个步骤:

  • step1、下载protoc 。 protoc下载地址
#将protoc.exe文件配置到环境变量,配置完之后验证
$ protoc --version
libprotoc 3.19.4
  • step2、安装go插件。

前面提到过protoc支持多语言,包括C、C++、Java、Python、PHP、Ruby、Kotlin等语言。
但是不支持Go、Dart等语言。所以基于Go语言需要额外安装插件。只需要下面一个简单的命令。

go get google.golang.org/protobuf/cmd/protoc-gen-go
go install google.golang.org/protobuf/cmd/protoc-gen-go

go get 命令会获取依赖包到go env的 GOMODCACHE 目录下。
go intall 命令会将对应的可执行文件安装到go env下的 GOPATH/bin 目录下。
因此需要配置GOPATH/bin为环境变量。否则会报错“‘protoc-gen-go’ 不是内部或外部命令,也不是可运行的程序或批处理文件。”

一个简单的proto3文件样例。

// 指定proto语言版本
syntax = "proto3";
// 生成*.pb.go文件的包路径
option go_package = "/pb";
// proto包路径 
package protobuf.demo;

message Person{
  string name = 1;
  int32 age = 2;
}

通过protoc生成 *.pb.go 文件:

protoc -I . --go_out=./proto ./proto/person.proto
# 这里的三个 . 都表示当前目录

三、proto3语言指南

通过上面编写的简单proto文件,可以发现,proto文件中定义message与我们创建一个Go语言中的struct结构体类似。针对Go语言中的一些复杂类型,例如:数组[]int、集合map、枚举enum、嵌套等,proto自然也有相对应的定义。

  • repeated

repeated关键字的作用是用来定义数组,使用方式是repeated 数组类型 属性名称 = 字段编号;

message Person {
	repeated string name = 1;
}
  • map

map类型的定义方式是map <键类型,值类型> 属性名称 = 字段编号; ,这里需要注意对于map的键类型,只能定义为基本数据类型,但是值的类型可以是任何支持的类型。

message Person {
	map <string, Pets> pets =1;
}
// 嵌套
message Pets{
  string  Type = 1;
  string  name = 2;
}
  • enum

对于枚举的定义,需要用到enum关键字。

message Person{
  Sex sex = 5;
}
enum Sex{
  Sex_MAN = 0;
  SEX_WOMAN = 1;
}

一个完整示例。

// 指定proto语言版本
syntax = "proto3";
// 生成*.pb.go文件的包路径
option go_package = "/pb";
// proto包路径
package protobuf.demo;

message Person{
  string name = 1;
  int32 age = 2;
  bool marry = 3;
  repeated string phoneNum = 4;
  map<string, string> address = 5;
  Sex sex = 6;
  Pets pets = 7;
}

message Pets{
  string  Type = 1;
  string  name = 2;
}

enum Sex{
  Sex_MAN = 0;
  SEX_WOMAN = 1;
}

定义完proto文件后,通过protoc生成 *.pb.go 文件,执行如下命令:

protoc -I . --go_out=./proto ./proto/person.proto
# 这里的三个 . 都表示当前目录

四、序列化与反序列化

以序列化和反序列化为例,演示如何使用由proto编译生成的*.pb.go文件

package proto

import (
	"encoding/json"
	"log"
	"os"
	"testing"

	"google.golang.org/protobuf/proto"

	"protobuf-demo/proto/pb"
)

var fileName1 = "person-proto.txt"
var fileName2 = "person-json.txt"

type Pets struct {
	Type string
	Name string
}

type Person struct {
	Name     string
	Age      int32
	PhoneNum []string
	Address  map[string]string
	Pets     *Pets
}

// 序列化
func TestSerialize(t *testing.T) {
	personFromProto := &pb.Person{
		Name:     "我是小明",
		Age:      18,
		PhoneNum: []string{"188", "120"},
		//Sex:      pb.Sex_Sex_MAN,
		Pets: &pb.Pets{
			Type: "Cat",
			Name: "Tom",
		},
	}
	marshal1, _ := proto.Marshal(personFromProto)
	create, _ := os.Create(fileName1)
	defer create.Close()
	n, err := create.Write(marshal1)
	if err != nil {
		log.Fatal("create.Write(marshal) has err:", err)
		return
	}
	log.Println("proto.Marshal——Serialize Success:", n)

	// why proto?let's look look json.
	personFromStruct := &Person{
		Name:     "我是小明",
		Age:      18,
		PhoneNum: []string{"188", "120"},
		//Sex:      pb.Sex_Sex_MAN,
		Pets: &Pets{
			Type: "Cat",
			Name: "Tom",
		},
	}
	marshal2, _ := json.Marshal(personFromStruct)
	create, _ = os.Create(fileName2)
	defer create.Close()
	n, err = create.Write(marshal2)
	if err != nil {
		log.Fatal("create.Write(marshal) has err:", err)
		return
	}
	log.Println("json.Marshal——Serialize Success:", n)
	// 日志输出如下:所以说proto更小
	//2024/01/25 18:13:16 proto.Marshal——Serialize Success: 38
	//2024/01/25 18:13:16 json.Marshal——Serialize Success: 107
}

// 反序列化
func TestDeserialize(t *testing.T) {
	read, _ := os.ReadFile(fileName1)
	person := pb.Person{}
	err := proto.Unmarshal(read, &person)
	if err != nil {
		log.Fatal("proto.Unmarshal(read, &person) has err:", err)
		return
	}
	log.Printf("Deserialize Success: %+v", person)
}

五、引入grpc-gateway

grpc-gateway是protoc的一个插件,它会读取proto文件中的grpc服务的定义并将其生成RestFul JSON API,并将其反向代理到我们的grpc服务中。

在这里插入图片描述

grpc-gatway是在grpc上做的一个拓展。但是grpc并不能很好的支持客户端,以及传统的RESTful API。因此grpc-gateway诞生了,该项目可以为grpc服务提供HTTP+JSON接口。

1、插件安装

首先,在项目中去创建一个tools的文件,然后执行go mod tidy

//go:build tools
// +build tools

package tools

import (
	_ "github.com/envoyproxy/protoc-gen-validate"
	_ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway"
	_ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2"
	_ "google.golang.org/grpc/cmd/protoc-gen-go-grpc"
	_ "google.golang.org/protobuf/cmd/protoc-gen-go"
)

通过执行go install将这些可执行文件安装在GOPATH/bin目录下。

go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway
go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2
go install google.golang.org/protobuf/cmd/protoc-gen-go
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc

2、定义proto文件

// 指定proto语言版本
syntax = "proto3";
// 生成*.pb.go文件的包路径
option go_package = "/pb";

package protobuf.demo;

// 导入api注释依赖【注意将这里的注释依赖包放在当前项目的根目录下】
import "google/api/annotations.proto";

message Book {
  int32 id = 1;
  string name = 2;
}


message CreateBookRequest {
  string name = 1;
}

message CreateBookResponse{
  string code = 1;
  string message = 2;
  Book data = 3;
}

// 定义服务
service BookService {
  // 创建Book
  rpc CreateBook (CreateBookRequest) returns (CreateBookResponse) {
    option (google.api.http) = {
      // POST  /v1/books
      post: "/v1/books"
      body: "*"
    };
  };
}

注意这里需要导入api注释依赖。

由于项目依赖了google的proto文件,所以在使用protoc生成go文件的时候,需要将依赖的proto文件复制到项目中,依赖的proto文件仓库 google/api 和 google/protobuf 。下载下来,放在当前项目中的根目录下。注释依赖一般可以自动检测,不行就手动配置依赖。

3、生成go文件

执行命令

protoc -I . --grpc-gateway_out=./proto/gen --grpc-gateway_opt logtostderr=true --grpc-gateway_opt paths=source_relative --go_out=./proto/gen --go_opt paths=source_relative --go-grpc_out=./proto/gen --go-grpc_opt paths=source_relative ./proto/book.proto
# 如果报错没有目录,则手动创建目录

不要被这么长的命令唬住了,记住 . 表示当前目录就行。
几个命令分别对应前面安装的三个插件:protoc-gen-go、protoc-gen-go-grpc和protoc-gen-grpc-gateway
protoc -I .
–grpc-gateway_out=./proto/gen
–grpc-gateway_opt logtostderr=true --grpc-gateway_opt paths=source_relative
–go_out=./proto/gen
–go_opt paths=source_relative
–go-grpc_out=./proto/gen
–go-grpc_opt paths=source_relative
./proto/book.proto

4、实现Service服务

package service

import (
	"context"
	"log"

	"protobuf-demo/db"
	pb "protobuf-demo/proto/gen/proto"
)

type BookService struct {
	// 这里是要实现的服务
	pb.UnimplementedBookServiceServer
}

// 这里是实现的方法
func (b *BookService) CreateBook(ctx context.Context, req *pb.CreateBookRequest) (*pb.CreateBookResponse, error) {
	resp := &pb.CreateBookResponse{}
	db.Db.Mux.Lock()
	defer db.Db.Mux.Unlock()

	id := db.Db.GetId()
	book := pb.Book{
		Name: req.GetName(),
		Id:   id,
	}

	err := db.Db.Save(&book)
	if err != nil {
		return resp, err
	}

	resp.Data = &book
	log.Printf("user %s create a book, %+v", db.GetUserId(ctx), &book)
	return resp, nil
}

5、gRPC服务启动方法

package grpc

import (
	"log"
	"net"

	"protobuf-demo/config"
	"protobuf-demo/grpc/middle"
	pb "protobuf-demo/proto/gen/proto"
	"protobuf-demo/service"

	"google.golang.org/grpc"
)

// Run grpc的启动方法
func Run() error {
	//Step1:监听端口,用于提供grpc服务
	grpcAddr := config.GetRpcAddr()
	listen, err := net.Listen("tcp", grpcAddr)
	if err != nil {
		log.Fatalf("tcp listen failed: %v", err)
		return err
	}
	defer listen.Close()

	// Step2:可以为这个grpc服务加一些定制化的特性
	option := []grpc.ServerOption{
		// 这里可以加一些middleware中间件
		grpc.UnaryInterceptor(middle.AuthInterceptor),
	}
	// Step3:创建一个grpc服务,它是空的服务,还不能接收/处理任何请求
	server := grpc.NewServer(option...)

	// Step4:进行服务注册
	registerServer(server)

	log.Printf("Serving gRPC on %s", listen.Addr())

	// Step5:grpc服务接收从监听端口过来的流量请求,对外提供服务
	return server.Serve(listen)
}

func registerServer(server *grpc.Server) {
	// 注册 BookService 服务
	pb.RegisterBookServiceServer(server, service.NewBookService())
}

6、gateway服务启动方法

package gateway

import (
	"context"
	"log"
	"net/http"

	"protobuf-demo/config"
	"protobuf-demo/gateway/middle"
	"protobuf-demo/handler"
	pb "protobuf-demo/proto/gen/proto"

	"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
)

// Run gateway的启动方法
func Run() error {

	// Step1:创建一个客户端,连接grpc服务
	ctx := context.Background()
	option := []grpc.DialOption{
		// 这里可以加一些middleware中间件
		grpc.WithChainUnaryInterceptor(middle.AuthInterceptor),
		grpc.WithTransportCredentials(insecure.NewCredentials()),
	}
	// 创建grpc连接  连接到127.0.0.1:8001
	conn, err := grpc.DialContext(ctx, config.GetRpcAddr(), option...)
	if err != nil {
		log.Fatalf("dial failed: %v", err)
		return err
	}

	// Step2:创建一个ServeMux,它是 grpc-gateway 的请求多路复用器。
	serveMuxOption := []runtime.ServeMuxOption{
		// 响应拦截
		runtime.WithForwardResponseOption(middle.Interceptor),
		// 错误页自定义
		runtime.WithRoutingErrorHandler(middle.RoutingErrorHandler),
		// 自定义保留哪些请求头信息到整个上下文中
		runtime.WithIncomingHeaderMatcher(func(s string) (string, bool) {
			if s == "User-Id" {
				return s, true
			}
			return runtime.DefaultHeaderMatcher(s)
		}),
	}

	mux := runtime.NewServeMux(serveMuxOption...)

	// 自定义一些不好用proto编译的接口,比如这里的上传/下载接口
	{
		if err = mux.HandlePath(http.MethodPost, "/v1/objects", handler.Upload); err != nil {
			return err
		}
		if err = mux.HandlePath(http.MethodGet, "/v1/objects/{name}", handler.Download); err != nil {
			return err
		}
	}

	// Step3:将http路由注册到前面创建的ServeMux,通过grpc-gateway反向代理,从而提供http服务
	err = newGateway(ctx, conn, mux)
	if err != nil {
		log.Fatalf("register handler failed: %v", err)
		return err
	}

	// Step4:创建一个http服务
	server := http.Server{
		Addr: config.GetHttpAddr(), // 127.0.0.1:8002
		// http服务需要处理的ServeMux
		Handler: mux,
	}

	log.Printf("Serving Http on %s", server.Addr)

	// Step5:进行监听并提供服务
	err = server.ListenAndServe()
	if err != nil {
		return err
	}
	return nil
}

func newGateway(ctx context.Context, conn *grpc.ClientConn, mux *runtime.ServeMux) error {
	// 注册 BookService 服务,进行反向代理
	err := pb.RegisterBookServiceHandler(ctx, mux, conn)
	if err != nil {
		return err
	}
	return nil
}

7、main函数启动

package main

import (
	"log"
	"os"

	"protobuf-demo/gateway"
	"protobuf-demo/grpc"
)

func main() {
	// 启动grpc服务
	go func() {
		err := grpc.Run()
		if err != nil {
			log.Fatal(os.Stderr, err)
			os.Exit(1)
		}
	}()
	//	启动gateway服务
	err := gateway.Run()
	log.Fatal(os.Stderr, err)
	os.Exit(1)
}

8、验证

启动服务之后,进行接口验证。

# 新增接口
curl --request POST \
  --url http://localhost:8002/v1/books \
  --header 'X-User-Id: ' \
  --header 'content-type: application/json' \
  --data '{
    "name": "三国演义"
}'

接下来,完成所有的增删改查接口。如下:

# 查询接口
curl --request GET \
  --url http://localhost:8002/v1/books/1 \
  --header 'content-type: application/json'
  
# 更新接口
curl --request POST \
  --url http://localhost:8002/v1/books/1 \
  --header 'content-type: application/json' \
  --data '{
    "id": 1,
    "name": "三国演义2"
}'

# 删除接口
curl --request DELETE \
  --url http://localhost:8002/v1/books/1 \
  --header 'content-type: application/json'

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

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

相关文章

Mysql-日志介绍 日志配置

环境部署 docker run -d -p 3306:3306 --privilegedtrue -v $(pwd)/logs:/var/lib/logs -v $(pwd)/conf:/etc/mysql/conf.d -v $(pwd)/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD654321 --name mysql mysql:5.7运行指令的目录下新建好这些文件&#xff1a; 日志类型 日…

dhcp服务器的ip池的待分配ip地址是否冲突的检测机制

看到有的资料说&#xff0c;dhcp服务器在分配ip地址时&#xff0c;要检测是否待分配的ip地址是否存在冲突&#xff0c;会向广播域发出&#xff0c;对应ip发出icmp的ping消息来验证是否冲突。特地用自己的公司的交换机验证一下&#xff0c;在交换机上镜像抓包观察一下。 wiresha…

社交媒体与新闻:Facebook在信息传播中的角色

社交媒体的崛起不仅改变了人们的日常交流方式&#xff0c;也对新闻传播产生了深远的影响。在众多社交媒体平台中&#xff0c;Facebook以其庞大的用户基础和强大的社交网络机制&#xff0c;成为信息传播的中流砥柱。本文将深入探讨Facebook在社交媒体与新闻传播的交汇点上扮演的…

Qt应用开发(安卓篇)——调用java代码,使用安卓api

一、前言 在Qt on Android开发的时候&#xff0c;它不像在嵌入式linux&#xff0c;几乎全部的操作都是可以通过文件来完成。很多场景下的功能都需要使用安卓的API去实现&#xff0c;开发人员需要通过这些API进行编程&#xff0c;无需访问源码&#xff0c;或者理解内部的机制。比…

IP报文格式

IP报文格式 报文格式 图1 IP头格式 表1 IP头字段解释 字段长度含义Version4比特 4&#xff1a;表示为IPV4&#xff1b;6&#xff1a;表示为IPV6。IHL4比特首部长度&#xff0c;如果不带Option字段&#xff0c;则为20&#xff0c;最长为60&#xff0c;该值限制了记录路由选项。…

阅读go语言工具源码系列之gopacket(谷歌出品)----第二集 layers-巧妙的抽象与无聊的协议包

上一集中我们讲到了wpcap.dll的go封装方法&#xff0c;对于linux系统下libpcap的go封装采用的是常用的cgo方式&#xff0c;想了解的可以看看pcap文件夹中的pcap_unix.go。 我们得到了wpcap.dll的go调用&#xff0c;就可以利用它来进行列举所有网络设备&#xff0c;例如以下代码…

docker容器生命周期管理命令

文章目录 前言1、docker create2、docker run2.1、常用选项2.2、系统2.3、网络2.4、健康检查 3、docker start/stop/restart4、docker kill5、docker rm6、docker pause/unpause总结 前言 在云原生时代&#xff0c;Docker已成为必不可少的容器管理工具。通过掌握Docker常用的容…

派网AX50C做多宽带路由和核心交换机配置实战教程

接近300办公人员的工厂需要网络升级&#xff0c;我规划设计和部署实施了以下方案&#xff0c;同样是简约不简单&#xff0c;在满足性能需求稳定性的前提下&#xff0c;既有经济性&#xff0c;又有安全性。 派网做路由器&#xff0c;刚好开启默认防病毒策略&#xff0c;省下来一…

【QT+QGIS跨平台编译】之十一:【libzip+Qt跨平台编译】(一套代码、一套框架,跨平台编译)

文章目录 一、libzip介绍二、文件下载三、文件分析四、pro文件五、编译实践一、libzip介绍 libzip是一个开源C库,用于读取,创建和修改zip文件。 libzip可以从数据缓冲区,文件或直接从其他zip归档文件直接复制的压缩数据中添加文件。在不关闭存档的情况下所做的更改可以还原…

TCP 状态转换以及半关闭

TCP 状态转换&#xff1a; 上图中还没有进行握手的时候状态是关闭的。 三次握手状态的改变&#xff1a; 客户端发起握手。 调用 connect() 函数时状态转化为&#xff1a;SYN_SENT。调用 listen() 函数时状态转换为&#xff1a;LISTEN。ESTABLISHED是被连接的状态。 四次挥手…

林浩然的哲学奇遇记:与罗素一起穿越西方哲学史

林浩然的哲学奇遇记&#xff1a;与罗素一起穿越西方哲学史 The Philosophical Adventures of Lin Haoran: Journeying Through Western Philosophy with Russell 在一个阳光灿烂的早晨&#xff0c;林浩然同学迎着微风&#xff0c;拿着一本厚重的罗素名著《西方哲学史》&#xf…

了解维特比算法:通信系统和自然语言处理中解码的基石

一、介绍 在数字通信和信号处理领域&#xff0c;维特比算法是一种革命性的纠错和解码方法。该算法以 1967 年推出的 Andrew Viterbi 的名字命名&#xff0c;已成为数字通信和自然语言处理领域的基础。本文旨在深入研究维特比算法的复杂性&#xff0c;探讨其理论基础、实际应用以…

通俗易懂理解小波池化以及WaveCNet网络模型

重要说明&#xff1a;本文从网上资料整理而来&#xff0c;仅记录博主学习相关知识点的过程&#xff0c;侵删。 一、参考资料 github代码&#xff1a;WaveCNet 小波变换和曲波变换用于池化层 通俗易懂理解小波变换(Wavelet Transform) 二、相关介绍 关于小波变换的详细介绍…

C#,数据检索算法之插值搜索(Interpolation Search)的源代码

数据检索算法是指从数据集合&#xff08;数组、表、哈希表等&#xff09;中检索指定的数据项。 数据检索算法是所有算法的基础算法之一。 本文提供插值搜索&#xff08;Interpolation Search&#xff09;的源代码。 1 文本格式 using System; namespace Legalsoft.Truffer.…

极限【高数笔记】

【分类】分为了两大类&#xff0c;一个是数列的极限&#xff0c;一个是函数的极限 【数列的极限】 1.定义&#xff1a; 简单来讲&#xff0c;就是&#xff0c;当n无限趋近于无穷时&#xff0c;数列{an}无限趋近一个常数A&#xff0c;此时&#xff0c;常数A就是它们此时情况下的…

三极管实际电路设计

上图电路有个致命缺陷。那就是Q2正常我们是用NPN三极管。而上图用了PNP。导致MOS管高低电平都无法关闭。 解决方法&#xff1a; 把R2减小为200欧、或者330欧姆。 &#xff08;因为MOS打开需要压差&#xff0c;把SG端压差减小到规定以下就可以关闭&#xff09;。 同时增大R1为2…

RustDesk私有化部署,自建远程桌面搭建教程

以linux操作系统为例&#xff1a; 解压安装 # 使用wget进行下载1.1.8-2版本&#xff08;最新版本可以看上述发布地址&#xff09; wget https://github.com/rustdesk/rustdesk-server/releases/download/1.1.8-2/rustdesk-server-linux-amd64.zip # 使用unzip解压 unzip rust…

防火墙综合实验

实验需求&#xff1a; 1、生产区在工作时间内可以访问服务器区&#xff0c;仅可以访问http服务器。 2、办公区全天可以访问服务器区&#xff0c;其中&#xff0c;10.0.2.20可以访问FTP服务器和HTTP服务器&#xff0c;10.0.2.10仅可以ping通10.0.3.10。 3、办公区在访问服务器…

Chain-of-Thought Prompting Elicits Reasoning in Large Language Models导读

通过生成一系列中间推理步骤&#xff08;即“思维链”&#xff09;显著提高大型语言模型进行复杂推理的能力 这篇论文探讨了如何通过生成一系列中间推理步骤&#xff08;即“思维链”&#xff09;显著提高大型语言模型进行复杂推理的能力。研究人员使用一种简单的方法——思维…

BAT学习笔记:常用指令详解及图示

文章目录 一、 &#xff08;关闭本行命令回显&#xff09;详解及图示二、echo off&#xff08; 关闭命令回显&#xff09;详解及图示三、%VAR_NAME% (取变量值&#xff09;详解四、set (设置变量&#xff09;详解及图示五、if (条件判断) 详解及图示六、not (条件取反&#xff…