導航:首頁 > 編程語言 > 並發編程一致性

並發編程一致性

發布時間:2022-06-16 21:40:27

Ⅰ 如何處理並發java

如果你是負責實現演算法的,其實並不需要考慮所謂的並發性,因為演算法與具體業務是不相乾的,同樣的一個演算法被多次調用並不會引起什麼不良的後果,即使客戶程序丟給你一個被並發訪問的參數,你也不需要考慮它,這是客戶程序應該考慮的事。
我想你不必去費心設計一個單例,單例有它自身的問題和缺陷。這里只需要將類中的方法聲明為public static就應該能滿足要求了。

Ⅱ 大學計算機基礎知識點整理

1、計算機組成原理

如果你不是做操作系統/驅動程序的,直接和硬體打交道的機會很少,因為操作系統已經把他們屏蔽掉了,提供了抽象的API給我們使用。

但是還必須理解馮諾依曼體系的結構,CPU和內存,硬碟,各種外設之間的關系,寄存器、緩存等知識。CPU有哪些指令,如何執行這些指令,如果實現數組,結構體,函數調用,這就涉及到匯編的知識。像原碼,反碼,補碼,定點數、浮點數的表示和運算也是編程中必備的知識,幾乎每種語言都要涉及。

現在很多語言都是在虛擬機上運行的,你只要是了解了計算機的組成原理,再去看哪些虛擬機,就會發現概念都是相通的。另外CPU中的緩存,緩存一致性協議,DMA的非同步思想都會在應用層中有所體現。《編碼》是一個更加科普性,但是也更加有趣的講組成原理的書。

2、操作系統

操作系統是比較枯燥的,站在應用層的角度,我認為重點是掌握操作系統對外提供的抽象,包括進程、線程,文件,虛擬內存,以及進程間的通信問題。

幾乎所有的編程語言都會涉及到對多進程或者多線程編程的支持,特別是多線程的並發編程,所以必須得搞明白他們的本質是什麼,線程都有哪些實現方式。得真正地體會到「進程是資源分配的最小單位,線程是調度的最小單位。」這句話的含義。

幾乎所有的編程語言都會涉及到鎖和死鎖,最好在最底層理解鎖是怎麼實現的。需要理解虛擬內存和物理內存直接的關系,分段和分頁,文件系統的基本原理。對於進程的調度,頁面分配/置換演算法,磁碟的調度演算法,I/O系統,我認為優先順序比較低。

3、資料庫

這個和日常工作結合極其緊密,不用我再多說,包括最基本的SQL,各種範式,事務及其隔離級別,事務的實現方式,索引及其實現方式,B+樹等等。

4、編譯原理

你一輩子也許都不會去寫一個編譯器,但是很有可能會利用現成的工具去生成/操作一個抽象語法樹(AST),甚至可以會寫一個DSL(領域特定語言)。所以你得理解詞法分析、語法分析、語義分析,中間代碼生成,代碼優化這個基本編譯的過程。

(2)並發編程一致性擴展閱讀

Cache的原理

如果存在(命中),則直接返回該數據;如果不存在(失效),再去訪問內存——先把內存中的相應數據載入緩存,再將其返回處理器。

提供「高速緩存」的目的是讓數據訪問的速度適應CPU的處理速度,通過減少訪問內存的次數來提高數據存取的速度。

Cache技術所依賴的原理是」程序執行與數據訪問的局部性原理「,這種局部性表現在兩個方面:時間局部性:如果程序中的某條指令一旦執行,不久以後該指令可能再次執行,如果某數據被訪問過,不久以後該數據可能再次被訪問。

Ⅲ 高並發下,資料庫成最大問題怎麼辦

一、資料庫結構的設計

為了保證資料庫的一致性和完整性,在邏輯設計的時候往往會設計過多的表間關聯,盡可能的降低數據的冗餘。(例如用戶表的地區,我們可以把地區另外存放到一個地區表中)如果數據冗餘低,數據的完整性容易得到保證,提高了數據吞吐速度,保證了數據的完整性,清楚地表達數據元素之間的關系。不要用自增屬性欄位作為主鍵與子表關聯。不便於系統的遷移和數據恢復。對外統計系統映射關系丟失。
表的設計具體注意的問題:
1、數據行的長度不要超過8020位元組,如果超過這個長度的話在物理頁中這條數據會佔用兩行從而造成存儲碎片,降低查詢效率。
2、能夠用數字類型的欄位盡量選擇數字類型而不用字元串類型的(電話號碼),這會降低查詢和連接的性能,並會增加存儲開銷。這是因為引擎在處理查詢和連接回逐個比較字元串中每一個字元,而對於數字型而言只需要比較一次就夠了。
3、對於不可變字元類型char和可變字元類型varchar都是8000位元組,char查詢快,但是耗存儲空間,varchar查詢相對慢一些但是節省存儲空間。在設計欄位的時候可以靈活選擇,例如用戶名、密碼等長度變化不大的欄位可以選擇CHAR,對於評論等長度變化大的欄位可以選擇VARCHAR。
4、欄位的長度在最大限度的滿足可能的需要的前提下,應該盡可能的設得短一些,這樣可以提高查詢的效率,而且在建立索引的時候也可以減少資源的消耗。

二、查詢的優化

