主頁 > 後端開發 > 微服務設計 10 大反模式和陷阱!

微服務設計 10 大反模式和陷阱!

2020-10-02 11:16:14 後端開發

作者:颯然Hang
rowkey.me/blog/2018/06/02/microservice-pitfall/

O’Reilly的電子書《Microservices AntiPatterns and Pitfalls》講述了在微服務設計實作時十種最常見的反模式和陷阱,本文基于此書,將這十個點列出,

資料驅動遷移反模式(Data-Driven Migration)

如上圖所示,此種反模式的問題在于微服務的粒度沒有最終確定之前就做了資料遷移,如此當不斷的調整服務粒度時,那么資料庫就免不了頻繁遷移,帶來極大的成本,更好的方式如下圖所示:

即先分離功能,資料庫先保持之前的單體,等到服務粒度最終確定之后,再分離資料庫,前后端分離與不分離的本質區別,推薦看下,

超時反模式(The Timeout)

微服務架構是由一系列分離的服務組成的,這些服務之間通過一些遠程協議進行互相之間的通信,其中牽扯到了服務的可用性和回應性問題,如下圖所示:

  • 可用性:服務消費方能夠連接服務方,并可以向其發送請求,

  • 回應性:服務方能夠在消費方期望時間內給予請求回應,

為了防止服務的不可用和無法回應,通常的做法就是設定一個呼叫超時,此種做法表面上看是沒問題的,但是試想一下如下情景:發起一個購買100個商品的請求,請求成功回傳一個確認號,如果當請求超時但是請求在服務端已經成功執行了,此時這個交易實際是完成的,但是消費方沒有拿到確認號,如果重試請求,那么服務方需要一個復雜的機制判斷這是否一次重復提交,

一種解決此問題的方案是設定一個較長的超時時間,如一個服務的通常回應耗時需要2s,最大耗時需要5s,那么超時時間可以設定為10s,但這樣的問題就是如果服務不可用,所有消費方都得等待10s,這個是非常損耗性能的,

解決超時反模式的方案就是使用“斷路器模式”,就類似于房屋中的電源斷路器,當斷路器關閉,電流可以通過,當斷路器打開,那么電流中斷一直到斷路器關閉,斷路器模式就是說當檢測到服務方無法回應時就打開,后續的請求都會被拒絕掉,一旦服務方可回應了,那么斷路器關閉,恢復請求,其作業模式如下圖所示:

斷路器會持續地監測遠程服務,確保其是可回應的,只要服務可回應,那么斷路器會一直關閉,允許請求通過,如果服務突然不可回應,那么斷路器打開,拒絕后續的請求,而后續如果斷路器又檢測到服務恢復了,那么斷路器會自動關閉,請求也就恢復了,此種方案與超時時間相比,最大的優勢就是一旦服務不可回應,那么斷路器模式可以讓請求立刻回傳而不是需要等待一定的時間,

Hystrix的Netflix是此種斷路器模式的一種開源實作,此外,Akka中也包含了一個斷路器實作:Akka CircuitBreaker類,

關于“斷路器模式”的詳細資訊可見:https://martinfowler.com/bliki/CircuitBreaker.html,關注微信公眾號:Java技術堆疊,在后臺回復:設計模式,可以獲取我整理的 N 篇設計模式 教程,都是干貨,

共享反模式(“I Was Taught to Share”)

微服務被普遍認為是一種不共享任何東西的架構,但實際上只能是盡可能地少共享,畢竟在某些層面代碼被多個服務共享也能帶來一定好處,

例如,與單獨部署一套安全服務(認證和授權)其他所有服務都通過遠程訪問此服務相比,把安全相關的功能封裝成jar包(security.jar),然后其他服務都集成此jar包,就能夠避免每次都要發起對安全服務的訪問,從而提高性能和可靠性,但后面的方案帶來的問題就是依賴噩夢:每一個服務都依賴多個自定義的jar包,如此不僅打破了服務之間的邊界背景關系,同時也引入了諸如總體可靠性、變更控制、易測驗性、部署等問題,

