Java面試——2021校招騰訊客戶端開發三面

騰訊面試問題:

Q:   如何保證消息不會丟失?

第一種:生產者弄丟了數據。生產者將數據發送到 RabbitMQ 的時候,可能數據就在半路給搞丟了,因爲網絡問題啥的.

第二種:RabbitMQ 弄丟了數據。MQ還沒有持久化自己掛了

第三種:消費端弄丟了數據。剛消費到,還沒處理,結果進程掛了,比如重啓了。

消息的丟失在分在的MQ中的三個部分:1生產者到MQ中生產者消息有丟失,這個時候MQ採用的消息確認的機制保證消息能夠發送到MQ中confirm機制,2 MQ中本身發生消息的丟失 這個時候採用的是消息的持久化操作。3消息到消費者的時候消息丟失,採用的是消息的手動確認的機制。

Q:如何保證事務的一致性?能具體說說原理嗎?

銀行轉賬的例子,保證數據的最終一致性

Q:在引入XX組件之前,你是怎麼解決這個問題的?

一直同步的操作的。但是當人數註冊太多時候,可能出現註冊失敗的情況。

Q:爲什麼選用這個組件呢?用XX不行嗎?

這個MQ的組件能夠實現異步的操作和解耦。不用這個也行,但是容易出現問題。

Q:表模型?

表模型有一對一  一對多 多對多的關係。

Q:sql是怎麼優化的?

4.1數據的字段和表的優化

1儘量使用TINYINT、SMALLINT、MEDIUM_INT作爲整數類型而非INT,如果非負則加上UNSIGNED

2VARCHAR的長度只分配真正需要的空間

3使用枚舉或整數代替字符串類型

4儘量使用TIMESTAMP而非DATETIME,

5單表不要有太多字段,建議在20以內

6避免使用NULL字段,很難查詢優化且佔用額外索引空間

7用整型來存IP

8將大字段、訪問頻率低的字段拆分到單獨的表中存儲,分離冷熱數據。

9禁止在數據庫中存儲明文密碼。

10表必須有主鍵,推薦使用UNSIGNED自增列作爲主鍵。

11禁止冗餘索引。

12禁止重複索引

13不在低基數列上建立索引,例如“性別”。

14合理使用覆蓋索引減少IO,避免排序。

15用IN代替OR。SQL語句中IN包含的值不應過多,應少於1000個。

16表字符集使用UTF8,必要時可申請使用UTF8MB4字符集。

17建議使用合理的分頁方式以提高分頁效率。

18 SELECT只獲取必要的字段,禁⽌止使用SELECT *

19 採用合適的分庫分表策略。例如千庫十表、十庫百表等。

20 減少與數據庫交互次數,儘量採用批量SQL語句。

21 採用合適的分庫分表策略。例如千庫十表、十庫百表等。

4.2數據的索引優化

索引並不是越多越好,要根據查詢有針對性的創建,考慮在WHERE和ORDER BY命令上涉及的列建立索引,可根據EXPLAIN來查看是否用了索引還是全表掃描。

應避免在WHERE子句中對字段進行NULL值判斷,否則將導致引擎放棄使用索引進行全表掃描。

值分佈很稀少的字段不適合建索引,例如"性別"這種只有兩三個值的字段

字符字段只建前綴索引

字符字段最好不要做主鍵

不用外鍵,由程序保證約束

儘量不用UNIQUE,由程序保證約束

使用多列索引時主意順序和查詢條件保持一致,同時刪除不必要的單列索引

引擎目前廣泛使用的是MyISAM和InnoDB兩種引擎:

MyISAM:MyISAM引擎是MySQL 5.1及之前版本的默認引擎,它的特點是:

1不支持行鎖,讀取時對需要讀到的所有表加鎖,寫入時則對錶加排它鎖

2不支持事務

3不支持外鍵

4不支持崩潰後的安全恢復

5在表有讀取查詢的同時,支持往表中插入新紀錄

6支持BLOB和TEXT的前500個字符索引,支持全文索引

7支持延遲更新索引,極大提升寫入性能

8對於不會進行修改的表,支持壓縮表,極大減少磁盤空間佔用

InnoDB:InnoDB在MySQL 5.5後成爲默認索引,它的特點是:

1支持行鎖,採用MVCC來支持高併發

2支持事務

3支持外鍵

4支持崩潰後的安全恢復

5不支持全文索引 總體來講,MyISAM適合SELECT密集型的表,而InnoDB適合INSERT和UPDATE密集型的表

4.3數據庫的查詢優化

1可通過開啓慢查詢日誌來找出較慢的SQL

2不做列運算:SELECT id WHERE age + 1 = 10,任何對列的操作都將導致表掃描,它包括數據庫教程函數、計算表達式等等,查詢時要儘可能將操作移至等號右邊

3sql語句儘可能簡單:一條sql只能在一個cpu運算;大語句拆小語句,減少鎖時間;一條大sql可以堵死整個庫

