kotlin半生對象_如何在Kotlin中使用Actor實現對象池

kotlin半生對象

by osha1

由osha1

如何在Kotlin中使用Actor實現對象池 (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.

我們在jasync-sql中使用對象池來管理與數據庫的連接。 在這篇文章中,我將分享如何通過與Actor一起使用Kotlin協程以高性能,無鎖的方式完成操作。

An object pool has a very simple API to work with. It is a pool of objects with two methods: take() and return().

對象池具有非常簡單的API。 它是具有兩種方法的對象池: 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.

乍看之下,這似乎是一個非常簡單的問題。 這里的主要問題是它必須兼具高性能和線程安全性,這就是實現它有趣且棘手的原因。

但是,嘿! 為什么我們仍然需要一個對象池? (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是一個用于訪問關系數據庫(如MySQL和PostgreSQL)的庫。 數據庫連接是需要對象池的一個很好的例子。 通過從Connection-Pool獲取連接,使用數據庫并將其返回給 ,可以完成對數據庫的訪問。

With a connection pool we get a couple of advantages over creating connections per each SQL query:

使用連接池,與每個SQL查詢創建連接相比,我們有兩個優點:

  • 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.

    重用連接 -由于啟動與數據庫的連接的開銷很高(握手等),因此連接池允許保持連接處于活動狀態,從而減少了開銷。

  • 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.

    限制資源 -為每個用戶請求創建數據庫連接可能會使數據庫不堪重負。 使用池有效地增加了障礙,限制了并發連接的最大數量。

Well, I am sold, but…
好吧,我被賣了,但是…

連接池在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,這是一個已解決的問題。 在這種情況下,從我的經驗來看 , HikariCP是一個很好的選擇,但還有很多其他選擇。 在jasync-sql的情況下,無法使用HikariCP ,因為HikariCP與JDBC API一起使用,并且jasync-sql驅動程序未實現該完整的API,僅實現了一部分。

What about other Object pools in Java world?
Java世界中的其他對象池又如何呢?

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.

有許多實現,但是事實證明,您通常會發現一些特定要求,而該要求不是您所使用的池所實現的。

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.

在我們的情況下,該要求是無障礙的。 在我們的池中,由于庫是異步的,因此所有操作都必須是非阻塞的。 例如,大多數實現中的take()操作立即返回一個對象或阻塞直到對象準備就緒。 我們的take()返回Future<Connecti on>,當連接準備就緒時,它將完成并繼續。

I haven’t seen such an implementation in the wild.

我還沒有在野外看到這樣的實現。

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

對象池是否已被棄用? 軟件工程堆棧交換是一個為專業人士,學者和工作的學生提供的問答網站 。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.

另一個很難找到替代方法的要求是,需要嘗試與現有的實現盡可能保持兼容。

In case you want to see other implementations you can check here:

如果您想查看其他實現,可以在這里查看:

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中的對象池-Google搜索 對象池是應用程序將創建的特定對象的集合,并將這些對象保存在手中…… www.google.co.il

那么我們如何實現對象池呢? (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.

在深入研究細節之前,讓我們觀察一下對象池中的其他需求,為清晰起見,這些需求在上面已被省略,但它們是必要的細節。

介面 (Interfaces)

The Object pool interface looks like this:

對象池接口如下所示:

interface AsyncObjectPool<T&gt; {  fun take(): CompletableFuture&lt;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:

另外,當池要創建新對象(連接)時,它將調用ObjectFactory 。 工廠有更多其他方法來處理對象生命周期:

  • 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驗證對象仍然有效的方法。 該方法應該是快速的,并且僅檢查內存中的構造。 對于連接,我們通常檢查最后一個查詢沒有引發異常,也沒有從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.

    測試 -與驗證類似,但檢查更為詳盡。 我們允許測試方法變慢并訪問網絡等。此方法用于檢查空閑對象是否仍然有效。 對于連接,這類似于select 0

  • destroy — called to clean up the object when the pool is not using it anymore.

    銷毀 -在池不再使用該對象時調用以清除該對象。

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:

對于池配置,我們具有以下屬性:

  • maxObjects — maximum number of connections we allow.

    maxObjects —我們允許的最大連接數。

  • maxIdle — time that we leave the connection open without use. After that time it will be reclaimed.

    maxIdle —我們不使用連接而保持打開狀態的時間。 在那之后,它將被回收。

  • 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 —當一個連接請求到達并且沒有可用的連接時,我們將該請求置于隊列中。 如果隊列已滿(其大小通過maxQueueSize傳遞),它將不等待而是返回一個錯誤。

  • createTimeout — maximum time to wait for a new connection to be created.

    createTimeout —等待創建新連接的最長時間。

  • testTimeout — maximum time to wait for a test query on an idle connection. If it passes we will consider the connection as erroneous.

    testTimeout —在空閑連接上等待測試查詢的最長時間。 如果通過,我們將認為連接錯誤。

  • 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 -在此期間,我們將測試如果空閑連接有效且騰出通過連接maxIdle 。 我們還將刪除通過testTimeout連接。

原始實施 (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.

對象池的第一個實現是單線程的。 所有操作都發送到負責執行這些操作的工作線程。 此方法稱為線程約束 。 對象創建和測試操作處于阻塞狀態,而查詢執行本身是非阻塞的。

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).

這種方法有問題,因為操作是一個接一個地完成的。 最重要的是,如上所述,有幾項操作正在阻塞。 在某些情況和用例中工作時,存在各種高延遲情況(例如,例如此處 )。

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 。 這是上述單線程方法解決問題的一種解決方法。 分區池創建多個SingleThreadedObjectPools ,每個都有其自己的工作程序。 當請求連接時,將通過線程ID上的模數來選擇一個池。 分區池實際上是池的池;-)

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.

我提到了這是一種解決方法,因為它有其自身的問題:您可能仍在阻塞,但是速率較低-而且它消耗更多的線程和資源。

基于Actor的實現 (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是具有郵箱的實體。 它接收到其郵箱的消息,然后一個接一個地處理它們。 郵箱是一種將事件從外界傳遞給演員的渠道。

A coroutines actor employs lock-free algorithms to allow fast and performant execution of events without the need for locks and synchronized blocks.

協程演員使用無鎖算法來快速,高效地執行事件,而無需鎖和synchronized塊。

You can see an elaborated explanation here.

您可以在此處看到詳細的說明。

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.

在我們的情況下,這些事件將為takegiveBack 。 除此之外,我們還將擁有objectCreated發送給自己的內部消息,例如objectCreated等。這使得objectCreated具有不受并發問題困擾的狀態,因為它始終限于同一順序執行。 另外,傳遞這些事件的通道是使用無鎖算法的隊列,因此它非常高效,避免爭用并且通常具有很高的性能。

There is an excellent video explaining how this was implemented (note that this is “heavy” algorithmic staff):

有一個精彩的視頻解釋了如何實現(請注意,這是“繁重的”算法工作人員):

Let’s recap what we have until now:

讓我們回顧一下到目前為止所擁有的:

  • An actor receives messages and processes them one by one.

    演員接收消息并對其進行逐一處理。
  • Usually messages will contain a CompletableFuture that should be completed when the actor processes it.

    通常,消息將包含CompletableFuture ,當actor處理該消息時應將其完成。

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放在隊列中,并將使用回調機制通知自己何時可以完成原始的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中的消息處理不應被阻塞或延遲actor。 如果發生這種情況,它將延遲所有等待在隊列中處理的消息,并且會減慢整個actor操作的速度。

That’s why, in case we have long running operations inside the actor, we use the callback mechanism.

這就是為什么如果我們在actor中長時間運行操作的原因,我們使用回調機制。

讓我們看一下用例的更多細節 (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東西。 它將帶有回調的消息發送給參與者。 演員將執行以下操作之一:

  • If the object is available — the actor will simply return it.

    如果對象可用-演員將簡單地將其返回。
  • 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.

    如果池尚未通過創建對象的限制,則actor將創建一個新對象,并在對象準備就緒時將其返回。

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.

在這種情況下,對象創建可能會花費一些時間,因此參與者會將回調從對象創建連接到原始的獲取請求回調。

  • 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).

    將請求放入可用對象的隊列中(除非隊列已滿,在這種情況下只會返回錯誤)。

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有人想將對象還給池(釋放它)。 這也可以通過發送給演員的消息來完成。 演員將執行以下操作之一:

  • If someone is waiting on the wait queue — it will borrow the object to it.

    如果有人在等待隊列中等待,它將向其借用該對象。
  • In other cases it will just keep the object on the pool for requests to come, so the object remains idle.

    在其他情況下,它將對象僅保留在池中以等待請求,因此該對象保持空閑狀態。

Test — periodically, someone from outside will notify the actor to test connections:

Test -定期,外部人員會通知參與者測試連接:

  • The actor will release the idle connection that hasn’t been used for a long time (it’s configurable).

    actor將釋放很長時間未使用的空閑連接(它是可配置的)。
  • 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測試其他空閑對象。 它將向工廠發送一個回調并將這些對象標記為In Use ,以防止在測試完成之前借用它們。

  • The actor will check for timeouts in tests and destroy time-outed objects.

    參與者將檢查測試中的超時并破壞超時的對象。

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.

對象池中可能存在各種泄漏。 我希望一些內部錯誤更容易發現和修復,而其他一些則是由于某些用戶錯誤而被拿回但未返回的對象。 在這種情況下,對象可能永遠保留在“ 使用中”隊列中。

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 。 因此,如果用戶失去了連接,當Java的Garbage-Collector清理連接時,它將自動從地圖中刪除。

In addition we added a log message in such cases that says: “LEAK-DETECTED”.

此外,在這種情況下,我們添加了一條日志消息,內容為: “泄漏檢測到”

而已! (That’s it!)

The full Kotlin source code of the object pool is available here:

對象池的完整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異步數據庫驅動程序-jasync-sql / jasync-sql github.com

In an upcoming post I will compare performance metrics of the different implementations.

在下一篇文章中,我將比較不同實現的性能指標。

If you want to read more about Kotlin there is a nice introduction here:

如果您想了解有關Kotlin的更多信息,這里有一個不錯的介紹:

And for coroutines in general check out this video:

對于一般的協程,請觀看以下視頻:

Finally if you want to learn more about Actors implementation using coroutines in Kotlin, then head over here:

最后,如果您想了解有關Kotlin中使用協程的Actor實現的更多信息,請前往此處:

Kotlin/kotlinx.coroutinesLibrary support for Kotlin coroutines . Contribute to Kotlin/kotlinx.coroutines development by creating an account on…github.com

Kotlin / kotlinx.coroutines庫特林 協同程序的庫支持。 通過在 github.com 上創建一個帳戶,為Kotlin / kotlinx.coroutines開發做出貢獻

Thanks for reading! ??

謝謝閱讀! ??

翻譯自: https://www.freecodecamp.org/news/how-to-implement-an-object-pool-with-an-actor-in-kotlin-ed06d3ba6257/

kotlin半生對象

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/393055.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/393055.shtml
英文地址,請注明出處:http://en.pswp.cn/news/393055.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

linux運行apktool簽名,解決Linux中使用ApkTool遇到問題

8種機械鍵盤軸體對比本人程序員&#xff0c;要買一個寫代碼的鍵盤&#xff0c;請問紅軸和茶軸怎么選&#xff1f;遇到問題在Linux中使用IntelliDroid工具時&#xff0c;按要求配置好環境之后&#xff0c;始終無法成功運行該工具內部的ApkTool&#xff0c;導致后續的安卓靜態分析…

python 腳本學習(二)

task1&#xff1a; 在一個文件中&#xff0c;單詞之間使用空格、分號、逗號或者句號分隔&#xff0c;請提取全部單詞。 代碼實例&#xff1a; 1234567891011#!/usr/local/python27/bin/python2.7import sys import re words [] with open(sys.argv[1]) as f: for line in f: #…

2.2 Consumer API官網剖析(博主推薦)

不多說&#xff0c;直接上干貨&#xff01; 一切來源于官網 http://kafka.apache.org/documentation/ 2.2 Consumer API 2.2、消費者API 隨著0.9.0版本&#xff0c;我們已經增加了一個新的Java消費者替換我們現有的基于zookeeper的高級和低級消費者。這個客戶端還是測試版的質量…

leetcode1053. 交換一次的先前排列(貪心算法)

給你一個正整數的數組 A&#xff08;其中的元素不一定完全不同&#xff09;&#xff0c;請你返回可在 一次交換&#xff08;交換兩數字 A[i] 和 A[j] 的位置&#xff09;后得到的、按字典序排列小于 A 的最大可能排列。 如果無法這么操作&#xff0c;就請返回原數組。 示例 1&a…

mybatis-generator-gui如何打包成exe

快速閱讀&#xff1a; ? 用wix和inno setup把mybatis-generator-gui 打包成exe和安裝文件。 以后使用的時候方便&#xff0c;不用每次打開eclipse運行。 使用inno setup 5 和wix 3.11 基于mybatis generator開發一款界面工具, 非常容易及快速生成Mybatis的Java POJO文件及數據…

分步表單如何實現 html_HTML表單入門的分步指南

分步表單如何實現 htmlby Abhishek Jakhar通過阿比舍克賈卡(Abhishek Jakhar) HTML表單入門的分步指南 (A step-by-step guide to getting started with HTML forms) 總覽 (Overview) HTML forms are required when you want to collect some data from the person who visits…

linux網絡服務偶爾失效,判斷linux下的網絡服務是否正常啟動

# 自動判斷samba,http,named,dovecot,tomcat等服務是否正常啟動##作者&#xff1a;胡昌文#時間&#xff1a;2008-09-28#MSN&#xff1a;[email]hucw_rhcehotmail.com[/email]###!/bin/shSAMBA1netstat -nutlp | grep :137 | grep smbdSAMBA2netstat -nutlp | grep :138 | grep …

leetcode809. 情感豐富的文字

有時候人們會用重復寫一些字母來表示額外的感受&#xff0c;比如 “hello” -> “heeellooo”, “hi” -> “hiii”。我們將相鄰字母都相同的一串字符定義為相同字母組&#xff0c;例如&#xff1a;“h”, “eee”, “ll”, “ooo”。 對于一個給定的字符串 S &#xff…

NeHe OpenGL教程 第三十課:碰撞檢測

轉自【翻譯】NeHe OpenGL 教程 前言 聲明&#xff0c;此 NeHe OpenGL教程系列文章由51博客yarin翻譯&#xff08;2010-08-19&#xff09;&#xff0c;本博客為轉載并稍加整理與修改。對NeHe的OpenGL管線教程的編寫&#xff0c;以及yarn的翻譯整理表示感謝。 NeHe OpenGL第三十課…

andorid手機電腦操作

之前一直使用androidscreencast在pc上對手機進行操作,好久都沒用了,前些天再次用的時候,提演示樣例如以下: 決定還是自己寫一個吧,由于7月份要做一個小分享,打算講一些android的東西,須要在電腦上顯示手機這邊的畫面,提供一定的操作. 花了一點時間做好了,給大家截一個圖,代碼放…

struct.error: cannot convert argument to integer解決辦法

更新Python包轉載于:https://www.cnblogs.com/long5683/p/11086768.html

sphinx_Sphinx之謎:如何輕松地編寫代碼

sphinx為什么我在這里&#xff1f; (Why Am I Here?) You, the reader, are here because you wrote some awesome tool in Python, and you want to make it accessible and easy to use.讀者之所以在這里&#xff0c;是因為您使用Python編寫了一些很棒的工具&#xff0c;并且…

linux貪吃蛇c程序,Linux環境下C語言實現貪吃蛇游戲

Linux環境下C語言實現貪吃蛇游戲[liultest snake]$ more snake.c#include #include #include #include #include #define NUM 60struct direct //用來表示方向的{int cx;int cy;};typedef struct node //鏈表的結點{int cx;int cy;struct node *back;struct node *next;}node;v…

Java正則表達式的使用和詳解(上)

1.匹配驗證-驗證Email是否正確 public static void main(String[] args) {// 要驗證的字符串String str "servicexsoftlab.net";// 郵箱驗證規則String regEx "[a-zA-Z_]{1,}[0-9]{0,}(([a-zA-z0-9]-*){1,}\\.){1,3}[a-zA-z\\-]{1,}";// 編譯正則表達式P…

在組策略中使用腳本為域用戶添加網絡打印機

使用腳本為用戶添加網絡打印機 如果你想讓培訓部門的用戶登錄后就能添加網絡打印機&#xff0c;就可以使用登錄腳本來實現。其中DCServer是域控制&#xff0c;MarketPC1是市場部門的計算機&#xff0c;韓立輝用戶是培訓部門的用戶。下面就驗證使用組策略為培訓部門的用戶添加網…

leetcode257. 二叉樹的所有路徑(回溯算法)

給定一個二叉樹&#xff0c;返回所有從根節點到葉子節點的路徑。 說明: 葉子節點是指沒有子節點的節點。 示例: 輸入: 1 / 2 3 5 輸出: [“1->2->5”, “1->3”] 解釋: 所有根節點到葉子節點的路徑為: 1->2->5, 1->3 代碼 /*** Definition for a b…

英特爾神經計算棒_如何設置英特爾Movidius神經計算棒

英特爾神經計算棒by Rishal Hurbans由Rishal Hurbans 如何設置英特爾Movidius神經計算棒 (How to set up the Intel Movidius Neural Compute Stick) In 2017 I was approached by Intel to join their Innovator Programme. After a couple interviews I was inducted as an …

linux 腳本中的push,linux shell之pushd、popd和dirs的使用講解

1 問題我們有時候需要保存多個路徑&#xff0c;上下鍵切換不方便&#xff0c;用cd-只能到上個目錄&#xff0c;我們可以用dirs和pushd和popd2 dirs、pushd、popddirs: 這個命令顯示棧里面所有的路徑&#xff0c;一定會包含當前路徑,常用參數如下dirs -v 顯示棧里面的所有路徑和…

為什么我從 Git Flow 開發模式切換到了 Trunk Based 開發模式?

我已經使用 Git Flow 構建我的 Git 分支有幾年了。但是&#xff0c;我遇到了 Git Flow 的一些問題&#xff0c;其中大部分來自長期存在的分支。解決這些問題的方案就是 Trunk Based Development。這是一個非常簡單的技術&#xff0c;也是有效的持續交付的基礎。在這篇文章中&am…

DedeCMS 提示信息! ----------dede_addonarticle

把數據保存到數據庫附加表 dede_addonarticle 時出錯&#xff0c;請把相關信息提交給DedeCms官方。Duplicate entry ’2532′ for key ‘PRIMARY’出現這種情況其實是你的主鍵是不可重復的&#xff0c;現在重復插入值為2532的主鍵了。可以去掉主鍵唯一&#xff0c;或是設成自增…