在一個使用面向物件編程語言的單體應用中,使用abstract類和介面實作代碼復用和共享是一個良好的實踐,但當從單體切換到微服務架構時,對于很多自定義的共享類和工具類(日期、字串、計算)的處理要考慮到微服務間共享的東西越少越有利于保持服務間的邊界背景關系,從而更利于快速測驗和部署,以下是幾種推薦的方式,也是解決“共享反模式”的方案:

**共享專案
**

將共享的代碼作為一個專案在編譯期與各個服務集成,此種方式便于變更和開發軟體,但是最大的問題在于很難發覺哪一個共享模塊被修改以及修改的原因,也無法確定自己的服務是否需要這些變更,尤其是在服務發布前期發現某一個共享模塊發生了變動的話需要再一次的測驗才能走后續流程,

共享庫

此種方式即將共享的代碼作為類別庫集成到服務中,如此每次共享的庫有改動,服務都需要重新打包、測驗、重啟,但相比起第一種,其有版本標記,能夠更好地控制服務的部署和開發,服務開發者可以自己控制何時將共享庫的改動集成進來,

更進一步的,如果采用此種方案,一定要避免把所有共享的代碼都打包進一個jar包中如common.jar,否則會很難確定何時要把庫的變動集成到服務中,更好的做法是將共享代碼分成幾個單獨背景關系的庫,如:security.jar、dateutils.jar、persistence.jar等,如此會比較容易的確定何時去集成共享庫的變動,

冗余

此種方案違反DRY原則,在每一服務中都冗余一份共享代碼,能夠避免依賴共享也能夠保持邊界背景關系,但是一旦共享的代碼有變動,那么所有服務都需要改動,因此,此種方案適用于共享模塊非常穩定,極小可能變動的情況,

**服務合并
**

當多個服務共享的代碼變動比較頻繁時可以采用此種方案合并成一個服務,如此就避免了多了服務頻繁的測驗和部署,也避免了依賴共享庫,

可達性報告反模式(Reach-in Reporting)

微服務中各個服務以及其相應的資料都是包含在一個單獨的邊界背景關系中的,也就是說資料是隔離到多個資料庫中的,因此,這也會使得收集微服務的各種資料生成報告變得相對困難,一般來說有四種方案解決這個問題,其中,前三種都是從各個微服務中拉取資料,是這里所說的反模式,被稱作“Reach-in Reporting”,

資料庫拉取模式

報告服務直接從各個服務的資料庫中拉取資料從而生成各種報告,此種方式簡單迅速,但是會讓報告服務和業務服務相互依賴,是一種資料庫共享集成風格(通過共享的資料庫將多個應用耦合在一起),如此一旦資料庫有改動,所有相關服務都要改動,也就打破了微服務中極為重要的邊界背景關系,

HTTP拉取模式

與資料庫拉取模式相比,此種方式不再是直接去訪問服務的資料庫,而是通過HTTP介面去請求服務的資料,此種方式能夠保持服務的邊界背景關系,但是性能比較慢,而且HTTP請求無法很好的承載大資料,

批量拉取模式

此種方式會有一個單獨的報告資料庫/資料倉庫來存盤各個服務的聚合資料,會通過一個批量任務(離線或者基于增量實時)將服務更新的資料匯入到報告資料庫/資料倉庫中,與資料庫拉取模式一樣,此種方式這也是一種資料庫共享集成風格,會打破服務的邊界背景關系,

異步事件推送模式

此種方式即解決“Reach-in Reporting”反模式的方案,每個服務都把自己的發生的事件異步推送到一個資料捕獲服務,后續資料捕獲服務會將資料決議存盤到報告資料庫中,此種方式實作起來較復雜,需要在服務和資料捕獲服務之間制定一種協議用于異步傳輸事件資料,但其能夠保持服務的邊界背景關系,同時也能保證資料的時效性,

沙粒陷阱(Grains of Sand)

微服務實作中最有挑戰的問題在于如何拆分service,如何控制服務的粒度,而正確的服務粒度則決定了微服務是否能夠成功實作,服務粒度也能夠影響到性能、健壯性、可靠性、易測驗性、部署等,

“沙粒陷阱”即把服務拆分的太細,其中的一個原因就是很多時候開發者會把一個class與一個服務等同,合理的,應該是一個服務組件(Service component)對應一個服務,一個服務組件具有清晰、簡潔的角色、職責,具有一組定義好的操作,其一般通過多個模塊(Java Class)實作,如果組件和模塊是一對一的關系,那么不僅僅會造成服務粒度過細同時也是一種不好的編程實踐:服務的實作都是通過一個Class,那么此Class會非常大并且承擔太多的責任,不利于測驗和維護,

