如何構建基於 DDD 領域驅動的微服務?

来源:https://www.cnblogs.com/javastack/archive/2023/01/13/17049160.html
-Advertisement-
Play Games

儘管微服務中的“微”一詞表示服務的規模,但它並不是使用微服務的唯一標準。當團隊轉向基於微服務的架構時,他們旨在提高敏捷性以及自主且頻繁地部署功能。很難確定這種架構風格的簡單定義。我喜歡Adrian Cockcroft的關於微服務的簡短定義: “ 面向服務的體繫結構,它由鬆散耦合的、具有上下文邊界的元 ...


儘管微服務中的“微”一詞表示服務的規模,但它並不是使用微服務的唯一標準。當團隊轉向基於微服務的架構時,他們旨在提高敏捷性以及自主且頻繁地部署功能。很難確定這種架構風格的簡單定義。我喜歡Adrian Cockcroft的關於微服務的簡短定義: “ 面向服務的體繫結構,它由鬆散耦合的、具有上下文邊界的元素組成。”

儘管這定義了高級設計啟髮式技術,但微服務架構具有一些獨特的特性,使其有別於以往的面向服務的架構。以下是其中一些特征。這些以及其他一些文檔都有據可查-Martin Fowler的文章和Sam Newman的Building Microservices,僅舉幾例。

  1. 服務具有圍繞業務上下文而不是任意技術上抽象的明確定義的邊界
  2. 通過意圖公開界面隱藏實現細節並公開功能
  3. 服務不會共用超出其邊界的內部結構。例如,不共用資料庫。
  4. 服務可以抵抗故障。
  5. 團隊獨立擁有職能,並能夠自主發佈變更
  6. 團隊擁護自動化文化。例如,自動化測試,持續集成和持續交付

簡而言之,我們可以將這種體繫結構樣式總結如下:

松耦合的面向服務的體繫結構,其中每個服務都包含在定義明確的有界上下文中,從而可以快速,頻繁且可靠地交付應用程式。

領域驅動設計和有界上下文

微服務的力量來自明確定義其職責並劃分它們之間的邊界。此處的目的是在邊界內建立高凝聚力,併在邊界外建立低耦合(banq註:高凝聚低耦合)。也就是說,趨於一起改變的事物應該屬於同一事物。就像在許多現實生活中的問題一樣,這說起來容易做起來難,業務在不斷發展,邏輯的假設前提也會隨之改變。因此,重構的能力是設計系統時要考慮的另一關鍵問題。

領域驅動設計(DDD)是關鍵,在我們看來,這是設計微服務時必不可少的工具,無論是打破整體還是實施未開發項目。領域驅動的設計(Eric Evans在他的書中提出)是一組思想、原理和模式,可幫助基於業務領域的基礎模型設計軟體系統。開發人員和領域專家共同合作,以通用的通用語言創建業務模型。然後,他們將這些模型綁定到有意義的系統,併在這些系統與從事這些服務的團隊之間建立協作協議。更重要的是,他們設計了系統之間的概念輪廓或邊界。

微服務設計從這些概念中汲取了靈感,因為所有這些原理都有助於構建可以相互獨立變化和發展的模塊化系統。

在繼續進行之前,讓我們快速瞭解一下DDD的一些基本術語。域驅動設計的完整概述超出了本博客的範圍。我們強烈建議任何嘗試構建微服務的人推薦Eric Evans的書籍。

領域:代表組織的工作。例如它是零售或電子商務。

子域:組織或組織內的業務部門。域由多個子域組成。

無所不在的語言:這是用於表達模型的語言。在下麵的示例中,Item是一個模型,屬於這些子域中每個子域的通用語言。開發人員,產品經理,領域專家和業務涉眾都同意使用相同的語言,併在其工件(代碼,產品文檔等)中使用該語言。

有界上下文:域驅動的設計將有界上下文定義為“單詞或語句能確定其含義的設置”。簡而言之,這意味著模型是有意義的邊界。在上面的示例中,“項目”在每種上下文中的含義不同。在目錄上下文中,項目表示可售產品,而在購物車上下文中,則表示客戶已將其添加到購物車中的項目。在“運輸”上下文中,它表示將要運送給客戶的倉庫物料。這些模型中的每一個都是不同的,並且每個都有不同的含義,並且可能包含不同的屬性。通過將這些模型分離並隔離在它們各自的邊界內,我們可以自由地表達模型而沒有歧義。

