1. 程式人生 > >Dubbo學習系列之十六(ELK海量日誌分析框架)

Dubbo學習系列之十六(ELK海量日誌分析框架)

  外賣公司如何匹配騎手和訂單?淘寶如何進行商品推薦?或者讀者興趣匹配?還有海量資料儲存搜尋、實時日誌分析、應用程式監控等場景,Elasticsearch或許可以提供一些思路,作為業界最具影響力的海量搜尋與分析產品,搜尋軟體公司 Elastic 上市了!首日市值翻倍!Elastic 從小工具「逆襲」成為上市公司,依靠其技術影響者眾多企業,並促進整個行業發展的模式變革,向眾多渴望創業的程式設計師證明了一個道理:技術創業是可行的,並且有著良好的前景。你要不要試試呢?

準備:

Idea2019.03/Gradle5.6.2/JDK11.0.4/RHEL7.6/VMware15Pro/Lombok0.27/logback1.2.3/SpringBoot2.2.0RELEASE/ElasticSearch7.2.0/LogStash7.2.0/Kibana7.2.0/NodeJs10.14.2/npm6.4.1/Git2.18.0

難度:新手--戰士--老兵--大師

目標:

1.Logback使用複習

2.Linux下ELK框架搭建

3.Springboot整合ELK實現海量日誌處理框架

4.Springboot下使用ES的API

步驟:

為了遇見各種問題,同時保持時效性,我儘量使用最新的軟體版本。程式碼地址:其中的day21,https://github.com/xiexiaobiao/dubbo-project.git

Part1 Linux下的ELK

1.先介紹下ELK套件:

  • ElasticSearch:(以下簡稱ES)搜尋引擎。基於Lucene打造,特點是分散式、零配置、自動發現、索引自動分片、索引副本機制,最方便的就是Restful介面。能夠水平擴充套件,每秒鐘可處理海量事件,同時能夠自動管理索引和查詢在叢集中的分佈方式,以實現極其流暢的操作。
  • Logstash:資料採集器。可同一時刻採集多來源的資料,以連續流傳輸,並能實時解析和轉換資料,能自定義過濾器,最後將資料傳送到指定儲存庫, 當然,ES 是其首選儲存庫。其採用可插拔框架,擁有 200 多個外掛。可將不同的輸入選擇、過濾器和輸出選擇混合搭配。
  • Kibana:ES資料視覺化工具,如柱狀圖、線狀圖、餅圖、旭日圖等,這些類似於常用的報表工具,支援許可權訪問控制,還有特定的查詢語法來進行復雜的查詢操作。

其實:ES可以用作文件型儲存,類似MongoDB,適用於非事務型分散式儲存場景。API十分豐富,但也存在一定的難度和複雜度。

典型的 ELK 套件方案:

 

  • Beats:如果考慮到機器負載問題,還有輕量級(相比Logstash)的beat元件級資料採集器,能從成千上萬臺機器和系統向 Logstash 或 ES 傳送資料,Beats是一個系列,有Filebeat/Packetbeat/Winlogbeat/Heartbeat/Auditbeat等,用於採集不同來源類別的資料。如果再加上緩衝層,可演變為如下強大架構,併發能力更上一層樓!

 

 

2.Linux虛擬機器的安裝、網路、檔案共享、YUM安裝見我下篇,或者網搜,想必進入這個文章的linux也該略有基礎了。

3.我這裡 ELK 三者全部安裝在一臺Linux虛擬機器(IP:192.168.1.204)上,注意下載的ELK版本要一致,目前最新為V7.4.2,但下載實在蝸牛速度,只好先用點已有的舊貨上場,抱歉!

4.開始ES的安裝:下載elasticsearch-7.2.0-linux-x86_64.tar.gz,放/usr/elastic下,並解壓,ES不能使用root使用者啟動,會提示錯誤!

 

 

 切換為普通使用者,並將檔案主更新為普通使用者,再啟動:

[root@localhost ~]# chown -Rv biao /usr/elastic/
[biao@localhost usr]$ ./elastic/elasticsearch-7.2.0/bin/elasticsearch

5.首次啟動測試:

[root@localhost ~]# curl localhost:9200
[root@localhost ~]# curl localhost:9300

 

 6.預設情況下,ES 只允許本機訪問,如果需要遠端訪問,可以修改 ES 安裝目錄的config/elasticsearch.yml檔案,去掉network.host的註釋,並將它的值改成所在OS的IP:192.168.1.204,然後重新啟動 ES。

[root@localhost ~]# vim /usr/elastic/elasticsearch-7.2.0/config/elasticsearch.yml

 

如果需要從window主機訪問,注意開啟Linux相應的埠或直接關閉防火牆, URL訪問:http://192.168.1.204:9200/再次啟動出現錯誤,提示有3個問題,各個擊破!

 

每個程序最大同時開啟檔案數太小:

[root@localhost usr]# sysctl -w vm.max_map_count=262144
vm.max_map_count = 262144

ulimit 用於限制 shell 啟動程序所佔用的資源:

[root@localhost usr]# vim /etc/security/limits.conf

 

檢視設定後的值:

[root@localhost usr]# ulimit -Hn
[root@localhost usr]# ulimit -Sn

最後設定一個seed_host,見步驟6中的第一圖,按Ctrl+c退出。

7.開始安裝elasticsearch-head:一款ES叢集視覺化管理工具,可直接操作ES的資料,這也太野了吧,生產中必須要加以限制!這個工具有多種方式安裝,比如doker/plugin/npm等,因linux上環境欠缺,我就直接在window上使用npm安裝了(window上先安裝node.js環境即可使用npm),這其實是將elasticsearch-head獨立執行,參考後面的(整合ELK整體目標架構圖):

D盤根目錄下,使用git bash命令,下載原始碼:

git clone git://github.com/mobz/elasticsearch-head.git

下載原始碼完成後CMD命令列操作:

C:\Users\KOOL>D:
D:\>cd D:\elasticsearch-head
D:\elasticsearch-head>npm install -g cnpm --registry=https://registry.npm.taobao.org
D:\elasticsearch-head>npm install
D:\elasticsearch-head>npm run start

如下圖即為安裝成功!

 

8.訪問:http://localhost:9100/

輸入ES的 IP+port --> connect, 如果此時顯示空白,請先使用 http://192.168.1.204:9200/ 測試確保外部可以連線ES,然後檢視:

 

即可確認為跨域問題,需修改ES配置檔案elasticsearch.yml,在檔案末尾加入以下配置,注意冒號後的空格!

  • http.cors.enabled: true #是否允許跨域
  • http.cors.allow-origin: "*"

 

9.再重啟ES,連線ES端,可以發現ES對logstash/kibana都做了儲存,果然是自家的,特殊照顧,字首有點號區分:

檢視indices資訊,以下為已經啟動了Logstash和Kibana的狀態:

 

node資訊:

 

檢視shard資訊:

 

10.開始Logstash安裝:

下載檔案logstash-7.2.0.tar.gz,略,放/usr/logstash下,解壓,測試logstash啟動是否正常:

[root@localhost logstash]cd logstash-7.2.0
[root@localhost logstash-7.2.0]# ./bin/logstash -e 'input { stdin { } } output { stdout {} }'

 

啟動後,輸入hello world,如下則成功!ctrl+D退出。

 

另外,可以下載測試資料做測試:

[root@localhost logstash-7.2.0]# wget http://files.grouplens.org/datasets/movielens/ml-latest-small.zip
[root@localhost logstash-7.2.0]# unzip ml-latest-small.zip
[root@localhost logstash-7.2.0]# vim config/logstash-test.conf

logstash-test.conf內容如下:

input {
  file {
    path => "/usr/logstash/logstash-7.2.0/ml-latest-small/movies.csv" #注意修改為自己的目錄
    start_position => "beginning"
    sincedb_path => "/dev/null"
  }
}
filter {
  csv {
    separator => ","
    columns => ["id","content","genre"]
  }
  mutate {
    split => { "genre" => "|" }
    remove_field => ["path", "host","@timestamp","message"]
  }
  mutate {

    split => ["content", "("]
    add_field => { "title" => "%{[content][0]}"}
    add_field => { "year" => "%{[content][2]}"}
  }
  mutate {
    convert => {
      "year" => "integer"
    }
    strip => ["title"]
    remove_field => ["path", "host","@timestamp","message","content"]
  }
}
output {
   elasticsearch {
     hosts => http://192.168.1.204:9200 #注意修改為自己的ES
     index => "movies"
     document_id => "%{id}"
   }
  stdout {}
}