在數據窗口使用SQL時,盡量把使用的索引放在選擇的首列;演算法的結構盡量簡單;
在查詢時,不要過多地使用通配符如SELECT* FROM T1語句,要用到幾列就選擇幾列如:SELECT COL1,COL2 FROMT1;在可能的情況下盡量限制盡量結果集行數如:SELECT TOP 300 COL1,COL2,COL3 FROMT1,因為某些情況下用戶是不需要那麼多的數據的。
在沒有建索引的情況下,資料庫查找某一條數據,就必須進行全表掃描了,對所有數據進行一次遍歷,查找出符合條件的記錄。在數據量比較小的情況下,也許看不出明顯的差別,但是當數據量大的情況下,這種情況就是極為糟糕的了。
SQL語句在SQL SERVER中是如何執行的,他們擔心自己所寫的SQL語句會被SQLSERVER誤解。比如:
select * from table1 where name='zhangsan' and tID >10000和執行:
select * from table1 where tID > 10000 andname='zhangsan'
一些人不知道以上兩條語句的執行效率是否一樣,因為如果簡單的從語句先後上看,這兩個語句的確是不一樣,如果tID是一個聚合索引,那麼後一句僅僅從表的10000條以後的記錄中查找就行了;而前一句則要先從全表中查找看有幾個name='zhangsan'的,而後再根據限制條件條件tID>10000來提出查詢結果。
事實上,這樣的擔心是不必要的。SQLSERVER中有一個「查詢分析優化器」,它可以計算出where子句中的搜索條件並確定哪個索引能縮小表掃描的搜索空間,也就是說,它能實現自動優化。雖然查詢優化器可以根據where子句自動的進行查詢優化,但有時查詢優化器就會不按照您的本意進行快速查詢。

所以,優化查詢最重要的就是,盡量使語句符合查詢優化器的規則避免全表掃描而使用索引查詢。
具體要注意的:
1.應盡量避免在 where 子句中對欄位進行 null 值判斷,否則將導致引擎放棄使用索引而進行全表掃描,如:

select id from t where num is null

可以在num上設置默認值0,確保表中num列沒有null值,然後這樣查詢:

select id from t where num=0
2.應盡量避免在 where子句中使用!=或<>操作符,否則將引擎放棄使用索引而進行全表掃描。優化器將無法通過索引來確定將要命中的行數,因此需要搜索該表的所有行。
3.應盡量避免在 where 子句中使用 or 來連接條件,否則將導致引擎放棄使用索引而進行全表掃描,如:

select id from t where num=10 or num=20

可以這樣查詢:

select id from t where num=10
union all
select id from t where num=20s
4.in 和 not in 也要慎用,因為IN會使系統無法使用索引,而只能直接搜索表中的數據。如:

select id from t where num in(1,2,3)
對於連續的數值,能用 between 就不要用 in 了:
select id from t where num between 1 and 3
6.必要時強制查詢優化器使用某個索引,如在 where子句中使用參數,也會導致全表掃描。因為SQL只有在運行時才會解析局部變數,但優化程序不能將訪問計劃的選擇推遲到運行時;它必須在編譯時進行選擇。然而,如果在編譯時建立訪問計劃,變數的值還是未知的,因而無法作為索引選擇的輸入項。如下面語句將進行全表掃描:

select id from t where num=@num
可以改為強制查詢使用索引:
select id from t with(index(索引名)) where num=@num
7.應盡量避免在 where 子句中對欄位進行表達式操作,這將導致引擎放棄使用索引而進行全表掃描。如:

SELECT * FROM T1 WHERE F1/2=100
應改為:
SELECT * FROM T1 WHERE F1=100*2
SELECT * FROM RECORD WHERESUBSTRING(CARD_NO,1,4)=』5378』
應改為:
SELECT * FROM RECORD WHERE CARD_NO LIKE 『5378%』
SELECT member_number, first_name, last_name FROMmembers
WHERE DATEDIFF(yy,datofbirth,GETDATE()) >21
應改為:
SELECT member_number, first_name, last_name FROMmembers
WHERE dateofbirth <DATEADD(yy,-21,GETDATE())
即:任何對列的操作都將導致表掃描,它包括資料庫函數、計算表達式等等,查詢時要盡可能將操作移至等號右邊。
8.應盡量避免在where子句中對欄位進行函數操作,這將導致引擎放棄使用索引而進行全表掃描。如:

select id from t wheresubstring(name,1,3)='abc'--name以abc開頭的id
select id from t wheredatediff(day,createdate,'2005-11-30')=0--『2005-11-30』生成的id
應改為:
select id from t where name like 'abc%'
select id from t where createdate>='2005-11-30' andcreatedate<'2005-12-1'
9.不要在 where 子句中的「=」左邊進行函數、算術運算或其他表達式運算,否則系統將可能無法正確使用索引。
10.在使用索引欄位作為條件時,如果該索引是復合索引,那麼必須使用到該索引中的第一個欄位作為條件時才能保證系統使用該索引,否則該索引將不會被使用,並且應盡可能的讓欄位順序與索引順序相一致。
11.很多時候用 exists是一個好的選擇:

elect num from a where num in(select num from b)
用下面的語句替換:
select num from a where exists(select 1 from b where num=a.num)
但是後者的效率顯然要高於前者。因為後者不會產生大量鎖定的表掃描或是索引掃描。
如果你想校驗表裡是否存在某條紀錄,不要用count(*)那樣效率很低,而且浪費伺服器資源。可以用EXISTS代替。如:
IF (SELECT COUNT(*) FROM table_name WHERE column_name ='xxx')
可以寫成:
IF EXISTS (SELECT * FROM table_name WHERE column_name = 'xxx')
12.盡量使用表變數來代替臨時表。如果表變數包含大量數據,請注意索引非常有限(只有主鍵索引)。
13.避免頻繁創建和刪除臨時表,以減少系統表資源的消耗。
14.臨時表並不是不可使用,適當地使用它們可以使某些常式更有效,例如,當需要重復引用大型表或常用表中的某個數據集時。但是,對於一次性事件,最好使用導出表。
15.在新建臨時表時,如果一次性插入數據量很大,那麼可以使用 select into 代替 create table,避免造成大量log ,以提高速度;如果數據量不大,為了緩和系統表的資源,應先create table,然後insert。
16.如果使用到了臨時表,在存儲過程的最後務必將所有的臨時表顯式刪除,先 truncate table ,然後 drop table,這樣可以避免系統表的較長時間鎖定。
17.在所有的存儲過程和觸發器的開始處設置 SET NOCOUNT ON ,在結束時設置 SET NOCOUNT OFF。無需在執行存儲過程和觸發器的每個語句後向客戶端發送 DONE_IN_PROC 消息。
18.盡量避免大事務操作,提高系統並發能力。
19.盡量避免向客戶端返回大數據量,若數據量過大,應該考慮相應需求是否合理。

20.避免使用不兼容的數據類型。例如float和int、char和varchar、binary和varbinary是不兼容的。數據類型的不兼容可能使優化器無法執行一些本來可以進行的優化操作。例如:

SELECT name FROM employee WHERE salary >60000
在這條語句中,如salary欄位是money型的,則優化器很難對其進行優化,因為60000是個整型數。我們應當在編程時將整型轉化成為錢幣型,而不要等到運行時轉化。

23、能用DISTINCT的就不用GROUP BY

SELECT OrderID FROM Details WHERE UnitPrice > 10 GROUP BYOrderID
可改為:
SELECT DISTINCT OrderID FROM Details WHERE UnitPrice > 10
24.能用UNION ALL就不要用UNION

UNION ALL不執行SELECTDISTINCT函數,這樣就會減少很多不必要的資源

35.盡量不要用SELECT INTO語句。

SELECT INOT 語句會導致表鎖定,阻止其他用戶訪問該表。

四、建立高效的索引
創建索引一般有以下兩個目的:維護被索引列的唯一性和提供快速訪問表中數據的策略。
大型資料庫有兩種索引即簇索引和非簇索引,一個沒有簇索引的表是按堆結構存儲數據,所有的數據均添加在表的尾部,而建立了簇索引的表,其數據在物理上會按照簇索引鍵的順序存儲,一個表只允許有一個簇索引,因此,根據B樹結構,可以理解添加任何一種索引均能提高按索引列查詢的速度,但會降低插入、更新、刪除操作的性能,尤其是當填充因子(FillFactor)較大時。所以對索引較多的表進行頻繁的插入、更新、刪除操作,建表和索引時因設置較小的填充因子,以便在各數據頁中留下較多的自由空間,減少頁分割及重新組織的工作。
索引是從資料庫中獲取數據的最高效方式之一。95%的資料庫性能問題都可以採用索引技術得到解決。作為一條規則,我通常對邏輯主鍵使用唯一的成組索引,對系統鍵(作為存儲過程)採用唯一的非成組索引,對任何外鍵列[欄位]採用非成組索引。不過,索引就象是鹽,太多了菜就咸了。你得考慮資料庫的空間有多大,表如何進行訪問,還有這些訪問是否主要用作讀寫。
實際上,您可以把索引理解為一種特殊的目錄。微軟的SQL SERVER提供了兩種索引:聚集索引(clusteredindex,也稱聚類索引、簇集索引)和非聚集索引(nonclusteredindex,也稱非聚類索引、非簇集索引)。
聚集索引和非聚集索引的區別:
其實,我們的漢語字典的正文本身就是一個聚集索引。比如,我們要查「安」字,就會很自然地翻開字典的前幾頁,因為「安」的拼音是「an」,而按照拼音排序漢字的字典是以英文字母「a」開頭並以「z」結尾的,那麼「安」字就自然地排在字典的前部。如果您翻完了所有以「a」開頭的部分仍然找不到這個字,那麼就說明您的字典中沒有這個字;同樣的,如果查「張」字,那您也會將您的字典翻到最後部分,因為「張」的拼音是「zhang」。也就是說,字典的正文部分本身就是一個目錄,您不需要再去查其他目錄來找到您需要找的內容。
我們把這種正文內容本身就是一種按照一定規則排列的目錄稱為「聚集索引」。
如果您認識某個字,您可以快速地從自動中查到這個字。但您也可能會遇到您不認識的字,不知道它的發音,這時候,您就不能按照剛才的方法找到您要查的字,而需要去根據「偏旁部首」查到您要找的字,然後根據這個字後的頁碼直接翻到某頁來找到您要找的字。但您結合「部首目錄」和「檢字表」而查到的字的排序並不是真正的正文的排序方法,比如您查「張」字,我們可以看到在查部首之後的檢字表中「張」的頁碼是672頁,檢字表中「張」的上面是「馳」字,但頁碼卻是63頁,「張」的下面是「弩」字,頁面是390頁。很顯然,這些字並不是真正的分別位於「張」字的上下方,現在您看到的連續的「馳、張、弩」三字實際上就是他們在非聚集索引中的排序,是字典正文中的字在非聚集索引中的映射。我們可以通過這種方式來找到您所需要的字,但它需要兩個過程,先找到目錄中的結果,然後再翻到您所需要的頁碼。