更進一步的,服務的粒度并不應該受其中實作類的數目影響:有些服務可能只需要一個類就可以實作,而有些服務會需要多個類來實作,

為了避免“沙粒陷阱”,可以通過以下三種測驗來判斷服務粒度是否合理:

分析服務范圍和功能

要明確服務用來干什么?有哪些操作?一般通過使用檔案或者語言來描述服務的范圍和功能就能夠看出來服務是否做的作業太多,如果在描述中使用了“和”(“and”)或者“此外”(“in addition”)之類的詞,很有可能就是此服務職責太多,

服務的高內聚是一種良好的實踐,其明確一個服務提供的操作之間必須要是有關聯的,如對于一個顧客服務,有以下操作:

  • 添加顧客

  • 更新顧客資訊

  • 獲取顧客資訊

  • 通知顧客

  • 記錄顧客評論

  • 獲取顧客評論

其中的前三個操作都是對顧客的CRUD操作,是相關聯的,而后三者則無關,為了實作服務的高內聚,合理的應該是把此服務拆分成三個服務:顧客維護、顧客通知、顧客評論,

如此,以粗粒度的服務開始,然后逐漸拆分成細粒度的服務有利于對微服務的拆分,

分析資料庫事務

傳統的關系型資料庫都提供了ACID事務特性用于把多個更新操作打包成一個整體提交,要么都成功,要么都失敗,而在微服務中,由于服務都是一個個分離的應用,很難實作ACID,一般實作BASE事務(basic availability、soft state、eventual consistence)即可,但是無法避免的,仍然會有一些場景是需要ACID的,因此,當你不斷的需要在BASE和ACID事務做判斷和取舍的時候,很有可能就是服務粒度過細,

如果業務場景無法接受最終一致性,那么最好就是將服務粒度粗化一些,把多個更新操作放到一個服務中,

分析服務編排

這里主要說的是服務之間的互相通信,由于對服務的呼叫都是一次遠程呼叫,因此服務編排會非常大的影響微應用總體的性能,此外,它也會影響系統整體的健壯性和可靠性,越多的遠程呼叫,那么越高的幾率會有失敗或者超時的請求出現,

如果發現完成一次業務邏輯需要呼叫太多的遠程服務,就說明服務的粒度可能太細了,這時候就需要將服務粗化,而合并細粒度服務還能夠提高性能,提升總體的健壯性和可靠性,同時也減少了多個服務間的依賴,更利于測驗和部署,

此外,使用回應式編程技術異步并行呼叫遠程服務也是一種提升性能和可靠性的方案,

無因的開發者陷阱(Developer Without a Cause)

此陷阱主要講的是開發者或者架構師在做設計時很多時候是拍腦袋在做,沒有任何合理的原因或者原因是錯誤的,也不會做取舍,而想要解決此問題,不僅僅是架構師,開發者也需要同時了解技術帶來的好處以及缺陷,從中做權衡,

了解業務驅動是避免此陷阱的關鍵一步,每一個開發者和架構師都應該清楚的了解下面這些問題的答案:

  • 為什么要使用微服務?

  • 最重要的業務驅動是什么?

  • 架構中的哪一點是最為重要的?

假如易部署性、性能、健壯性、可擴展性是系統最看重的特性,那么對于不同的業務側重點,微服務的粒度需求也是不同的,細粒度的服務能夠達到更好的易測驗性和易部署性,而粗粒度的服務則有更好的性能、健壯性以及可靠性,

追隨流行陷阱(Jump on the Bandwagon)

微服務是目前非常流行的架構理念,越來越多的公司也都在緊跟這個潮流紛紛轉型微服務架構,而不管到底自己是否真的需要,為了避免此陷阱,需要首先了解微服務的優點和缺點,

