kotlin半生对象_如何在Kotlin中使用Actor实现对象池
kotlin半生對(duì)象
by osha1
由osha1
如何在Kotlin中使用Actor實(shí)現(xiàn)對(duì)象池 (How to implement an Object-Pool with an Actor in Kotlin)
We use object pool in jasync-sql to manage connections to the database. In this post, I will share how it is done in a performant, lock-free manner using Kotlin coroutines with an Actor.
我們?cè)趈async-sql中使用對(duì)象池來管理與數(shù)據(jù)庫(kù)的連接。 在這篇文章中,我將分享如何通過與Actor一起使用Kotlin協(xié)程以高性能,無鎖的方式完成操作。
An object pool has a very simple API to work with. It is a pool of objects with two methods: take() and return().
對(duì)象池具有非常簡(jiǎn)單的API。 它是具有兩種方法的對(duì)象池: take()和return() 。
On first sight it looks like a very simple problem. The main catch here is that it has to be both performant and thread-safe, and that’s what makes it interesting and tricky to implement.
乍看之下,這似乎是一個(gè)非常簡(jiǎn)單的問題。 這里的主要問題是它必須兼具高性能和線程安全性,這就是實(shí)現(xiàn)它有趣且棘手的原因。
但是,嘿! 為什么我們?nèi)匀恍枰粋€(gè)對(duì)象池? (But hey! Why do we need an object pool anyway?)
jasync-sql is a library to access relational databases like MySQL and PostgreSQL. Database connections are a great example of the need for object pools. The access to the database is done by obtaining a connection from a Connection-Pool, using it and returning it back to the pool.
jasync-sql是一個(gè)用于訪問關(guān)系數(shù)據(jù)庫(kù)(如MySQL和PostgreSQL)的庫(kù)。 數(shù)據(jù)庫(kù)連接是需要對(duì)象池的一個(gè)很好的例子。 通過從Connection-Pool獲取連接,使用數(shù)據(jù)庫(kù)并將其返回給池 ,可以完成對(duì)數(shù)據(jù)庫(kù)的訪問。
With a connection pool we get a couple of advantages over creating connections per each SQL query:
使用連接池,與每個(gè)SQL查詢創(chuàng)建連接相比,我們有兩個(gè)優(yōu)點(diǎn):
Reusing connections — since the overhead of initiating a connection to the database is high (handshake, etc), connection pools allow keeping connections alive, thus reducing that overhead.
重用連接 -由于啟動(dòng)與數(shù)據(jù)庫(kù)的連接的開銷很高(握手等),因此連接池允許保持連接處于活動(dòng)狀態(tài),從而減少了開銷。
Limiting resources — creating a DB connection per user request can be overwhelming to the DB. Using a pool effectively adds a barrier, limiting the number of maximum number of concurrent connections.
限制資源 -為每個(gè)用戶請(qǐng)求創(chuàng)建數(shù)據(jù)庫(kù)連接可能會(huì)使數(shù)據(jù)庫(kù)不堪重負(fù)。 使用池有效地增加了障礙,限制了并發(fā)連接的最大數(shù)量。
連接池在Java世界中解決不了嗎? (Isn’t a Connection Pool a solved problem in the Java world?)
Yes it is a solved problem if you’re using JDBC. In that case HikariCP is an excellent choice from my experience, but there are a lot of others. In the case of jasync-sql it is not possible to use HikariCP, because HikariCP works with the JDBC API, and the jasync-sql driver is not implementing that full-fledged API, only a subset of it.
是的,如果您使用JDBC,這是一個(gè)已解決的問題。 在這種情況下,從我的經(jīng)驗(yàn)來看 , HikariCP是一個(gè)很好的選擇,但還有很多其他選擇。 在jasync-sql的情況下,無法使用HikariCP ,因?yàn)镠ikariCP與JDBC API一起使用,并且jasync-sql驅(qū)動(dòng)程序未實(shí)現(xiàn)該完整的API,僅實(shí)現(xiàn)了一部分。
What about other Object pools in Java world?Java世界中的其他對(duì)象池又如何呢?There are numerous implementations, but it turns out that you usually find some specific requirement that was not implemented by that pool you’re using.
有許多實(shí)現(xiàn),但是事實(shí)證明,您通常會(huì)發(fā)現(xiàn)一些特定要求,而該要求不是您所使用的池所實(shí)現(xiàn)的。
In our case, that requirement was non-blocking. In our pool, all operations have to be non-blocking since the library is async. For example, the take() operation in most implementations returns an object immediately or blocks until an object is ready. Our take() returns a Future<Connection>, which will be completed and continued when the connection is ready to use.
在我們的情況下,該要求是無障礙的。 在我們的池中,由于庫(kù)是異步的,因此所有操作都必須是非阻塞的。 例如,大多數(shù)實(shí)現(xiàn)中的take()操作立即返回一個(gè)對(duì)象或阻塞直到對(duì)象準(zhǔn)備就緒。 我們的take()返回Future<Connecti on>,當(dāng)連接準(zhǔn)備就緒時(shí),它將完成并繼續(xù)。
I haven’t seen such an implementation in the wild.
我還沒有在野外看到這樣的實(shí)現(xiàn)。
I really like this answer from Stack Exchange:
我真的很喜歡來自Stack Exchange的答案:
Is object pooling a deprecated technique?Software Engineering Stack Exchange is a question and answer site for professionals, academics, and students working…softwareengineering.stackexchange.com
對(duì)象池是否已被棄用? 軟件工程堆棧交換是一個(gè)為專業(yè)人士,學(xué)者和工作的學(xué)生提供的問答網(wǎng)站 。softwareengineering.stackexchange.com
Another requirement that makes it hard to find an alternative is the need to try and stay compatible as much as possible with the current implementation we have.
另一個(gè)很難找到替代方法的要求是,需要嘗試與現(xiàn)有的實(shí)現(xiàn)盡可能保持兼容。
In case you want to see other implementations you can check here:
如果您想查看其他實(shí)現(xiàn),可以在這里查看:
object pool in java - Google Searchobject pool is a collection of a particular object that an application will create and keep on hand for those…www.google.co.il
Java中的對(duì)象池-Google搜索 對(duì)象池是應(yīng)用程序?qū)?chuàng)建的特定對(duì)象的集合,并將這些對(duì)象保存在手中…… www.google.co.il
那么我們?nèi)绾螌?shí)現(xiàn)對(duì)象池呢? (So how did we implement Object Pool?)
Before we dive into the details, let’s observe other requirements from the object pool that were omitted above for clarity but are necessary details.
在深入研究細(xì)節(jié)之前,讓我們觀察一下對(duì)象池中的其他需求,為清晰起見,這些需求在上面已被省略,但它們是必要的細(xì)節(jié)。
介面 (Interfaces)
The Object pool interface looks like this:
對(duì)象池接口如下所示:
interface AsyncObjectPool<T> { fun take(): CompletableFuture<T> fun giveBack(item: T): CompletableFuture<AsyncObjectPool<T>> fun close(): CompletableFuture<AsyncObjectPool<T>>}In addition, when a pool wants to create new objects (connections) it will call the ObjectFactory. The factory has a couple more methods to handle the object lifecycle:
另外,當(dāng)池要?jiǎng)?chuàng)建新對(duì)象(連接)時(shí),它將調(diào)用ObjectFactory 。 工廠有更多其他方法來處理對(duì)象生命周期:
validate — a method to check that the object is still valid. The method should be fast and check only in-memory constructs. For connections we usually check that the last query did not throw an exception and did not get a termination message from netty.
validate — 驗(yàn)證對(duì)象仍然有效的方法。 該方法應(yīng)該是快速的,并且僅檢查內(nèi)存中的構(gòu)造。 對(duì)于連接,我們通常檢查最后一個(gè)查詢沒有引發(fā)異常,也沒有從netty獲得終止消息。
test — similar to validate, but a more exhaustive check. We allow test method to be slow and access the network etc. This method is used to check that idle objects are still valid. For connections, that will be something similar to select 0.
測(cè)試 -與驗(yàn)證類似,但檢查更為詳盡。 我們?cè)试S測(cè)試方法變慢并訪問網(wǎng)絡(luò)等。此方法用于檢查空閑對(duì)象是否仍然有效。 對(duì)于連接,這類似于select 0 。
destroy — called to clean up the object when the pool is not using it anymore.
銷毀 -在池不再使用該對(duì)象時(shí)調(diào)用以清除該對(duì)象。
The complete interface is:
完整的界面是:
interface ObjectFactory<T> { fun create(): CompletableFuture<;out T> fun destroy(item: T) fun validate(item: T): Try<T> fun test(item: T): CompletableFuture<T>}For pool configuration we have the following properties:
對(duì)于池配置,我們具有以下屬性:
maxObjects — maximum number of connections we allow.
maxObjects —我們?cè)试S的最大連接數(shù)。
maxIdle — time that we leave the connection open without use. After that time it will be reclaimed.
maxIdle —我們不使用連接而保持打開狀態(tài)的時(shí)間。 在那之后,它將被回收。
maxQueueSize — when a request for a connection arrives and no connection is available, we put the request on hold in a queue. In case the queue is full (its size passed maxQueueSize) it will not wait but instead return an error.
maxQueueSize —當(dāng)一個(gè)連接請(qǐng)求到達(dá)并且沒有可用的連接時(shí),我們將該請(qǐng)求置于隊(duì)列中。 如果隊(duì)列已滿(其大小通過maxQueueSize傳遞),它將不等待而是返回一個(gè)錯(cuò)誤。
createTimeout — maximum time to wait for a new connection to be created.
createTimeout —等待創(chuàng)建新連接的最長(zhǎng)時(shí)間。
testTimeout — maximum time to wait for a test query on an idle connection. If it passes we will consider the connection as erroneous.
testTimeout —在空閑連接上等待測(cè)試查詢的最長(zhǎng)時(shí)間。 如果通過,我們將認(rèn)為連接錯(cuò)誤。
validationInterval — on this interval, we will test if the idle connections are active and free up connections that passed maxIdle. We will also remove connections that passed testTimeout.
validationInterval -在此期間,我們將測(cè)試如果空閑連接有效且騰出通過連接maxIdle 。 我們還將刪除通過testTimeout連接。
原始實(shí)施 (Original implementation)
The first implementation of object pool was single threaded. All operations were sent to a worker thread that was responsible to execute them. This method is known as thread-confinement. Object creation and test operations were blocking and query execution itself was non-blocking.
對(duì)象池的第一個(gè)實(shí)現(xiàn)是單線程的。 所有操作都發(fā)送到負(fù)責(zé)執(zhí)行這些操作的工作線程。 此方法稱為線程約束 。 對(duì)象創(chuàng)建和測(cè)試操作處于阻塞狀態(tài),而查詢執(zhí)行本身是非阻塞的。
This method is problematic because operations are done one after another. On top of that, there are a couple of operations that are blocking as mentioned above. There were various cases of high latency when working in some scenarios and use cases (like here for example).
這種方法有問題,因?yàn)椴僮魇且粋€(gè)接一個(gè)地完成的。 最重要的是,如上所述,有幾項(xiàng)操作正在阻塞。 在某些情況和用例中工作時(shí),存在各種高延遲情況(例如,例如此處 )。
As a workaround PartitionedPool was introduced. This is a workaround to the block issue with the above single-threaded approach. The partitioned pool creates multiple SingleThreadedObjectPools, each with its own worker. When a connection is requested, a pool is selected by a modulus on the thread id. The partitioned pool is actually a pool of pools ;-)
作為解決方法,引入了PartitionedPool 。 這是上述單線程方法解決塊問題的一種解決方法。 分區(qū)池創(chuàng)建多個(gè)SingleThreadedObjectPools ,每個(gè)都有其自己的工作程序。 當(dāng)請(qǐng)求連接時(shí),將通過線程ID上的模數(shù)來選擇一個(gè)池。 分區(qū)池實(shí)際上是池的池;-)
I mentioned this is a workaround since it has its own problems: you might still be blocking, but at a lower rate — plus it consume more threads and resources.
我提到了這是一種解決方法,因?yàn)樗衅渥陨淼膯栴}:您可能仍在阻塞,但是速率較低-而且它消耗更多的線程和資源。
基于Actor的實(shí)現(xiàn) (Actor based implementation)
An Actor is an entity that has a mailbox. It receives messages to its mailbox and processes them one after the other. The mailbox is a sort of a channel to pass events from the outside world to the actor.
Actor是具有郵箱的實(shí)體。 它接收到其郵箱的消息,然后一個(gè)接一個(gè)地處理它們。 郵箱是一種將事件從外界傳遞給演員的渠道。
A coroutines actor employs lock-free algorithms to allow fast and performant execution of events without the need for locks and synchronized blocks.
協(xié)程演員使用無鎖算法來快速,高效地執(zhí)行事件,而無需鎖和synchronized塊。
You can see an elaborated explanation here.
您可以在此處看到詳細(xì)的說明。
In our case those events will be take and giveBack. In addition to those, we will have internal messages that the actor sends to itself like objectCreated etc. That allows the actor to have states that does not suffer from concurrency problems, as it is always confined to the same sequential execution. In addition the channel that passes those events is a queue that is using lock-free algorithms so it is very efficient, avoids contention, and generally has very high performance.
在我們的情況下,這些事件將為take和giveBack 。 除此之外,我們還將擁有objectCreated發(fā)送給自己的內(nèi)部消息,例如objectCreated等。這使得objectCreated具有不受并發(fā)問題困擾的狀態(tài),因?yàn)樗冀K限于同一順序執(zhí)行。 另外,傳遞這些事件的通道是使用無鎖算法的隊(duì)列,因此它非常高效,避免爭(zhēng)用并且通常具有很高的性能。
There is an excellent video explaining how this was implemented (note that this is “heavy” algorithmic staff):
有一個(gè)精彩的視頻解釋了如何實(shí)現(xiàn)(請(qǐng)注意,這是“繁重的”算法工作人員):
Let’s recap what we have until now:
讓我們回顧一下到目前為止所擁有的:
- An actor receives messages and processes them one by one. 演員接收消息并對(duì)其進(jìn)行逐一處理。
Usually messages will contain a CompletableFuture that should be completed when the actor processes it.
通常,消息將包含CompletableFuture ,當(dāng)actor處理該消息時(shí)應(yīng)將其完成。
Messages will be completed immediately or delayed (like in case we are waiting for a connection to be created). If it is delayed the actor will put the Future in a queue, and will use a callback mechanism to notify itself when the original future can be completed.
消息將立即完成或延遲(例如,在我們等待連接建立的情況下)。 如果延遲,則參與者將把Future放在隊(duì)列中,并將使用回調(diào)機(jī)制通知自己何時(shí)可以完成原始的Future 。
- Message processing in the actor should not be blocked or delay the actor. If this happens, it will delay all messages waiting to be processed in the queue and will slow down the entire actor operation. actor中的消息處理不應(yīng)被阻塞或延遲actor。 如果發(fā)生這種情況,它將延遲所有等待在隊(duì)列中處理的消息,并且會(huì)減慢整個(gè)actor操作的速度。
That’s why, in case we have long running operations inside the actor, we use the callback mechanism.
這就是為什么如果我們?cè)赼ctor中長(zhǎng)時(shí)間運(yùn)行操作的原因,我們使用回調(diào)機(jī)制。
讓我們看一下用例的更多細(xì)節(jié) (Let’s see more details on the use cases)
Take — someone wants an object from the pool. It will send a message with a callback to the actor. The actor will do one of the following things:
Take -有人要從游泳池里Take東西。 它將帶有回調(diào)的消息發(fā)送給參與者。 演員將執(zhí)行以下操作之一:
- If the object is available — the actor will simply return it. 如果對(duì)象可用-演員將簡(jiǎn)單地將其返回。
- If the pool hasn’t passed the limit of created objects — the actor will create a new object and return it when the object is ready. 如果池尚未通過創(chuàng)建對(duì)象的限制,則actor將創(chuàng)建一個(gè)新對(duì)象,并在對(duì)象準(zhǔn)備就緒時(shí)將其返回。
In such a case, object creation can take time, so the actor will connect the callback from the object creation to the original take request callback.
在這種情況下,對(duì)象創(chuàng)建可能會(huì)花費(fèi)一些時(shí)間,因此參與者會(huì)將回調(diào)從對(duì)象創(chuàng)建連接到原始的獲取請(qǐng)求回調(diào)。
- Will put the request in a queue for an available object (unless the queue is full and in that case will just return an error). 將請(qǐng)求放入可用對(duì)象的隊(duì)列中(除非隊(duì)列已滿,在這種情況下只會(huì)返回錯(cuò)誤)。
GiveBack — someone wants to give an object back to the pool (release it). This is also done by a message to the actor. The actor will do one of the following:
GiveBack有人想將對(duì)象還給池(釋放它)。 這也可以通過發(fā)送給演員的消息來完成。 演員將執(zhí)行以下操作之一:
- If someone is waiting on the wait queue — it will borrow the object to it. 如果有人在等待隊(duì)列中等待,它將向其借用該對(duì)象。
- In other cases it will just keep the object on the pool for requests to come, so the object remains idle. 在其他情況下,它將對(duì)象僅保留在池中以等待請(qǐng)求,因此該對(duì)象保持空閑狀態(tài)。
Test — periodically, someone from outside will notify the actor to test connections:
Test -定期,外部人員會(huì)通知參與者測(cè)試連接:
- The actor will release the idle connection that hasn’t been used for a long time (it’s configurable). actor將釋放很長(zhǎng)時(shí)間未使用的空閑連接(它是可配置的)。
The actor will test other idle objects using the ObjectFactory. It will send a callback to the factory and mark those objects as In Use, to prevent from borrowing them until the test is completed.
參與者將使用ObjectFactory測(cè)試其他空閑對(duì)象。 它將向工廠發(fā)送一個(gè)回調(diào)并將這些對(duì)象標(biāo)記為In Use ,以防止在測(cè)試完成之前借用它們。
- The actor will check for timeouts in tests and destroy time-outed objects. 參與者將檢查測(cè)試中的超時(shí)并破壞超時(shí)的對(duì)象。
Those are the main use cases.
這些是主要的用例。
泄漏 (Leaks)
There can be all sort of leaks in an object pool. Some are internal bugs which I hope are easier to spot and fix, and others are objects that were taken but not returned due to some user error. In such cases, objects might remain in the “In Use” queue forever.
對(duì)象池中可能存在各種泄漏。 我希望一些內(nèi)部錯(cuò)誤更容易發(fā)現(xiàn)和修復(fù),而其他一些則是由于某些用戶錯(cuò)誤而被拿回但未返回的對(duì)象。 在這種情況下,對(duì)象可能永遠(yuǎn)保留在“ 使用中”隊(duì)列中。
To avoid such cases, the “In Use” Map is using Java’s WeakHashMap. So if a user lost a connection it will be automatically removed from the map when it is cleaned by Java’s Garbage-Collector.
為了避免這種情況, “使用中”地圖使用Java的WeakHashMap 。 因此,如果用戶失去了連接,當(dāng)Java的Garbage-Collector清理連接時(shí),它將自動(dòng)從地圖中刪除。
In addition we added a log message in such cases that says: “LEAK-DETECTED”.
此外,在這種情況下,我們添加了一條日志消息,內(nèi)容為: “泄漏檢測(cè)到” 。
而已! (That’s it!)
The full Kotlin source code of the object pool is available here:
對(duì)象池的完整Kotlin源代碼在此處提供:
jasync-sql/jasync-sqlJava async database driver for MySQL and PostgreSQL written in Kotlin - jasync-sql/jasync-sqlgithub.com
jasync-sql / jasync-sql 用Kotlin編寫的用于MySQL和PostgreSQLJava異步數(shù)據(jù)庫(kù)驅(qū)動(dòng)程序-jasync-sql / jasync-sql github.com
In an upcoming post I will compare performance metrics of the different implementations.
在下一篇文章中,我將比較不同實(shí)現(xiàn)的性能指標(biāo)。
If you want to read more about Kotlin there is a nice introduction here:
如果您想了解有關(guān)Kotlin的更多信息,這里有一個(gè)不錯(cuò)的介紹:
And for coroutines in general check out this video:
對(duì)于一般的協(xié)程,請(qǐng)觀看以下視頻:
Finally if you want to learn more about Actors implementation using coroutines in Kotlin, then head over here:
最后,如果您想了解有關(guān)Kotlin中使用協(xié)程的Actor實(shí)現(xiàn)的更多信息,請(qǐng)前往此處:
Kotlin/kotlinx.coroutinesLibrary support for Kotlin coroutines . Contribute to Kotlin/kotlinx.coroutines development by creating an account on…github.com
Kotlin / kotlinx.coroutines庫(kù)特林 協(xié)同程序的庫(kù)支持。 通過在 github.com 上創(chuàng)建一個(gè)帳戶,為Kotlin / kotlinx.coroutines開發(fā)做出貢獻(xiàn)
Thanks for reading! ??
謝謝閱讀! ??
翻譯自: https://www.freecodecamp.org/news/how-to-implement-an-object-pool-with-an-actor-in-kotlin-ed06d3ba6257/
kotlin半生對(duì)象
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的kotlin半生对象_如何在Kotlin中使用Actor实现对象池的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 梦到跟前任接吻预示着什么
- 下一篇: sphinx_Sphinx之谜:如何轻松