註意:必須瞭解子域和有界上下文之間的區別。子域屬於問題空間,即您的企業如何看待問題,而受限上下文屬於解決方案空間,即我們將如何實施問題的解決方案。從理論上講,每個子域可能具有多個有界上下文,儘管我們努力為每個子域提供一個有界上下文。

微服務與有限上下文如何相關

現在,微服務在哪裡適合?可以說每個有界上下文都映射到微服務嗎?是的,我們將明白為什麼。在某些情況下,有界上下文的邊界或輪廓可能很大。

考慮上面的例子。定價綁定上下文具有三個不同的模型-價格,定價項目和折扣,每個模型負責目錄項目的價格,計算項目列表的總價格並分別應用折扣。我們可以創建一個包含以上所有模型的系統,但是它可能會成為一個不合理的大型應用程式。如前所述,每個數據模型都有其不變性和業務規則。隨著時間的流逝,如果我們不小心的話,系統可能會變成一個泥濘的大球,邊界模糊,職責重疊,甚至可能回到我們開始的地方—一個整體。

對系統進行建模的另一種方法是將相關模型分離或分組為單獨的微服務。在DDD中,這些模型(價格,定價項目和折扣)被稱為聚合Aggregates。聚合是組成相關模型的獨立模型。您只能通過已發佈的界面更改聚合的狀態,並且聚合可確保一致性,並且不變數保持良好狀態。

聚合是關聯對象的集群,被視為數據更改的單元。外部引用僅限於AGGREGATE的一個成員,稱為根。一組一致性規則適用於AGGREGATE的邊界。

同樣,沒有必要一定要將每個聚合建模為一個獨特的微服務。圖中的服務(聚合)是一對一關係,但這不一定是規則。在某些情況下,在單個服務中托管多個聚合可能是有意義的,尤其是當我們不完全瞭解業務領域時。需要註意的重要一點是,只能在單個聚合中保證一致性,並且只能通過已發佈的界面修改聚合。任何違反這些規定的行為都有變成大泥球的風險。

上下文映射—精確劃分微服務邊界的一種方法

整體結構通常由不同的模型組成,大多數模型是緊密耦合的-模型可能知道彼此的親密細節,更改一個模型可能會對另一個模型產生副作用,依此類推。分解整體時,確定這些模型(在這種情況下為集合)及其關係至關重要。上下文映射可以幫助我們做到這一點。它們用於標識和定義各種有界上下文和聚合之間的關係。在上面的示例中,有界上下文定義了模型的邊界(價格,折扣等),而上下文映射定義了這些模型之間以及不同有界上下文之間的關係。

上下文映射的完整探索不在本博客的討論範圍之內,但我們將通過一個示例進行說明。下圖顯示了處理電子商務訂單付款的各種應用程式。

  1. 購物車上下文負責訂單的線上授權;訂單上下文流程過帳付款後的付款流程;聯絡中心會處理所有例外情況,例如重試付款和更改用於訂單的付款方式
  2. 為了簡單起見,讓我們假設所有這些上下文都是作為單獨的服務實現的
  3. 所有這些上下文封裝了相同的模型。
  4. 請註意,這些模型在邏輯上是相同的。也就是說,它們都遵循相同的通用域語言-付款方式,授權和結算。只是它們是不同上下文的一部分。

重新定義服務邊界—將聚合映射到正確的上下文

錯誤案例如下圖:

電子商務中所有模型都直接與單個支付聚合的網關上下文(payment gateway context)集成,支付需要保證事務性,但是由於與多個服務集成,支付的事務性就不能通過在各種服務之間強制執行不變性和一致性來實現,(banq註:當然有人就提出分散式事務的概念來在這些不同服務之間實現支付過程的事務性,這其實是在錯誤設計基礎上的偽概念)。

請註意,支付網關中的任何更改都將迫使更改多個服務,並可能更改多個團隊,因為不同的組可以擁有這些上下文。

進行一些調整並使聚合與正確的上下文對齊,我們可以更好地表示這些子域如下圖。發生了很多變化。讓我們回顧一下更改:

通常,整體(單體)或遺留應用程式具有許多聚合,通常具有重疊的邊界。創建這些聚合及其依賴關係的上下文地圖有助於我們瞭解將要從這些整體中擺脫出來的任何新微服務的輪廓。請記住,微服務架構的成功或失敗取決於聚合之間的低耦合以及這些聚合之間的高內聚性。

