java开发工程师的自我评价
前言
京東到家訂單中心系統(tǒng)業(yè)務(wù)中,無論是外部商家的訂單生產(chǎn),或是內(nèi)部上下游系統(tǒng)的依賴,訂單查詢的調(diào)用量都非常大,造成了訂單數(shù)據(jù)讀多寫少的情況。
我們把訂單數(shù)據(jù)存儲(chǔ)在MySQL中,但顯然只通過DB來支撐大量的查詢是不可取的。同時(shí)對于一些復(fù)雜的查詢,MySQL支持得不夠友好,所以訂單中心系統(tǒng)使用了Elasticsearch來承載訂單查詢的主要壓力。
Elasticsearch作為一款功能強(qiáng)大的分布式搜索引擎,支持近實(shí)時(shí)的存儲(chǔ)、搜索數(shù)據(jù),在京東到家訂單系統(tǒng)中發(fā)揮著巨大作用,目前訂單中心ES集群存儲(chǔ)數(shù)據(jù)量達(dá)到10億個(gè)文檔,日均查詢量達(dá)到5億。
隨著京東到家近幾年業(yè)務(wù)的快速發(fā)展,訂單中心ES架設(shè)方案也不斷演進(jìn),發(fā)展至今ES集群架設(shè)是一套實(shí)時(shí)互備方案,很好地保障了ES集群讀寫的穩(wěn)定性,下面就給大家介紹一下這個(gè)歷程以及過程中遇到的一些坑。
一、網(wǎng)絡(luò)編程基礎(chǔ)回顧
1. Socket
Socket本身有“插座”的意思,不是Java中特有的概念,而是一個(gè)語言無關(guān)的標(biāo)準(zhǔn),任何可以實(shí)現(xiàn)網(wǎng)絡(luò)編程的編程語言都有Socket。在Linux環(huán)境下,用于表示進(jìn)程間網(wǎng)絡(luò)通信的特殊文件類型,其本質(zhì)為內(nèi)核借助緩沖區(qū)形成的偽文件。既然是文件,那么理所當(dāng)然的,我們可以使用文件描述符引用套接字。
與管道類似的,Linux系統(tǒng)將其封裝成文件的目的是為了統(tǒng)一接口,使得讀寫套接字和讀寫文件的操作一致。區(qū)別是管道主要應(yīng)用于本地進(jìn)程間通信,而套接字多應(yīng)用于網(wǎng)絡(luò)進(jìn)程間數(shù)據(jù)的傳遞。
可以這么理解:Socket就是網(wǎng)絡(luò)上的兩個(gè)應(yīng)用程序通過一個(gè)雙向通信連接實(shí)現(xiàn)數(shù)據(jù)交換的編程接口API。
Socket通信的基本流程具體步驟如下所示:
(1)服務(wù)端通過Listen開啟監(jiān)聽,等待客戶端接入。
(2)客戶端的套接字通過Connect連接服務(wù)器端的套接字,服務(wù)端通過Accept接收客戶端連接。在connect-accept過程中,操作系統(tǒng)將會(huì)進(jìn)行三次握手。
(3)客戶端和服務(wù)端通過write和read發(fā)送和接收數(shù)據(jù),操作系統(tǒng)將會(huì)完成TCP數(shù)據(jù)的確認(rèn)、重發(fā)等步驟。
(4)通過close關(guān)閉連接,操作系統(tǒng)會(huì)進(jìn)行四次揮手。
針對Java編程語言,java.net包是網(wǎng)絡(luò)編程的基礎(chǔ)類庫。其中ServerSocket和Socket是網(wǎng)絡(luò)編程的基礎(chǔ)類型。
SeverSocket是服務(wù)端應(yīng)用類型。Socket是建立連接的類型。當(dāng)連接建立成功后,服務(wù)器和客戶端都會(huì)有一個(gè)Socket對象示例,可以通過這個(gè)Socket對象示例,完成會(huì)話的所有操作。對于一個(gè)完整的網(wǎng)絡(luò)連接來說,Socket是平等的,沒有服務(wù)器客戶端分級(jí)情況。
2. IO模型介紹
對于一次IO操作,數(shù)據(jù)會(huì)先拷貝到內(nèi)核空間中,然后再從內(nèi)核空間拷貝到用戶空間中,所以一次read操作,會(huì)經(jīng)歷兩個(gè)階段:
(1)等待數(shù)據(jù)準(zhǔn)備
(2)數(shù)據(jù)從內(nèi)核空間拷貝到用戶空間
基于以上兩個(gè)階段就產(chǎn)生了五種不同的IO模式。
這五種IO模式不難發(fā)現(xiàn)存在這兩對關(guān)系:同步和異步、阻塞和非阻塞。那么稍微解釋一下:
同步和異步
- 同步: 同步就是發(fā)起一個(gè)調(diào)用后,被調(diào)用者未處理完請求之前,調(diào)用不返回。
- 異步: 異步就是發(fā)起一個(gè)調(diào)用后,立刻得到被調(diào)用者的回應(yīng)表示已接收到請求,但是被調(diào)用者并沒有返回結(jié)果,此時(shí)我們可以處理其他的請求,被調(diào)用者通常依靠事件,回調(diào)等機(jī)制來通知調(diào)用者其返回結(jié)果。
同步和異步的區(qū)別最大在于異步的話調(diào)用者不需要等待處理結(jié)果,被調(diào)用者會(huì)通過回調(diào)等機(jī)制來通知調(diào)用者其返回結(jié)果。
阻塞和非阻塞
- 阻塞: 阻塞就是發(fā)起一個(gè)請求,調(diào)用者一直等待請求結(jié)果返回,也就是當(dāng)前線程會(huì)被掛起,無法從事其他任務(wù),只有當(dāng)條件就緒才能繼續(xù)。
- 非阻塞: 非阻塞就是發(fā)起一個(gè)請求,調(diào)用者不用一直等著結(jié)果返回,可以先去干其他事情。
阻塞和非阻塞是針對進(jìn)程在訪問數(shù)據(jù)的時(shí)候,根據(jù)IO操作的就緒狀態(tài)來采取的不同方式,說白了是一種讀取或者寫入操作方法的實(shí)現(xiàn)方式,阻塞方式下讀取或者寫入函數(shù)將一直等待,而非阻塞方式下,讀取或者寫入方法會(huì)立即返回一個(gè)狀態(tài)值。
如果組合后的同步阻塞(blocking-IO)簡稱BIO、同步非阻塞(non-blocking-IO)簡稱NIO和異步非阻塞(asynchronous-non-blocking-IO)簡稱AIO又代表什么意思呢?
- BIO (同步阻塞I/O模式): 數(shù)據(jù)的讀取寫入必須阻塞在一個(gè)線程內(nèi)等待其完成。這里使用那個(gè)經(jīng)典的燒開水例子,這里假設(shè)一個(gè)燒開水的場景,有一排水壺在燒開水,BIO的工作模式就是, 叫一個(gè)線程停留在一個(gè)水壺那,直到這個(gè)水壺?zé)_,才去處理下一個(gè)水壺。但是實(shí)際上線程在等待水壺?zé)_的時(shí)間段什么都沒有做。
- NIO(同步非阻塞): 同時(shí)支持阻塞與非阻塞模式,但這里我們以其同步非阻塞I/O模式來說明,那么什么叫做同步非阻塞?如果還拿燒開水來說,NIO的做法是叫一個(gè)線程不斷的輪詢每個(gè)水壺的狀態(tài),看看是否有水壺的狀態(tài)發(fā)生了改變,從而進(jìn)行下一步的操作。
- AIO(異步非阻塞I/O模型): 異步非阻塞與同步非阻塞的區(qū)別在哪里?異步非阻塞無需一個(gè)線程去輪詢所有IO操作的狀態(tài)改變,在相應(yīng)的狀態(tài)改變后,系統(tǒng)會(huì)通知對應(yīng)的線程來處理。對應(yīng)到燒開水中就是,為每個(gè)水壺上面裝了一個(gè)開關(guān),水燒開之后,水壺會(huì)自動(dòng)通知我水燒開了。
java 中的 BIO、NIO和AIO理解為是 Java 語言在操作系統(tǒng)層面對這三種 IO 模型的封裝。程序員在使用這些 封裝API 的時(shí)候,不需要關(guān)心操作系統(tǒng)層面的知識(shí),也不需要根據(jù)不同操作系統(tǒng)編寫不同的代碼,只需要使用Java的API就可以了。由此,為了使讀者對這三種模型有個(gè)比較具體和遞推式的了解,并且和本文主題NIO有個(gè)清晰的對比,下面繼續(xù)延伸。
Java BIO
BIO編程方式通常是是Java的上古產(chǎn)品,自JDK 1.0-JDK1.4就有的東西。編程實(shí)現(xiàn)過程為:首先在服務(wù)端啟動(dòng)一個(gè)ServerSocket來監(jiān)聽網(wǎng)絡(luò)請求,客戶端啟動(dòng)Socket發(fā)起網(wǎng)絡(luò)請求,默認(rèn)情況下SeverSocket會(huì)建立一個(gè)線程來處理此請求,如果服務(wù)端沒有線程可用,客戶端則會(huì)阻塞等待或遭到拒絕。服務(wù)器實(shí)現(xiàn)模式為一個(gè)連接一個(gè)線程,即客戶端有連接請求時(shí)服務(wù)器端就需要啟動(dòng)一個(gè)線程進(jìn)行處理。大致結(jié)構(gòu)如下:
如果要讓 BIO 通信模型能夠同時(shí)處理多個(gè)客戶端請求,就必須使用多線程(主要原因是 socket.accept()、socket.read()、 socket.write() 涉及的三個(gè)主要函數(shù)都是同步阻塞的),也就是說它在接收到客戶端連接請求之后為每個(gè)客戶端創(chuàng)建一個(gè)新的線程進(jìn)行鏈路處理,處理完成之后,通過輸出流返回應(yīng)答給客戶端,線程銷毀。這就是典型的 一請求一應(yīng)答通信模型 。我們可以設(shè)想一下如果這個(gè)連接不做任何事情的話就會(huì)造成不必要的線程開銷,不過可以通過線程池機(jī)制改善,線程池還可以讓線程的創(chuàng)建和回收成本相對較低。使用線程池機(jī)制改善后的 BIO 模型圖如下:
BIO方式適用于連接數(shù)目比較小且固定的架構(gòu),這種方式對服務(wù)器資源要求比較高,并發(fā)局限于應(yīng)用中,是JDK1.4以前的唯一選擇,但程序直觀簡單易懂。Java BIO編程示例網(wǎng)上很多,這里就不進(jìn)行coding舉例了,畢竟后面NIO才是重點(diǎn)。
Java NIO
NIO(New IO或者No-Blocking IO),從JDK1.4 開始引入的非阻塞IO,是一種非阻塞+ 同步的通信模式。這里的No Blocking IO用于區(qū)分上面的BIO。
NIO本身想解決 BIO的并發(fā)問題,通過Reactor模式的事件驅(qū)動(dòng)機(jī)制來達(dá)到Non Blocking的。當(dāng) socket 有流可讀或可寫入 socket 時(shí),操作系統(tǒng)會(huì)相應(yīng)的通知應(yīng)用程序進(jìn)行處理,應(yīng)用再將流讀取到緩沖區(qū)或?qū)懭氩僮飨到y(tǒng)。
也就是說,這個(gè)時(shí)候,已經(jīng)不是一個(gè)連接就 要對應(yīng)一個(gè)處理線程了,而是有效的請求,對應(yīng)一個(gè)線程,當(dāng)連接沒有數(shù)據(jù)時(shí),是沒有工作線程來處理的。
當(dāng)一個(gè)連接創(chuàng)建后,不需要對應(yīng)一個(gè)線程,這個(gè)連接會(huì)被注冊到 多路復(fù)用器上面,所以所有的連接只需要一個(gè)線程就可以搞定,當(dāng)這個(gè)線程中的多路復(fù)用器 進(jìn)行輪詢的時(shí)候,發(fā)現(xiàn)連接上有請求的話,才開啟一個(gè)線程進(jìn)行處理,也就是一個(gè)請求一個(gè)線程模式。
NIO提供了與傳統(tǒng)BIO模型中的Socket和ServerSocket相對應(yīng)的SocketChannel和ServerSocketChannel兩種不同的套接字通道實(shí)現(xiàn),如下圖結(jié)構(gòu)所示。這里涉及的Reactor設(shè)計(jì)模式、多路復(fù)用Selector、Buffer等暫時(shí)不用管,后面會(huì)講到。
NIO 方式適用于連接數(shù)目多且連接比較短(輕操作)的架構(gòu),比如聊天服務(wù)器,并發(fā)局 限于應(yīng)用中,編程復(fù)雜,JDK1.4 開始支持。同時(shí),NIO和普通IO的區(qū)別主要可以從存儲(chǔ)數(shù)據(jù)的載體、是否阻塞等來區(qū)分:
Java AIO
與 NIO 不同,當(dāng)進(jìn)行讀寫操作時(shí),只須直接調(diào)用 API 的 read 或 write 方法即可。這兩種方法均為異步的,對于讀操作而言,當(dāng)有流可讀取時(shí),操作系統(tǒng)會(huì)將可讀的流傳入 read 方 法的緩沖區(qū),并通知應(yīng)用程序;對于寫操作而言,當(dāng)操作系統(tǒng)將 write 方法傳遞的流寫入完畢時(shí),操作系統(tǒng)主動(dòng)通知應(yīng)用程序。即可以理解為,read/write 方法都是異步的,完成后會(huì)主動(dòng)調(diào)用回調(diào)函數(shù)。在 JDK7 中,提供了異步文件通道和異步套接字通道的實(shí)現(xiàn),這部分內(nèi)容被稱作 NIO.
AIO 方式使用于連接數(shù)目多且連接比較長(重操作)的架構(gòu),比如相冊服務(wù)器,充分調(diào)用 OS 參與并發(fā)操作,編程比較復(fù)雜,JDK7 開始支持。
目前來說 AIO 的應(yīng)用還不是很廣泛,Netty 之前也嘗試使用過 AIO,不過又放棄了。
二、NIO核心組件介紹
1. Channel
在NIO中,基本所有的IO操作都是從Channel開始的,Channel通過Buffer(緩沖區(qū))進(jìn)行讀寫操作。
read()表示讀取通道中數(shù)據(jù)到緩沖區(qū),write()表示把緩沖區(qū)數(shù)據(jù)寫入到通道。
Channel有好多實(shí)現(xiàn)類,這里有三個(gè)最常用:
- SocketChannel:一個(gè)客戶端發(fā)起TCP連接的Channel
- ServerSocketChannel:一個(gè)服務(wù)端監(jiān)聽新連接的TCP Channel,對于每一個(gè)新的Client連接,都會(huì)建立一個(gè)對應(yīng)的SocketChannel
- FileChannel:從文件中讀寫數(shù)據(jù)
其中SocketChannel和ServerSocketChannel是網(wǎng)絡(luò)編程中最常用的,一會(huì)在最后的示例代碼中會(huì)有講解到具體用法。
2. Buffer
概念
Buffer也被成為內(nèi)存緩沖區(qū),本質(zhì)上就是內(nèi)存中的一塊,我們可以將數(shù)據(jù)寫入這塊內(nèi)存,之后從這塊內(nèi)存中讀取數(shù)據(jù)。也可以將這塊內(nèi)存封裝成NIO Buffer對象,并提供一組常用的方法,方便我們對該塊內(nèi)存進(jìn)行讀寫操作。
Buffer在java.nio中被定義為抽象類:
我們可以將Buffer理解為一個(gè)數(shù)組的封裝,我們最常用的ByteBuffer對應(yīng)的數(shù)據(jù)結(jié)構(gòu)就是byte[]
屬性
Buffer中有4個(gè)非常重要的屬性:capacity、limit、position、mark
最后
現(xiàn)在其實(shí)從大廠招聘需求可見,在招聘要求上有高并發(fā)經(jīng)驗(yàn)優(yōu)先,包括很多朋友之前都是做傳統(tǒng)行業(yè)或者外包項(xiàng)目,一直在小公司,技術(shù)搞的比較簡單,沒有怎么搞過分布式系統(tǒng),但是現(xiàn)在互聯(lián)網(wǎng)公司一般都是做分布式系統(tǒng)。
所以說,如果你想進(jìn)大廠,想脫離傳統(tǒng)行業(yè),這些技術(shù)知識(shí)都是你必備的,下面自己手打了一份Java并發(fā)體系思維導(dǎo)圖,希望對你有所幫助。
資料獲取方式:戳這里免費(fèi)下載
怎么搞過分布式系統(tǒng),但是現(xiàn)在互聯(lián)網(wǎng)公司一般都是做分布式系統(tǒng)。
所以說,如果你想進(jìn)大廠,想脫離傳統(tǒng)行業(yè),這些技術(shù)知識(shí)都是你必備的,下面自己手打了一份Java并發(fā)體系思維導(dǎo)圖,希望對你有所幫助。
資料獲取方式:戳這里免費(fèi)下載
總結(jié)
以上是生活随笔為你收集整理的java开发工程师的自我评价的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 不孕不育怎么办
- 下一篇: java开发工程师面试题总结