4不用`SELECT *``

5OR改寫成IN:OR的效率是n級別,IN的效率是log(n)級別,in的個數建議控制在200以內

不用函數和觸發器,在應用程序實現

應儘量避免在 where子句中使用!=或<>操作符,否則將引擎放棄使用索引而進行全表掃描。

應儘量避免在where子句中使用or來連接條件,將導致引擎放棄使用索引而進行全表掃描

in 和 not in 也要慎用,否則會導致全表掃描

儘量避免大事務操作,提高系統併發能力。

不要寫一些沒有意義的查詢,這類代碼不會返回任何結果集,但是會消耗系統資源的。

4.4數據的讀寫分離

也是目前常用的優化,從庫讀主庫寫,一般不要採用雙主或多主引入很多複雜性,儘量採用文中的其他方案來提高性能。同時目前很多拆分的解決方案同時也兼顧考慮了讀寫分離。

4.5數據庫的分庫分表

表分區:MySQL在5.1版引入的分區是一種簡單的水平拆分,用戶需要在建表的時候加上分區參數,應用是透明的無需修改代碼

水平切用於分表:分庫就是根據業務耦合性,將關聯度低的不同表存儲在不同的數據庫,做法與大系統拆分爲多個小系統類似,按業務分類進行獨立劃分。與“微服務治理”的做法相似,每個微服務使用單獨的一個數據庫。

垂直分表分庫操作:是基於數據庫中的列進行,某個表字段較多,可以新建一張擴展表,將不經常用或者字段長度較大的字段拆出到擴展表中。在字段很多的情況下,通過大表拆小表,更便於開發與維護,也能避免跨頁問題。

分庫分表的策略:

1最簡單的都是可以通過取模的方式進行路由

2 Reange範圍:按照時間id 年份的來劃分

分片原則

能不分就不分,參考單表優化

分片數量儘量少,分片儘量均勻分佈在多個數據結點上,因爲一個查詢SQL跨分片越多,則總體性能越差,雖然要好於所有數據在一個分片的結果,在必要的時候進行擴容,增加分片數量

分片規則需要慎重選擇做好提前規劃,分片規則的選擇,需要考慮數據的增長模式,數據的訪問模式,分片關聯性問題,以及分片擴容問題,最近的分片策略爲範圍分片,枚舉分片,一致性Hash分片,這幾種分片都有利於擴容

儘量不要在一個事務中的SQL跨越多個分片,分佈式事務一直是個不好處理的問題

查詢條件儘量優化,儘量避免Select * 的方式,大量數據結果集下,會消耗大量帶寬和CPU資源,查詢儘量避免返回大量結果集,並且儘量爲頻繁使用的查詢語句建立索引。

通過數據冗餘和表分區賴降低跨庫Join的可能。

這裏特別強調一下分片規則的選擇問題,如果某個表的數據有明顯的時間特徵,比如訂單、交易記錄等,則他們通常比較合適用時間範圍分片,因爲具有時效性的數據,我們往往關注其近期的數據,查詢條件中往往帶有時間字段進行過濾,比較好的方案是,當前活躍的數據,採用跨度比較短的時間段進行分片,而歷史性的數據,則採用比較長的跨度存儲。

總體上來說,分片的選擇是取決於最頻繁的查詢SQL的條件,因爲不帶任何Where語句的查詢SQL,會遍歷所有的分片,性能相對最差,因此這種SQL越多,對系統的影響越大,所以我們要儘量避免這種SQL的產生。

分庫分表線上部署:

1停機遷移方案

2簡單來說,就是在線上系統裏面,之前所有寫庫的地方,增刪改操作,都除了對老庫增刪改,都加上對新庫的增刪改,這就是所謂雙寫,同時寫倆庫,老庫和新庫。

分庫分表存在的問題是:

1跨庫關聯查詢:

字段冗餘:比如我們查詢合同庫的合同表的時候需要關聯客戶庫的客戶表,我們可以直接把一些經常關聯查詢的客戶字段放到合同表,通過這種方式避免跨庫關聯查詢的問題

數據同步:比如商戶系統要查詢產品系統的產品表,我們乾脆在商戶系統創建一張產品表,通過ETL 或者其他方式定時同步產品數據。

全局表(廣播表):比如行名行號信息被很多業務系統用到,如果我們放在覈心繫統,每個系統都要去關聯查詢,這個時候我們可以在所有的數據庫都存儲相同的基礎數據。

ER 表(綁定表):將一下存在邏輯外鍵的一下表格放置在同一個數據庫中。

2分佈式事務

全局事務(比如XA 兩階段提交;應用、事務管理器(TM)、資源管理器(DB))

基於可靠消息服務的分佈式事務

柔性事務TCC

3排序、翻頁、函數計算問題

跨節點多庫進行查詢時,會出現limit 分頁,order by 排序的問題。max、min、sum、count 之類的函數在進行計算的時候,也需要先在每個分片上執行相應的函數,然後將各個分片的結果集進行彙總和再次計算,最終將結果返回

4全局主鍵避重問題:(MySQL 的數據庫裏面字段有一個自增的屬性,Oracle 也有Sequence 序列。如果是一個數據庫,那麼可以保證ID 是不重複的,但是水平分表以後,每個表都按照自己的規律自增,肯定會出現ID 重複的問題,這個時候我們就不能用本地自增的方式了):

利用UUID(Universally Unique Identifier 通用唯一識別碼)

把序號維護在數據庫的一張表中。這張表記錄了全局主鍵的類型、位數、起始值。

雪花算法Snowflake(64bit)

4.6數據的緩存實現

緩存可以發生在這些層次:MySQL內部:在系統調優參數介紹了相關設置

數據訪問層:比如MyBatis針對SQL語句做緩存,而Hibernate可以精確到單個記錄,這裏緩存的對象主要是持久化對象Persistence Object

應用服務層:這裏可以通過編程手段對緩存做到更精準的控制和更多的實現策略,這裏緩存的對象是數據傳輸對象Data Transfer Object

Web層:針對web頁面做緩存

瀏覽器客戶端:用戶端的緩存

直寫式(Write Through):在數據寫入數據庫後,同時更新緩存,維持數據庫與緩存的一致性。這也是當前大多數應用緩存框架如Spring Cache的工作方式。這種實現非常簡單,同步好,但效率一般。

回寫式(Write Back):當有數據要寫入數據庫時,只會更新緩存,然後異步批量的將緩存數據同步到數據庫上。這種實現比較複雜,需要較多的應用邏輯,同時可能會產生數據庫與緩存的不同步,但效率非常高。

4.7數據庫集羣方案

使用的數據庫集羣的方案來提高和優化數據庫。基於HA的機制的mycat的高可用

4.8升級硬件

這個不多說了,根據MySQL是CPU密集型還是I/O密集型,通過提升CPU和內存、使用SSD,都能顯著提升MySQL性能

Q:你認爲什麼時候建立索引?

應該使用索引的情況

1.較頻繁地作爲查詢條件的字段

2.經常用連接(join)的字段

3.經常需要根據範圍進行搜索的字段

4.需要排序的字段

不應該使用索引的情況

唯一性很小的情況(select count(discount(column))/count(column) )

表數據需要頻繁修改

字段不在where語句出現時不要添加索引

數據量少的表不要使用索引

Q:MySQL中連表的方式有哪幾種?特點是什麼?

內連接:AB中相交的數據

左連接:A 中全部,並在B中找到A中所有的符合的數據

右連接:B中全部,並在A中找到B中所有的符合的數據

全連接 :AB 中所有的表的數據的集合

Q:索引會在什麼時候失效?

1.有or必全文索引;

2.複合索引未用左列字段;

3.like以%開頭;

4.需要類型轉換;

5.where中索引列有運算;

6.where中索引列使用了函數;

7.如果mysql覺得全表掃描更快時(數據少);

什麼時沒必要用

1.唯一性差;

2.頻繁更新的字段不用(更新索引消耗);

3.where中不用的字段;

4.索引使用<>時,效果一般;

Q:說說sql語句從輸入到執行的過程?

一、SQL語句執行原理:
第一步:客戶端把語句發給服務器端執行當我們在客戶端執行 select 語句時,客戶端會把這條 SQL 語句發送給服務器端,讓服務器端的進程來處理這語句。
第二步:語句解析當客戶端把 SQL 語句傳送到服務器後,服務器進程會對該語句進行解析。同理,這個解析的工作,也是在服務器端所進行的。雖然這只是一個解析的動作,但是,其會做很多“小動作”。

1. 查詢高速緩存(library cache)。服務器進程在接到客戶端傳送過來的 SQL 語句時,不會直接去數據庫查詢。而是會先在數據庫的高速緩存中去查找,是否存在相同語句的執行計劃。如果在數據高速緩存中,則服務器進程就會直接執行這個 SQL 語句,省去後續的工作。所以,採用高速數據緩存的話,可以提高 SQL 語句的查詢效率。一方面是從內存中讀取數據要比從硬盤中的數據文件中讀取數據效率要高,另一方面,也是因爲這個語句解析的原因。不過這裏要注意一點,這個數據緩存跟有些客戶端軟件的數據緩存是兩碼事。有些客戶端軟件爲了提高查詢效率,會在應用軟件的客戶端設置數據緩存。由於這些數據緩存的存在,可以提高客戶端應用軟件的查詢效率。但是,若其他人在服務器進行了相關的修改,由於應用軟件數據緩存的存在,導致修改的數據不能及時反映到客戶端上。從這也可以看出,應用軟件的數據緩存跟數據庫服務器的高速數據緩存不是一碼事。
2. 語句合法性檢查(data dict cache)。當在高速緩存中找不到對應的 SQL 語句時,則服務器進程就會開始檢查這條語句的合法性。這裏主要是對 SQL 語句的語法進行檢查,看看其是否合乎語法規則。如果服務器進程認爲這條 SQL 語句不符合語法規則的時候,就會把這個錯誤信息,反饋給客戶端。這個語法檢查的過程中,不會對 SQL 語句中所包含的表名、列名等等進行 SQL 他只是語法
上的檢查。
3. 語言含義檢查(data dict cache)。若 SQL 語句符合語法上的定義的話,則服務器進程接下去會對語句中的字段、表等內容進行檢查。看看這些字段、表是否在數據庫中。如果表名與列名不準確的話,則數據庫會就會反饋錯誤信息給客戶端。所以,有時候我們寫 select 語句的時候,若語法與表名或者列名同時寫錯的話,系統是先提示說語法錯誤,等到語法完全正確後,再提示說列名或表名錯誤
4. 獲得對象解析鎖(control structer)。當語法、語義都正確後,系統就會對我們需要查詢的對象加鎖。這主要是爲了保障數據的一致性,防止我們在查詢的過程中,其他用戶對這個對象的結構發生改變。
5. 數據訪問權限的核對(data dict cache)。當語法、語義通過檢查之後,客戶端還不一定能夠取得數據。服務器進程還會檢查,你所連接的用戶是否有這個數據訪問的權限。若你連接上服務器的用戶不具有數據訪問權限的話,則客戶端就不能夠取得這些數據。有時候我們查詢數據的時候,辛辛苦苦地把 SQL 語句寫好、編譯通過,但是,最後系統返回個 “沒有權限訪問數據”的錯誤信息,讓我們氣半死。這在前端應用軟件開發調試的過程中,可能會碰到。所以,要注意這個問題,數據庫服務器進程先檢查語法與語義,然後纔會檢查訪問權限。
6. 確定最佳執行計劃 ?。當語句與語法都沒有問題,權限也匹配的話,服務器進程還是不會直接對數據庫文件進行查詢。服務器進程會根據一定的規則,對這條語句進行優化。不過要注意,這個優化是有限的。一般在應用軟件開發的過程中,需要對數據庫的 sql 語言進行優化,這個優化的作用要大大地大於服務器進程的自我優化。所以,一般在應用軟件開發的時候,數據庫的優化是少不了的。當服務器進程的優化器確定這條查詢語句的最佳執行計劃後,就會將這條 SQL 語句與執行計劃保存到數據高速緩存(library cache)。如此的話,等以後還有這個查詢時,就會省略以上的語法、語義與權限檢查的步驟,而直接執行 SQL 語句,提高 SQL 語句處理效率。
第三步:語句執行
語句解析只是對 SQL 語句的語法進行解析,以確保服務器能夠知道這條語句到底表達的是什麼意思。等到語句解析完成之後,數據庫服務器進程纔會真正的執行這條 SQL 語句。這個語句執行也分兩種情況。
一是若被選擇行所在的數據塊已經被讀取到數據緩衝區的話,則服務器進程會直接把這個數據傳遞給客戶端,而不是從數據庫文件中去查詢數據。若數據不在緩衝區中,則服務器進程將從數據庫文件中查詢相關數據,並把這些數據放入到數據緩衝區中(buffer cache)。
第四步:提取數據
當語句執行完成之後,查詢到的數據還是在服務器進程中,還沒有被傳送到客戶端的用戶進程。所以,在服務器端的進程中,有一個專門負責數據提取的一段代碼。他的作用就是把查詢到的數據結果返回給用戶端進程,從而完成整個查詢動作。從這整個查詢處理過程中,我們在數據庫開發或者應用軟件開發過

程中,需要注意以下幾點:
一是要了解數據庫緩存跟應用軟件緩存是兩碼事情。數據庫緩存只有在數據庫服務器端才存在,在客戶端是不存在的。只有如此,才能夠保證數據庫緩存中的內容跟數據庫文件的內容一致。才能夠根據相關的規則,防止數據髒讀、錯讀的發生。而應用軟件所涉及的數據緩存,由於跟數據庫緩存不是一碼事情,所以,應用軟件的數據緩存雖然可以提高數據的查詢效率,但是,卻打破了數據一致性的要求,有時候會發生髒讀、錯讀等情況的發生。所以,有時候,在應用軟件上有專門一個功能,用來在必要的時候清除數據緩存。不過,這個數據緩存的清除,也只是清除本機上的數據緩存,或者說,只是清除這個應用程序的數據緩存,而不會清除數據庫的數據緩存。
二是絕大部分 SQL 語句都是按照這個處理過程處理的。我們 DBA 或者基於 Oracle 數據庫的開發人員瞭解這些語句的處理過程,對於我們進行涉及到 SQL 語句的開發與調試,是非常有幫助的。有時候,掌握這些處理原則,可以減少我們排錯的時間。特別要注意,數據庫是把數據查詢權限的審查放在語法語義的後面進行檢查的。所以,有時會若光用數據庫的權限控制原則,可能還不能滿足應用軟件權限控制的需要。此時,就需要應用軟件的前臺設置,實現權限管理的要求。而且,有時應用數據庫的權限管理,也有點顯得繁瑣,會增加服務器處理的工作量。因此,對於記錄、字段等的查詢權限控制,大部分程序涉及人員喜歡在應用程序中實現,而不是在數據庫上實現。

DBCC DROPCLEANBUFFERS
從緩衝池中刪除所有清除緩衝區。
DBCC FREEPROCCACHE
從過程緩存中刪除所有元素。
DBCC FREESYSTEMCACHE
從所有緩存中釋放所有未使用的緩存條目
SQL語句中的函數、關鍵字、排序等執行順序:
1. FROM 子句返回初始結果集。
2. WHERE 子句排除不滿足搜索條件的行。
3. GROUP BY 子句將選定的行收集到 GROUP BY 子句中各個唯一值的組中。
4. 選擇列表中指定的聚合函數可以計算各組的彙總值。
5. 此外,HAVING 子句排除不滿足搜索條件的行。
6. 計算所有的表達式;
7. 使用 order by 對結果集進行排序。
8. 查找你要搜索的字段。

Q:爲什麼使用WebSocket協議?有參考過業界其它更好的協議嗎?

websocket與http

WebSocket是HTML5中的協議,支持持久連接;而Http協議不支持持久連接。

首先HTMl5指的是一系列新的API,或者說新規範,新技術。WebSocket是HTML5中新協議、新API.跟HTTP協議基本沒有關係。

Http協議本身只有1.0和1.1,也就是所謂的Keep-alive,把多個Http請求合併爲一個。

Websocket是什麼樣的協議,具體有什麼優點

首先,Websocket是一個持久化的協議,相對於HTTP這種非持久的協議來說

HTTP的生命週期通過 Request 來界定,也就是一個 Request 一個 Response ,那麼在 HTTP1.0 中,這次HTTP請求就結束了。

在HTTP1.1中進行了改進,使得有一個keep-alive,也就是說,在一個HTTP連接中,可以發送多個Request,接收多個Response。但是請記住 Request = Response , 在HTTP中永遠是這樣,也就是說一個request只能有一個response。而且這個response也是被動的,不能主動發起。

Websocket的作用

(1)ajax輪詢

ajax輪詢的原理非常簡單,讓瀏覽器隔個幾秒就發送一次請求,詢問服務器是否有新信息。

(2)long poll(長輪詢)

long poll 其實原理跟 ajax輪詢 差不多,都是採用輪詢的方式,不過採取的是阻塞模型(一直打電話,沒收到就不掛電話),也就是說,客戶端發起連接後,如果沒消息,就一直不返回Response給客戶端(對於PHP有最大執行時間,建議沒消息,執行到一定時間也返回)。直到有消息才返回,返回完之後,客戶端再次建立連接,週而復始。

從上面可以看出其實這兩種方式,都是在不斷地建立HTTP連接,關閉HTTP協議,由於HTTP是非狀態性的,每次都要重新傳輸 identity info (鑑別信息),來告訴服務端你是誰。然後等待服務端處理,可以體現HTTP協議的另外一個特點,被動性。

何爲被動性呢,其實就是,服務端不能主動聯繫客戶端,只能有客戶端發起。從上面很容易看出來,不管怎麼樣,上面這兩種都是非常消耗資源的。

ajax輪詢 需要服務器有很快的處理速度和資源。(速度)long poll 需要有很高的併發,也就是說同時接待客戶的能力。(場地大小)

(3)WebSocket

Websocket解決了HTTP的這幾個難題。首先,被動性,當服務器完成協議升級後(HTTP->Websocket),服務端就可以主動推送信息給客戶端啦。解決了上面同步有延遲的問題。

解決服務器上消耗資源的問題:其實我們所用的程序是要經過兩層代理的,即HTTP協議在Nginx等服務器的解析下,然後再傳送給相應的Handler(php等)來處理。簡單地說,我們有一個非常快速的 接線員(Nginx) ,他負責把問題轉交給相應的 客服(Handler) 。Websocket就解決了這樣一個難題,建立後,可以直接跟接線員建立持久連接,有信息的時候客服想辦法通知接線員,然後接線員在統一轉交給客戶。

由於Websocket只需要一次HTTP握手,所以說整個通訊過程是建立在一次連接/狀態中,也就避免了HTTP的非狀態性,服務端會一直知道你的信息,直到你關閉請求,這樣就解決了接線員要反覆解析HTTP協議,還要查看identity info的信息。

目前唯一的問題是:不兼容低版本的IE

Q:系統的併發量大概有多少?數據量呢?

 

Q:假如明天是活動高峯?QPS預計會翻10倍,你要怎麼做?

 

Q:你剛剛說到的限流算法,它們都有什麼優缺點呢?

1 計數器算法、 歡動窗口算法、漏桶算法、 令牌算法

計數器算法是使用計數器在週期內累加訪問次數,當達到設定的限流值時,觸發限流策略。下一個週期開始時,進行清零,重新計數。此算法在單機還是分佈式環境下實現都非常簡單,使用redis的incr原子自增性和線程安全即可輕鬆實現。

https://img-blog.csdnimg.cn/20190716091143141.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MTg0NjMyMA==,size_16,color_FFFFFF,t_70 這個算法通常用於QPS限流和統計總訪問量,對於秒級以上的時間週期來說,會存在一個非常嚴重的問題,那就是臨界問題,如下圖:

https://img-blog.csdnimg.cn/20190716091413825.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MTg0NjMyMA==,size_16,color_FFFFFF,t_70

假設1min內服務器的負載能力爲100,因此一個週期的訪問量限制在100,然而在第一個週期的最後5秒和下一個週期的開始5秒時間段內,分別湧入100的訪問量,雖然沒有超過每個週期的限制量,但是整體上10秒內已達到200的訪問量,已遠遠超過服務器的負載能力,由此可見,計數器算法方式限流對於週期比較長的限流,存在很大的弊端。

滑動窗口算法是將時間週期分爲N個小週期,分別記錄每個小週期內訪問次數,並且根據時間滑動刪除過期的小週期。

如下圖,假設時間週期爲1min,將1min再分爲2個小週期,統計每個小週期的訪問數量,則可以看到,第一個時間週期內,訪問數量爲75,第二個時間週期內,訪問數量爲100,超過100的訪問則被限流掉了  

https://img-blog.csdnimg.cn/20190716091612718.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MTg0NjMyMA==,size_16,color_FFFFFF,t_70

由此可見,當滑動窗口的格子劃分的越多,那麼滑動窗口的滾動就越平滑,限流的統計就會越精確。此算法可以很好的解決固定窗口算法的臨界問題。

漏桶算法是訪問請求到達時直接放入漏桶,如當前容量已達到上限(限流值),則進行丟棄(觸發限流策略)。漏桶以固定的速率進行釋放訪問請求(即請求通過),直到漏桶爲空。

https://img-blog.csdnimg.cn/20190716090944456.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MTg0NjMyMA==,size_16,color_FFFFFF,t_70

令牌桶算法是程序以r(r=時間週期/限流值)的速度向令牌桶中增加令牌,直到令牌桶滿,請求到達時向令牌桶請求令牌,如獲取到令牌則通過請求,否則觸發限流策略。

https://img-blog.csdnimg.cn/20190716090944463.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MTg0NjMyMA==,size_16,color_FFFFFF,t_70

Q:除了限流還有別的方式嗎?

合法性限流

先看一下第一層限流,也就是合法性限流,首先,什麼是合法性限流?它說的是僅僅限制那些合法的用戶請求能夠抵達到秒殺服務器,而將一些非法的請求全部進行攔截掉。因此這裏就需要注意了,在請求合法性限流以前,就得先知道哪些請求是合法的,哪些是非法的。我舉一些非法的例子,比如在秒殺活動期間,實際參與秒殺活動的用戶可能是人,也可能是機器人,並且還可能存在同一用戶反覆購買同一件商品的行爲,也就是我們說的刷單行爲。那麼顯然機器人和用戶刷單都是一種不合理的行爲,這種行爲會影響到其他正常用戶的購物體驗,因此就屬於不合法的請求。而關於如何限制這些不合法的請求,那麼就得具體問題具體分析和討論了。

比如說,如果非法請求的發起者是機器人,那麼最容易想到的方法就是使用驗證碼,並且驗證碼還有一個作用,它可以拉長用戶的訪問時間。舉個例子,假設某一秒鐘有100萬個用戶同時下單,但如果使用了驗證碼,那麼用戶從輸入驗證碼到整個下單的整個過程就可能需要三秒鐘,也就是說下單量仍然是100萬不變,但下單的總體時間可能從一秒鐘拉長到了三秒,那麼原來需要一秒的時間,現在就需要三秒,原來100萬的請求,現在每秒鐘就只需要處理33萬,因此也可以降低流量的峯值。

再來看一下IP限制,如果通過網絡技術監測到了某個IP的下單頻率,在毫秒級別或者反覆購買同一件商品,那麼就能斷定下單的是機器人或者是不合法的用戶,這樣我們就可以將IP加到黑名單之中,從而減少不合法的流量。還有一種做法是隱藏秒殺的入口地址,他指的是在秒殺開始之前,服務器並不會向外界暴露秒殺服務的地址,當秒殺服務開始之後纔開放地址。接下來我們再看一下第二層限流,也就是負載性限流。

負載限流

先看一下負載限流的理論基礎是什麼,一個是集羣,一個是網絡7層模型,我們在搭建集羣時經常會用到一些工具,比如說Nginx和LVS,這些都可以用於負載限流。假設經過了第1層合法性限流以後,還是有33萬的請求,如果通過集羣搭建了三臺服務器,那麼每臺服務器也就只需要承載11萬個請求量了,這樣也能降低請求的併發量。

但是根據網絡7層模型,Nginx處於第7層,除此以外,在網絡7層模型之中的其他層也可以進行負載,比方說我們在第2層的數據鏈路層,也可以通過MAC地址進行負載,比如我們可以生成一個虛擬MAC,然後將MAC地址映射到其他三個真實的服務器上,同樣的也可以在網絡第3層通過IP進

那麼能否進行級聯負載呢,我們假設當請求到來時,能否先在第2層進行負載,然後再在第3層,之後再在第4層、第7層分別都進行一次負載。如果這樣做在功能上肯定是可以實現的,但這種級聯的做法也會同時增加請求的路徑。每增加一次負載就會增加一個轉發路徑,而每增加一個轉發路徑就可能帶來網絡延遲問題,因此太多的級聯負載也是不推薦的。那麼對於級聯負載常見的做法有哪一些?我認爲單獨的使用Nginx,或者Nginx和LVS來實行二級負載,就已經對於大部分系統足夠了。

剛纔提到的LVS是處於第4層,它是通過網絡端口進行的負載,而Nginx是第7層應用級別的負載,還有我們這裏說的負載都是通過軟件進行的負載,也就是軟負載。除此以外我們還可以購買一些硬件工具進行負載,也就是硬負載,常見的硬負載工具,有F5、Array等。大家可能已經發現了,前兩層限流都是想辦法將請求攔截在抵達服務器之前,但是如果請求已經抵達到了服務器,又該如何進行限流?其實就是我們馬上要講到第3層限流。,也就是服務限流。

服務限流

首先我們可以通過Web 服務器本身進行限流,比方說Tomcat是一款比較熟悉的Web服務器,如果連接Tomcat的數量太多,就可能造成Tomcat的不穩定,該怎麼辦呢?我們可以把Tomcat最大連接數設置爲一個合理的值,比方說我們可以設置Tomcat最大連接數值爲300,如果超過300的連接請求就會被Tomcat無條件拒絕,這樣就可以保證Tomcat穩定性了。再比如我們也可以在服務器的內部,通過編寫一些算法來進行限流。常見的算法比如說令牌桶算法、漏桶算法。對於這些算法,如果你的編寫有些困難,我們也可以直接調用一些類庫裏邊已經存在的API。

除了剛纔講的服務器配置參數以及限流算法以外,我們在服務器之中還可以使用隊列來進行限流。這個說的隊列主要是消息隊列。這裏我們拿一個例子來說,假設每秒鐘有10萬的請求量,並且系統裏邊有A、B、C三個子系統,每秒鐘能夠處理的極限分別是2萬請求、3萬請求和4萬請求。在不使用消息隊列的情況下,如果這10萬請求分別平均分給這三個子系統,那麼每個子系統就需要處理3.3萬的請求。很顯然在每秒鐘之內,系統A只能處以2萬請求,如果接收到了3.3萬請求,就可能導致系統A延遲甚至崩潰的情況,而如果使用消息隊列就可以很好的解決這種問題。消息隊列本質是一種緩衝區,當10萬請求到來時消息隊列可以將這10萬請求臨時存儲,然後三個子系統再分別根據自己的性能,分別去消息隊列中針對性的去拉取特定數量的請求。比方說系統A的極限是2萬,那麼他每次最多就只需要從隊列之中取2萬數據就夠了,這樣就可以避免超額請求對系統造成的壓力的情況了。

除了前面介紹的服務器限流以及隊列限流以外,我們還可以使用第3個服務限流,也就是緩存限流,限流的本質是爲了不斷的削減請求的數量,而緩存的作用是爲了減少用戶請求服務端的數量,因此緩存也可以作爲限流的一個實現方案,但爲了有效的使用緩存進行限流,我們需要先將系統設計成前後端分離或者動靜分離的結構,然後分別的對靜態以及動態緩存進行限流。

先看一下對靜態請求如何進行緩存,當客戶端第1次請求服務端的時候,服務端會將網頁的基本結構代碼顯示給客戶端,比如我們第1次訪問某個網站時,我讓服務器就會將搭建此網站的html、JavaScript腳本等代碼響應給客戶端,那麼客戶端就可以將這些html、JavaScript代碼緩存到客戶端瀏覽器之中。那麼這樣一來,當用戶以後再次訪問這個網站時,就可以直接從本地瀏覽器的緩存中獲取html、JavaScript代碼了。對於html這種體系比較小的代碼,我們可以直接將其緩存的瀏覽器之中,但是如果體積較大的圖片,我們最好將它們緩存的Nginx或者通過Nginx轉發在OSS等雲服務器之中。而如果是視頻等一些體積特別大的靜態資源,也可以叫它緩存在CDN中,利用CDN區域部署、就近訪問的特點,來提高用戶的訪問速度。並且我們知道各個緩存並不是獨立的,也可以相互補充,比如說OSS也可以作爲CDN的回源站點。

接下來再看一下動態緩存,對於動態緩存一般先建議緩存在本地的服務器之中,如果本地服務器的緩存失效,我們再緩存到由Redis組成的遠程集羣之中進行二次的查詢,也就是說我們可以搭建本地緩存以及遠程緩存組成的二級結構進行動態請求的緩存,需要注意的是緩存的級別也並不是越多越好。我們可以在CPU、內存、硬盤、網絡等節點上分別設置緩存,並且每個節點裏邊還可以再次細分出多級緩存來。如果這樣做就必須要考慮多級緩存帶來的一致性問題了,緩衝的級別越多,一致性的問題就越嚴重,而解決這種一致性問題又會增加系統的開發成本以及系統的額外開銷。還要知道的是我們緩存的級別越多,請求在系統內部的跳轉路徑也會越長,而這也就類似於多級負載帶來的問題。

那麼對於大部分項目而言,我們使用靜態緩存加上二級動態緩存已經完全足夠了。總的來說,我們靜態緩存可以將大量的靜態資源緩存在服務器以外的地方,而動態緩存可以很大程度上減少請求抵達數據庫的次數。

最後我們再來看一下監控限流,我們知道CPU、內存、併發量等都是衡量系統穩定性的重要指標,如果他們的使用頻率過高,也可能造成系統的不穩定。因此我們也可以創建一些線程專門用於監控這些指標,比方說我們可以建立一個線程,專門用於監控CPU的利用率,如果CPU利用率達到了極限,就可以臨時性的採取服務降級或拒絕策略。這裏說的服務降級實際上與精兵簡政的思想類似,它指的是當系統資源不足時,我們就可以把查看三個月以前的歷史訂單、歷史評論等一些非核心的服務臨時關閉,從而爲系統節約出一部分的資源來。在採用服務降級或拒絕策略一段時間之後,CPU等資源利用率就會恢復到正常狀態,我們就可以重新接收並處理新的請求了。

這裏介紹了合法性限流、負載性流、服務限流,其中合法性限流可以攔截大量的非法請求,而負載限流可以通過集羣技術抵抗大規模的流量衝擊。服務限流則是通過對服務器的參數配置、限流算法、MQ緩存以及監控等手段進行限流。

Q:Redis都有哪幾種持久化方式?各自有什麼優缺點?

Redis的持久化的方式

redis提供兩種方式進行持久化,一種是RDB持久化(原理是將Reids在內存中的數據庫記錄定時 dump到磁盤上的RDB持久化),另外一種是AOF(append only file)持久化(原理是將Reids的操作日誌以追加的方式寫入文件)

RDB持久化是指在指定的時間間隔內將內存中的數據集快照寫入磁盤,實際操作過程是fork一個子進程,先將數據集寫入臨時文件,寫入成功後,再替換之前的文件,用二進制壓縮存儲

AOF持久化以日誌的形式記錄服務器所處理的每一個寫、刪除操作,查詢操作不會記錄,以文本的方式記錄,可以打開文件看到詳細的操作記錄。

 

AOF

RDB

優點

1AOF 可以更好的保護數據不丟失,一般 AOF 會每隔 1 秒,通過一個後臺線程執行一次fsync操作,最多丟失 1 秒鐘的數據

2AOF 日誌文件以 append-only 模式寫入,所以沒有任何磁盤尋址的開銷,寫入性能非常高,而且文件不容易破損,即使文件尾部破損,也很容易修復。

3AOF 日誌文件即使過大的時候,出現後臺重寫操作,也不會影響客戶端的讀寫.

4AOF 日誌文件的命令通過非常可讀的方式進行記錄,這個特性非常適合做災難性的誤刪除的緊急恢復。

1RDB 會生成多個數據文件,每個數據文件都代表了某一個時刻中 redis 的數據,這種多個數據文件的方式,非常適合做冷備,可以將這種完整的數據文件發送到一些遠程的安全存儲上去

2RDB 對 redis 對外提供的讀寫服務,影響非常小,可以讓 redis 保持高性能

3相對於 AOF 持久化機制來說,直接基於 RDB 數據文件來重啓和恢復 redis 進程,更加快速。

缺點

1對於同一份數據來說,AOF 日誌文件通常比 RDB 數據快照文件更大

2AOF 開啓後,支持的寫 QPS 會比 RDB 支持的寫 QPS 低AOF 這種較爲複雜的基於命令日誌 / merge / 回放的方式,比基於 RDB 每次持久化一份完整的數據快照文件的方式

1如果想要在 redis 故障時,儘可能少的丟失數據,那麼 RDB 沒有 AOF 好

2RDB 每次在 fork 子進程來執行 RDB 快照數據文件生成的時候,如果數據文件特別大,可能會導致對客戶端提供的服務暫停數毫秒,或者甚至數秒。

Q:假設master節點宕機了,你會怎麼進行數據恢復?

採用的是的哨兵機制的做的,能夠在在master中的宕機後,能夠迅速的選擇內用節點作爲主節點。並在這個接下來做數據的同步

1、兩種數據丟失的情況

主備切換的過程,可能會導致數據丟失

(1)異步複製導致的數據丟失

因爲master -> slave的複製是異步的,所以可能有部分數據還沒複製到slave,master就宕機了,此時這些部分數據就丟失了

(2)腦裂導致的數據丟失

腦裂,也就是說,某個master所在機器突然脫離了正常的網絡,跟其他slave機器不能連接,但是實際上master還運行着

此時哨兵可能就會認爲master宕機了,然後開啓選舉,將其他slave切換成了master這個時候,集羣裏就會有兩個master,也就是所謂的腦裂

此時雖然某個slave被切換成了master,但是可能client還沒來得及切換到新的master,還繼續寫向舊master的數據可能也丟失了

因此舊master再次恢復的時候,會被作爲一個slave掛到新的master上去,自己的數據會清空,重新從新的master複製數據

2、解決異步複製和腦裂導致的數據丟失

min-slaves-to-write 1
min-slaves-max-lag 10

要求至少有1個slave,數據複製和同步的延遲不能超過10秒

如果說一旦所有的slave,數據複製和同步的延遲都超過了10秒鐘,那麼這個時候,master就不會再接收任何請求了

上面兩個配置可以減少異步複製和腦裂導致的數據丟失

(1)減少異步複製的數據丟失

有了min-slaves-max-lag這個配置,就可以確保說,一旦slave複製數據和ack延時太長,就認爲可能master宕機後損失的數據太多了,那麼就拒絕寫請求,這樣可以把master宕機時由於部分數據未同步到slave導致的數據丟失降低的可控範圍內

(2)減少腦裂的數據丟失

如果一個master出現了腦裂,跟其他slave丟了連接,那麼上面兩個配置可以確保說,如果不能繼續給指定數量的slave發送數據,而且slave超過10秒沒有給自己ack消息,那麼就直接拒絕客戶端的寫請求這樣腦裂後的舊master就不會接受client的新數據,也就避免了數據丟失上面的配置就確保了,如果跟任何一個slave丟了連接,在10秒後發現沒有slave給自己ack,那麼就拒絕新的寫請求。因此在腦裂場景下,最多就丟失10秒的數據。

那麼對於client,我們可以採取降級措施,將數據暫時寫入本地緩存和磁盤中,在一段時間後重新寫入master來保證數據不丟失;也可以將數據寫入kafka消息隊列,隔一段時間去消費kafka中的數據。

Redis的主從複製:

1、redis的複製功能是支持多個數據庫之間的數據同步。一類是主數據庫(master)一類是從數據庫(slave),主數據庫可以進行讀寫操作,當發生寫操作的時候自動將數據同步到從數據庫,而從數據庫一般是隻讀的,並接收主數據庫同步過來的數據,一個主數據庫可以有多個從數據庫,而一個從數據庫只能有一個主數據庫。

2、通過redis的複製功能可以很好的實現數據庫的讀寫分離,提高服務器的負載能力。主數據庫主要進行寫操作,而從數據庫負責讀操作。

1:當一個從數據庫啓動時,會向主數據庫發送sync命令,

2:主數據庫接收到sync命令後會開始在後臺保存快照(執行rdb操作),並將保存期間接收到的命令緩存起來

3:當快照完成後,redis會將快照文件和所有緩存的命令發送給從數據庫。

4:從數據庫收到後,會載入快照文件並執行收到的緩存的命令。

Q:假設Redis的一個key對應的list數據非常多?你會怎麼解決?(重複問,問到不會爲止)

可見,當存儲量特別大的時候,可以將key進行hash分散處理,可以減少存儲內存。並且當key的數量很大的時候,redis取值性能還是很高的。

Q:除了緩存和限流還有別的方式嗎?

消息中間件 ,合法性限制 分佈式的限流。

Q:讀寫分離會有哪些問題?

1.複製數據延遲
可能會出現 slave 延遲導致讀寫不一致等問題,當然你也可以使用監控偏移量 offset,如果 offset 超出範圍就切換到 master 上,邏輯切換,而具體延遲多少,可以通過 info replication 的 offset 指標進行排查。

2.從節點故障問題
對於從節點的故障問題,需要在客戶端維護一個可用從節點可用列表,當從節點故障時,立刻切換到其他從節點或主節點,redis Cluster 可以解決這個問題

3.配置不一致

主機和從機不同,經常導致主機和從機的配置不同,並帶來問題。

數據丟失:主機和從機有時候會發生配置不一致的情況,例如 maxmemory 不一致,如果主機配置 maxmemory 爲8G,從機 slave 設置爲4G,這個時候是可以用的,而且還不會報錯。但是如果要做高可用,讓從節點變成主節點的時候,就會發現數據已經丟失了,而且無法挽回。

4.規避全量複製
全量複製指的是當 slave 從機斷掉並重啓後,runid 產生變化而導致需要在 master 主機裏拷貝全部數據。這種拷貝全部數據的過程非常耗資源。

全量複製是不可避免的,例如第一次的全量複製是不可避免的,這時我們需要選擇小主節點,且maxmemory 值不要過大,這樣就會比較快。同時選擇在低峯值的時候做全量複製。

造成全量複製的原因
(1)是主從機的運行 runid 不匹配。解釋一下,主節點如果重啓,runid 將會發生變化。如果從節點監控到 runid 不是同一個,它就會認爲你的節點不安全。當發生故障轉移的時候,如果主節點發生故障,那麼從機就會變成主節點。我們會在後面講解哨兵和集羣。

(2)複製緩衝區空間不足,比如默認值1M,可以部分複製。但如果緩存區不夠大的話,首先需要網絡中斷,部分複製就無法滿足。其次需要增大複製緩衝區配置(relbacklogsize),對網絡的緩衝增強。

解決方案
在一些場景下,可能希望對主節點進行重啓,例如主節點內存碎片率過高,或者希望調整一些只能在啓動時調整的參數。如果使用普通的手段重啓主節點,會使得runid發生變化,可能導致不必要的全量複製。

爲了解決這個問題,Redis提供了debug reload的重啓方式:重啓後,主節點的runid和offset都不受影響,避免了全量複製。

(3)當一個主機下面掛了很多個 slave 從機的時候,主機 master 掛了,這時 master 主機重啓後,因爲 runid 發生了變化,所有的 slave 從機都要做一次全量複製。這將引起單節點和單機器的複製風暴,開銷會非常大。

解決方案:
可以採用樹狀結構降低多個從節點對主節點的消耗

從節點採用樹狀樹非常有用,網絡開銷交給位於中間層的從節點,而不必消耗頂層的主節點。但是這種樹狀結構也帶來了運維的複雜性,增加了手動和自動處理故障轉移的難度

(4)單機器的複製風暴
由於 Redis 的單線程架構,通常單臺機器會部署多個 Redis 實例。當一臺機器(machine)上同時部署多個主節點(master)時,如果每個 master 主機只有一臺 slave 從機,那麼當機器宕機以後,會產生大量全量複製。這種情況是非常危險的情況,帶寬馬上會被佔用,會導致不可用。

解決方案:
應該把主節點儘量分散在多臺機器上,避免在單臺機器上部署過多的主節點。
當主節點所在機器故障後提供故障轉移機制,避免機器恢復後進行密集的全量複製

Q:你之前說到的websocket?能具體講講整個鏈路模型是怎麼樣的嗎?

 

Q:講一講TCP連接過程?少一次握手會造成什麼問題?

 

Q:TCP的keepalive瞭解嗎?說一說它和http的keepalive的區別?

 

Q:你這個websocket會不會丟失消息?

 

Q:你要怎麼解決這個消息丟失?能具體說說嗎?

第一種:生產者弄丟了數據。生產者將數據發送到 RabbitMQ 的時候,可能數據就在半路給搞丟了,因爲網絡問題啥的.

第二種:RabbitMQ 弄丟了數據。MQ還沒有持久化自己掛了

第三種:消費端弄丟了數據。剛消費到,還沒處理,結果進程掛了,比如重啓了。

消息的丟失在分在的MQ中的三個部分:1生產者到MQ中生產者消息有丟失,這個時候MQ採用的消息確認的機制保證消息能夠發送到MQ中confirm機制,2 MQ中本身發生消息的丟失 這個時候採用的是消息的持久化操作。3消息到消費者的時候消息丟失,採用的是消息的手動確認的機制。

Q:介紹下HashMap?(後來問了都是HashMap一系列問題,都比較基礎,這裏就不細說了)

Q:講一講線程池?

如果併發的線程數量很多,並且每個線程都是執行一個時間很短的任務就結束了,這樣頻繁創建線程就會大大降低系統的效率,因爲頻繁創建線程和銷燬線程需要時間。

1線程池狀態

在ThreadPoolExecutor中定義了一個volatile變量,另外定義了幾個static final變量表示線程池的各個狀態:

volatile int runState;

static final int RUNNING    = 0;

static final int SHUTDOWN   = 1;

static final int STOP       = 2;

static final int TERMINATED = 3;

runState表示當前線程池的狀態,它是一個volatile變量用來保證線程之間的可見性;

下面的幾個static final變量表示runState可能的幾個取值。

  當創建線程池後,初始時,線程池處於RUNNING狀態;

  如果調用了shutdown()方法,則線程池處於SHUTDOWN狀態,此時線程池不能夠接受新的任務,它會等待所有任務執行完畢;

  如果調用了shutdownNow()方法,則線程池處於STOP狀態,此時線程池不能接受新的任務,並且會去嘗試終止正在執行的任務;

  當線程池處於SHUTDOWN或STOP狀態,並且所有工作線程已經銷燬,任務緩存隊列已經清空或執行結束後,線程池被設置爲TERMINATED狀態。

2任務的執行

ThreadPoolExecutor類中其他的一些比較重要成員變量:

rivate final BlockingQueue<Runnable> workQueue; //任務緩存隊列,用來存放等待執行的任務

private final ReentrantLock mainLock = new ReentrantLock();   //線程池的主要狀態鎖,對線程池狀態(比如線程池大小//、runState等)的改變都要使用這個鎖

private final HashSet<Worker> workers = new HashSet<Worker>();  //用來存放工作集

private volatile long  keepAliveTime;    //線程存貨時間  

private volatile boolean allowCoreThreadTimeOut//是否允許爲核心線程設置存活時間

private volatile int   corePoolSize;    //核心池的大小(即線程池中的線程數目大於這個參數時,提交的任務會被放進任務緩存隊列)

private volatile int   maximumPoolSize;   //線程池最大能容忍的線程數

private volatile int   poolSize;          //線程池中當前的線程數

private volatile RejectedExecutionHandler handler; //任務拒絕策略

private volatile ThreadFactory threadFactory;   //線程工廠,用來創建線程

private int largestPoolSize;   //用來記錄線程池中曾經出現過的最大線程數

private long completedTaskCount;   //用來記錄已經執行完畢的任務個數

1)首先,要清楚corePoolSize和maximumPoolSize的含義;

2)其次,要知道Worker是用來起到什麼作用的;

3)要知道任務提交給線程池之後的處理策略,這裏總結一下主要有4點:

如果當前線程池中的線程數目小於corePoolSize,則每來一個任務,就會創建一個線程去執行這個任務;

如果當前線程池中的線程數目>=corePoolSize,則每來一個任務,會嘗試將其添加到任務緩存隊列當中,若添加成功,則該任務會等待空閒線程將其取出去執行;若添加失敗(一般來說是任務緩存隊列已滿),則會嘗試創建新的線程去執行這個任務

如果當前線程池中的線程數目達到maximumPoolSize,則會採取任務拒絕策略進行處理;

如果線程池中的線程數量大於 corePoolSize時,如果某線程空閒時間超過keepAliveTime,線程將被終止,直至線程池中的線程數目不大於corePoolSize;如果允許爲核心池中的線程設置存活時間,那麼核心池中的線程空閒時間超過keepAliveTime,線程也會被終止。

3線程池中的線程初始化

默認情況下,創建線程池之後,線程池中是沒有線程的,需要提交任務之後纔會創建線程。

4任務緩存隊列及排隊策略

在前面我們多次提到了任務緩存隊列,即workQueue,它用來存放等待執行的任務

5任務拒絕策略

當線程池的任務緩存隊列已滿並且線程池中的線程數目達到maximumPoolSize,如果還有任務到來就會採取任務拒絕策略,通常有以下四種策略:

ThreadPoolExecutor.AbortPolicy:丟棄任務並拋出RejectedExecutionException異常

ThreadPoolExecutor.DiscardPolicy:也是丟棄任務,但是不拋出異常

ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列最前面的任務,然後重新嘗試執行任務(重複此過程)

ThreadPoolExecutor.CallerRunsPolicy:由調用線程處理該任務

6線程池的關閉

ThreadPoolExecutor提供了兩個方法,用於線程池的關閉,分別是shutdown()和shutdownNow(),其中:

    shutdown():不會立即終止線程池,而是要等所有任務緩存隊列中的任務都執行完後才終止,但再也不會接受新的任務

    shutdownNow():立即終止線程池,並嘗試打斷正在執行的任務,並且清空任務緩存隊列,返回尚未執行的任務

7線程池容量的動態調整

ThreadPoolExecutor提供了動態調整線程池容量大小的方法:setCorePoolSize()和setMaximumPoolSize(),

setCorePoolSize:設置核心池大小

setMaximumPoolSize:設置線程池最大能創建的線程數目大小

當上述參數從小變大時,ThreadPoolExecutor進行線程賦值,還可能立即創建新的線程來執行任務。

如果是不採用是這個那就在隊列中的線程是不可能出隊列的,就是如果是的非公平的鎖的話那就永遠不能出隊列。那可能能執行不到該線程。

Q:你在項目哪方面用到線程池?

Q:講一講Java內存模型?

  1. 線程池的使用場景有哪些
    線程池適合單系統的大量的異步任務處理,比如發送短信、保存日誌。

  2. 說說創建線程池的重要參數

    • corePoolSize:線程池的大小。線程池創建之後不會立即去創建線程,而是等待線程的到來。當前執行的線程數大於該值時,線程會加入到緩衝隊列。
    • maximumPoolSize:線程池中創建的最大線程數。
    • keepAliveTime:空閒的線程多久時間後被銷燬。默認情況下,該值在線程數大於corePoolSize時,對超出corePoolSize值的這些線程起作用。
    • unit:TimeUnit枚舉類型的值,代表keepAliveTime時間單位。
    • handler:線程拒絕策略。
  3. 這些參數怎麼設置,線程池調優怎麼做

  • 基本思想:
    • 併發高、業務執行時間長,在於整體架構的設計,能否使用中間件對任務進行拆分和解耦。
    • 併發不高、任務執行時間長的業務要區分開:
      1. IO密集型的任務,因爲IO操作並不佔用CPU,可以加大線程池中的線程數目,讓CPU處理更多的業務
      2. CPU密集型任務,線程池中的線程數設置得少一些,減少線程上下文的切換。
    • 高併發、任務執行時間短的業務,線程池線程數可以設置爲CPU核數+1,減少線程上下文的切換。
    • 具體設置:

      1. corePoolSize = 每秒需要多少個線程處理
        threadcount = tasks/(1/taskcost) =taskstaskcout =  (500~1000)0.1 = 50~100 個線程。corePoolSize設置應該大於50。根據8020原則,如果80%的每秒任務數小於800,那麼corePoolSize設置爲80即可。
      2. queueCapacity = (coreSizePool/taskcost) * responsetime
        計算可得 queueCapacity = 80/0.1*1 = 80,意思是隊列裏的線程可以等待1s,超過了的需要新開線程來執行。切記不能設置爲Integer.MAX_VALUE,這樣隊列會很大,線程數只會保持在corePoolSize大小,當任務陡增時,不能新開線程來執行,響應時間會隨之陡增。
      3. maxPoolSize = (max(tasks)- queueCapacity)/(1/taskcost)(最大任務數-隊列容量)/每個線程每秒處理能力 = 最大線程數。計算可得 maxPoolSize = (1000-80)/10 = 92。
      4. rejectedExecutionHandler:根據具體情況來決定,任務不重要可丟棄,任務重要則要利用一些緩衝機制來處理。
      5. keepAliveTime和allowCoreThreadTimeout:採用默認能滿足。

Q:講一講你瞭解的GC的知識?

1 JVM 內存模型

2GC垃圾的算法

3常用的垃圾回收機器

4堆內存模型。

Q:C/C++熟嗎?

Q:考了三道簡單題,一個是雙指針,一個是dp。最後還問了一道外排序?

Q:再考了兩道IQ題:燒繩子和洗牌問題。

Q:講一講線程和進程?

進程是資源分配的最小的單位,線程是執行的最小的單位。

Q:進程的通信方式?

五種:消息隊列 信號量 共享內存 socket 管道通信

Q:線程的通信方式?

wait/notify 等待

Volatile 內存共享

CountDownLatch 併發工具

CyclicBarrier 併發工具

Q:網絡字節序和CPU字節序?

網絡字節序:網絡字節序是TCP/IP中規定好的一種數據表示格式,它與具體的CPU類型、操作系統等無關,從而可以保證數據在不同主機之間傳輸時能夠被正確解釋。網絡字節序採用big endian排序方式。

Q:講一講中間人僞造證書的攻擊?

SSL,即Secure Sockets Layer,是爲網絡通信提供安全及數據完整性的一種安全協議。SSL是介於傳輸層和應用層之間的網絡協議,爲了提供足夠的安全性和數據完整性,SSL主要採用了一下幾種措施:

1. 使用證書確認用戶和服務器;

2. 在數據尾部添加校驗碼,防止中途篡改;

3. 加密傳輸數據和校驗碼,防止中途竊取。

可以看出,SSL協議被設計的十分安全,要攻破它並不容易。但是我們可以利用瀏覽器對服務器證書檢查的缺陷,通過僞造CA證書的方式,進行SSL中間人攻擊。

Q:如果這邊安排你轉移動端開發?你這邊有興趣嗎?

Q:先來個自我介紹?

Q:介紹一下你的項目是做什麼?

Q:你建立連接時的認證是怎麼實現的?

Q:消息的整個鏈路是怎麼樣的?

Q:爲什麼要設置keepaalive?

Q:爲什麼keepalive時間要設置成這樣?

Q:講一講TCP的TIME_WAIT?

Q:在心跳包的發送間隔上是怎麼考慮的?

Q:服務器過時斷開和客戶端斷開它們的優缺點?

Q:客戶端斷開連接的過程都發了哪些包?

Q:說一說BIO/NIO/AIO?

Q:說一說Reactor的模型?

Q:你的項目都是服務端開發,你對移動端開發有經驗嗎?

Q:C/C++會嗎?

Q:有學過彙編嗎?有了解NDK嗎?

Q:STL有了解過嗎?

Q:C++的指針有多少個字節?

Q:C++的內存管理?

Q: C++從代碼到可執行二進制文件的過程?

Q:一個可執行的二進制文件,裏面應該包含哪些內容?

Q:如何讓應用只允許打開一個窗口?

Q:局部變量在內存哪個位置?靜態變量呢?

Q:系統默認有多少個端口?爲什麼有這麼多個端口?

Q:443端口默認是幹嘛的?

443端口bai:即網頁瀏覽端口,主要是用於HTTPS服務,是提供加密du和通過安全端口zhi傳輸的另一種HTTP。

Q:80端口默認是幹嘛的?

80端口:爲HTTP(HyperText Transport Protocol)即超文本傳輸協議開放的,此爲上網衝浪使用次數最多的協議,主要用於WWW(World Wide Web)即萬維網傳輸信息的協議。

1、443端口:HTTPS服務一般是通過SSL(安全套接字層)來保證安全性的,但是SSL漏洞可能會受到黑客的攻擊,比如可以黑掉在線銀行系統,盜取信用卡賬號等。

2、80端口:木馬程序可以利用80端口來攻擊計算機的,例如Executor、RingZero等。

Q:講一講Https?

建立連接獲取證書

1) SSL 客戶端通過 TCP 和服務器建立連接之後(443 端口),並且在一般的 tcp 連接協商(握手)過程中請求證書。即客戶端發出一個消息給服務器,這個消息裏面包含了自己可實現的算法列表和其它一些需要的消息, SSL 的服務器端會迴應一個數據包,這裏面確定了這次通信所需要的算法,然後服務器向客戶端返回證書。(證書裏面包含了服務器信息:域名。申請證書的公司,公共祕鑰)

證書驗證

2) Client 在收到服務器返回的證書後,判斷簽發這個證書的公共簽發機構,並使用這個機構的公共祕鑰確認簽名是否有效,客戶端還會確保證書中列出的域名就是它正在連接的域名。

數據加密和傳輸

3) 如果確認證書有效,那麼生成對稱祕鑰並使用服務器的公共祕鑰進行加密。然後發送給服務器,服務器使用它的私鑰對它進行解密,這樣兩臺計算機可以開始進行對稱加密進行通信。

Q:Https有什麼不好的地方嗎?

大家都知道現在的網站分爲兩種,一種是傳統的http協議,另外一種則是加密的https,其實這兩種是一脈相承的,https就是基於http基礎上實現的,加入了SSL或者TLS。相對於Https,http簡單方便,開發起來也方便,但是卻有個重要的缺點。那就是其用戶端和客戶端之間的數據一旦被抓包,其相關的交互信息就會被泄露,因爲在傳輸中它們是不加密的,相當於在“裸奔”,除非服務器自己對關鍵信息進行了加密,否則數據保密性根本無從談起。
1、SSL/TLS協議的主要作用

     SSL(Secure Sockets Layer 安全套接層),及其繼任者傳輸層安全(Transport Layer Security,TLS)是爲網絡通信提供安全及數據完整性的一種安全協議。TLS與SSL在傳輸層對網絡連接進行加密(來自百度百科)

     SSL/TLS協議的主要作用就是:

      1、認證用戶和服務器,保證各自的數據都發送到正確的位置上去。

      2、對發送的數據進行加密,保護數據。

      3、保證數據在發送過程中的完整性。
2、https傳輸過程簡述

      1、首先https的服務端必須要擁有一個CA認證合法授權的證書,沒有這個證書,客戶端在訪問該服務器時就會提醒用戶這個網站是不受信任的。只有通過CA認證的服務器纔是可靠的,這保證了用戶在訪問服務器的安全性。瀏覽器會保持一個信任的CA機構列表,通過這些機構出查詢所訪問的服務器提供的證書是否合法。

      2、如果此時發現證書是合法OK的,那麼就從這個服務器端的證書中獲取到了加密祕鑰,這個加密祕鑰會溝通商議出一個隨機的對稱祕鑰,服務端在傳輸信息使用該祕鑰進行加密。而客戶端在收到這部分信息後,在瀏覽器側通過之前得到的對稱祕鑰進行解密,相反如果客戶端想要向服務端發送消息時也是如此。

3、Https相比於Http協議的優點和缺點

1、優點:相比於http,https可以提供更加優質保密的信息,保證了用戶數據的安全性,此外https同時也一定程度上保護了服務端,使用惡意攻擊和僞裝數據的成本大大提高。

 2、缺點: 缺點也同樣很明顯,第一https的技術門檻較高,多數個人或者私人網站難以支撐,CA機構頒發的證書都是需要年費的,此外對接Https協議也需要額外的技術支持;其二,目前來說大多數網站並不關心數據的安全性和保密性,其https最大的優點對它來說並不適用;其三,https加重了服務端的負擔,相比於http其需要更多的資源來支撐,同時也降低了用戶的訪問速度;第四,目前來說Http網站仍然大規模使用,在瀏覽器側也沒有特別大的差別,很多用戶不關心的話根本不感知。

Q:說一說進程通信、線程通信?

進程是資源分類的最小的單位 線程是的執行的最小的單位,在一個進程中有多個線程,且多個線程資源是共享的,但在不同的的進程資源是不能共享的。

線程:使用volatile關鍵字、使用Object類的wait() 和 notify() 方法、使用JUC工具類 CountDownLatch、基本LockSupport實現線程間的阻塞和喚醒。

進程:信號量 socket 共享內存 消息隊列 管道通信

Q:可以說說大端小端嗎?

Q:進程空間從高位到低位都有些什麼?

Q:一張600*600的圖片加載到內存會佔多少空間?

Q:字節碼可以跨平臺運行嗎?

            可是在實現跨平臺運行的

Q:講一講缺頁中斷?

進程線性地址空間裏的頁面不必常駐內存,在執行一條指令時,如果發現他要訪問的頁沒有在內存中(即存在位爲0),那麼停止該指令的執行,併產生一個頁不存在的異常,對應的故障處理程序可通過從外存加載該頁的方法來排除故障,之後,原先引起的異常的指令就可以繼續執行,而不再產生異常。

Q:有了解過一些加密算法和編碼算法嗎?

            Brcpt加密,加密和驗證的原理

Q:(之後就問了一道簡單算法,技術問題就結束了)

Q:你在學校學的什麼課程?

Q:爲什麼選你現在的這個方向呢?

Q:你最近有看什麼書嗎?

Q:你還有什麼問題想問我的?

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章