同樣重要的是要註意,有界上下文本身就是合適的內聚單元。即使上下文具有多個聚合,整個上下文及其聚合也可以組成一個微服務。我們發現這種啟髮式方法對於有些晦澀的領域特別有用-考慮組織正在涉足的新業務領域。您可能對分離的正確邊界沒有足夠的瞭解,並且聚集體的任何過早分解都可能導致昂貴的重構。想象一下,由於數據遷移,不得不將兩個資料庫合併為一個,因為我們偶然發現兩個聚合屬於同一類。但是請確保通過介面將這些聚合充分隔離,以使它們不知道彼此的複雜細節。

事件風暴-識別服務邊界的另一種技術

事件風暴是識別系統中的聚合(以及微服務)的另一種必不可少的技術。這對於破壞整體結構以及設計複雜的微服務生態系統都是有用的工具。我們已經使用這種技術分解了一個複雜的應用程式,並且打算在單獨的博客中介紹Event Storming的經驗。對於此博客的範圍,我們想給出一個快速的高級概述。如果您有興趣進一步探索,請觀看Alberto Brandelloni的視頻。

簡而言之,事件風暴是在應用程式團隊(在我們的情況下為整體)中進行的頭腦風暴,以識別系統中發生的各種領域事件和流程。團隊還確定這些事件影響及其後續影響的彙總或模型。在團隊進行此練習時,他們會確定不同的重疊概念,模棱兩可的領域語言以及相互衝突的業務流程。他們將相關模型分組,重新定義聚合併確定重覆的過程。隨著這些工作的進行,這些集合所屬的有限上下文變得清晰起來。如果所有團隊都在同一個房間(物理或虛擬)中,並開始在Scrum風格的白板上繪製事件,命令和過程的映射,那麼Event Storming研討會將非常有用。在本練習結束時,

  1. 重新定義的聚合列表。這些可能成為新的微服務
  2. 這些微服務之間需要流動的域事件
  3. 直接從其他應用程式或用戶調用的命令

我們在一場Event Storming研討會結束時展示了一個示例板。對於團隊來說,這是一次很棒的協作活動,以就正確的聚合和有限的上下文達成一致。除了進行出色的團隊建設活動外,團隊在本次會議中脫穎而出,對領域,通用語言和精確的服務邊界有著共同的理解。

微服務之間的通信

一個整體在一個流程邊界內托管了多個聚合體。因此,在此邊界內可以管理聚合體的事務一致性,例如,如果客戶下訂單,我們可以減少項目的庫存,並向客戶發送電子郵件,全部都在一次交易事務中。所有操作都會成功,或者全部都會失敗。但是,當我們打破整體並將聚合散佈到不同的環境中時,我們將擁有數十甚至數百個微服務。迄今為止,在整體結構的單個邊界記憶體在的流程現在分佈在多個分散式系統中。要在所有這些分散式系統上實現事務的完整性和一致性非常困難,而且要付出代價-系統的可用性。

微服務也是分散式系統。因此,CAP定理也適用於它們- “分散式系統只能提供三個所需特征中的兩個:一致性,可用性和分區容限(CAP中的“ C”,“ A”和“ P”)。” 在現實世界的系統中,分區容忍度是無法商議的- 網路不可靠,虛擬機可能宕機,區域之間的延遲會變得更糟,等等。

因此,我們可以選擇“可用性”或“一致性”。現在,我們知道在任何現代應用程式中,犧牲可用性也不是一個好主意。

圍繞最終一致性設計應用程式

如果您嘗試跨多個分散式系統構建事務,那麼您將再次陷入困境。變成最糟糕的一種分散式整體事務。如果任何一個系統點不可用,則整個流程將不可用,通常會導致令人沮喪的客戶體驗、失去保障失敗的承諾等。

此外,對一項服務的更改通常可能需要對另一項服務進行更改,從而導致複雜而昂貴的部署。因此,我們最好根據自己的用例來設計應用程式,以容忍一點點的不一致性,以提高可用性。對於上面的示例,我們可以使所有進程非同步,從而最終保持一致。我們可以非同步發送電子郵件,而與其他流程無關。

如果承諾的物品以後在倉庫中不可用,該項目可能被延期訂購,或者我們可以停止接受超過某個閾值的項目的訂單。