Ⅳ JAVA開發工程師必須懂什麼

java Netty實戰課程java高性能分布式RPC教程課程 免費下載

鏈接:https://pan..com/s/1MpUM62h4nvHnUGMan-R6YA

提取碼:kvvv

Java是一門面向對象的編程語言,不僅吸收了C++語言的各種優點,還摒棄了C++里難以理解的多繼承、指針等概念,因此Java語言具有功能強大和簡單易用兩個特徵。Java語言作為靜態面向對象編程語言的代表,極好地實現了面向對象理論,允許程序員以優雅的思維方式進行復雜的編程

Ⅳ 如何搭建億級並發的系統架構

想設計億萬級高並發架構,你要先知道高並發是什麼?

面對流量高峰,不同的企業是如何通過技術手段解決高並發難題的呢?

0、引言

軟體系統有三個追求:高性能、高並發、高可用,俗稱三高。三者既有區別也有聯系,門門道道很多,全面討論需要三天三夜,本篇討論高並發。

高並發(High Concurrency)。並發是操作系統領域的一個概念,指的是一段時間內多任務流交替執行的現象,後來這個概念被泛化,高並發用來指大流量、高請求的業務情景,比如春運搶票,電商雙十一,秒殺大促等場景。

很多程序員每天忙著搬磚,平時接觸不到高並發,哪天受不了跑去面試,還常常會被面試官犀利的高並發問題直接KO,其實吧,高並發系統也不高深,我保證任何一個智商在線的看過這篇文章後,都能戰勝恐懼,重拾生活的信心。

本文先介紹高並發系統的度量指標,然後講述高並發系統的設計思路,再梳理高並發的關鍵技術,最後結合作者的經驗做一些延伸探討。

1、高並發的度量指標

既然是高並發系統,那並發一定要高,不然就名不副實。並發的指標一般有QPS、TPS、IOPS,這幾個指標都是可歸為系統吞吐率,QPS越高系統能hold住的請求數越多,但光關注這幾個指標不夠,我們還需要關注RT,即響應時間,也就是從發出request到收到response的時延,這個指標跟吞吐往往是此消彼長的,我們追求的是一定時延下的高吞吐。

比如有100萬次請求,99萬次請求都在10毫秒內響應,其他次數10秒才響應,平均時延不高,但時延高的用戶受不了,所以,就有了TP90/TP99指標,這個指標不是求平均,而是把時延從小到大排序,取排名90%/99%的時延,這個指標越大,對慢請求越敏感。

除此之外,有時候,我們也會關注可用性指標,這可歸到穩定性。

一般而言,用戶感知友好的高並發系統,時延應該控制在250毫秒以內。

什麼樣的系統才能稱為高並發?這個不好回答,因為它取決於系統或者業務的類型。不過我可以告訴你一些眾所周知的指標,這樣能幫助你下次在跟人扯淡的時候稍微靠點兒譜,不至於貽笑大方。

通常,資料庫單機每秒也就能抗住幾千這個量級,而做邏輯處理的服務單台每秒抗幾萬、甚至幾十萬都有可能,而消息隊列等中間件單機每秒處理個幾萬沒問題,所以我們經常聽到每秒處理數百萬、數千萬的消息中間件集群,而像阿某的API網關,每日百億請求也有可能。

2、高並發的設計思路

高並發的設計思路有兩個方向:

Ⅵ java工程師需要掌握哪些知識

1、語法:必須比較熟悉,在寫代碼的時候,IDE(Integrated Development Environment,集成開發環境)的編輯器對某一行報錯應該能夠根據報錯信息知道是什麼樣的語法錯誤,並且知道任何修正。

2、命令:必須熟悉JDK(Java Development Kit,Java開發工具箱——JDK 是整個Java的核心,包括了Java運行環境,Java工具和Java基礎的類庫。JDK是學好Java的第一步。)帶的一些常用命令及其常用選項,命令至少需要熟悉:appletviewer、HtmlConverter、jar、 java、javac、javadoc、javap、javaw、native2ascii、serialver,如果這些命令你沒有全部使用過,那麼你對java實際上還很不了解。

3、工具:必須至少熟練使用一種IDE的開發工具,例如Eclipse、Netbeans、JBuilder、Jdeveloper、IDEA、JCreator或者Workshop,包括進行工程管理、常用選項的設置、插件的安裝配置以及進行調試。

4、API(Application Programming Interface,應用程序編程介面):Java的核心API是非常龐大的,但是有一些內容筆者認為是必須熟悉的,否則不可能熟練的運用Java,包括:
◆java.lang包下的80%以上的類的功能的靈活運用。
◆java.util包下的80%以上的類的靈活運用,特別是集合類體系、規則表達式、zip、以及時間、隨機數、屬性、資源和Timer.
◆java.io包下的60%以上的類的使用,理解IO體系的基於管道模型的設計思路以及常用IO類的特性和使用場合。
◆java.math包下的100%的內容。
◆java.net包下的60%以上的內容,對各個類的功能比較熟悉。
◆java.text包下的60%以上的內容,特別是各種格式化類。
◆熟練運用JDBC. 80%、java.security包下40%以上的內容,如果對於安全沒有接觸的話根本就不可能掌握java.
◆AWT的基本內容,包括各種組件事件、監聽器、布局管理器、常用組件、列印。
◆Swing的基本內容,和AWT的要求類似。
◆XML處理,熟悉SAX、DOM以及JDOM的優缺點並且能夠使用其中的一種完成XML的解析及內容處理。