優點:

  • 易部署:容易部署是微服務的一個很大的優點,畢竟相比起一個龐大的單體應用,一個小并且職責單一的微服務的部署非常簡單并且帶來的風險也會小很多,而持續部署技術則進一步放大了這個優點,

  • 易測驗:職責單一、共享依賴少使得測驗一個微服務是很容易的,而基于微服務做回歸測驗與單體大應用相比也是很容易的, 控制變更:每個服務的范圍和邊界背景關系使得很容易控制服務的功能變動,

  • 模塊化:微服務就是一個高度模塊化的架構風格,這種風格也是一種敏捷方式的表達,能夠很快的回應變化,一個系統模塊化程度越高,就越容易測驗、部署和發布變更,一個服務粒度劃分合理的微服務系統是所有架構中模塊化程度最高的架構形式,

  • 可擴展性:由于每一個服務都是一個職責單一的細粒度服務,因此此種架構風格是所有架構分隔中可擴展性最高的,其非常容易擴展某一個或者某幾個功能從而滿足整體系統的需求,而得益于服務的容器化特性以及各種運維監控工具,服務也能夠自動化進行啟動和關閉,

缺點:

  • 組織變動:微服務需要組織在很多層面進行變動,研發團隊需要包含UI、后端開發、規則處理、資料庫處理建模等多種職位,從而使得一個小的團隊能夠具有實作微服務的所有技術堆疊,同時,傳統的單體、分層應用架構的軟體發布流程也需要更新為自動化、高效的部署流水線,

  • 性能:由于服務都是隔離的,因此發起對服務的遠程呼叫肯定是會影響性能的,服務編排、運行環境都是影響性能的很大因素,了解遠程呼叫的延遲、需要與多少服務通信都是與性能相關的需要掌握的資訊,

  • 可靠性:和性能一樣,服務的遠程呼叫越多,那么失敗的幾率就越高,總體的可靠性就會越低,

  • DevOps:隨著微服務架構而來的是成千上百的服務,手動管理這么多的服務是很不現實的,這就對于自動化運維部署、協作提出了很高的挑戰,需要依賴非常多的操作工具和實踐,是一個非常復雜的作業,目前差不多有12種型別的操作工具(監控工具、服務注冊、發現工具、部署工具等)和框架在微服務架構中被使用,其中每一種又包含了很多具體的工具和產品供選擇,對于這些工具和框架的選擇一般都會需要將近數月的研究、測驗、權衡分析才能做出最適合的技術選型,

了解了微服務的優缺點后,下一步則需要根據實際的業務來分析微服務是不是解決這些問題的最佳方案,可以采取以下問題:

  • 業務和技術的目標是什么?

  • 使用微服務是為了完成什么?

  • 目前和可預知的痛點是什么?

  • 應用的最關鍵的技術特性是什么?(性能、易部署性、易測驗性、可擴展性)

回答這些問題再結合微服務的優缺點能夠讓你明確現在是否是使用微服務的適當時機,

除了微服務以外,還有其他7種比較普遍使用的架構供選擇:

  • 基于服務的架構(Service-Based)

  • 面向服務的架構(Service-Oriented)

  • 分層架構(Layered)

  • 微內核架構(Microkernel)

  • 基于空間的架構(Space-Based)

  • 事件驅動架構(Event-Driven)

  • 流水線架構(Pipeline)

靜態合約陷阱(The Static Contract)

微服務的消費方和服務提供方之間會有一個合約/協議用來規定輸入輸出資料的格式、操作名稱等等,一般情況下這個合約是不變的,但是如果沒有使用版本號來管理服務介面,那么就會進入“靜態合約”陷阱,

給合約打上版本標記不僅僅能夠避免巨大的變動(服務提供方修改合約使得所有消費方也都得修改),還能夠提供向后兼容性,這里有兩種技術可以實作合約的版本號:

在頭部資訊附加版本號

如圖,此種方式即在遠程訪問協議的頭部添加版本資訊,而如果遠程協議使用的是REST,那么還可以使用vendor mime type(vnd)來指定合約的版本號,如下:


POST /trade/buy
Accept: application/vnd.svc.trade.v2+json

服務接受到請求,能夠通過正則等手段簡單決議出其中的合約版本號再根據版本號做相應的處理,

如果使用訊息佇列,那么可以將版本號放置在屬性部分(Property section),JMS的一個例子如下:


String msg = createJSON("acct","12345","sedol","2046251","shares","1000");
jsmContext.createProducer()
  .setProperty("version",2)
  .send(queue,msg);