執行下測試資料,注意先啟動ES:

[root@localhost logstash-7.2.0]# ./bin/logstash -f /usr/logstash/logstash-7.2.0/config/logstash-test.conf

報錯:There is insufficient memory for the Java Runtime Environment to continue.虛擬機器的記憶體不夠,如下命令檢視記憶體情況:

[root@localhost logstash-7.2.0]# free -h

建議直接虛擬機器修改為 4G 記憶體,再跑此測試資料!執行成功後,先放著。

11.開始Kibana安裝:下載,略,kibana-7.2.0-linux-x86_64.tar.gz複製到目錄/usr/kibana下,解壓:

[root@localhost ~]# cp /mnt/hgfs/00sharetoVM/kibana-7.2.0-linux-x86_64.tar.gz  /usr/kibana

以root啟動會提示不能使用root執行,可使用加 --allow-root 引數解決,這裡我直接換成普通使用者:

[root@localhost usr]# chown -Rv biao /usr/kibana/
[biao@localhost kibana-7.2.0-linux-x86_64]$ pwd
/usr/kibana/kibana-7.2.0-linux-x86_64
[biao@localhost kibana-7.2.0-linux-x86_64]$ vim config/kibana.yml

#以下為配置專案:

server.port: 5601
server.host: "192.168.1.204"  #虛擬機器的IP
elasticsearch.hosts: ["http://192.168.1.204:9200"]
kibana.index: ".kibana"

啟動Kibana,注意先啟動ES:

[biao@localhost kibana-7.2.0-linux-x86_64]$ ./bin/kibana

 

再配合上面處於啟動狀態的Logstash測試資料,外部開啟URL地址:http://192.168.1.204:5601/

 

12.啟動Kibana,如遇到錯誤:Elasticsearch cluster did not respond with license information.只需仔細配置 ES ,不是缺少xpack外掛,7.X已經整合該外掛了!

[biao@localhost elasticsearch-7.2.0]$ vim config/elasticsearch.yml

以下為配置項:

cluster.name: my-application
node.name: node-1
path.data: /tmp/es/data
path.logs: /tmp/es/logs
network.host: 192.168.1.204  #建議不要寫為網上的0.0.0.0
http.port: 9200
discovery.seed_hosts: ["192.168.1.204"]
cluster.initial_master_nodes: ["node-1"]

Part2 驗收測試

1.先實現Springboot應用整合ELK做日誌處理:整體目標架構如下圖:

 

2.建立springboot工程,我使用idea直接建一個簡單的gradle project,終於擺脫前面的mall專案了!

 

3.引入依賴,非常建議逐步引入,使用過程中觀察缺少依賴對應用的影響,這樣能更好的學習各個元件的作用:

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.12'
    // https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-parent
    compile group: 'org.springframework.boot', name: 'spring-boot-starter-parent', version: '2.2.0.RELEASE', ext: 'pom'

    //Core starter, including auto-configuration support, logging and YAML
    compile group: 'org.springframework.boot', name: 'spring-boot-starter', version: '2.2.0.RELEASE'
    //Starter for testing Spring Boot applications with libraries including JUnit, Hamcrest and Mockito
    testCompile group: 'org.springframework.boot', name: 'spring-boot-starter-test', version: '2.2.0.RELEASE'
    //Starter for building web, including RESTful, applications using Spring MVC. Uses Tomcat as the default embedded container
    compile group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: '2.2.0.RELEASE'
    //
    testCompile group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.3'
    // https://mvnrepository.com/artifact/net.logstash.logback/logstash-logback-encoder
    compile group: 'net.logstash.logback', name: 'logstash-logback-encoder', version: '6.2'
    // 本來這裡的scope應該為providedCompile,即只存在於編譯和測試階段,但似乎gradle無法識別,maven環境下未測試
    compile group: 'org.projectlombok', name: 'lombok', version: '1.18.10'
    // https://mvnrepository.com/artifact/org.elasticsearch.client/elasticsearch-rest-high-level-client
    compile group: 'org.elasticsearch.client', name: 'elasticsearch-rest-high-level-client', version: '7.2.0'
    // https://mvnrepository.com/artifact/org.elasticsearch/elasticsearch
    compile group: 'org.elasticsearch', name: 'elasticsearch', version: '7.2.0'
    // https://mvnrepository.com/artifact/org.elasticsearch.client/transport
    compile group: 'org.elasticsearch.client', name: 'transport', version: '7.2.0'
}