5、測試:Junit測試是程序員測試,即所謂白盒測試。一位合格的Java開發工程師必須熟悉使用junit編寫測試用例完成代碼的自動測試。

6、管理:必須熟悉使用Ant(中文譯為螞蟻,是一種基於Java的build工具。)完成工程管理的常用任務,例如工程編譯、生成javadoc、生成jar、版本控制、自動測試。

7、排錯:應該可以根據異常信息比較快速的定位問題的原因和大致位置。

8、思想:必須掌握OOP(Object Oriented Programming,面向對象編程)的主要要求,這樣使用Java開發的系統才能是真正的Java系統。

9、規范:編寫的代碼必須符合流行的編碼規范,例如類名首字母大寫,成員和方法名首字母小寫,方法名的第一個單詞一般是動詞,包名全部小寫等,這樣程序的可讀性才比較好。

10、博學:掌握J2EE 、Oracle 、WebLogic、Jboss、Spring、Struts、Hibernate 等流行技術,掌握軟體架構設計思想、搜索引擎優化、緩存系統設計、網站負載均衡、系統性能調優等實用技術。

Ⅶ 如何解決:java 線程

Java 多線程並發編程會有許多不同的問題,主要有如下問題的應用:

多線程讀寫共享數據同步問題
並發讀數據,保持各個線程讀取到的數據一致性的問題。

解決方案:
synchronized關鍵字和Lock並發鎖:主要解決多線程共享數據同步問題。
ThreadLocal主要解決多線程中數據因並發產生不一致問題。

Ⅷ 服務化架構的分布式事務問題用什麼方法解決

1. 性能和時延問題

在服務化之前,業務通常都是本地API調用,本地方法調用性能損耗較小。服務化之後,服務提供者和消費者之間採用遠程網路通信,增加了額外的性能損耗:

1) 客戶端需要對消息進行序列化,主要佔用CPU計算資源。

2) 序列化時需要創建二進制數組,耗費JVM堆內存或者堆外內存。

3) 客戶端需要將序列化之後的二進制數組發送給服務端,佔用網路帶寬資源。

4) 服務端讀取到碼流之後,需要將請求數據報反序列化成請求對象,佔用CPU計算資源。

5) 服務端通過反射的方式調用服務提供者實現類,反射本身對性能影響就比較大。

6) 服務端將響應結果序列化,佔用CPU計算資源。

7) 服務端將應答碼流發送給客戶端,佔用網路帶寬資源。

8) 客戶端讀取應答碼流,反序列化成響應消息,佔用CPU資源。

通過分析我們發現,一個簡單的本地方法調用,切換成遠程服務調用之後,額外增加了很多處理流程,不僅佔用大量的系統資源,同時增加了時延。一些復雜
的應用會拆分成多個服務,形成服務調用鏈,如果服務化框架的性能比較差、服務調用時延也比較大,業務服務化之後的性能和時延將無法滿足業務的性能需求。

1.1RPC框架高性能設計

影響RPC框架性能的主要因素有三個。

1) I/O調度模型:同步阻塞I/O(BIO)還是非阻塞I/O(NIO)。

2) 序列化框架的選擇:文本協議、二進制協議或壓縮二進制協議。

3) 線程調度模型:串列調度還是並行調度,鎖競爭還是無鎖化演算法。

1. I/O調度模型

在I/O編程過程中,當需要同時處理多個客戶端接入請求時,可以利用多線程或者I/O多路復用技術進行處理。I/O多路復用技術通過把多個I/O的
阻塞復用到同一個select的阻塞上,從而使得系統在單線程的情況下可以同時處理多個客戶端請求。與傳統的多線程/多進程模型比,I/O多路復用的最大
優勢是系統開銷小,系統不需要創建新的額外進程或者線程,也不需要維護這些進程和線程的運行,降低了系統的維護工作量,節省了系統資源。

JDK1.5_update10版本使用epoll替代了傳統的select/poll,極大地提升了NIO通信的性能,它的工作原理如圖1-1所示。

圖1-1非阻塞I/O工作原理

Netty是一個開源的高性能NIO通信框架:它的I/O線程NioEventLoop由於聚合了多路復用器Selector,可以同時並發處理成
百上千個客戶端Channel。由於讀寫操作都是非阻塞的,這就可以充分提升I/O線程的運行效率,避免由於頻繁I/O阻塞導致的線程掛起。另外,由於
Netty採用了非同步通信模式,一個I/O線程可以並發處理N個客戶端連接和讀寫操作,這從根本上解決了傳統同步阻塞I/O一連接一線程模型,架構的性能、彈性伸縮能力和可靠性都得到了極大的提升。

Netty被精心設計,提供了很多獨特的性能提升特性,使它做到了在各種NIO框架中性能排名第一,它的性能優化措施總結如下。

