<dfn id="hx5t3"><strike id="hx5t3"><em id="hx5t3"></em></strike></dfn>

    <thead id="hx5t3"></thead><nobr id="hx5t3"><font id="hx5t3"><rp id="hx5t3"></rp></font></nobr>

    <listing id="hx5t3"></listing>

    <var id="hx5t3"></var>
    <big id="hx5t3"></big>

      
      

      <output id="hx5t3"><ruby id="hx5t3"></ruby></output>
      <menuitem id="hx5t3"><dfn id="hx5t3"></dfn></menuitem>

      <big id="hx5t3"></big>

        rustic_z

        rustic_z 查看完整檔案

        填寫現居城市  |  填寫畢業院校  |  填寫所在公司/組織填寫個人主網站
        編輯
        _ | |__ _ _ __ _ | '_ \| | | |/ _` | | |_) | |_| | (_| | |_.__/ \__,_|\__, | |___/ 個人簡介什么都沒有

        個人動態

        rustic_z 收藏了文章 · 2018-08-18

        【Java】留下沒有基礎眼淚的面試題

        前言

        只有光頭才能變強

        本文力求簡單講清每個知識點,希望大家看完能有所收獲

        一、如何減少線程上下文切換

        使用多線程時,不是多線程能提升程序的執行速度,使用多線程是為了更好地利用CPU資源!

        程序在執行時,多線程是CPU通過給每個線程分配CPU時間片來實現的,時間片是CPU分配給每個線程執行的時間,因時間片非常短,所以CPU通過不停地切換線程執行。

        線程不是越多就越好的,因為線程上下文切換是有性能損耗的,在使用多線程的同時需要考慮如何減少上下文切換

        一般來說有以下幾條經驗

        • 無鎖并發編程。多線程競爭時,會引起上下文切換,所以多線程處理數據時,可以用一些辦法來避免使用鎖,如將數據的ID按照Hash取模分段,不同的線程處理不同段的數據
        • CAS算法。Java的Atomic包使用CAS算法來更新數據,而不需要加鎖。
        • 控制線程數量。避免創建不需要的線程,比如任務很少,但是創建了很多線程來處理,這樣會造成大量線程都處于等待狀態
        • 協程。在單線程里實現多任務的調度,并在單線程里維持多個任務間的切換

          • 協程可以看成是用戶態自管理的“線程”。不會參與CPU時間調度,沒有均衡分配到時間。非搶占式

        還可以考慮我們的應用是IO密集型的還是CPU密集型的。

        • 如果是IO密集型的話,線程可以多一些。
        • 如果是CPU密集型的話,線程不宜太多。

        參考資料:

        二、計算機網絡

        2.1MAC地址已經是唯一了,為什么需要IP地址?

        或者可以反過來問:已經有IP地址了,為什么需要MAC地址??在zhihu上還蠻多類似的問題的:

        我來簡單總結一下為什么有了MAC(IP)還需要IP(MAC):

        • MAC是鏈路層,IP是網絡層,每一層干每一層的事兒,之所以在網絡上分鏈路層、網絡層(...,就是將問題簡單化。
        • 歷史的兼容問題。

        已經有IP地址了,為什么需要MAC地址??

        • 現階段理由:DHCP基于MAC地址分配IP。

        MAC地址已經是唯一了,為什么需要IP地址?

        • MAC無網段概念,非類聚,不好管理。
        如果有更好的看法,不妨在評論區下留言哦~

        參考資料:

        2.2TCP狀態

        TCP 每個狀態說一下,TIME-WAIT狀態說一下

        TCP總共有11個狀態,狀態之間的轉換是這樣的:

        流程圖:

        下面我簡單總結一下每個狀態:

        • CLOSED:初始狀態,表示TCP連接是“關閉著的”或“未打開的”。
        • LISTEN:表示服務器端的某個SOCKET處于監聽狀態,可以接受客戶端的連接。
        • SYN-SENT:表示客戶端已發送SYN報文。當客戶端SOCKET執行connect()進行連接時,它首先發送SYN報文,然后隨即進入到SYN_SENT狀態。
        • SYN_RCVD:表示服務器接收到了來自客戶端請求連接的SYN報文。當TCP連接處于此狀態時,再收到客戶端的ACK報文,它就會進入到ESTABLISHED狀態。
        • ESTABLISHED:表示TCP連接已經成功建立。
        • FIN-WAIT-1第一次主動請求關閉連接,等待對方的ACK響應。
        • CLOSE_WAIT:對方發了一個FIN報文給自己,回應一個ACK報文給對方。此時進入CLOSE_WAIT狀態。

          • 接下來呢,你需要檢查自己是否還有數據要發送給對方,如果沒有的話,那你也就可以close()這個SOCKET并發送FIN報文給對方,即關閉自己到對方這個方向的連接
        • FIN-WAIT-2:主動關閉端接到ACK后,就進入了FIN-WAIT-2。在這個狀態下,應用程序還有接受數據的能力,但是已經無法發送數據。
        • LAST_ACK:當被動關閉的一方在發送FIN報文后,等待對方的ACK報文的時候,就處于LAST_ACK 狀態
        • CLOSED:當收到對方的ACK報文后,也就可以進入到CLOSED狀態了。
        • TIME_WAIT:表示收到了對方的FIN報文,并發送出了ACK報文。TIME_WAIT狀態下的TCP連接會等待2*MSL
        • CLOSING:罕見的狀態。表示雙方都正在關閉SOCKET連接

        TIME_WAIT狀態一般用來處理以下兩個問題:

        • 關閉TCP連接時,確保最后一個ACK正常運輸(或者可以認為是:等待以便重傳ACK)
        • 網絡上可能會有殘余的數據包,為了能夠正常處理這些殘余的數據包。使用TIME-WAIT狀態可以確保創建新連接時,先前網絡中殘余的數據都丟失了。

        TIME_WAIT過多怎么解決?

        如果在高并發,多短鏈接情景下,TIME_WAIT就會過多。

        可以通過調整內核參數解決:vi /etc/sysctl.conf 加入以下內容設置:

        • reuse是表示是否允許重新應用處于TIME-WAIT狀態的socket用于新的TCP連接;
        • recyse是加速TIME-WAIT sockets回收

        我們可以知道TIME_WAIT狀態是主動關閉連接的一方出現的,我們不要輕易去使用上邊兩個參數。先看看是不是可以重用TCP連接來盡量避免這個問題(比如我們HTTP的KeepAlive)~

        參考資料:

        2.3TCP滑動窗口

        TCP是一個可靠的傳輸協議,它要保證所有的數據包都可以到達,這需要重傳機制來支撐。

        重傳機制有以下幾種:

        • 超時重傳
        • 快速重傳
        • SACK 方法

        滑動窗口可以說是TCP非常重要的一個知識點。TCP的滑動窗口主要有兩個作用:

        • 提供TCP的可靠性
        • 提供TCP的流控特性

        簡略滑動窗口示意圖:

        詳細滑動窗口示意圖:

        • #1已收到ack確認的數據。
        • #2發還沒收到ack的。
        • #3在窗口中還沒有發出的(接收方還有空間)。
        • #4窗口以外的數據(接收方沒空間)

        接受端控制發送端的圖示:

        2.4擁塞控制

        TCP不是一個自私的協議,當擁塞發生的時候,要做自我犧牲。就像交通阻塞一樣,每個車都應該把路讓出來,而不要再去搶路了

        擁塞控制主要是四個算法:

        • 1)慢啟動,
        • 2)擁塞避免,
        • 3)擁塞發生,
        • 4)快速恢復

        擁塞控制的作用:

        擁塞的判斷:

        • 重傳定時器超時
        • 收到三個相同(重復)的 ACK

        強烈建議閱讀:

        參考資料:

        三、操作系統

        3.1僵尸進程和孤兒進程是什么(區別)

        unix/linux環境下

        僵尸進程:

        • 父進程創建出子進程,子進程退出了,父進程沒有調用waitwaitId獲取子進程的信息(狀態),子進程的描述符仍在系統中。

        孤兒進程:

        • 父進程退出,子進程仍在運行中。這些子進程就叫做孤兒進程,孤兒進程將被init進程(進程號為1)所收養,并由init進程對它們完成狀態收集工作

        僵尸進程危害

        • 系統進程表是一項有限資源,如果系統進程表被僵尸進程耗盡的話,系統就可能無法創建新的進程。
        • 一個父進程創建了很多子進程,就是不回收,會造成內存資源的浪費。

        解決僵尸進程的手段:

        • 殺掉父進程,余下的僵尸進程會成為孤兒進程,最后被init進程管理
        • 子進程退出時向父進程發送SIGCHILD信號,父進程處理SIGCHILD信號。在信號處理函數中調用wait進行處理僵尸進程
        • fork兩次:原理是將子進程成為孤兒進程,從而其的父進程變為init進程,通過init進程可以處理僵尸進程

        參考資料:

        3.2操作系統進程間通信的方式有哪些?

        首先要知道的是:進程和線程的關注點是不一樣的:

        • 進程間資源是獨立的,關注的是通訊問題。
        • 線程間資源是共享的,關注的是安全問題。
        操作系統進程間通信的方式有哪些?
        • 管道(pipe):管道是一種半雙工的通信方式,數據只能單向流動,而且只能在具有親緣關系的進程間使用。進程的親緣關系通常是指父子進程關系。
        • 有名管道(named pipe):有名管道也是半雙工的通信方式,但是它允許無親緣關系進程之間的通信。
        • 消息隊列(message queue):消息隊列是消息的鏈表,存放在內核中并由消息隊列表示符標示。消息隊列克服了信號傳遞信息少,管道只能承載無格式字節流以及緩沖區大小受限制等缺點。
        • 共享內存(shared memory):共享內存就是映射一段內被其它進程所訪問的內存,共享內存由一個進程創建,但是多個進程都可以訪問。共享內存是最快的IPC,它是針對其它進程通信方式運行效率低的而專門設計的。它往往與其它通信機制。如信號量,配合使用,來實現進程間的同步和通信。
        • 套接字(socket):套接字也是進程間的通信機制,與其它通信機制不同的是,它可以用于不同機器間的進程通信。
        • 信號(signal):信號是一種比較復雜的通信方式,用于通知接受進程進程某個時間已經發生。
        • 信號量(semaphore):信號量是一個計數器,可以用來控制多個進程對共享資源的訪問。

          • 它常作為一種鎖的機制,防止某進程正在訪問共享資源時,其它進程也訪問該資源。因此它主要作為不同進程或者同一進程之間不同線程之間同步的手段。

        3.3操作系統線程間通信的方式有哪些?

        操作系統線程間通信的方式有哪些?(可以直接理解成:線程之間同步的方式有哪些)
        • 鎖機制:包括互斥鎖、條件變量、讀寫鎖
        • 信號量機制(Semaphore):包括無名線程信號量和命名線程信號量
        • 信號機制(Signal):類似進程間的信號處理

        線程間的通信目的主要是用于線程同步。

        參考資料:

        擴展閱讀:

        3.4操作系統進程調度算法有哪些?

        操作系統進程調度算法有哪些?
        • 先來先服務算法(FCFS)

          • 誰先來,就誰先執行
        • 短進程/作業優先算法(SJF)

          • 誰用的時間少、就先執行誰
        • 最高響應比優先算法(HRN)

          • 對FCFS方式和SJF方式的一種綜合平衡
        • 最高優先數算法

          • 系統把處理機分配給就緒隊列中優先數最高的進程
        • 基于時間片的輪轉調度算法

          • 每個進程所享受的CPU處理時間都是一致的
        • 最短剩余時間優先算法

          • 短作業優先算法的升級版,只不過它是搶占式
        • 多級反饋排隊算法

          • 設置多個就緒隊列,分別賦予不同的優先級,如逐級降低,隊列1的優先級最高

        參考筆記:

        四、拓展閱讀

        此部分是看別人的博文已經寫得很好了,分享給大家~

        4.1ConcurrentHashMap中的擴容是否需要對整個表上鎖?

        ConcurrentHashMap中的擴容是否需要對整個表上鎖?

        總結(摘抄)要點:

        • 通過給每個線程分配桶區間(默認一個線程分配的桶是16個),避免線程間的爭用。
        • 通過為每個桶節點加鎖,避免 putVal 方法導致數據不一致。
        • 同時,在擴容的時候,也會將鏈表拆成兩份,這點和 HashMap 的 resize 方法類似。

        參考資料:

        4.2什么是一致性Hash算法(原理)?

        什么是一致性Hash算法(原理)?

        總結(摘抄)要點:

        • 一致性Hash算法將整個哈希值空間組織成一個虛擬的圓環,好處就是提高容錯性和可擴展性。

          • 對于節點的增減都只需重定位環空間中的一小部分數據。

        參考資料:

        4.3MySQL date、datetime和timestamp類型的區別

        MySQL date、datetime和timestamp類型的區別

        總結(摘抄)要點:

        • date精確到天,datetime和timestamp精確到秒
        • datetime和timestamp的區別:

          • timestamp會跟隨設置的時區變化而變化,而datetime保存的是絕對值不會變化
          • timestamp儲存占用4個字節,datetime儲存占用8個字節
          • 可表示的時間范圍不同,timestamp只能到表示到2038年,datetime可到9999年

        參考資料:

        4.4判斷一個鏈表是否有環/相交

        判斷一個鏈表是否有環(實際上就是看看有無遍歷到重復的節點),解決方式(3種):

        1. for遍歷兩次
        2. 使用hashSet做緩存,記錄已遍歷過的節點
        3. 使用兩個指針,一前一后遍歷,總會出現前指針==后指針的情況

        參考資料:


        判斷兩個無環鏈表是否相交,解決方式(2種):

        • 將第一個鏈表尾部的next指針指向第二個鏈表,兩個鏈表組成一個鏈表。

          • 判斷這一個鏈表是否有環,有環則相交,無環則不相交
        • 直接判斷兩個鏈表的尾節點是否相等,如果相等則相交,否則不相交

        判斷兩個有環鏈表是否相交(注:當一個鏈表中有環,一個鏈表中沒有環時,兩個鏈表必不相交):

        • 找到第一個鏈表的環點,然后將環斷開(當然不要忘記了保存它的下一個節點),然后再來遍歷第二個鏈表,如果發現第二個鏈表從有環變成了無環,那么他們就是相交的嘛,否則就是不相交的了。

        參考資料:

        4.5keepAlive含義

        • HTTP協議的Keep-Alive意圖在于連接復用,同一個連接上串行方式傳遞請求-響應數據
        • TCP的KeepAlive機制意圖在于?;?、心跳,檢測連接錯誤

        參考資料:

        最后

        如果大家有更好的理解方式或者文章有錯誤的地方還請大家不吝在評論區留言,大家互相學習交流~~~

        如果想看更多的原創技術文章,歡迎大家關注我的微信公眾號:Java3y。Java技術群討論:742919422。公眾號還有海量的視頻資源哦,關注即可免費領取。

        可能感興趣的鏈接:

        查看原文

        rustic_z 收藏了文章 · 2018-08-18

        Java 內存模型 JMM 淺析

        JMM簡介

        Java Memory Model簡稱JMM, 是一系列的Java虛擬機平臺對開發者提供的多線程環境下的內存可見性、是否可以重排序等問題的無關具體平臺的統一的保證。(可能在術語上與Java運行時內存分布有歧義,后者指堆、方法區、線程棧等內存區域)。
        并發編程有多種風格,除了CSP(通信順序進程)、Actor等模型外,大家最熟悉的應該是基于線程和鎖的共享內存模型了。在多線程編程中,需要注意三類并發問題:

        1. 原子性
        2. 可見性
        3. 重排序

        原子性涉及到,一個線程執行一個復合操作的時候,其他線程是否能夠看到中間的狀態、或進行干擾。典型的就是i++的問題了,兩個線程同時對共享的堆內存執行++操作,而++操作在JVM、運行時、CPU中的實現都可能是一個復合操作, 例如在JVM指令的角度來看是將i的值從堆內存讀到操作數棧、加上一、再寫回到堆內存的i,這幾個操作的期間,如果沒有正確的同步,其他線程也可以同時執行,可能導致數據丟失等問題。常見的原子性問題又叫競太條件,是基于一個可能失效的結果進行判斷,如讀取-修改-寫入。 可見性和重排序問題都源于系統的優化。

        由于CPU的執行速度和內存的存取速度嚴重不匹配,為了優化性能,基于時間局部性、空間局部性等局部性原理,CPU在和內存間增加了多層高速緩存,當需要取數據時,CPU會先到高速緩存中查找對應的緩存是否存在,存在則直接返回,如果不存在則到內存中取出并保存在高速緩存中?,F在多核處理器越基本已經成為標配,這時每個處理器都有自己的緩存,這就涉及到了緩存一致性的問題,CPU有不同強弱的一致性模型,最強的一致性安全性最高,也符合我們的順序思考的模式,但是在性能上因為需要不同CPU之間的協調通信就會有很多開銷。

        典型的CPU緩存結構示意圖如下

        clipboard.png

        CPU的指令周期通常為取指令、解析指令讀取數據、執行指令、數據寫回寄存器或內存。串行執行指令時其中的讀取存儲數據部分占用時間較長,所以CPU普遍采取指令流水線的方式同時執行多個指令, 提高整體吞吐率,就像工廠流水線一樣。

        clipboard.png

        讀取數據和寫回數據到內存相比執行指令的速度不在一個數量級上,所以CPU使用寄存器、高速緩存作為緩存和緩沖,在從內存中讀取數據時,會讀取一個緩存行(cache line)的數據(類似磁盤讀取讀取一個block)。數據寫回的模塊在舊數據沒有在緩存中的情況下會將存儲請求放入一個store buffer中繼續執行指令周期的下一個階段,如果存在于緩存中則會更新緩存,緩存中的數據會根據一定策略flush到內存。

        clipboard.png

        上面這段代碼執行時我們可能認為count = 1會在stop = false前執行完成,這在上面的CPU執行圖中顯示的理想狀態下是正確的,但是要考慮上寄存器、緩存緩沖的時候就不正確了, 例如stop本身在緩存中但是count不在,則可能stop更新后再count的write buffer寫回之前刷新到了內存。

        另外CPU、編譯器(對于Java一般指JIT)都可能會修改指令執行順序,例如上述代碼中count = 1和stop = false兩者并沒有依賴關系,所以CPU、編譯器都有可能修改這兩者的順序,而在單線程執行的程序看來結果是一樣的,這也是CPU、編譯器要保證的as-if-serial(不管如何修改執行順序,單線程的執行結果不變)。由于很大部分程序執行都是單線程的,所以這樣的優化是可以接受并且帶來了較大的性能提升。但是在多線程的情況下,如果沒有進行必要的同步操作則可能會出現令人意想不到的結果。例如在線程T1執行完initCountAndStop方法后,線程T2執行printResult,得到的可能是0, false, 可能是1, false, 也可能是0, true。如果線程T1先執行doLoop(),線程T2一秒后執行initCountAndStop, 則T1可能會跳出循環、也可能由于編譯器的優化永遠無法看到stop的修改。

        由于上述這些多線程情況下的各種問題,多線程中的程序順序已經不是底層機制中的執行順序和結果,編程語言需要給開發者一種保證,這個保證簡單來說就是一個線程的修改何時對其他線程可見,因此Java語言提出了JavaMemoryModel即Java內存模型,對于Java語言、JVM、編譯器等實現者需要按照這個模型的約定來進行實現。Java提供了Volatile、synchronized、final等機制來幫助開發者保證多線程程序在所有處理器平臺上的正確性。

        在JDK1.5之前,Java的內存模型有著嚴重的問題,例如在舊的內存模型中,一個線程可能在構造器執行完成后看到一個final字段的默認值、volatile字段的寫入可能會和非volatile字段的讀寫重排序。

        所以在JDK1.5中,通過JSR133提出了新的內存模型,修復之前出現的問題。

        重排序規則

        volatile和監視器鎖

        clipboard.png

        其中普通讀指getfield, getstatic, 非volatile數組的arrayload, 普通寫指putfield, putstatic, 非volatile數組的arraystore。

        volatile讀寫分別是volatile字段的getfield, getstatic和putfield, putstatic。

        monitorenter是進入同步塊或同步方法,monitorexist指退出同步塊或同步方法。

        上述表格中的No指先后兩個操作不允許重排序,如(普通寫, volatile寫)指非volatile字段的寫入不能和之后任意的volatile字段的寫入重排序。當沒有No時,說明重排序是允許的,但是JVM需要保證最小安全性-讀取的值要么是默認值,要么是其他線程寫入的(64位的double和long讀寫操作是個特例,當沒有volatile修飾時,并不能保證讀寫是原子的,底層可能將其拆分為兩個單獨的操作)。

        final字段

        final字段有兩個額外的特殊規則

        1. final字段的寫入(在構造器中進行)以及final字段對象本身的引用的寫入都不能和后續的(構造器外的)持有該final字段的對象的寫入重排序。例如,下面的語句是不能重排序的

               x.finalField = v; ...; sharedRef = x;
          
        2. final字段的第一次加載不能和持有這個final字段的對象的寫入重排序,例如下面的語句是不允許重排序的

              x = sharedRef; ...; i = x.finalField
          

        內存屏障

        處理器都支持一定的內存屏障(memory barrier)或柵欄(fence)來控制重排序和數據在不同的處理器間的可見性。例如,CPU將數據寫回時,會將store請求放入write buffer中等待flush到內存,可以通過插入barrier的方式防止這個store請求與其他的請求重排序、保證數據的可見性??梢杂靡粋€生活中的例子類比屏障,例如坐地鐵的斜坡式電梯時,大家按順序進入電梯,但是會有一些人從左側繞過去,這樣出電梯時順序就不相同了,如果有一個人攜帶了一個大的行李堵住了(屏障),則后面的人就不能繞過去了:)。另外這里的barrier和GC中用到的write barrier是不同的概念。

        在此我向大家推薦一個架構學習交流群。交流學習群號:478030634 里面會分享一些資深架構師錄制的視頻錄像:有Spring,MyBatis,Netty源碼分析,高并發、高性能、分布式、微服務架構的原理,JVM性能優化、分布式架構等這些成為架構師必備的知識體系。還能領取免費的學習資源,目前受益良多

        內存屏障的分類

        幾乎所有的處理器都支持一定粗粒度的barrier指令,通常叫做Fence(柵欄、圍墻),能夠保證在fence之前發起的load和store指令都能嚴格的和fence之后的load和store保持有序。通常按照用途會分為下面四種barrier

        LoadLoad Barriers

        Load1; LoadLoad; Load2;

        保證Load1的數據在Load2及之后的load前加載

        StoreStore Barriers

        Store1; StoreStore; Store2

        保證Store1的數據先于Store2及之后的數據 在其他處理器可見

        LoadStore Barriers

        Load1; LoadStore; Store2

        保證Load1的數據的加載在Store2和之后的數據flush前

        StoreLoad Barriers

        Store1; StoreLoad; Load2

        保證Store1的數據在其他處理器前可見(如flush到內存)先于Load2和之后的load的數據的加載。StoreLoad Barrier能夠防止load讀取到舊數據而不是最近其他處理器寫入的數據。

        幾乎近代的所有的多處理器都需要StoreLoad,StoreLoad的開銷通常是最大的,并且StoreLoad具有其他三種屏障的效果,所以StoreLoad可以當做一個通用的(但是更高開銷的)屏障。

        所以,利用上述的內存屏障,可以實現上面表格中的重排序規則

        clipboard.png

        為了支持final字段的規則,需要對final的寫入增加barrier

        x.finalField = v; StoreStore; sharedRef = x;
        

        插入內存屏障

        基于上面的規則,可以在volatile字段、synchronized關鍵字的處理上增加屏障來滿足內存模型的規則

        1. volatile store前插入StoreStore屏障
        2. 所有final字段寫入后但在構造器返回前插入StoreStore
        3. volatile store后插入StoreLoad屏障
        4. 在volatile load后插入LoadLoad和LoadStore屏障
        5. monitor enter和volatile load規則一致,monitor exit 和volatile store規則一致。

        HappenBefore

        前面提到的各種內存屏障對應開發者來說還是比較復雜底層,因此JMM又可以使用一系列HappenBefore的偏序關系的規則方式來說明,要想保證執行操作B的線程看到操作A的結果(無論A和B是否在同一個線程中執行), 那么在A和B之間必須要滿足HappenBefore關系,否則JVM可以對它們任意重排序。

        HappenBefore規則列表

        HappendBefore規則包括

        1. 程序順序規則: 如果程序中操作A在操作B之前,那么同一個線程中操作A將在操作B之前進行
        2. 監視器鎖規則: 在監視器鎖上的鎖操作必須在同一個監視器鎖上的加鎖操作之前執行
        3. volatile變量規則: volatile變量的寫入操作必須在該變量的讀操作之前執行
        4. 線程啟動規則: 在線程上對Thread.start的調用必須在該線程中執行任何操作之前執行
        5. 線程結束規則: 線程中的任何操作都必須在其他線程檢測到該線程已經結束之前執行
        6. 中斷規則: 當一個線程在另一個線程上調用interrupt時,必須在被中斷線程檢測到interrupt之前執行
        7. 傳遞性: 如果操作A在操作B之前執行,并且操作B在操作C之前執行,那么操作A在操作C之前執行。

        其中顯示鎖與監視器鎖有相同的內存語義,原子變量與volatile有相同的內存語義。鎖的獲取和釋放、volatile變量的讀取和寫入操作滿足全序關系,所以可以使用volatile的寫入在后續的volatile的讀取之前進行。

        可以利用上述HappenBefore的多個規則進行組合。

        例如線程A進入監視器鎖后,在釋放監視器鎖之前的操作根據程序順序規則HappenBefore于監視器釋放操作,而監視器釋放操作HappenBefore于后續的線程B的對相同監視器鎖的獲取操作,獲取操作HappenBefore與線程B中的操作。

        在此我向大家推薦一個架構學習交流群。交流學習群號:478030634 里面會分享一些資深架構師錄制的視頻錄像:有Spring,MyBatis,Netty源碼分析,高并發、高性能、分布式、微服務架構的原理,JVM性能優化、分布式架構等這些成為架構師必備的知識體系。還能領取免費的學習資源,目前受益良多

        大家覺得文章對你還是有一點點幫助的,大家可以點擊下方二維碼進行關注?!禞ava爛豬皮》公眾號聊的不僅僅是Java技術知識,還有面試等干貨,后期還有大量架構干貨。大家一起關注吧!關注爛豬皮,你會了解的更多..............

        查看原文

        rustic_z 贊了文章 · 2018-08-01

        為Spring Cloud Config插上管理的翅膀

        最近一致在更新Spring Cloud Config的相關內容,主要也是為這篇埋個伏筆,相信不少調研過Spring Cloud Config的用戶都會吐槽它的管理能力太弱。因此,就有了下面為講推薦的這個開源項目,希望對已經入坑Spring Cloud Config的童鞋們有所幫助!

        簡介

        在Spring Cloud的微服務架構方案中雖然提供了Spring Cloud Config來擔任配置中心的角色,但是該項目的功能在配置的管理層面還是非常欠缺的。初期我們可以依賴選取的配置存儲系統(比如:Gitlab、Github)給我們提供的配置管理界面來操作所有的配置信息,但是這樣的管理還是非常粗粒度的,因此這個項目的目的就是解決這個問題,通過此項目,我們將提供一套基于Spring Cloud Config配置中心的可視化管理系統。

        在該項目中,我們對于服務治理、配置存儲、可視化操作都做了抽象,只要目的就是為了盡可能的兼容所有Spring Cloud Config的用戶。任何Spring Cloud Config僅需要通過一些簡單的配置,或者遷移工具就能將原來正在使用的配置中心統一的管理起來。

        項目地址

        如果您覺得該項目對您有用,歡迎Star、Follow支持我們!

        架構概覽

        本項目采用了前后端分離的架構,通過core模塊抽象了前端需要的操作,再通過persistence和discovery模塊隔離不同的配置倉庫和不同的服務注冊中心,從而達到前端程序不需要關心到底使用了什么存儲配置以及使用了什么注冊中心,這樣用戶可以根據自己的需要自由的組合不同的配置存儲和服務治理機制,盡可能的匹配大部分Spring Cloud用戶的需求。

        WechatIMG126.jpeg

        部署方式

        由于SCCA的架構對各個功能模塊做了比較細致的拆分,所以它存在多種不同模式的部署方式,所以它既可以為已經在使用Spring Cloud Config提供服務,也可以為從零開始使用Spring Cloud Config的用戶。

        在SCCA中我們的可部署內容自底向上分為三個部分:

        • Spring Cloud 配置中心:基于Spring Cloud Config構建的配置中心服務端。
        • SCCA REST 服務端:SCCA的核心模塊,實現了SCCA配置管理的持久化內容以及所有的管理操作API。
        • SCCA UI 服務端:SCCA的前端模塊,實現了可視化的配置管理操作界面。

        下面我們來看看SCCA支持哪些多樣的部署方式。

        全分離模式

        全分離模式就是將上述三個部分都以獨立的進程進行部署,每一個部分都可以做高可用,具體部署結構可以如下圖所示:

        scca-deploy-1.png

        這種模式既可以適用于已經在使用Spring Cloud Config的用戶,也適用于正準備開始適用的用戶。其中,位于最底層的Spring Cloud配置中心就是一個最原始的Spring Cloud Config Server。所以,對于已經在使用Spring Cloud Config的用戶只需要再部署一套SCCA REST 服務端SCCA UI 服務端,并做一些配置就可以使用SCCA來管理所有的配置信息了。

        案例

        半分離模式

        所謂的半分離模式就是將上述的三個模塊中的兩個進行組合部署,以降低復雜度的部署方式。
        SCCA UI模塊與SCCA REST模塊合并

        如下圖所示,我們可以將SCCA UI服務端SCCA REST服務端組合在一個程序中來部署,這樣可以有效的降低全分離模式的部署復雜度,同時對于已經在使用Spring Cloud Config的用戶來說非常友好,已經部署的配置中心可以繼續沿用。

        scca-deploy-2.png

        案例

        注意:對接不同存儲配置中心的配置參考分離部署中兩個SCCA REST服務端的不同配置內容進行調整。

        All-In-One模式

        最后介紹一種比較暴力的使用模式,SCCA支持將所有三個模塊整合在一起使用和部署,在一個Spring Boot應用中同時包含:Spring Cloud 配置中心、SCCA REST 服務端以及SCCA UI 服務端,具體如下所示:

        scca-deploy-4.png

        案例

        配置詳解

        本章節分別對三個核心模塊的構建方式以及核心的配置內容。下面所有的構建都是基于Spring Boot構建的,所以您需要對Spring Boot項目的構建有基本的認識,這里不做介紹。

        Spring Cloud配置中心的構建與配置

        在SCCA的架構中,配置中心的核心完全采用Spring Cloud Config,所以如何構建一個配置中心完全遵循Spring Cloud Config的使用方法。由于目前SCCA的REST模塊主要實現了對Git存儲和DB存儲的綜合管理,所以對于Spring Cloud Config的使用也只能支持這兩種模式。下面分別介紹兩種配置中心的搭建與配置。

        Git存儲模式

        這里主要介紹幾種主要的并且SCCA能夠比較好支持的配置模式:

        第一種:多個項目使用多個不同Git倉庫存儲的模式

        spring.cloud.config.server.git.uri=https://github.com/dyc87112/{application}.git
        spring.cloud.config.server.git.username=
        spring.cloud.config.server.git.password=

        這種模式下不同的項目會對應的不同的Git倉庫,如果項目中spring.application.name=user-service,那么它的配置倉庫會定位到https://github.com/dyc87112/user-service.git倉庫下的配置。配置文件按application-{profile}.properties的格式存儲,{profile}代表環境名。

        第二種:多個項目公用一個Git倉庫不同目錄的存儲模式

        spring.cloud.config.server.git.uri=https://github.com/dyc87112/config-repo.git
        spring.cloud.config.server.git.search-paths=/{application}
        spring.cloud.config.server.git.username=
        spring.cloud.config.server.git.password=

        這種模式下不同的項目會對應到https://github.com/dyc87112/config-repo.git倉庫下的不同目錄,如果項目中spring.application.name=user-service,那么它的配置倉庫會定位到https://github.com/dyc87112/config-repo.git倉庫下的/user-service目錄。配置文件按application-{profile}.properties的格式存儲,{profile}代表環境名。

        案例:Spring Cloud 配置中心(Git存儲)

        Db存儲模式

        在使用Db存儲模式的時候,必須使用Spring Cloud的Edgware版本以上。比如,可以采用下面的配置:

        # config server with jdbc
        spring.profiles.active=jdbc
        spring.cloud.config.server.jdbc.sql=SELECT `p_key`, `p_value` FROM property a, project b, env c, label d where a.project_id=b.id and a.env_id=c.id and a.label_id=d.id and b.name=? and c.name=? and d.name=?
        
        # Datasource, share with scca-rest-server
        spring.datasource.url=jdbc:mysql://localhost:3306/config-db
        spring.datasource.username=root
        spring.datasource.password=
        spring.datasource.driver-class-name=com.mysql.jdbc.Driver

        主要分為兩個部分:

        • 激活采用DB存儲的模式:將spring.profiles.active設置為jdbc,同時指定獲取配置的SQL,用戶直接復制采用一樣的配置即可。
        • 指定存儲配置的DB連接信息,除了mysql之外也可以使用其他主流關系型數據庫。

        這里需要注意的,使用的DB要與后續介紹的SCCA REST模塊采用同一個DB

        案例:Spring Cloud 配置中心(DB存儲)

        SCCA REST服務端的構建與配置

        在構建SCCA REST服務端的時候針對對接不同的配置存儲有一些不同的配置要求,所以下面按目前支持的存儲模式做不同的介紹。

        Git存儲模式

        當對接的配置中心采用Git存儲的時候,需要引入以下核心依賴:

        <dependency>
            <groupId>com.didispace</groupId>
            <artifactId>scca-rest</artifactId>
            <version>1.0.0-RELEASE</version>
        </dependency>
        
        <!-- scca persistence dependency -->
        <dependency>
            <groupId>com.didispace</groupId>
            <artifactId>scca-persistence-git</artifactId>
            <version>1.0.0-RELEASE</version>
        </dependency>

        需要按如下配置:

        # if config server use git, need config these properties
        scca.git.username=
        scca.git.password=
        scca.git.repo-uri=https://github.com/dyc87112/{application}.git
        scca.git.base-path=
        scca.git.file-pattern=application-{profile}.properties
        
        # Datasource
        spring.datasource.url=jdbc:mysql://localhost:3306/config-db
        spring.datasource.username=root
        spring.datasource.password=
        spring.datasource.driver-class-name=com.mysql.jdbc.Driver

        主要兩部分:

        • 對接的git存儲的配置:

          • scca.git.username:訪問git的用戶名
          • scca.git.password:訪問git的密碼
          • scca.git.repo-uri:配置git倉庫的地址,與配置中心的spring.cloud.config.server.git.uri配置一致
          • scca.git.base-path:配置文件存儲的相對路徑,與配置中心的spring.cloud.config.server.git.search-paths配置一致
          • scca.git.file-pattern:配置文件的命名規則
        • SCCA內部邏輯的存儲庫數據源信息
        案例:SCCA REST 服務端(對接Git存儲的配置中心)

        Db存儲模式

        當對接的配置中心采用Git存儲的時候,需要引入以下核心依賴:

        <dependency>
            <groupId>com.didispace</groupId>
            <artifactId>scca-rest</artifactId>
            <version>1.0.0-RELEASE</version>
        </dependency>
        
        <!-- scca persistence dependency -->
        <dependency>
            <groupId>com.didispace</groupId>
            <artifactId>scca-persistence-db</artifactId>
            <version>1.0.0-RELEASE</version>
        </dependency>

        需要按如下配置:

        # Datasource
        spring.datasource.url=jdbc:mysql://localhost:3306/config-db
        spring.datasource.username=root
        spring.datasource.password=
        spring.datasource.driver-class-name=com.mysql.jdbc.Driver

        需要注意,當配置中心采用DB存儲的時候,這里的數據源需要一致

        案例:SCCA REST 服務端(對接DB存儲的配置中心)

        服務發現支持

        如果SCCA REST模塊在訪問配置中心的時候基于服務發現的話還需要引入對應的支持依賴和配置

        與Eureka的整合

        如果使用eureak,那么需要引入如下依賴:

        <!-- scca discovery dependency-->
        <dependency>
            <groupId>com.didispace</groupId>
            <artifactId>scca-discovery-eureka</artifactId>
            <version>1.0.0-RELEASE</version>
        </dependency>

        并且在配置中加入eureka的配置,比如:

        eureka.client.serviceUrl.defaultZone=http://eureka.didispace.com/eureka/

        更多相關配置請參與Spring Cloud Netflix Eureka的配置文檔。

        與Consul的整合

        如果使用consul,那么需要引入如下依賴:

        <!-- scca discovery dependency-->
        <dependency>
            <groupId>com.didispace</groupId>
            <artifactId>scca-discovery-consul</artifactId>
            <version>1.0.0-RELEASE</version>
        </dependency>

        并且在配置中加入consul的相關配置,比如:

        spring.cloud.consul.host=localhost
        spring.cloud.consul.port=8500

        更多相關配置請參與Spring Cloud Consul的配置文檔。

        公共配置

        SCCA REST模塊還有一個特別的配置scca.rest.context-path=/xhr,該配置主要用來配置所有SCCA REST模塊接口的前綴,該接口主要用于與SCCA UI模塊對接時候使用,兩邊必須對接一致才能順利對接。

        SCCA UI服務端的構建與配置

        SCCA UI服務端需要引入以下核心依賴:

        <dependency>
            <groupId>com.didispace</groupId>
            <artifactId>scca-ui</artifactId>
            <version>1.0.0-RELEASE</version>
        </dependency>

        另外,還需要在配置中指定具體要訪問的SCCA REST模塊的位置,主要有兩種模式:

        • 指定地址的配置:
        scca.ui.rest-server-url=http://localhost:10130
        • 基于服務發現的配置:
        scca.ui.rest-server-name=scca-rest-server

        除了上面的配置之后,還需要引入eureka或consul的依賴以及做對應的配置

        最后,還有一個scca.ui.rest-server-context-path=/xhr配置,用來描述要訪問的SCCA REST模塊接口的前綴,與SCCA REST服務端的scca.rest.context-path=/xhr配置相對應。

        案例:SCCA UI 服務端

        管理功能

        通過之前介紹的任何一個部署方式搭建了配置中心和管理端之后,我們就可以打開瀏覽器訪問我們的UI模塊實現對配置中心的管理了。

        訪問地址為:http://localhost:10032/admin/,ip與端口根據實際部署UI模塊的情況進行調整。

        系統配置

        在管理各個項目的配置之前,我們需要先做一些基礎配置,比如:環境的配置、環境所屬的參數配置,加密相關的配置等。

        環境配置

        環境配置主要用來維護要使用SCCA統一管理的環境以及對應的Spring Cloud Config服務端信息。

        ![user-guilder-2.png
        ](https://upload-images.jianshu...

        如上圖所示,通過“新增環境”按鈕可以添加一個部署環境。當我們使用了Eureka、Consul等注冊中心時,只需要配置注冊中心的訪問地址和配置中心的服務名以及配置中心訪問的前綴,后續就可以方便的使用這個環境的配置中心來進行加密解密、拉取配置等一系列的操作了。

        如果不采用服務發現的機制取找到配置中心,也可以將注冊中心地址留空,配置中心服務名一欄直接配置訪問注冊中心的URL即可。

        環境參數配置

        環境參數配置主要用來配置每個環境所屬的一些特有配置信息,比如:redis的地址,eureka的地址等等。這些配置信息將用戶后續為各項目在各個環境配置的時候給予參考和快捷的替換操作提供元數據。

        加密管理

        加密管理主要用來維護一些通常需要加密的Key,這樣可以在后續編輯配置內容的時候,方便的進行批量加密操作。

        配置中心

        在完成了上面的系統配置之后,用戶就可以進入配置中心模塊,這里會提供具體的管理配置內容的功能。目前主要有兩部分組成:項目管理和配置管理。

        項目管理

        項目管理主要用來維護需要在各個環境部署的應用的配置信息,這里可以維護這個項目需要部署在什么環境,有多少配置的版本。

        user-guilder-4.png

        這里的三個基本概念與Spring Cloud Config的幾個概念的對應關系如下:

        • 項目名稱:application
        • 部署環境:profile
        • 配置版本:label
        這里配置版本(label),我們會默認采用master。需要同時存在多個配置版本,實現灰度配置的時候,用戶也可以自己添加label。

        配置管理

        配置管理功能是SCCA的核心,在這里用戶可以方便對各個應用、各個環境、各個版本的配置進行編輯、加密等操作。同時,也提供了一些快捷的操作,比如:根據環境參數配置一鍵替換、根據加密Key清單實現一鍵加密、通過配置中心可以加載到的配置信息等(更多便捷功能持續添加中...)

        user-guilder-5.png

        客戶端接入

        本頁主要提供給沒有使用過Spring Cloud Config的用戶閱讀。如果您已經使用過Spring Cloud Config,那么客戶端如何通過Spring Cloud Config的配置中心加載配置相信已經掌握,在使用本項目的時候,無非就是搭建SCCA-REST模塊和SCCA-UI模塊來幫助管理您目前的配置內容。

        客戶端加載

        通過前面幾節內容,如果您已經完成了SCCA中幾個要素的搭建,下面就來看看如何創建一個Spring Boot項目并通過配置中心來加載配置信息。

        絕對地址接入

        1. 創建一個基本的Spring Boot項目,并在pom.xml中引入依賴

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>

        2. 創建應用主類

        @SpringBootApplication
        public class Application {
        
            public static void main(String[] args) {
                new SpringApplicationBuilder(Application.class).web(true).run(args);
            }
        
        }

        3. 創建bootstrap.properties配置文件(也可以使用yaml可以)

        spring.application.name=config-client
        server.port=12000
        
        spring.cloud.config.uri=http://localhost:10032/scca-config-server
        spring.cloud.config.profile=stage
        spring.cloud.config.label=master

        上述配置參數與scca中維護元素的對應關系如下:

        • spring.application.name:對應scca中的項目名
        • spring.cloud.config.profile:項目配置的環境名
        • spring.cloud.config.label:項目配置的版本名
        • spring.cloud.config.uri:配置中心的訪問絕對地址

        服務發現接入

        1. 創建一個基本的Spring Boot項目,并在pom.xml中引入依賴

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
        上面以通過eureka做注冊中心的依賴,如果用consul,只需要將spring-cloud-starter-eureka換成spring-cloud-starter-consul-discovery即可。

        2. 創建應用主類

        @EnableDiscoveryClient
        @SpringBootApplication
        public class Application {
        
            public static void main(String[] args) {
                new SpringApplicationBuilder(Application.class).web(true).run(args);
            }
        
        }

        3. 創建bootstrap.properties配置文件(也可以使用yaml可以)

        spring.application.name=config-client
        server.port=12000
        
        spring.cloud.config.discovery.enabled=true
        spring.cloud.config.discovery.serviceId=config-server
        spring.cloud.config.profile=stage
        spring.cloud.config.label=master

        上述配置參數與scca中維護元素的對應關系如下:

        • spring.application.name:對應scca中的項目名
        • spring.cloud.config.profile:項目配置的環境名
        • spring.cloud.config.label:項目配置的版本名
        • spring.cloud.config.discovery.enabled:開啟服務發現功能
        • spring.cloud.config.discovery.serviceId:配置中心的服務名

        讀取配置

        通過上面的兩種方式從配置中心拉取配置之后,在Spring Boot項目中就可以輕松的使用所有配置內容了,比如:

        @RefreshScope
        @RestController
        public class TestController {
        
            @Value("${a.b.c}")
            private String abc;
        
            @RequestMapping("/abc")
            public String abc() {
                return this.abc;
            }
        
        }

        兩個主要注解的說明:

        • @Value("${a.b.c}"):讀取配置key為a.b.c的value值
        • @RefreshScope:下面的配置信息可以通過/refresh端點實現動態刷新

        其他參考

        如果您還不了解Spring Cloud Config,您也可以閱讀下面的幾篇了解一下最原始的Spring Cloud Config配置中心和客戶端接入方式

        查看原文

        贊 37 收藏 25 評論 0

        rustic_z 回答了問題 · 2018-03-22

        register db 'default', sql :unknown driver ""。

        這是因為beego沒有默認引入mysql驅動,需要你自己區導入mysql驅動包。

        go get -u github.com/go-sql-driver/mysql

        下載mysql驅動包到本地,然后在main.go或者你的db init的類中import該包就行了。

        _ "github.com/go-sql-driver/mysql"

        關注 1 回答 1

        rustic_z 收藏了文章 · 2018-01-30

        kafka設計與原理詳解

        Kafka 設計與原理詳解

        [TOC]

        kafka 應用場景

        • 日志收集

        • 消息系統 解耦生產者和消費者、緩存消息。

        • 用戶活動跟蹤: 就是我們在做的。

        • 運營指標:生產各種操作的集中反饋。

        • 流式處理:比如spark steaming

        kafka的發布對象是topic。每類數據我們可以歸為一個topic。向topic發送消息的我們稱為生產者、從topic訂閱消息的稱為consumer。producer 和 consumer 可以同時讀寫數據。

        • topic: 消息主題。

        • producer: 生產者到topic的一方。

        • consumer: 訂閱topic消費消息的一方。

        • broker :

        kafka topic & partition

        Alt text

        kafka 集群會保存所有消息,不管消息有沒有被消費;通過設置消息過期時間,可以來定制的刪除消息。比如我們設置過期時間為2天。

        一個消息被生產出來,寫入到多個partition。消息就是以partition作為存儲單位,每個partition可以通過調整以適應它所在的機器,而一個topic對應多個partition,這樣整個集群就可以適應各個大小的數據了。第二,也可以提高并發,因為可以以partition 為單位來讀寫了。

        Kafka 核心組件

        replications partitions and leaders

        怎么實現持久化?

        kafka能夠做數據持久化??梢詾槊總€topic設置副本容量。 如果副本容量設為3,那么一份數據就會被放在3臺不同的機器上。一般設為2.

        關于partition。

        topic的存放形式是partition。每一個topic都可以設置partition數量。partition的數量決定了log的數量。producer 在生產消息時,會把消息發布到topic的各個partition中。
        上面說的副本都是以partition為單位的,不過只有一個partition的副本會被選為leader作為讀寫用。

        kafka從0.8開始提供partition級別的replication,replication的數量可在$KAFKA_HOME/config/server.properties中配置。

        default.replication.factor = 1
        

        如何設置partition值要考慮的因素?

        一個partition只能被一個消費者消費(但是一個消費者可以同時消費多個partition。),所以,運行的partition的數量要大于運行的comsumer的數量,否則就會有消費者消費不到數據。另一方面,建議partition的數量大于broker 的數量。這樣leader partition 的數據就能均勻的分布在各個broker中,最終使得集群負載均衡。

        (如果小于會怎樣樣,會造成比較集中的存儲在單個broker之中嗎。)。注意:kafka需要為每個partition分配一些內存來緩存消息數據,如果parttion數量越大,分配更大的heap space。

        partition每一個都會保存作為一個repilca么? 不是的。partition的概念是根據partition 方法來將數據分布存儲。

        producers

        producer發送消息。

        producer 可以直接發送到broker對應的leader partition中,不需要經歷任何一個中介的轉發。為實現這個特性,每個broker都可以響應producer的請求,并返回topic的一些元信息,這些元信息包括哪些機器是存活的,topic的你leader partition都在哪?,F階段哪些leader partition 是可以直接訪問的?

        如果訪問的不是leader partition 怎么搞? 而且我看是可以指定多個進行訪問的。

        producer 和 partition 。

        producer 可以控制以什么樣的將消息推送到客戶端。實現方法包括隨機,實現一類隨機負載均衡的算法,或者指定一些分區算法。kafka 提供了用戶自定義分區的方法,用戶可以為每一個消息指定一個partitionkey,通過這個key來實現一些hash 分區算法。

        效率。

        batch的方式將有效的提高效率,減少網絡和磁盤io的占用。這里batch的大小,可以再producer來設置,比如煥春100s,緩存1000條,或者數據的大小。

        關于消息的完整性。

        producer 可以異步的并行的向kafka發送消息,但是通常你producer在發送完消息之后會得到一個future的響應,返回的是offset或者發送過程遇到的錯誤。這里,acks 這參數很重要,這個參數決定了producer要求leader partition收到的確認副本數。如果acks設置數量為0,表示producer不會等待broker的響應,所以,producer無法知道消息是否發送成功,這有可能會導致數據丟失,但這也是吞吐量最大的方式。

        如果acks設置為1,表示producer 和laeder partition收到消息的時得到的broker的一個確認,這樣會有更好的可靠性。如果設置為-1,則組要等待所有partition收到消息。這樣能保持最高的可靠性。

        kafka 消息。

        kafka消息有一個定長的header和變長的字節組成。kafka沒有限定單個消息的大小,但一般不超過一mb,通??刂圃?-10kb之間。

        Consumers

        kafka 提供了兩套api。sample api 。是一套無狀態的api。每次請求都需要指定offset。所以也是最靈活的。

        在kafka中,當前消息的offset是由consumer來維護的。consumer可以自己決定讀哪些數據。比如,consumer 可以重新消費已經消費國的數據。這些數據有一個過期限制。這個限制是可配置的。

        high-level api 封裝了對集群的訪問??梢酝该鞯南M一個topic。自己本身維持了一個消費隊列,每次消費下一個。

        這里consumer 用組來模擬了廣播和訂閱兩個功能。組是嫁接topic和consumer 的橋梁。 組對topic是來說是組內的成員都可以接受到消息,相當于廣播,組對成員來說,是訂閱,即你在這個組里才能接受到這個消息。所以都在一個組,就相當于一個大廣播。

        kafka 的核心特性

        壓縮

        kafka 支持以batch的方式來發送消息。在此之上,還支持對消息的壓縮。 producer端進行壓縮之后,在consumer端進行解壓。這么做的好處是,往往大數據的瓶頸在于網絡,而不是cpu(所以會損耗一定的cpu。)
        消息壓縮的信息,存儲在消息頭部的描述壓縮屬性字節。這個字節的后兩位表示消息的壓縮采用的編碼,若后兩位為0,則表示消息未被壓縮。

        消息可靠性

        在消息系統中,保證消息的可靠性是很重要的。在實際消息的傳遞過程中,會出現如下3種情況:

        • 一個消息傳遞失敗

        • 一個消息被發送多次

        • exactly once,一個消息發送成功并且僅發送了一次。

        有許多系統聲稱它們實現了exactly-once,但一般沒有考慮生產者或消費者在生產和消費過程中有可能失敗的情況。比如雖然一個producer成功發送一個消息,但消息丟失,或者成功發送到broker,也被consumer成功取走,但是這個consumer在處理消息時失敗了。

        這里從兩個角度來分析這個。

        從producer的角度:在發送端,看producer會等待broker成功接收到消息的反饋,如果沒有接到broker的反饋信息,producer 會重新發送,(我們知道kafka有備份機制,可以通過參數設置是否等待所有節點都收到消息,而本身的消息也有緩存)

        從consumer的角度:因為consumer 可以調整offset,所以可以重復消費消息。也保證了,一條消息被發送一次就ok。

        備份機制

        備份機制是Kafka0.8版本的新特性,備份機制的出現大大提高了Kafka集群的可靠性、穩定性。有了備份機制后,Kafka允許集群中的節點掛掉后而不影響整個集群工作。一個備份數量為n的集群允許n-1個節點失敗。在所有備份節點中,有一個節點作為lead節點,這個節點保存了其它備份節點列表,并維持各個備份間的狀體同步。下面這幅圖解釋了Kafka的備份機制:
        Alt text

        kafka 高效性相關設計

        消息持久化

        首先這里,kafka是高度依賴文件系統和緩存的。

        文件系統的速度。文件系統的速度并不是想象中的慢或者快。對于,順序寫入和隨機寫入兩者有很大速度差。一個7200的硬盤順序寫入有600m/s的速度,隨機寫入有100k/s的速度。

        緩存思路。所以,基本的數據寫入思路是,先拿內存緩存數據再刷新到磁盤。但是,眾所周知,內存的垃圾回收的代價很大,尤其當數據量過大的時候,垃圾回收會非常昂貴。

        感覺這塊理解的不是很好
        基于以上,得到的一個結論就是利用文件系統并且依靠頁緩存比維護一個內存緩存或者其他結構要好。而事實上,數據被傳輸到內核頁,稍后會被刷新。這里加上了一個配置項來控制讓系統的用戶來控制數據在什么時候被刷新到物理硬盤上。

        常數時間性能保證

        沒太理解
        消息系統中持久化數據結構的設計通常是維護者一個和消費隊列有關的B樹或者其它能夠隨機存取結構的元數據信息。B樹是一個很好的結構,可以用在事務型與非事務型的語義中。但是它需要一個很高的花費,盡管B樹的操作需要O(logN)。通常情況下,這被認為與常數時間等價,但這對磁盤操作來說是不對的。磁盤尋道一次需要10ms,并且一次只能尋一個,因此并行化是受限的。
        直覺上來講,一個持久化的隊列可以構建在對一個文件的讀和追加上,就像一般情況下的日志解決方案。盡管和B樹相比,這種結構不能支持豐富的語義,但是它有一個優點,所有的操作都是常數時間,并且讀寫之間不會相互阻塞。這種設計具有極大的性能優勢:最終系統性能和數據大小完全無關,服務器可以充分利用廉價的硬盤來提供高效的消息服務。
        事實上還有一點,磁盤空間的無限增大而不影響性能這點,意味著我們可以提供一般消息系統無法提供的特性。比如說,消息被消費后不是立馬被刪除,我們可以將這些消息保留一段相對比較長的時間(比如一個星期)。

        進一步提高效率

        在web開發中,每次一條log都會產生一次寫操作,這些小的寫操作的量非常大,另外這些log也要至少被一個或以上consumer消費。
        所以,這里出現了兩個比較低效的場景。

        • 太多小的io操作。

        • 過多的字節拷貝。

        太多小的io操作??梢酝ㄟ^發送messageset來搞定。所以對消息的處理,這里沒有分開的序列化和反序列化的上步驟,消息的字段可以按需反序列化(如果沒有需要,可以不用反序列化)。

        過多的字節拷貝。 為了解決這個問題,kafka設計了一個標準字節消息。producer,broker,consumer共享這一種消息格式。kafka的message log 在broker端就是一些目錄文件。這些文件都是按照message set 來存的。

        而這種通用的方式,非常重要: 持久化log塊的網絡傳輸。這傳輸通過一鐘非常搞笑的途徑來實現頁面緩存和socket之間的數據傳遞。 叫sendfile

        這里解釋下sendfile的作用,先聲明下一般的數據從文件到socket的路徑:

        1. 操作系統將數據從磁盤讀到內核空間的頁緩存中。

        2. 應用將數據從內核空間讀到用戶空間的頁緩存中。

        3. 應用將數據從用戶空間寫到內核空間的socket緩存。

        4. 操作系統將數據從socket緩存寫入到網卡緩存中。

        這鐘方式非常低效,因為這里有四次拷貝,兩次系統調用。如果使用sendfile,就可以避免兩次拷貝:操作系統將數據直接從頁緩存發送到網絡上。所以這個過程,只有第一步和最后一步是需要的。利用上述zero copy,數據只需要拷貝到頁緩存一次,就可以重復被consumer利用。這樣通過頁緩存和sendfile的結合,下游有很多consumers,也不會對整個集群服務造成壓力。

        kafka 集群部署

        為了提高性能,盡量與hadoop的集群分開部署。如果共享節點的話,會影響其使用頁面緩存的性能。

        kafka的性能主要在磁盤上。

        kafka依賴于zookper,一般使用專用服務器來管理。zookeeper集群的節點采用偶數個。注意,zookeeper集群越大其讀寫性能越慢,因為zookeeper 要在節點之間同步數據。一個3節點的zookeeper集群允許一個節點失敗,一個5節點的集群允許2個節點失敗。

        集群大小

        衡量kafka集群所需的大小,最好是用模擬負載來測算一下。如果不想用模擬實驗,最好的方法是根據磁盤。

        kafka 主要配置

        broker config

        log.dirs /tmp/kafka-logs Kafka數據存放的目錄??梢灾付ǘ鄠€目錄,中間用逗號分隔,當新partition被創建的時會被存放到當前存放partition最少的目錄。

        參考

        http://blog.csdn.net/suifeng3051/article/details/48053965

        查看原文

        rustic_z 贊了文章 · 2018-01-30

        kafka設計與原理詳解

        Kafka 設計與原理詳解

        [TOC]

        kafka 應用場景

        • 日志收集

        • 消息系統 解耦生產者和消費者、緩存消息。

        • 用戶活動跟蹤: 就是我們在做的。

        • 運營指標:生產各種操作的集中反饋。

        • 流式處理:比如spark steaming

        kafka的發布對象是topic。每類數據我們可以歸為一個topic。向topic發送消息的我們稱為生產者、從topic訂閱消息的稱為consumer。producer 和 consumer 可以同時讀寫數據。

        • topic: 消息主題。

        • producer: 生產者到topic的一方。

        • consumer: 訂閱topic消費消息的一方。

        • broker :

        kafka topic & partition

        Alt text

        kafka 集群會保存所有消息,不管消息有沒有被消費;通過設置消息過期時間,可以來定制的刪除消息。比如我們設置過期時間為2天。

        一個消息被生產出來,寫入到多個partition。消息就是以partition作為存儲單位,每個partition可以通過調整以適應它所在的機器,而一個topic對應多個partition,這樣整個集群就可以適應各個大小的數據了。第二,也可以提高并發,因為可以以partition 為單位來讀寫了。

        Kafka 核心組件

        replications partitions and leaders

        怎么實現持久化?

        kafka能夠做數據持久化??梢詾槊總€topic設置副本容量。 如果副本容量設為3,那么一份數據就會被放在3臺不同的機器上。一般設為2.

        關于partition。

        topic的存放形式是partition。每一個topic都可以設置partition數量。partition的數量決定了log的數量。producer 在生產消息時,會把消息發布到topic的各個partition中。
        上面說的副本都是以partition為單位的,不過只有一個partition的副本會被選為leader作為讀寫用。

        kafka從0.8開始提供partition級別的replication,replication的數量可在$KAFKA_HOME/config/server.properties中配置。

        default.replication.factor = 1
        

        如何設置partition值要考慮的因素?

        一個partition只能被一個消費者消費(但是一個消費者可以同時消費多個partition。),所以,運行的partition的數量要大于運行的comsumer的數量,否則就會有消費者消費不到數據。另一方面,建議partition的數量大于broker 的數量。這樣leader partition 的數據就能均勻的分布在各個broker中,最終使得集群負載均衡。

        (如果小于會怎樣樣,會造成比較集中的存儲在單個broker之中嗎。)。注意:kafka需要為每個partition分配一些內存來緩存消息數據,如果parttion數量越大,分配更大的heap space。

        partition每一個都會保存作為一個repilca么? 不是的。partition的概念是根據partition 方法來將數據分布存儲。

        producers

        producer發送消息。

        producer 可以直接發送到broker對應的leader partition中,不需要經歷任何一個中介的轉發。為實現這個特性,每個broker都可以響應producer的請求,并返回topic的一些元信息,這些元信息包括哪些機器是存活的,topic的你leader partition都在哪?,F階段哪些leader partition 是可以直接訪問的?

        如果訪問的不是leader partition 怎么搞? 而且我看是可以指定多個進行訪問的。

        producer 和 partition 。

        producer 可以控制以什么樣的將消息推送到客戶端。實現方法包括隨機,實現一類隨機負載均衡的算法,或者指定一些分區算法。kafka 提供了用戶自定義分區的方法,用戶可以為每一個消息指定一個partitionkey,通過這個key來實現一些hash 分區算法。

        效率。

        batch的方式將有效的提高效率,減少網絡和磁盤io的占用。這里batch的大小,可以再producer來設置,比如煥春100s,緩存1000條,或者數據的大小。

        關于消息的完整性。

        producer 可以異步的并行的向kafka發送消息,但是通常你producer在發送完消息之后會得到一個future的響應,返回的是offset或者發送過程遇到的錯誤。這里,acks 這參數很重要,這個參數決定了producer要求leader partition收到的確認副本數。如果acks設置數量為0,表示producer不會等待broker的響應,所以,producer無法知道消息是否發送成功,這有可能會導致數據丟失,但這也是吞吐量最大的方式。

        如果acks設置為1,表示producer 和laeder partition收到消息的時得到的broker的一個確認,這樣會有更好的可靠性。如果設置為-1,則組要等待所有partition收到消息。這樣能保持最高的可靠性。

        kafka 消息。

        kafka消息有一個定長的header和變長的字節組成。kafka沒有限定單個消息的大小,但一般不超過一mb,通??刂圃?-10kb之間。

        Consumers

        kafka 提供了兩套api。sample api 。是一套無狀態的api。每次請求都需要指定offset。所以也是最靈活的。

        在kafka中,當前消息的offset是由consumer來維護的。consumer可以自己決定讀哪些數據。比如,consumer 可以重新消費已經消費國的數據。這些數據有一個過期限制。這個限制是可配置的。

        high-level api 封裝了對集群的訪問??梢酝该鞯南M一個topic。自己本身維持了一個消費隊列,每次消費下一個。

        這里consumer 用組來模擬了廣播和訂閱兩個功能。組是嫁接topic和consumer 的橋梁。 組對topic是來說是組內的成員都可以接受到消息,相當于廣播,組對成員來說,是訂閱,即你在這個組里才能接受到這個消息。所以都在一個組,就相當于一個大廣播。

        kafka 的核心特性

        壓縮

        kafka 支持以batch的方式來發送消息。在此之上,還支持對消息的壓縮。 producer端進行壓縮之后,在consumer端進行解壓。這么做的好處是,往往大數據的瓶頸在于網絡,而不是cpu(所以會損耗一定的cpu。)
        消息壓縮的信息,存儲在消息頭部的描述壓縮屬性字節。這個字節的后兩位表示消息的壓縮采用的編碼,若后兩位為0,則表示消息未被壓縮。

        消息可靠性

        在消息系統中,保證消息的可靠性是很重要的。在實際消息的傳遞過程中,會出現如下3種情況:

        • 一個消息傳遞失敗

        • 一個消息被發送多次

        • exactly once,一個消息發送成功并且僅發送了一次。

        有許多系統聲稱它們實現了exactly-once,但一般沒有考慮生產者或消費者在生產和消費過程中有可能失敗的情況。比如雖然一個producer成功發送一個消息,但消息丟失,或者成功發送到broker,也被consumer成功取走,但是這個consumer在處理消息時失敗了。

        這里從兩個角度來分析這個。

        從producer的角度:在發送端,看producer會等待broker成功接收到消息的反饋,如果沒有接到broker的反饋信息,producer 會重新發送,(我們知道kafka有備份機制,可以通過參數設置是否等待所有節點都收到消息,而本身的消息也有緩存)

        從consumer的角度:因為consumer 可以調整offset,所以可以重復消費消息。也保證了,一條消息被發送一次就ok。

        備份機制

        備份機制是Kafka0.8版本的新特性,備份機制的出現大大提高了Kafka集群的可靠性、穩定性。有了備份機制后,Kafka允許集群中的節點掛掉后而不影響整個集群工作。一個備份數量為n的集群允許n-1個節點失敗。在所有備份節點中,有一個節點作為lead節點,這個節點保存了其它備份節點列表,并維持各個備份間的狀體同步。下面這幅圖解釋了Kafka的備份機制:
        Alt text

        kafka 高效性相關設計

        消息持久化

        首先這里,kafka是高度依賴文件系統和緩存的。

        文件系統的速度。文件系統的速度并不是想象中的慢或者快。對于,順序寫入和隨機寫入兩者有很大速度差。一個7200的硬盤順序寫入有600m/s的速度,隨機寫入有100k/s的速度。

        緩存思路。所以,基本的數據寫入思路是,先拿內存緩存數據再刷新到磁盤。但是,眾所周知,內存的垃圾回收的代價很大,尤其當數據量過大的時候,垃圾回收會非常昂貴。

        感覺這塊理解的不是很好
        基于以上,得到的一個結論就是利用文件系統并且依靠頁緩存比維護一個內存緩存或者其他結構要好。而事實上,數據被傳輸到內核頁,稍后會被刷新。這里加上了一個配置項來控制讓系統的用戶來控制數據在什么時候被刷新到物理硬盤上。

        常數時間性能保證

        沒太理解
        消息系統中持久化數據結構的設計通常是維護者一個和消費隊列有關的B樹或者其它能夠隨機存取結構的元數據信息。B樹是一個很好的結構,可以用在事務型與非事務型的語義中。但是它需要一個很高的花費,盡管B樹的操作需要O(logN)。通常情況下,這被認為與常數時間等價,但這對磁盤操作來說是不對的。磁盤尋道一次需要10ms,并且一次只能尋一個,因此并行化是受限的。
        直覺上來講,一個持久化的隊列可以構建在對一個文件的讀和追加上,就像一般情況下的日志解決方案。盡管和B樹相比,這種結構不能支持豐富的語義,但是它有一個優點,所有的操作都是常數時間,并且讀寫之間不會相互阻塞。這種設計具有極大的性能優勢:最終系統性能和數據大小完全無關,服務器可以充分利用廉價的硬盤來提供高效的消息服務。
        事實上還有一點,磁盤空間的無限增大而不影響性能這點,意味著我們可以提供一般消息系統無法提供的特性。比如說,消息被消費后不是立馬被刪除,我們可以將這些消息保留一段相對比較長的時間(比如一個星期)。

        進一步提高效率

        在web開發中,每次一條log都會產生一次寫操作,這些小的寫操作的量非常大,另外這些log也要至少被一個或以上consumer消費。
        所以,這里出現了兩個比較低效的場景。

        • 太多小的io操作。

        • 過多的字節拷貝。

        太多小的io操作??梢酝ㄟ^發送messageset來搞定。所以對消息的處理,這里沒有分開的序列化和反序列化的上步驟,消息的字段可以按需反序列化(如果沒有需要,可以不用反序列化)。

        過多的字節拷貝。 為了解決這個問題,kafka設計了一個標準字節消息。producer,broker,consumer共享這一種消息格式。kafka的message log 在broker端就是一些目錄文件。這些文件都是按照message set 來存的。

        而這種通用的方式,非常重要: 持久化log塊的網絡傳輸。這傳輸通過一鐘非常搞笑的途徑來實現頁面緩存和socket之間的數據傳遞。 叫sendfile

        這里解釋下sendfile的作用,先聲明下一般的數據從文件到socket的路徑:

        1. 操作系統將數據從磁盤讀到內核空間的頁緩存中。

        2. 應用將數據從內核空間讀到用戶空間的頁緩存中。

        3. 應用將數據從用戶空間寫到內核空間的socket緩存。

        4. 操作系統將數據從socket緩存寫入到網卡緩存中。

        這鐘方式非常低效,因為這里有四次拷貝,兩次系統調用。如果使用sendfile,就可以避免兩次拷貝:操作系統將數據直接從頁緩存發送到網絡上。所以這個過程,只有第一步和最后一步是需要的。利用上述zero copy,數據只需要拷貝到頁緩存一次,就可以重復被consumer利用。這樣通過頁緩存和sendfile的結合,下游有很多consumers,也不會對整個集群服務造成壓力。

        kafka 集群部署

        為了提高性能,盡量與hadoop的集群分開部署。如果共享節點的話,會影響其使用頁面緩存的性能。

        kafka的性能主要在磁盤上。

        kafka依賴于zookper,一般使用專用服務器來管理。zookeeper集群的節點采用偶數個。注意,zookeeper集群越大其讀寫性能越慢,因為zookeeper 要在節點之間同步數據。一個3節點的zookeeper集群允許一個節點失敗,一個5節點的集群允許2個節點失敗。

        集群大小

        衡量kafka集群所需的大小,最好是用模擬負載來測算一下。如果不想用模擬實驗,最好的方法是根據磁盤。

        kafka 主要配置

        broker config

        log.dirs /tmp/kafka-logs Kafka數據存放的目錄??梢灾付ǘ鄠€目錄,中間用逗號分隔,當新partition被創建的時會被存放到當前存放partition最少的目錄。

        參考

        http://blog.csdn.net/suifeng3051/article/details/48053965

        查看原文

        贊 4 收藏 62 評論 0

        rustic_z 關注了問題 · 2016-08-19

        解決git忽略已經被提交的文件

        現在項目的根目錄放了 .gitignore 文件,并且git遠程倉庫的項目根目錄已經有了 logs文件夾。

        由于每次本地運行項目,都會生成新的log文件,但是我并不想提交logs文件夾里面的內容,所以要在.gitignore寫logs的規則。

        我嘗試過添加以下規則
        logs/*.log
        logs/
        /logs/

        但是運行git status的時候,始終能看到modified:logs/xx.log 。

        請問是我的規則編寫錯誤,還是我某個地方有理解錯誤?

        關注 74 回答 8

        rustic_z 關注了問題 · 2016-07-31

        lnmp環境搭建wordpress不加載css?

        最近在美國搬瓦工vps搭建了一個lnmp環境.用的是lnmp一鍵安裝包.但是出現wordpress無法加載css的問題.

        • 問題描述:

        我在nginx配置了一個虛擬機,新安裝wordpress后,能正常進入到wordpress的安裝頁面,但是沒有樣式,只有html.如下圖:
        圖片描述


        我打開開發者工具查看,如下:
        圖片描述


        可以看到樣式都是已經加載了的.
        但是我發現有些樣式加載后返回的是空,列入這樣
        紅框中的兩個樣式都是空的,不知道是什么原因.
        clipboard.png


        當我配置完wordpress后,進入后臺,發現后臺的css是正確加載了

        clipboard.png


        后臺配置好后,返回首頁,老問題又出現了

        clipboard.png


        請各位朋友看下原因.

        關注 4 回答 2

        認證與成就

        • 獲得 1 次點贊
        • 獲得 5 枚徽章 獲得 0 枚金徽章, 獲得 1 枚銀徽章, 獲得 4 枚銅徽章

        擅長技能
        編輯

        (??? )
        暫時沒有

        開源項目 & 著作
        編輯

        (??? )
        暫時沒有

        注冊于 2015-12-25
        個人主頁被 382 人瀏覽

        一本到在线是免费观看_亚洲2020天天堂在线观看_国产欧美亚洲精品第一页_最好看的2018中文字幕