有時,您可能會遇到一個場景,該場景可能需要跨越不同流程邊界的兩個聚合中的強ACID樣式事務。這是重新查看這些聚合併將它們合併為一個極好的標誌。在我們開始在不同過程邊界中分解這些聚合之前,事件風暴和上下文映射將有助於及早識別這些依賴性。將兩個微服務合併為一個成本很高,這是我們應該努力避免的事情。

支持事件驅動的架構

微服務可能會在其聚合上發出本質上的變化。這些稱為領域事件,並且對這些更改感興趣的任何服務都可以偵聽這些事件併在其域內採取相應的操作。這種方法避免了任何行為上的耦合:一個域不規定其他域應該做什麼,以及時間耦合-一個過程的成功完成不依賴於同時可用的所有系統。當然,這將意味著系統最終將保持一致。

在上面的示例中,訂單服務發佈了一個事件-訂單已取消。訂閱該事件的其他服務處理其各自的域功能:付款服務退還款項,庫存服務調整項目的庫存,依此類推。要確保此集成的可靠性和彈性的幾點註意事項:

  • 生產者應確保至少生產一次事件。如果這樣做失敗,則應確保存在回退機制以重新觸發事件
  • 消費者應確保以冪等的方式消費事件。如果再次發生同一事件,那麼在用戶端不應有任何副作用。事件也可能不按順序到達。消費者可以使用時間戳記或版本號欄位來保證事件的唯一性。

由於某些用例的性質,不一定總是可以使用基於事件的集成。請查看購物車服務和付款服務之間的集成。這是一個同步集成,因此我們需要註意一些事項。這是行為耦合的一個示例-Cart服務可能從Payment服務中調用REST API,並指示其授權訂單付款,而時間耦合則需要Payment服務用於Cart服務才能接受訂單。這種耦合減少了這些上下文的自治性,並且可能減少了不希望的依賴性。有幾種方法可以避免這種耦合,但是使用所有這些選項,我們將失去向客戶提供即時反饋的能力。

  • 將REST API轉換為基於事件的集成。但是,如果支付服務僅公開REST API,則此選項可能不可用
  • 購物車服務立即接受訂單,並且有一個批處理作業來接管訂單並調用支付服務API
  • 購物車服務會產生一個本地事件,然後調用付款服務API

在失敗和上游依賴項(付款服務)不可用的情況下,將上述內容與重試結合使用可以使設計更具彈性。例如,在發生故障的情況下,可以通過事件或基於批次的重試來備份購物車和付款服務之間的同步集成。這種方法會對客戶體驗產生額外的影響:客戶可能輸入了不正確的付款明細,並且當我們離線處理付款時,我們不會將其線上。否則,收回失敗的付款可能會增加業務成本。但是,很有可能,購物車服務對於支付服務的不可用性或故障具有彈性,其缺點勝於缺點。例如,如果我們無法離線收款,我們可以通知客戶。

避免針對特定消費者數據需求的服務之間的編排

任何面向服務的體繫結構中的反模式之一是,這些服務都可以滿足消費者的特定訪問模式。通常,這發生在消費者團隊與服務團隊緊密合作時。如果團隊正在開發整體應用程式,則他們通常會創建一個跨不同聚合邊界的單一API,從而緊密耦合這些聚合。

讓我們考慮一個例子。說Web中的“訂單詳細信息”頁面,移動應用程式需要在單個頁面上同時顯示訂單的詳細信息和針對該訂單處理的退款的詳細信息。在整體應用程式中,Order GET API(假設它是REST API)一起查詢Orders和Refunds,合併兩個聚合,然後將複合響應發送給調用方。由於聚合屬於相同的過程邊界,因此無需太多開銷即可執行此操作。因此,消費者可以在一個調用中獲得所有必要的數據。

如果訂單和退款是不同上下文的一部分,則數據不再存在於單個微服務或聚合邊界內。為消費者保留相同功能的一種選擇是使訂單服務負責調用退款服務並創建複合響應。此方法引起以下問題:

  1. 訂單服務現在與另一個服務集成在一起,純粹是為了支持需要退款數據和訂單數據的消費者。現在,訂單服務的自治性降低了,因為退款總額中的任何更改都將導致訂單總額中的更改。
  2. 訂單服務具有另一個集成,因此要考慮另一個故障點-如果退款服務出現故障,訂購服務是否仍可以發送部分數據,並且消費者可以正常地故障嗎?
  3. 如果消費者需要更改以從“退款”聚合中獲取更多數據,則現在需要兩個團隊來進行更改
  4. 如果在整個平臺上都遵循這種模式,則可能導致各種域服務之間的依存關係錯綜複雜,所有這些都是因為這些服務滿足了調用者的特定訪問模式。