1) 零拷貝:(1)Netty的接收和發送ByteBuffer採用DIRECT
BUFFERS,使用堆外直接內存進行Socket讀寫,不需要進行位元組緩沖區的二次拷貝。如果使用傳統的堆內存(HEAP
BUFFERS)進行Socket讀寫,JVM會將堆內存Buffer拷貝一份到直接內存中,然後才寫入Socket中。相比於堆外直接內存,消息在發送
過程中多了一次緩沖區的內存拷貝。(2)Netty提供了組合Buffer對象,可以聚合多個ByteBuffer對象,用戶可以像操作一個Buffer
那樣方便地對組合Buffer進行操作,避免了傳統通過內存拷貝的方式將幾個小Buffer合並成一個大的Buffer。(3)Netty的文件傳輸採用
了transferTo方法,它可以直接將文件緩沖區的數據發送到目標Channel,避免了傳統通過循環write方式導致的內存拷貝問題。

2)
內存池:隨著JVM虛擬機和JIT即時編譯技術的發展,對象的分配和回收是個非常輕量級的工作。但是對於緩沖區Buffer,情況卻稍有不同,特別是對於
堆外直接內存的分配和回收,是一件耗時的操作。為了盡量重用緩沖區,Netty提供了基於內存池的緩沖區重用機制。性能測試表明,採用內存池的
ByteBuf相比於朝生夕滅的ByteBuf,性能高23倍左右(性能數據與使用場景強相關)。

3)
無鎖化的串列設計:在大多數場景下,並行多線程處理可以提升系統的並發性能。但是,如果對於共享資源的並發訪問處理不當,會帶來嚴重的鎖競爭,這最終會導
致性能的下降。為了盡可能地避免鎖競爭帶來的性能損耗,可以通過串列化設計,即消息的處理盡可能在同一個線程內完成,期間不進行線程切換,這樣就避免了多
線程競爭和同步鎖。為了盡可能提升性能,Netty採用了串列無鎖化設計,在I/O線程內部進行串列操作,避免多線程競爭導致的性能下降。表面上看,串列
化設計似乎CPU利用率不高,並發程度不夠。但是,通過調整NIO線程池的線程參數,可以同時啟動多個串列化的線程並行運行,這種局部無鎖化的串列線程設
計相比一個隊列-多個工作線程模型性能更優。

4) 高效的並發編程:volatile的大量、正確使用;CAS和原子類的廣泛使用;線程安全容器的使用;通過讀寫鎖提升並發性能。

2. 高性能序列化框架

影響序列化性能的關鍵因素總結如下。

1) 序列化後的碼流大小(網路帶寬的佔用)。

2) 序列化&反序列化的性能(CPU資源佔用)。

3) 是否支持跨語言(異構系統的對接和開發語言切換)。

4) 並發調用的性能表現:穩定性、線性增長、偶現的時延毛刺等。

相比於JSON等文本協議,二進制序列化框架性能更優異,以Java原生序列化和Protobuf二進制序列化為例進行性能測試對比,結果如圖1-2所示。

圖1-2序列化性能測試對比數據

在序列化框架的技術選型中,如無特殊要求,盡量選擇性能更優的二進制序列化框架,碼流是否壓縮,則需要根據通信內容做靈活選擇,對於圖片、音頻、有大量重復內容的文本文件(例如小說)可以採用碼流壓縮,常用的壓縮演算法包括GZip、Zig-Zag等。

3. 高性能的Reactor線程模型

該模型的特點總結如下。

1) 有專門一個NIO線程:Acceptor線程用於監聽服務端,接收客戶端的TCP連接請求。

2) 網路I/O操作:讀、寫等由一個NIO線程池負責,線程池可以採用標準的JDK線程池實現,它包含一個任務隊列和N個可用的線程,由這些NIO線程負責消息的讀取、解碼、編碼和發送。

3) 1個NIO線程可以同時處理N條鏈路,但是1個鏈路只對應1個NIO線程,防止產生並發操作。

由於Reactor模式使用的是非同步非阻塞I/O,所有的I/O操作都不會導致阻塞,理論上一個線程可以獨立處理所有I/O相關的操作,因此在絕大多數場景下,Reactor多線程模型都可以完全滿足業務性能需求。

Reactor線程調度模型的工作原理示意如圖1-3所示。

圖1-3高性能的Reactor線程調度模型

1.2業務最佳實踐

要保證高性能,單依靠分布式服務框架是不夠的,還需要應用的配合,應用服務化高性能實踐總結如下:

1) 能非同步的盡可能使用非同步或者並行服務調用,提升服務的吞吐量,有效降低服務調用時延。

2) 無論是NIO通信框架的線程池還是後端業務線程池,線程參數的配置必須合理。如果採用JDK默認的線程池,最大線程數建議不超過20個。因為JDK的線程池默認採用N個線程爭用1個同步阻塞隊列方式,當線程數過大時,會導致激烈的鎖競爭,此時性能不僅不會提升,反而會下降。

3)
盡量減小要傳輸的碼流大小,提升性能。本地調用時,由於在同一塊堆內存中訪問,參數大小對性能沒有任何影響。跨進程通信時,往往傳遞的是個復雜對象,如果
明確對方只使用其中的某幾個欄位或者某個對象引用,則不要把整個復雜對象都傳遞過去。舉例,對象A持有8個基本類型的欄位,2個復雜對象B和C。如果明確
服務提供者只需要用到A聚合的C對象,則請求參數應該是C,而不是整個對象A。

4) 設置合適的客戶端超時時間,防止業務高峰期因為服務端響應慢導致業務線程等應答時被阻塞,進而引起後續其他服務的消息在隊列中排隊,造成故障擴散。

