通关GO语言22 网络编程:Go 语言如何通过 RPC 实现跨平台服务?

news/2024/5/18 17:00:20/文章来源:https://blog.csdn.net/fegus/article/details/126927887

在上一讲中,我为你讲解了 RESTful API 的规范以及实现,并且留了两个作业,它们分别是删除和修改用户,现在我为你讲解这两个作业。

删除一个用户比较简单,它的 API 格式和获取一个用户一样,但是 HTTP 方法换成了DELETE。删除一个用户的示例代码如下所示:

ch21/main.go

func main() {//省略没有修改的代码r.DELETE("/users/:id", deleteUser)
}
func deleteUser(c *gin.Context) {id := c.Param("id")i := -1//类似于数据库的SQL查询for index, u := range users {if strings.EqualFold(id, strconv.Itoa(u.ID)) {i = indexbreak}}if i >= 0 {users = append(users[:i], users[i+1:]...)c.JSON(http.StatusNoContent, "")} else {c.JSON(http.StatusNotFound, gin.H{"message": "用户不存在",})}
}

这个示例的逻辑就是注册 DELETE 方法,达到删除用户的目的。删除用户的逻辑是通过ID 查询:

  • 如果可以找到要删除的用户,记录索引并跳出循环,然后根据索引删除该用户;

  • 如果找不到要删除的用户,则返回 404。

实现了删除用户的逻辑后,相信你已经会修改一个用户的名字了,因为它和删除一个用户非常像,实现代码如下所示:

func main() {//省略没有修改的代码r.PATCH("/users/:id",updateUserName)
}
func updateUserName(c *gin.Context) {id := c.Param("id")i := -1//类似于数据库的SQL查询for index, u := range users {if strings.EqualFold(id, strconv.Itoa(u.ID)) {i = indexbreak}}if i >= 0 {users[i].Name = c.DefaultPostForm("name",users[i].Name)c.JSON(http.StatusOK, users[i])} else {c.JSON(http.StatusNotFound, gin.H{"message": "用户不存在",})}
}

整体代码逻辑和删除的差不多的,只不过这里使用的是 PATCH方法。

什么是RPC 服务

RPC,也就是远程过程调用,是分布式系统中不同节点调用的方式(进程间通信),属于 C/S 模式。RPC 由客户端发起,调用服务端的方法进行通信,然后服务端把结果返回给客户端。

RPC的核心有两个:通信协议序列化。在 HTTP 2 之前,一般采用自定义 TCP 协议的方式进行通信,HTTP 2 出来后,也有采用该协议的,比如流行的gRPC。

序列化反序列化是一种把传输内容编码和解码的方式,常见的编解码方式有 JSON、Protobuf 等。

在大多数 RPC的架构设计中,都有ClientClient StubServerServer Stub这四个组件,Client 和 Server 之间通过 Socket 进行通信。RPC 架构如下图所示:

图片2.png

(图片来自于 Google 搜索)

下面我为你总结下 RPC 调用的流程:

  • 客户端(Client)调用客户端存根(Client Stub),同时把参数传给客户端存根;

  • 客户端存根将参数打包编码,并通过系统调用发送到服务端;

  • 客户端本地系统发送信息到服务器;

  • 服务器系统将信息发送到服务端存根(Server Stub);

  • 服务端存根解析信息,也就是解码;

  • 服务端存根调用真正的服务端程序(Sever);

  • 服务端(Server)处理后,通过同样的方式,把结果再返回给客户端(Client)。

RPC 调用常用于大型项目,也就是我们现在常说的微服务,而且还会包含服务注册、治理、监控等功能,是一套完整的体系。

Go 语言 RPC 简单入门

RPC这么流行,Go 语言当然不会错过,在 Go SDK 中,已经内置了 net/rpc 包来帮助开发者实现 RPC。简单来说,net/rpc 包提供了通过网络访问服务端对象方法的能力。

现在我通过一个加法运算来演示 RPC的使用,它的服务端代码如下所示:

ch22/server/math_service.go