4.建立類,注意這裡直接將Controller放入口類ApplicationMain裡面的,簡單粗暴!

@RestController
@SpringBootApplication
//@Slf4j
public class ApplicationMain {

    private final Logger log = LoggerFactory.getLogger(ApplicationMain.class);

    public static void main(String[] args) {
        SpringApplication.run(ApplicationMain.class,args);
        System.out.println("ELK Application started.>>>>>>>>>>>>>>>>>>>>>>>>>>>");
    }

    @RequestMapping("/test")
    public String test() throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            Thread.sleep(1000);
            log.info("log from ELK app time: {}",System.currentTimeMillis());
        }
        return "ELK test success";
    }
}

5.建立logback-spring檔案,再複習下logback的使用,SLF4J是集合了各種日誌元件的框架,使用了門面模式,appender/logger/root是其中三大件,這裡就是使用logback將日誌傳給Logstash。另外,我還定義了一個file型別的log輸出,可以看到專案程式碼所在的目錄下的log檔案:

<?xml version="1.0" encoding="UTF-8"?>
<!--該日誌將日誌級別不同的log資訊儲存到不同的檔案中 -->
<configuration>
    <include resource="org/springframework/boot/logging/logback/defaults.xml" />

    <!--springProperty:在properties/yml檔案中找到對應的配置項 -->
    <springProperty scope="context" name="springAppName" source="spring.application.name" />
    <springProperty scope="context" name="logFilePath" source="logging.config.path" />

    <!-- 日誌在工程中的輸出位置 -->
    <property name="LOG_FILE" value="${BUILD_FOLDER:-build}/${springAppName}" />

    <!-- 控制檯的日誌輸出樣式 -->
    <property name="CONSOLE_LOG_PATTERN"
              value="%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}" />

    <!-- 控制檯輸出 appender-->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>INFO</level>
        </filter>
        <!-- 日誌輸出編碼 -->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
            <charset>utf8</charset>
        </encoder>
    </appender>

    <!-- 為logstash輸出的JSON格式的Appender -->
    <appender name="logstash"
              class="net.logstash.logback.appender.LogstashTcpSocketAppender">
        <destination>192.168.1.204:9665</destination>
        <!-- 日誌輸出編碼 -->
        <encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
            <providers>
                <timestamp>
                    <timeZone>UTC</timeZone>
                </timestamp>
                <pattern>
                    <pattern>
                        {
                        "severity": "%level",
                        "service": "${springAppName:-}",
                        "trace": "%X{X-B3-TraceId:-}",
                        "span": "%X{X-B3-SpanId:-}",
                        "exportable": "%X{X-Span-Export:-}",
                        "pid": "${PID:-}",
                        "thread": "%thread",
                        "class": "%logger{40}",
                        "rest": "%message"
                        }
                    </pattern>
                </pattern>
            </providers>
        </encoder>
    </appender>

    <!--檔案格式輸出appender-->
    <appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!--定義日誌輸出的路徑-->
        <!--這裡的scheduler.manager.server.home 沒有在上面的配置中設定,所以會使用java啟動時配置的值-->
        <!--比如通過 java -Dscheduler.manager.server.home=/path/to XXXX 配置該屬性-->
        <file>${logging.path}/spring-boot/elk.log</file>
        <!--定義日誌滾動的策略-->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--定義檔案滾動時的檔名的格式-->
            <fileNamePattern>${scheduler.manager.server.home}/logs/${app.name}.%d{yyyy-MM-dd.HH}.log
            </fileNamePattern>
            <!--60天的時間週期,日誌量最大20GB-->
            <maxHistory>60</maxHistory>
            <!-- 該屬性在 1.1.6版本後 才開始支援-->
            <totalSizeCap>20GB</totalSizeCap>
        </rollingPolicy>
        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <!--每個日誌檔案最大100MB-->
            <maxFileSize>100MB</maxFileSize>
        </triggeringPolicy>
        <!--定義輸出格式-->
        <encoder>
            <pattern>%d [%thread] %-5level %logger{36} [%file : %line] - %msg%n</pattern>
        </encoder>
    </appender>

    <!--logger 用來設定某一個包或者具體的某一個類的日誌列印級別以及指定appender-->
    <!--通過 LoggerFactory.getLogger("mytest") 可以獲取到這個logger-->
    <!--由於這個logger自動繼承了root的appender,root中已經有stdout的appender了,自己這邊又引入了stdout的appender-->
    <!--如果沒有設定 additivity="false" ,就會導致一條日誌在控制檯輸出兩次的情況,通過appender-ref做好分工,root負責console和logstash
      此logger負責file輸出-->
    <!--additivity表示要不要使用rootLogger配置的appender進行輸出-->
    <logger name="test" level="INFO" additivity="false">
        <appender-ref ref="file"/>
    </logger>

    <!-- 根logger,也是一種logger,且只有一個level屬性 -->
    <root level="INFO">
        <appender-ref ref="console" />
        <appender-ref ref="logstash" />
    </root>