在合約本身中附加版本號

此種方式版本號獨立于遠程訪問協議,與頭部資訊版本號相比,這也是其最大的優點,但與此同時,其缺點比較多,首先要從請求資訊主體中決議版本號,會出現很多決議的問題,其次,合約的模式可能會非常復雜,使得很難做資料轉換,最后,服務還要引入對模式的驗證邏輯,

我們到了嗎陷阱(Are We There Yet)

微服務架構中,各個服務都是獨立的個體,也就意味著所有客戶端或者API層和服務之間的通信都是一次遠程呼叫,如果對這些遠程呼叫的耗時沒有什么概念,那么就陷入了“Are We There Yet”陷阱,合理的做法需要去測驗遠程訪問的平均延遲、長尾延遲(95%、99%、99.%之外的請求延遲)等指標,而很多時候即使有很好的平均延遲,但是較差的長尾延遲會造成非常大的破壞,

在生產環境或者準生產環境測驗有助于去了解應用的真實性能,例如,一個業務請求需要呼叫四個服務,假設一個服務呼叫的延遲是100毫秒,那么加上業務請求本身的延遲,完成此次業務請求共需要500毫秒的延遲,這和單單從代碼上去看得出的結論是不一樣的,

了解目前所用協議的平均延遲是一方面,另一方面則需要對比其他遠程協議的延遲,從而在合適的地方使用合適的協議,如:JMS、AMQP、MSMQ,

如圖,AMQP協議的性能是最好的,那么結合業務場景,就可以選擇REST作為客戶端與服務間的通信協議,AMQP做為服務之間的通信協議以提高應用的性能,

當然,性能并非在選擇遠程協議時唯一考慮的因素,下一節中就會考慮利用訊息佇列的一些額外功能,

REST使用陷阱(Give It a Rest)

REST現在是微服務中用的最多的通信協議,流行的開發框架如DropWizard、Spring Boot都提供了REST支持,但是如果只選擇REST這一種協議,不去考慮其他諸如訊息佇列的優勢,那么就陷入了“REST使用”陷阱,畢竟異步通信、廣播、合并請求事務這些需求,REST是很難實作的,

訊息佇列標準目前包括平臺特定和平臺無關兩種,前者包括Java平臺中的JMS和C#平臺的MSMQ,后者則是AMQP,對于平臺特定的訊息標準JMS,其規范了API,因此切換broker實作(ActiveMQ、HornetQ)時無需修改API,但由于底層通信協議是不同的,集成的客戶端或者服務端jar包需要隨著修改,

對于平臺無關的訊息標準,其規范了協議實作標準,并沒有規范API,使得不同平臺之間都可以互相通信,而不管實際產品是什么,如一個使用了RabbitMQ的客戶端可以很容易地與一個StormMQ通信(假設使用的協議相同),也就是其獨立于平臺的特性使得RabbitMQ成為微服務架構中最流行的訊息佇列,

異步請求

異步通信是訊息佇列適用的場景之一,服務消費者發起請求后無需等待服務方回應能夠提高總體的性能,同時呼叫方無需擔心呼叫超時,也就無需使用斷路器,從而提高了系統的可靠性,

廣播

將訊息廣播給多個service是訊息佇列的又一個適用場景,一個訊息生產者向多個訊息接受者發送訊息,無需知道誰在接受訊息以及如何處理它,

事務請求

訊息系統提供了對事務訊息的支持:如果多個訊息被發送到了在一個交易背景關系的多個佇列或者主題中時,那么直到訊息發送者commit,服務才會真正的接受到相應的所有訊息(在commit之前會一直保存在佇列中),

因此對于服務消費者需要合并多個遠程請求到一個事務中的場景可以選擇事務訊息,

關注公眾號Java技術堆疊回復"面試"獲取我整理的2020最全面試題及答案,

推薦去我的博客閱讀更多:

1.Java JVM、集合、多執行緒、新特性系列教程

2.Spring MVC、Spring Boot、Spring Cloud 系列教程

3.Maven、Git、Eclipse、Intellij IDEA 系列工具教程

4.Java、后端、架構、阿里巴巴等大廠最新面試題

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

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/148105.html

標籤:Java

上一篇:Java 日期大小對比及日期轉換

下一篇:變數的四種參考

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more