5) 對於重要的服務,可以單獨部署到獨立的服務線程池中,與其他非核心服務做隔離,保障核心服務的高效運行。

6) 利用Docker等輕量級OS容器部署服務,對服務做物理資源層隔離,避免虛擬化之後導致的超過20%的性能損耗。

7) 設置合理的服務調度優先順序,並根據線上性能監控數據做實時調整。

2. 事務一致性問題

服務化之前,業務採用本地事務,多個本地SQL調用可以用一個大的事務塊封裝起來,如果某一個資料庫操作發生異常,就可以將之前的SQL操作進行回滾,只有所有SQL操作全部成功,才最終提交,這就保證了事務強一致性,如圖2-1所示。

服務化之後,三個資料庫操作可能被拆分到獨立的三個資料庫訪問服務中,此時原來的本地SQL調用演變成了遠程服務調用,事務一致性無法得到保證,如圖2-2所示。

圖2-2服務化之後引入分布式事務問題

假如服務A和服務B調用成功,則A和B的SQL將會被提交,最後執行服務C,它的SQL操作失敗,對於應用1消費者而言,服務A和服務B的相關
SQL操作已經提交,服務C發生了回滾,這就導致事務不一致。從圖2-2可以得知,服務化之後事務不一致主要是由服務分布式部署導致的,因此也被稱為分布
式事務問題。

2.1分布式事務設計方案

通常,分布式事務基於兩階段提交實現,它的工作原理示意圖如圖2-3所示。

圖2-3兩階段提交原理圖

階段1:全局事務管理器向所有事務參與者發送准備請求;事務參與者向全局事務管理器回復自己是否准備就緒。

階段2:全局事務管理器接收到所有事務參與者的回復之後做判斷,如果所有事務參與者都可以提交,則向所有事務提交者發送提交申請,否則進行回滾。事務參與者根據全局事務管理器的指令進行提交或者回滾操作。

分布式事務回滾原理圖如圖2-4所示。

圖2-4分布式事務回滾原理圖

兩階段提交採用的是悲觀鎖策略,由於各個事務參與者需要等待響應最慢的參與者,因此性能比較差。第一個問題是協議本身的成本:整個協議過程是需要加
鎖的,比如鎖住資料庫的某條記錄,且需要持久化大量事務狀態相關的操作日誌。更為麻煩的是,兩階段鎖在出現故障時表現出來的脆弱性,比如兩階段鎖的致命缺
陷:當協調者出現故障,整個事務需要等到協調者恢復後才能繼續執行,如果協調者出現類似磁碟故障等錯誤,該事務將被永久遺棄。

對於分布式服務框架而言,從功能特性上需要支持分布式事務。在實際業務使用過程中,如果能夠通過最終一致性解決問題,則不需要做強一致性;如果能夠避免分布式事務,則盡量在業務層避免使用分布式事務。

2.2分布式事務優化

既然分布式事務有諸多缺點,那麼為什麼我們還在使用呢?有沒有更好的解決方案來改進或者替換呢?如果我們只是針對分布式事務去優化的話,發現其實能改進的空間很小,畢竟瓶頸在分布式事務模型本身。

那我們回到問題的根源:為什麼我們需要分布式事務?因為我們需要各個資源數據保持一致性,但是對於分布式事務提供的強一致性,所有業務場景真的都需
要嗎?大多數業務場景都能容忍短暫的不一致,不同的業務對不一致的容忍時間不同。像銀行轉賬業務,中間有幾分鍾的不一致時間,用戶通常都是可以理解和容忍
的。

在大多數的業務場景中,我們可以使用最終一致性替代傳統的強一致性,盡量避免使用分布式事務。

在實踐中常用的最終一致性方案就是使用帶有事務功能的MQ做中間人角色,它的工作原理如下:在做本地事務之前,先向MQ發送一個prepare消
息,然後執行本地事務,本地事務提交成功的話,向MQ發送一個commit消息,否則發送一個rollback消息,取消之前的消息。MQ只會在收到
commit確認才會將消息投遞出去,所以這樣的形式可以保證在一切正常的情況下,本地事務和MQ可以達到一致性。但是分布式調用存在很多異常場景,諸如
網路超時、VM宕機等。假如系統執行了local_tx()成功之後,還沒來得及將commit消息發送給MQ,或者說發送出去由於網路超時等原因,MQ
沒有收到commit,發生了commit消息丟失,那麼MQ就不會把prepare消息投遞出去。MQ會根據策略去嘗試詢問(回調)發消息的系統
(checkCommit)進行檢查該消息是否應該投遞出去或者丟棄,得到系統的確認之後,MQ會做投遞還是丟棄,這樣就完全保證了MQ和發消息的系統的
一致性,從而保證了接收消息系統的一致性。

3. 研發團隊協作問題

服務化之後,特別是採用微服務架構以後。研發團隊會被拆分成多個服務化小組,例如AWS的Two Pizza Team,每個團隊由2~3名研發負責服務的開發、測試、部署上線、運維和運營等。

隨著服務數的膨脹,研發團隊的增多,跨團隊的協同配合將會成為一個制約研發效率提升的因素。

3.1共用服務注冊中心

為了方便開發測試,經常會在線下共用一個所有服務共享的服務注冊中心,這時,一個正在開發中的服務發布到服務注冊中心,可能會導致一些消費者不可用。