package server
type MathService struct {
}
type Args struct {A, B int
}
func (m *MathService) Add(args Args, reply *int) error {*reply = args.A + args.Breturn nil
}

在以上代码中:

  • 定义了MathService,用于表示一个远程服务对象;

  • Args 结构体用于表示参数;

  • Add 这个方法实现了加法的功能,加法的结果通过 replay这个指针变量返回。

有了这个定义好的服务对象,就可以把它注册到暴露的服务列表中,以供其他客户端使用了。在Go 语言中,要注册一个一个RPC 服务对象还是比较简单的,通过 RegisterName 方法即可,示例代码如下所示:

ch22/server_main.go

package main
import ("gotour/ch22/server""log""net""net/rpc"
)
func main()  {rpc.RegisterName("MathService",new(server.MathService))l, e := net.Listen("tcp", ":1234")if e != nil {log.Fatal("listen error:", e)}rpc.Accept(l)
}

以上示例代码中,通过 RegisterName 函数注册了一个服务对象,该函数接收两个参数:

  • 服务名称(MathService);

  • 具体的服务对象,也就是我刚刚定义好的MathService 这个结构体。

然后通过 net.Listen 函数建立一个TCP 链接,在 1234 端口进行监听,最后通过 rpc.Accept 函数在该 TCP 链接上提供 MathService 这个 RPC 服务。现在客户端就可以看到MathService这个服务以及它的Add 方法了。

任何一个框架都有自己的规则,net/rpc 这个 Go 语言提供的RPC 框架也不例外。要想把一个对象注册为 RPC 服务,可以让客户端远程访问,那么该对象(类型)的方法必须满足如下条件:

  • 方法的类型是可导出的(公开的);

  • 方法本身也是可导出的;

  • 方法必须有 2 个参数,并且参数类型是可导出或者内建的;

  • 方法必须返回一个 error 类型。

总结下来,该方法的格式如下所示:

func (t *T) MethodName(argType T1, replyType *T2) error

这里面的 T1、T2都是可以被 encoding/gob 序列化的。

  • 第一个参数 argType 是调用者(客户端)提供的;

  • 第二个参数 replyType是返回给调用者结果,必须是指针类型。

有了提供好的RPC 服务,现在再来看下客户端如何调用,它的代码如下所示:

ch22/client_main.go

package main
import ("fmt""gotour/ch22/server""log""net/rpc"
)
func main()  {client, err := rpc.Dial("tcp",  "localhost:1234")if err != nil {log.Fatal("dialing:", err)}args := server.Args{A:7,B:8}var reply interr = client.Call("MathService.Add", args, &reply)if err != nil {log.Fatal("MathService.Add error:", err)}fmt.Printf("MathService.Add: %d+%d=%d", args.A, args.B, reply)
}

在以上实例代码中,首先通过 rpc.Dial 函数建立 TCP 链接,需要注意的是这里的 IP、端口要和RPC 服务提供的一致,确保可以建立 RCP 链接。

TCP 链接建立成功后,就需要准备远程方法需要的参数,也就是示例中的args 和 reply。参数准备好之后,就可以通过 Call 方法调用远程的RPC 服务了。Call 方法有 3 个参数,它们的作用分别如下所示:

  1. 调用的远程方法的名字,这里是MathService.Add,点前面的部分是注册的服务的名称,点后面的部分是该服务的方法

  2. 客户端为了调用远程方法提供的参数,示例中是args;

  3. 为了接收远程方法返回的结果,必须是一个指针,也就是示例中的& replay,这样客户端就可以获得服务端返回的结果了。

服务端和客户端的代码都写好了,现在就可以运行它们,测试 RPC调用的效果了。

首先运行服务端的代码,提供 RPC 服务,运行命令如下所示:

➜ go run ch22/server_main.go

然后运行客户端代码,测试调用 RPC的结果,运行命令如下所示:

➜ go run ch22/client_main.go

如果你看到了 MathService.Add: 7+8=15的结果,那么恭喜你,你完成了一个完整的RPC 调用。

基于 HTTP的RPC