前端的後端BFF

減輕這種風險的一種方法是讓消費者團隊管理各種域服務之間的編排。畢竟,呼叫者會更好地瞭解訪問模式,並且可以完全控制對這些模式的任何更改。這種方法將域服務與表示層分離開來,使它們專註於核心業務流程。但是,如果Web和移動應用程式開始直接調用不同的服務而不是從整體中調用一個複合API,則可能會導致這些應用程式的性能開銷–通過較低帶寬網路進行多次調用,處理和合併來自不同API的數據,等等。 。

相反,可以使用另一種稱為前端的後端的模式。在這種設計模式下,由消費者(在本例中為Web和移動團隊)創建和管理的後端服務負責跨多個域服務的集成,純粹是為了向客戶提供前端體驗。Web和移動團隊現在可以根據他們的用例設計數據合同。他們甚至可以使用GraphQL而不是REST API來靈活地查詢並準確獲取所需的信息。

重要的是要註意,此服務是由使用者團隊擁有和維護的,而不是由擁有域服務的團隊擁有和維護的。前端團隊現在可以根據自己的需求進行優化-移動應用程式可以請求更小的有效負載,減少來自移動應用程式的呼叫次數等等。查看下麵的業務流程的修訂視圖。

結論

在此博客中,我們觸及了各種概念,策略和設計啟發法,以便在我們進入微服務領域時,尤其是在嘗試將整體式服務拆分為基於域的微服務時,加以考慮。其中許多主題本身就是廣闊的主題,我認為我們沒有做出足夠的公正性來詳細解釋它們,但是我們想介紹一些關鍵主題以及我們採用這些主題的經驗。進一步閱讀(鏈接)部分提供了一些參考資料和一些有用的內容,供任何希望採用此方法的人使用。

原文:https://medium.com/walmartglobaltech/building-domain-driven-microservices-af688aa1b1b8
譯文:jdon.com/54558

近期熱文推薦:

1.1,000+ 道 Java面試題及答案整理(2022最新版)

2.勁爆!Java 協程要來了。。。

3.Spring Boot 2.x 教程,太全了!

4.別再寫滿屏的爆爆爆炸類了,試試裝飾器模式,這才是優雅的方式!!

5.《Java開發手冊(嵩山版)》最新發佈,速速下載!

覺得不錯,別忘了隨手點贊+轉發哦!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 話說又要過年了,現在過年可沒有小時候的味道了,小時候只顧著放鞭炮,現在只顧著各個群里蹲紅包。 但是手動搶肯定沒戲,畢竟手can誰也沒辦法! 那就只能試試能不能通過編程的方式實現自動化搶紅包了! 跟小編一樣財迷的鐵汁們 可以往下滑了👇👇 正文 現在捋一下思路,微信群發紅包的基本情況是:每一次發紅包 ...
  • 前言 之前也用過一些緩存中間件,框架,也想著自己是不是也能用Java寫一個出來,於是就有了這個想法,打算在寫的過程中同步進行總結。 源碼:weloe/Java-Distributed-Cache (github.com) 本篇代碼: Java-Distributed-Cache/src/main/j ...
  • 2023-01-12 一、逆向工程 1、逆向工程 資料庫中表影響程式中代碼(表影響java對象)。 MyBatis Generator:簡稱MGB,是一個專門為MyBatis框架使用定製的代碼生成器,可以快速的根據表生成對應的映射文件,介面,以及bean類。 2、正向工程 應用程式中代碼影響資料庫表 ...
  • 例題1:洛谷 P1775 我們可以設 dp[l][r] 為將區間 [l,r] 區間內的所有石子都合併成一堆時造成的最小代價。 如何求出 dp[l][r] 呢?此時我們可以枚舉一個斷點 k,把 [l,r] 區間分成兩個區間:$[l,k]$ 和 [k+1,r],很明顯,k ∈ [l,r-1] 現在就很容 ...
  • 指針: 什麼是指針?表示數據存儲的地址 語法:數據類型 *指針名 被指針對象 *prt 是值 prt 是地址 int *prt = &xxx,聲明指針並保存地址 //引入頭文件 #include <stdio.h> void main(){ int num = 1; int num2 = 200; ...
  • 1 簡介 Terraform是管理許多平臺的基礎設施的工具,如AWS、GCP和Azure。這篇文章將講解如何通過Terraform來管理GCP Pub/Sub。 創建GCP項目請參考:初始化一個GCP項目並用gcloud訪問操作 2 Terraform創建Pub/Sub 2.1 下載Terrafor ...
  • 之前給大家介紹的Aorm庫,都用上了嗎?這可是迄今為止我見過的,go領域最好用的資料庫操作庫了。本期文章,我們來說Aorm的全對象操作,它可以使你的系統更健壯。 ...
  • 臨近春節,這幾天手頭沒什麼事情,花了點時間,將自己近兩年收集的面試真題,進行了一番深度歸納總結,整理出了這份面試大綱,基本上涵蓋了國內一二線互聯網公司的Java面試題(一、二、三面技術面試)。 我這樣做的唯一目的是希望讓面試題本身有跡可循,不讓小伙伴們在準備面試的時候,不會被埋沒在茫茫題海中,面對眾... ...