解決方案:可以讓服務提供者開發方,只訂閱服務(開發的服務可能依賴其他服務),而不注冊正在開發的服務,通過直連測試正在開發的服務。

它的工作原理如圖3-1所示。

圖3-1隻訂閱,不發布

3.2直連提供者

在開發和測試環境下,如果公共的服務注冊中心沒有搭建,消費者將無法獲取服務提供者的地址列表,只能做本地單元測試或使用模擬樁測試。

還有一種場景就是在實際測試中,服務提供者往往多實例部署,如果服務提供者存在Bug,就需要做遠程斷點調試,這會帶來兩個問題:

1) 服務提供者多實例部署,遠程調試地址無法確定,調試效率低下。

2) 多個消費者可能共用一套測試聯調環境,斷點調試過程中可能被其他消費者意外打斷。

解決策略:繞過注冊中心,只測試指定服務提供者,這時候可能需要點對點直連,點對點直聯方式將以服務介面為單位,忽略注冊中心的提供者列表。

3.3多團隊進度協同

假如前端Web門戶依賴後台A、B、C和D
4個服務,分別由4個不同的研發團隊負責,門戶要求新特性2周內上線。A和B內部需求優先順序排序將門戶的優先順序排的比較高,可以滿足交付時間點。但是C和
D服務所在團隊由於同時需要開發其他優先順序更高的服務,因此把優先順序排的相對較低,無法滿足2周交付。

在C和D提供版本之前,門戶只能先通過打測試樁的方式完成Mock測試,但是由於並沒有真實的測試過C和D服務,因此需求無法按期交付。

應用依賴的服務越多,特性交付效率就越低下,交付的速度取決於依賴的最遲交付的那個服務。假如Web門戶依賴後台的100個服務,只要1個核心服務沒有按期交付,則整個進度就會延遲。

解決方案:調用鏈可以將應用、服務和中間件之間的依賴關系串接並展示出來,基於調用鏈首入口的交付日期作為輸入,利用依賴管理工具,可以自動計算出調用鏈上各個服務的最遲交付時間點。通過調用鏈分析和標准化的依賴計算工具,可以避免人為需求排序失誤導致的需求延期。

3.4服務降級和Mock測試

在實際項目開發中,由於小組之間、個人開發者之間開發節奏不一致,經常會出現消費者等待依賴的服務提供者提供聯調版本的情況,相互等待會降低項目的研發進度。

解決方案:服務提供者首先將介面定下來並提供給消費者,消費者可以將服務降級同Mock測試結合起來,在Mock測試代碼中實現容錯降級的業務邏輯(業務放通),這樣既完成了Mock測試,又實現了服務降級的業務邏輯開發,一舉兩得。

3.5協同調試問題

在實際項目開發過程中,各研發團隊進度不一致很正常。如果消費者坐等服務提供者按時提供版本,往往會造成人力資源浪費,影響項目進度。

解決方案:分布式服務框架提供Mock樁管理框架,當周邊服務提供者尚未完成開發時,將路由切換到模擬測試模式,自動調用Mock樁;業務集成測試和上線時,則要能夠自動切換到真實的服務提供者上,可以結合服務降級功能實現。

3.6介面前向兼容性

由於線上的Bug修復、內部重構和需求變更,服務提供者會經常修改內部實現,包括但不限於:介面參數變化、參數欄位變化、業務邏輯變化和數據表結構變化。

在實際項目中經常會發生服務提供者修改了介面或者數據結構,但是並沒有及時知會到所有消費者,導致服務調用失敗。

解決方案:

1) 制定並嚴格執行《服務前向兼容性規范》,避免發生不兼容修改或者私自修改不通知周邊的情況。

2) 介面兼容性技術保障:例如Thrift的IDL,支持新增、修改和刪除欄位,欄位定義位置無關性,碼流支持亂序等。

4. 總結

服務化之後,無論是服務化框架,還是業務服務,都面臨諸多挑戰,本章摘取了其中一些比較重要的問題,並給出解決方案和最佳實踐。對於本章節沒有列出的問題,則需要服務框架開發者和使用者在實踐中探索,找出一條適合自己產品的服務化最佳實踐。

Ⅸ 學習編程都學些什麼內容

編程看你學的是哪方面的編程了,是大數據相關的,還是java相關的,還是前端相關的,python的話屬於ai方面找工作的話學歷很看重。

如果是java的話:

閱讀全文

與並發編程一致性相關的資料

熱點內容
php開源留言板 瀏覽:49
新鄉市區疫情怎麼查詢app 瀏覽:158
我的世界伺服器怎麼弄圖 瀏覽:999
vc6的編譯框 瀏覽:198
程序員寫照 瀏覽:539
怎麼退出github伺服器版本 瀏覽:797
雲伺服器sip 瀏覽:910
對稱平衡型壓縮機 瀏覽:953
rust連接什麼伺服器 瀏覽:382
php刪除數組的空元素 瀏覽:74
有什麼古今翻譯的app 瀏覽:54
華為平板里的app熱門推薦怎麼關閉 瀏覽:731
kindle可以看pdf嗎 瀏覽:620
小米文件夾變小 瀏覽:324
為什麼安卓系統不設計橫屏 瀏覽:686
myeclipse編譯文件 瀏覽:586
水果解壓視頻教程 瀏覽:207
單片機控制的大一點的車 瀏覽:640
程序員中的榮譽 瀏覽:272
java的封裝性 瀏覽:387