RPC 除了可以通过 TCP 协议调用之外,还可以通过HTTP 协议进行调用,而且内置的net/rpc 包已经支持,现在我修改以上示例代码,支持 HTTP 协议的调用,服务端代码如下所示:

ch22/server_main.go

func main() {rpc.RegisterName("MathService", new(server.MathService))rpc.HandleHTTP()//新增的l, e := net.Listen("tcp", ":1234")if e != nil {log.Fatal("listen error:", e)}http.Serve(l, nil)//换成http的服务
}

以上是服务端代码的修改,只需修改两处,我已经在代码中标注出来了,很容易理解。

服务端修改的代码不算多,客户端修改的代码就更少了,只需要修改一处即可,修改的部分如下所示:

ch22/client_main.go

func main()  {client, err := rpc.DialHTTP("tcp",  "localhost:1234")//省略了其他没有修改的代码
}

从以上代码可以看到,只需要把建立链接的方法从 Dial 换成 DialHTTP 即可。

现在分别运行服务端和客户端代码,就可以看到输出的结果了,和上面使用TCP 链接时是一样的。

此外,Go 语言 net/rpc 包提供的 HTTP 协议的 RPC 还有一个调试的 URL,运行服务端代码后,在浏览器中输入 http://localhost:1234/debug/rpc 回车,即可看到服务端注册的RPC 服务,以及每个服务的方法,如下图所示:

image (3).png

如上图所示,注册的 RPC 服务方法的签名已经被调用的次数都可以看到。

JSON RPC 跨平台通信

以上我实现的RPC 服务是基于 gob 编码的,这种编码在跨语言调用的时候比较困难,而当前在微服务架构中,RPC 服务的实现者和调用者都可能是不同的编程语言,因此我们实现的 RPC 服务要支持多语言的调用。

基于 TCP 的 JSON RPC

实现跨语言 RPC 服务的核心在于选择一个通用的编码,这样大多数语言都支持,比如常用的JSON。在 Go 语言中,实现一个 JSON RPC 服务非常简单,只需要使用 net/rpc/jsonrpc 包即可。

同样以上面的示例为例,我把它改造成支持 JSON的RPC 服务,服务端代码如下所示:

ch22/server_main.go