一周排行
    -Advertisement-
    Play Games
  • Dapr Outbox 是1.12中的功能。 本文只介紹Dapr Outbox 執行流程,Dapr Outbox基本用法請閱讀官方文檔 。本文中appID=order-processor,topic=orders 本文前提知識:熟悉Dapr狀態管理、Dapr發佈訂閱和Outbox 模式。 Outbo ...
  • 引言 在前幾章我們深度講解了單元測試和集成測試的基礎知識,這一章我們來講解一下代碼覆蓋率,代碼覆蓋率是單元測試運行的度量值,覆蓋率通常以百分比表示,用於衡量代碼被測試覆蓋的程度,幫助開發人員評估測試用例的質量和代碼的健壯性。常見的覆蓋率包括語句覆蓋率(Line Coverage)、分支覆蓋率(Bra ...
  • 前言 本文介紹瞭如何使用S7.NET庫實現對西門子PLC DB塊數據的讀寫,記錄了使用電腦模擬,模擬PLC,自至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1.Windows環境下鏈路層網路訪問的行業標準工具(WinPcap_4_1_3.exe)下載鏈接:http ...
  • 從依賴倒置原則(Dependency Inversion Principle, DIP)到控制反轉(Inversion of Control, IoC)再到依賴註入(Dependency Injection, DI)的演進過程,我們可以理解為一種逐步抽象和解耦的設計思想。這種思想在C#等面向對象的編 ...
  • 關於Python中的私有屬性和私有方法 Python對於類的成員沒有嚴格的訪問控制限制,這與其他面相對對象語言有區別。關於私有屬性和私有方法,有如下要點: 1、通常我們約定,兩個下劃線開頭的屬性是私有的(private)。其他為公共的(public); 2、類內部可以訪問私有屬性(方法); 3、類外 ...
  • C++ 訪問說明符 訪問說明符是 C++ 中控制類成員(屬性和方法)可訪問性的關鍵字。它們用於封裝類數據並保護其免受意外修改或濫用。 三種訪問說明符: public:允許從類外部的任何地方訪問成員。 private:僅允許在類內部訪問成員。 protected:允許在類內部及其派生類中訪問成員。 示 ...
  • 寫這個隨筆說一下C++的static_cast和dynamic_cast用在子類與父類的指針轉換時的一些事宜。首先,【static_cast,dynamic_cast】【父類指針,子類指針】,兩兩一組,共有4種組合:用 static_cast 父類轉子類、用 static_cast 子類轉父類、使用 ...
  • /******************************************************************************************************** * * * 設計雙向鏈表的介面 * * * * Copyright (c) 2023-2 ...
  • 相信接觸過spring做開發的小伙伴們一定使用過@ComponentScan註解 @ComponentScan("com.wangm.lifecycle") public class AppConfig { } @ComponentScan指定basePackage,將包下的類按照一定規則註冊成Be ...
  • 操作系統 :CentOS 7.6_x64 opensips版本: 2.4.9 python版本:2.7.5 python作為腳本語言,使用起來很方便,查了下opensips的文檔,支持使用python腳本寫邏輯代碼。今天整理下CentOS7環境下opensips2.4.9的python模塊筆記及使用 ...