soword科技言
永久公益免费API接口
提供永久免费的API接口,查看更多API接口,如果您有其他免费API资源,请联系我们,造福人类。
提供商务开发:小程序,系统,APP
定制开发,免费评估,免费咨询,价格便宜,售后保障,前往开发服务中心联系开发客服中心
C ++析构函数如何在Envoy中有用的示例

一段时间以来,我一直在从事C ++项目(Envoy)的工作,有时我需要为它做贡献,所以我的C ++技能已经从“不存在”变为“真的很少”。我已经了解了什么是初始化程序列表,并且以该方法开头的方法~是析构函数。我几乎知道左值和右值是什么,但不是很清楚。

但是有一天,当编写一些C ++代码时,我发现了一些关于如何使用我尚未意识到的析构函数的令人兴奋的东西!(这篇博文中的tl; dr对于了解C ++的人是“朱莉娅终于明白了RAII是什么,并且它是有用的” :))

什么是析构函数?

C ++有对象。当C ++对象超出范围时,编译器将插入对其析构函数的调用。因此,如果您有一些类似的代码

function do_thing() {
Thing x{}; // this calls the Thing constructor
return 2;}

do_thing函数末尾将调用x的析构函数。因此,c ++生成的代码如下所示:

  • 创造新事物

  • 称新事物的破坏者

  • 返回2

显然,析构函数是如此复杂。有例外时,他们需要打电话给他们!有时他们会被手动调用。还有很多其他原因。但是有关C ++的知识有1000万件事,而这不是我们今天正在做的事情,我们只是在谈论一件事。

析构函数中会发生什么?

很多时间内存被释放,这是避免内存泄漏的方法。但这不是我们在本文中谈论的内容!我们正在谈论更有趣的事情。

我们感兴趣的东西:Envoy断路器

因此,我与Envoy进行了很多合作。3秒的Envoy刷新器:这是一个HTTP代理,您的应用程序向Envoy发出请求,然后将其代理到该应用程序要与之通信的服务器。

Envoy的一项非常有用的功能是称为“断路器”。基本上,这种想法是,如果您的应用程序与服务建立了500亿个连接,则可能会使该服务不堪重负。因此,Envoy会跟踪您与服务建立的TCP连接数,并且在达到限制时将阻止您发出新请求。默认max_connection限制

您如何跟踪连接数?

要使断路器保持TCP连接数,这意味着您需要准确计数当前打开的TCP连接数!你是怎样做的?好吧,它的工作方式是维护一个connections计数器并:

  • 每次打开连接时,增加计数器

  • 每次连接被破坏时(由于重置/超时/其他原因),请减少计数器

  • 创建新连接时,请检查connections计数器是否未超出限制

就这样!创建新连接时增加计数器非常容易。但是,如何确保连接被破坏后计数器递减可以通过多种方式破坏连接(它们可能会超时!它们可能会被Envoy关闭!它们可能会被服务器关闭!也许我没有想到的其他事情可能会发生!),这似乎很容易意外错过了关闭它们的方法。

破坏者进行营救

Envoy解决此问题的方法是ActiveClient为每个连接创建一个连接对象(在HTTP连接池中称为 )。

然后:

  • 在构造函数中增加计数器(代码

  • 减少析构函数中的计数器(代码

  • 创建新连接时检查计数器(代码

这样做的好处是,现在您无需确保计数器在所有正确的位置递减,您现在只需要组织代码ActiveClient即可在连接关闭时调用对象的析构函数。

ActiveClient在Envoy中析构函数在哪里被调用?好吧,Envoy维护了两个客户端列表(ready_clientsbusy_clients),当连接关闭时,Envoy从这些列表中删除了客户端。而且当它这样做时,它不需要做任何额外的清理工作!!在C ++中,每当从列表中删除对象时,都会调用其析构函数。因此 client.removeFromList(ready_clients_);,请注意所有清理工作。而且也没有忘记减少计数器的机会!!除非您不小心将对象留在其中一个列表中,否则肯定会一直发生,因为连接已关闭,无论如何这都是一个错误:)

区域情报研究所

Envoy在此使用的这种模式是一种极为常见的C ++编程模式,称为“资源获取即初始化”。我觉得这个名字很混乱,但这就是它的名字。基本上它的工作方式是:

  • 标识在初始化/完成连接时需要发生很多事情的资源(例如“连接”)

  • 为该连接建立一个类

  • 将所有初始化/完成代码放在构造函数/析构函数中

  • 确保在适当的时候调用对象的析构函数方法!(通过从向量中删除它/使其超出范围)

以前,我知道将这种模式用于某些显而易见的事情(确保在析构函数中释放所有内存,或者确保关闭文件描述符)。但是我没有意识到,对于那些似乎不太明显的资源,例如“减少计数器”,它也很有用。

该模式起作用的原因是因为C ++编译器/标准库做了很多工作来确保在完成对象处理后调用析构函数-编译器在异常之后的每个代码块的末尾插入析构函数调用和许多标准库集合确保从集合中删除对象时调用析构函数。

RAII为您提供及时,确定性且难以修改的资源清理

令人兴奋的是,此编程模式为您提供了一种调度清理资源的方式,该清理资源为:

  • 易于确保始终发生(当对象消失时,即使发生异常也总是发生!)

  • 提示性和确定性的(立即发生,并且一定会发生!)

RAII有哪些语言?

C ++和Rust具有RAII。可能还有其他语言。Java,Python,Go和垃圾收集语言通常不会。使用垃圾回收语言,您通常可以设置析构函数以在对象进行GC处理时运行。但往往(如在这种情况下,连接数)你想要的东西被清理的时候了当对象不再使用,而不是一些不确定的时期后,每当发生GC运行。

Python上下文管理器是一个相关的想法,您可以执行以下操作:

with conn_pool.connection() as conn:
do stuff



2023-03-22 10:04:19

新人小程序+APP定制199元起


发放福利,助力中小企业发展,真正在互联网中受益

点击询问定制

广告服务展示