func main() {rpc.RegisterName("MathService", new(server.MathService))l, e := net.Listen("tcp", ":1234")if e != nil {log.Fatal("listen error:", e)}for {conn, err := l.Accept()if err != nil {log.Println("jsonrpc.Serve: accept:", err.Error())return}//json rpcgo jsonrpc.ServeConn(conn)}
}

从以上代码可以看到,相比 gob 编码的RPC 服务,JSON 的 RPC 服务是把链接交给了jsonrpc.ServeConn这个函数处理,达到了基于 JSON 进行 RPC 调用的目的。

JSON RPC 的客户端代码也非常少,只需要修改一处,修改的部分如下所示:

ch22/client_main.go

func main()  {client, err := jsonrpc.Dial("tcp",  "localhost:1234")//省略了其他没有修改的代码
}

从以上代码可以看到,只需要把建立链接的 Dial方法换成 jsonrpc 包中的即可。

以上是使用 Go 语言作为客户端调用 RPC 服务的示例,其他编程语言也是类似的,只需要遵守 JSON-RPC 规范即可。

基于 HTTP的JSON RPC

相比基于 TCP 调用的RPC 来说,使用 HTTP肯定会更方便,也更通用。Go 语言内置的jsonrpc 并没有实现基于 HTTP的传输,所以就需要自己来实现,这里我参考 gob 编码的HTTP RPC 实现方式,来实现基于 HTTP的JSON RPC 服务

还是上面的示例,我改造下让其支持 HTTP 协议,RPC 服务端代码如下所示:

ch22/server_main.go

func main() {rpc.RegisterName("MathService", new(server.MathService))//注册一个path,用于提供基于http的json rpc服务http.HandleFunc(rpc.DefaultRPCPath, func(rw http.ResponseWriter, r *http.Request) {conn, _, err := rw.(http.Hijacker).Hijack()if err != nil {log.Print("rpc hijacking ", r.RemoteAddr, ": ", err.Error())return}var connected = "200 Connected to JSON RPC"io.WriteString(conn, "HTTP/1.0 "+connected+"\n\n")jsonrpc.ServeConn(conn)})l, e := net.Listen("tcp", ":1234")if e != nil {log.Fatal("listen error:", e)}http.Serve(l, nil)//换成http的服务
}

以上代码的实现基于 HTTP 协议的核心,即使用 http.HandleFunc 注册了一个 path,对外提供基于 HTTP 的 JSON RPC 服务。在这个 HTTP 服务的实现中,通过Hijack方法劫持链接,然后转交给 jsonrpc 处理,这样就实现了基于 HTTP 协议的 JSON RPC 服务。

实现了服务端的代码后,现在开始实现客户端调用,它的代码如下所示:

  func main()  {client, err := DialHTTP("tcp",  "localhost:1234")if err != nil {log.Fatal("dialing:", err)}args := server.Args{A:7,B:8}var reply interr = client.Call("MathService.Add", args, &reply)if err != nil {log.Fatal("MathService.Add error:", err)}fmt.Printf("MathService.Add: %d+%d=%d", args.A, args.B, reply)}// DialHTTP connects to an HTTP RPC server at the specified network address// listening on the default HTTP RPC path.func DialHTTP(network, address string) (*rpc.Client, error) {return DialHTTPPath(network, address, rpc.DefaultRPCPath)}// DialHTTPPath connects to an HTTP RPC server// at the specified network address and path.func DialHTTPPath(network, address, path string) (*rpc.Client, error) {var err errorconn, err := net.Dial(network, address)if err != nil {return nil, err}io.WriteString(conn, "GET "+path+" HTTP/1.0\n\n")// Require successful HTTP response// before switching to RPC protocol.resp, err := http.ReadResponse(bufio.NewReader(conn), &http.Request{Method: "GET"})connected := "200 Connected to JSON RPC"if err == nil && resp.Status == connected {return jsonrpc.NewClient(conn), nil}if err == nil {err = errors.New("unexpected HTTP response: " + resp.Status)}conn.Close()return nil, &net.OpError{Op:   "dial-http",Net:  network + " " + address,Addr: nil,Err:  err,}}

以上这段代码的核心在于通过建立好的TCP 链接,发送 HTTP 请求调用远程的HTTP JSON RPC 服务,这里使用的是 HTTP GET 方法。

分别运行服务端和客户端,就可以看到正确的HTTP JSON RPC 调用结果了。

总结

这一讲基于 Go 语言自带的RPC 框架,讲解了 RPC 服务的实现以及调用。通过这一讲的学习相信你可以很好地了解什么是 RPC 服务,基于 TCP 和 HTTP 实现的RPC 服务有什么不同,它们是如何实现的等等。

不过在实际的项目开发中,使用Go 语言自带的 RPC 框架并不多,但是这里我还是以自带的框架为例进行讲解,这样可以更好地理解 RPC 的使用以及实现原理。如果你可以很好地掌握它们,那么你使用第三方的 RPC 框架也可以很快上手。

在实际的项目中,比较常用的是Google的gRPC 框架,它是通过Protobuf 序列化的,是基于 HTTP/2 协议的二进制传输,并且支持很多编程语言,效率也比较高。关于 gRPC的使用可以看官网的文档,入门是很容易的。


精选评论

**仕:

回头再细细啃一遍

**克:

学到了很多,期待老师再出其他专栏。

**生:

受益匪浅🙏

**升:

哈哈哈,最后一章真不容易啊

    编辑回复:

    太棒了,必须给你打call

我们从 Go 语言的基础知识,到底层原理,再到实战,相信你已经学会了如何使用 Go 语言,并可以上手做项目了。这一路走来,非常感谢你对学习的坚持,以及对我的支持。

在本专栏的最后,我会和你聊下 Go 语言的前景,以及对于你学习 Go 语言编程和在今后职业发展方面,我的一些建议。

Go 语言的发展前景

随着这几年 Dokcer、K8s 的普及,云原生的概念也越来越火,而 Go 语言恰恰就是为云而生的编程语言,所以在云原生的时代,它就具备了天生的优势:易于学习、天然的并发、高效的网络支持、跨平台的二进制文件编译等。

CNCF(云原生计算基金会)对云原生的定义是:

  • 应用容器化;

  • 面向微服务架构;

  • 应用支持容器的编排调度。

我们可以看到,对于这三点有代表性的 Docker、K8s 以及 istio 都是采用 Go 语言编写的,所以 Go 语言在云原生中发挥了极大的优势。

在涉及网络通信、对象存储、协议等领域的工作中,Go 语言所展现出的优势要比 Python、C /C++ 更大,所以诸如字节跳动、腾讯等很多大厂都在拥抱 Go 语言的开发,甚至很多公司在业务这一层也采用 Go 语言来开发微服务,从而提高开发和运行效率。

总体来说,对 Go 语言的前景我还是比较看好的,所以本专栏是你 Go 语言学习的敲门砖,接下来我建议你可以对这一语言进行更加系统和全面的学习。

Go 语言学习建议

关于 Go 语言的学习,我建议从官方文档官方作者著作的书开始,这样你可以看到“原汁原味”的讲解。其实不只 Go 语言,任何一门语言都应该是这样,官方的内容是比较权威的。

基于官方文档入门后,你就可以参考一些第三方大牛写的相关书籍了。阅读不同人写的 Go 语言书籍,你可以融会贯通,更好地理解 Go 语言的知识点。比如在其他书上看不懂的内容,换一本你可能就看懂了。

阅读书籍还有一个好处是让你的学习具备系统性,而非零散的。现在大部分的我们都选择碎片化学习,其实通过碎片化的时间,系统地学习才是正确的方式。

不管是通过书籍、官网文档,还是视频、专栏的学习,我们都要结合示例进行练习,不能只用眼睛看,这样的学习效率很低,一定要将代码动手写出来,这样你对知识的理解程度和只看是完全不一样的,在这个过程中你可以通过写加深记忆通过调试加深理解通过结果验证你的知识

有了这些基础后,就可以看一些实战类的书籍、文章和视频了,这样你不只是学会了 Go 语言,还能用 Go 语言做项目,了解如何编码、分库、微服务、自动化部署等。

不管是学习 Go 语言还是其他编程语言,都要阅读源代码,通过阅读源代码了解底层的实现原理,以及学习他人优秀的代码设计,进而提升自己在 Go 语言上的技术能力。

当然一个工程师“源于代码”,但不能“止于代码”。

不止于编程语言

无论你是想走技术专家路线,还是技术管理路线,要想更多地发挥自己的价值,必然是要带人的,因为一个人再怎么努力、技术如何厉害,也比不上多人团队的协作

所以,当你工作 3 年具备骨干的能力后,就要开始尝试带人、做导师了,把自己学习编程的经验教给新人,让他们少走弯路,同时也能锻炼自己带人的能力,协调更多的人一起做事情。

这样当你有 5 年、7 年,甚至以上工作经验的时候,你的团队会越来越壮大,在团队中你所发挥的价值也越来越大;而在个人方面,你也可以做架构设计、技术难点攻关等更有价值的事情。

关于技术编程人员的成长,我有过一次分享。我把成长经历分为 9 个阶段,每一个阶段需要哪些技术,如何提升自己的段位,都有详细的介绍,你可以在>《技术编程人员成长的 9 个段位》中查看。

总结

具备自我驱动力,以及学习能力的人,在职场中的竞争力都不会太差。

希望这个专栏可以很好地帮到你,让你学到 Go 语言的知识,让你在职场中更具备竞争力。


精选评论

*阳:

非常感谢飞雪无情老师的分享,在我刚刚学习Go语言时恰好遇到了这个专栏, 在持续不断的学习过程中,收获了很多新的知识。 老师讲的通俗易懂,有干货有实践。赞

    编辑回复:

    也感谢你自己,加油

**鑫:

经常关注老师的博客,可以说老师就是我go的引路人,哈哈哈,马上准备转go了,面试前再来看看巩固一下基础。

*龙:

课程已学完,一个字,棒

**nathan大聖:

总体上课程还是很不错的,涉及到了golang的方方面面。如果能在某些主题上深入一些,比如goroutine和OS 线程的映射和调度等。谢谢老师!

    编辑回复:

    感谢反馈

*中:

醍醐灌顶

**玲:

老师,能推荐一些go语言的经典书籍吗?

    讲师回复:

    《Go语言程序设计》《Go语言实战》

**伟:

草草的看完了,但是还没学完,我昨天更领导说了要求转后台,接下来就是实战考验了。

    编辑回复:

    加油

*帅:

很高兴能遇到这么好的课程,很多知识点讲解的通俗易懂,有种豁然开朗的感觉,必须支持一下,感谢飞雪无情老师的杰作,以后要常来复习了

**升:

完结撒花!!!

    编辑回复:

    太棒了

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

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

二、JumpServer堡垒机管理员手册

JumpServer是一款非常简单好用的开源堡垒机,本文根据实际生产案例编辑的管理员手册,列出了JumpServer常用功能。JumpServer可以很好的保护公司内部服务器,并满足等保2.0安全需求。 目录 一、堡垒机用户创建 二、创建特权用户 三、创建普通…

金字塔思维

背景 1、大脑偏爱有规律的信息 2、把问题想全,同时可以深入 1 方法: 1.1 识别纵向信息逻辑: 被动接受了大量杂乱信息,通过金字塔思维识别信息的逻辑关系 1.2 横向分类:信息归类整理 穷尽所有要素、对要素进行分类…

docker、docker-compose部署oracle,plsql连接远程oracle

一、docker部署oracle 1. 下载镜像并启动容器 # 拉取阿里oracle_11g的镜像 docker pull registry.cn-hangzhou.aliyuncs.com/helowin/oracle_11g # 创建容器并启动 docker run -d -p 1521:1521 --name oracle11g registry.cn-hangzhou.aliyuncs.com/helowin/oracle_11g 2.…

我们该如何运营Facebook账号呢?

社交媒体带来的流量转化是巨大的,这也就是为什么跨境电商卖家会转战到社媒的原因了。我们经常会发现一个帖子突然就火了,那么这个帖子的相关产品都得到了极大的推广,其曝光率也是难以想象的,那么由此带来的转化也常常令人欣喜若狂…

间主任的烦恼① | 基于BOM的配套检查,保障生产不断线

王部长:(桌上电话铃声响起,王部长接完电话后说,)“小智,我们装配车间的张主任,这打电话来,又在说配套缺件问题。这周就要启动装配了,现在领料发现还有20种零件没有库存。…

CentOS系统磁盘的分区格式化和挂载操作

一、磁盘分区和格式化 lsblk命令查看挂载硬盘的情况,一下情况表示为分卷,需要先分卷,再格式化,然后再进行挂载。 分卷命令:fdisk /dev/vdb 输入n 输入p和1,直接回车 最后输入w保存。 格式化磁盘&#x…

USB摄像头驱动分析

1.构造一个usb_driver 2.设置 probe:2.1.分配video_device:video_device_alloc2.2 设置.fops.ioctl_ops (里面需要设置11项)如果要用内核提供 的缓冲区操作函数,还需要构造一个videobuf_queue_ops2.3.注册: video_register_device id_table:表示支持哪些…

基于单片机的指纹密码锁系统

目录 第1章 概述............................................................................................................ 6 1.1 指纹识别技术的发展................................................................................... 6 1.2 指纹识别原理......…

mirai登陆失败反复验证码或提示登录存在安全风险的解决方法

对于没有服务器的同学,可以进入官网领取免费1个月的轻量云服务器:云产品免费试用;需要选购的进:轻量应用服务器专场;不清楚怎么操作的可以看教程:腾讯云产品免费试用教程 转载请注明出处:小锋学…

Word控件Spire.Doc 【页面设置】教程(4) 如何在文档中插入分节符

在 Microsoft Word 中,您可以通过在所需位置插入分节符轻松地将文档拆分为多个部分,从而允许您对这些部分应用不同的格式或布局选项。如果您需要执行以下任何任务,Microsoft Word 中的分节符可能是天赐之物: 为文档的每个部分使用…

MySQL高级:(二)存储引擎

笔记来源:MySQL数据库教程天花板,mysql安装到mysql高级,强!硬! 文章目录2.1 存储引擎基本概念2.2 InnoDB:具备外键支持功能的事务存储引擎2.3 MyISAM:主要的非事务处理存储引擎2.3 Archive&…

Cannot get a STRING value from a NUMERIC cell poi异常解决;easy-poi;jeecg-boot-poi

Cannot get a STRING value from a NUMERIC cell poi异常解决;easy-poi;jeecg-boot-poi 出现原因: 在集成jeecg-boot框架开发之后 使用jeecg-boot-auto-poi (封装的easy-poi)导入数据时 因为excel中有函数表达式出现的错误 导入的代码 而在源码中CellValueServer这一个类,在…

如何用dos命令关闭端口

1、首先以管理员并且cmd,打开dos对话窗口 2、通过:netstat -aon|findstr 8080 找到PID码3、通过:taskkill /f /pid 35552 PID码,终止进程4、到这也就终止完成啦,是不是很简单呢?

2021 ICPC Southeastern Europe Regional Contest(更新至五题)

2021 ICPC Southeastern Europe Regional Contest A题签到 A. King of String Comparison 题意:给两个字符串,找出有多少对(l,r),满足在l到r区间内,s1的子串字典序小于s2的子串字典序。 思路…

手写 Vuex4.x 核心(Vuex源码实现)

通过 vuex 实现 TodoList : 我们先做一个小的 TodoList 的案例,应用 vuex 来实现,然后逻辑跑通之后再在此的基础上我们一点点手写自己的 vuex ,进而实现和原来一样的效果。 采用 vite 创建项目: yarn create vite v…

【算法】背包问题应用

01 背包 AcWing 423. 采药 代价&#xff1a; 采药时间 T 价值&#xff1a; w 0 - 1背包问题 #include <bits/stdc.h> using namespace std;const int N 1010;int n, m;int f[N];int main() {cin >> m >> n;for (int i 1; i < n; i ) {int v, w;cin &…

文件防泄密系统如何保障企业文档的安全性?

企业文件管理难点 1. 重要资料没有安全保障 企业管理中员工离职、换岗等人员流动现象十分常见&#xff0c;与公司有关的重要文件经常出现被复制、删除、外泄的情况&#xff0c;据数据显示&#xff0c;85%的职员可轻松下载这些具有"竞争力"的资料和信息&#xff0c;…

【正点原子STM32连载】第三十九章 DS18B20数字温度传感器实验 摘自【正点原子】MiniPro STM32H750 开发指南_V1.1

1&#xff09;实验平台&#xff1a;正点原子MiniPro H750开发板 2&#xff09;平台购买地址&#xff1a;https://detail.tmall.com/item.htm?id677017430560 3&#xff09;全套实验源码手册视频下载地址&#xff1a;http://www.openedv.com/thread-336836-1-1.html 4&#xff…

51单片机学习:LCD12864液晶显示实验(带字库)

实验名称&#xff1a;LCD12864液晶显示实验&#xff08;带字库&#xff09; 接线说明&#xff1a; 实验现象&#xff1a;下载程序后&#xff0c;LCD12864上显示汉字字符信息 注意事项&#xff1a; …

创建型之单例模式

文章目录创建型单例模式1. 饿汉式2. 懒汉式3. 双重检查4. sync.Once创建型 创建型设计模式包括&#xff1a;单例模式、工厂模式、建造者模式、原型模式。它主要解决对象的创建问题&#xff0c;封装复杂的创建过程&#xff0c;解耦对象的创建代码和使用代码。 单例模式 单例模…