</configuration>

6.建立application.yml檔案,用於上面的檔案中做值引用:

spring:
  application:
    name: ELK Application
logging:
  config:
    path: ./logs

7.新建一個logstash啟動配置檔案:

[root@localhost logstash-7.2.0]# vim config/logstash-java.conf

內容如下,注意這裡的port是應用接入的埠,output則是ES:

input{
   tcp {
     port  => 9665
     codec => json_lines
    }

}
output{
   elasticsearch{
     hosts => ["192.168.1.204:9200"]
 }
}

8.啟動logstash:

[root@localhost logstash-7.2.0]# ./bin/logstash -f /usr/logstash/logstash-7.2.0/config/logstash-java.conf

如應用啟動後出現錯誤:

Log destination 192.168.1.204:2004: connection failed. java.net.ConnectException: Connection refused: connect

請仔細檢查logstash-java.conf 和logback-spring.xml 的埠配置,必須一致!

9.啟動順序:

ES --> Kibana --> Logstash --> ELK Application

10.URL訪問:http://localhost:8080/test,應用產生log:

 

URL訪問Kibana ,略作下配置:http://192.168.1.204:5601/

 

 下一步:

 

  下一步:

 

至此,海量日誌分析框架完成!哪來的海量???這還不簡單,上面的程式碼中迴圈 i 改為一百億,去掉sleep!特此宣告,對海量實驗結果概不負責!至於kibana那些豐富多彩的展現和KQL查詢,各位自行去探索吧!

 

11.來操作一把 ES Java API:

官方文件中有使用 org.elasticsearch.client.transport.TransportClient 做 ES 的外部 client ,再去操作ES,但使用後卻發現已經 deprecated !換一個吧,我找到io.searchbox.client.JestClient,結果最新是2018年的,這?!再進行尋找一番,有個 org.elasticsearch.client.RestHighLevelClient 是最新的,且支援同步和非同步呼叫,趕緊又換掉前面的,唉,就像猴子下山一樣,好累,程式碼換了三波!這裡只是使用了一個儲存API,其他還有很多,可參考官網,使用方式類似。

程式碼就是在ApplicationMain中再新增一個APItest測試方法:

@RequestMapping("/api")
    public String APItest() throws InterruptedException, IOException {
        /** scheme 選項 http/tcp
         * 1. java客戶端的方式是以tcp協議在9300埠上進行通訊
         * 2. http客戶端的方式是以http協議在9200埠上進行通訊
         */
        RestHighLevelClient client = new RestHighLevelClient(
                //builder可以繼續新增多個HttpHost
                RestClient.builder(
                        new HttpHost("192.168.1.204", 9200, "http")));

         /** 有四種不同的方式來產生JSON格式的文件(document)
            .Manually (aka do it yourself) using native byte[] or as a String
            .Using a Map that will be automatically converted to its JSON equivalent
            .Using a third party library to serialize your beans such as Jackson
            .Using built-in helpers XContentFactory.jsonBuilder()
         */
        XContentBuilder builder = XContentFactory.jsonBuilder();
        builder.startObject();
        {
            builder.field("user", "biao");
            builder.timeField("postDate", new Date());
            builder.field("message", "trying out Elasticsearch");
        }
        builder.endObject();
        String index = "my_temp_index";
        IndexRequest indexRequest = new IndexRequest(index)
                .id("1")
                .timeout(TimeValue.timeValueSeconds(1))
                .setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL)
                .opType(DocWriteRequest.OpType.INDEX)
                .source(builder);

        //Synchronous execution
        IndexResponse indexResponse = client.index(indexRequest, RequestOptions.DEFAULT);
        System.out.println(indexResponse.toString());

        //asynchronous execution,
        // client.indexAsync(indexRequest, RequestOptions.DEFAULT, listener);

        client.close();
        return "ELK API test success";
    }

    ActionListener listener = new ActionListener() {
        @Override
        public void onResponse(Object o) {
            System.out.println("ELK API ASYN test success");
        }

        @Override
        public void onFailure(Exception e) {
            System.out.println("ELK API ASYN test failed");
        }
    };

 

同步測試:URL訪問:http://localhost:8080/api

 

結果如下,Index儲存成功,達到測試目標!

 

非同步測試:特別注意要將 client.close() 註釋掉,並實現 ActionListener 類:URL訪問:http://localhost:8080/api

結果如下,Index儲存成功,覆蓋了上面同步測試生成的index內容(是否覆蓋可配置),達到測試目標!

 

 

 

覆盤記:

1.ELK是一個可伸縮的框架,可按需進行裁剪,其中Logstash是一個 點對點 的資訊採集器,如果流量巨大,可以加入MQ或Redis緩衝,

2.ES出身就是分散式的,所以叢集方式可以做到多Node,多Shard,使用主從複製與冗餘儲存備份策略,自動平衡資料儲存點負載,

3.對於ES的概念,有個很好的對比圖,如果用過Mongodb,應該就好理解,只注意“文件”一詞,不是指我們常說的word/pdf檔案,而是一種有格式的描述型結構化資料,比如JSON:

 

4.再次注意ELK中各conf檔案的IP繫結概念,不建議使用0.0.0.0,事實上生產環境也不會直接全開!具體分析我在前篇《Linux下Redis叢集》中有解釋,這裡的bind類似,不再贅述。

5.ES分庫分片設定:

  • number_of_shards:每個索引的主分片數,預設值是 5 。這個配置在索引建立後不能修改。
  • number_of_replicas:每個主分片的副本數,預設值是 1 。對於活動的索引庫,這個配置可以隨時修改。

以下使用ES-Head方式,建立一個index,並配置為一個node上3個shard,每個shard有2個replica:

 

以上也可使用CURL方式:

curl -X PUT "localhost:9200/my_temp_index?pretty" -H 'Content-Type: application/json' -d'
{
    "settings": {
        "number_of_shards" :   1,
        "number_of_replicas" : 0
    }
}
'

 

具體展現如下:

 

然後,我們可以用 update-index-settings API 動態修改副本數,也可使用CURL方式:

 

修改後的效果:

 

5.ES為什麼快!?核心就是倒序索引和特殊的檔案壓縮,至於詳細,內容略多,在此僅作個引子。

6.本文完全沒用到dubbo,只是為了標題的連貫,故保留。

本文結束!

推薦閱讀:

  • Linux下Redis叢集
  • Dubbo學習系列之十五(Seata分散式事務方案TCC模式)
  • Dubbo學習系列之十四(Seata分散式事務方案AT模式)
  • Dubbo學習系列之十三(Mycat資料庫代理)
  • Dubbo學習系列之十二(Quartz任務排程)