Rabbitmq和Kafka最全講解

市面上流行的消息隊列有rabbitmq,kafka,Activemq等,所有這些都是爲了解決消息的分佈式消費,完成項目與服務的解耦。採取異步模式完成消息隊列提供者和消費者的通信,提高了系統的響應能力和信息吞吐量。

Rabbitmq

基本概念

  • Producer:消息生產者
  • Consumer:消息消費者
  • Exchange:消息交換機,指定消息按什麼規則傳遞到具體哪個隊列中
  • Queue:消息隊列,消息的載體
  • Routingkey:路由關鍵字,Exchange根據Routingkey來投遞消息給隊列
  • Binding:綁定。作用就是將Exchange和Queue按照某種路由規則綁定起來。
  • Channel:消息通道,也稱信道。在客戶端的每個連接裏可以建立多個Channel,每個Channel代表一個會話任務。

常用的轉發方式有

1)DirectExchange

直接交換器類型,根據routingkey採取點對點的信息轉發

                                   

@Configuration
public class UserRegisterQueueConfiguration {
    /**
     * 配置路由交換對象實例
     * @return
     */
    @Bean
    public DirectExchange userRegisterDirectExchange()
    {
        return new DirectExchange(ExchangeEnum.USER_REGISTER.getValue());
    }

    /**
     * 配置用戶註冊隊列對象實例
     * 並設置持久化隊列
     * @return
     */
    @Bean
    public Queue userRegisterQueue()
    {
        return new Queue(QueueEnum.USER_REGISTER.getName(),true);
    }

    /**
     * 將 用戶註冊隊列 綁定到 路由交換配置 上並設置指定路由鍵進行轉發
     * @return
     */
    @Bean
    public Binding userRegisterBinding()
    {
        return BindingBuilder.bind(userRegisterQueue()).to(userRegisterDirectExchange()).with(QueueEnum.USER_REGISTER.getRoutingKey());
    }
}

2)FanoutExchange

廣播交換器類型,將同一個message發送到所有同該Exchange 綁定的queue。不論RoutingKey是什麼,這條消息都會被投遞到所有與此Exchange綁定的queue中。

                                   

@Configuration
public class SenderConf {

        @Bean(name="Amessage")
        public Queue AMessage() {
            return new Queue("fanout.A");
        }


        @Bean(name="Bmessage")
        public Queue BMessage() {
            return new Queue("fanout.B");
        }

        @Bean(name="Cmessage")
        public Queue CMessage() {
            return new Queue("fanout.C");
        }

        @Bean
        FanoutExchange fanoutExchange() {
            return new FanoutExchange("fanoutExchange");//配置廣播路由器
        }

        @Bean
        Binding bindingExchangeA(@Qualifier("Amessage") Queue AMessage,FanoutExchange fanoutExchange) {
            return BindingBuilder.bind(AMessage).to(fanoutExchange);
        }

        @Bean
        Binding bindingExchangeB(@Qualifier("Bmessage") Queue BMessage, FanoutExchange fanoutExchange) {
            return BindingBuilder.bind(BMessage).to(fanoutExchange);
        }

        @Bean
        Binding bindingExchangeC(@Qualifier("Cmessage") Queue CMessage, FanoutExchange fanoutExchange) {
            return BindingBuilder.bind(CMessage).to(fanoutExchange);
        }
    
}

3)TopicExchange

主題交換器,根據Binding指定的RoutingKey,Exchange對key進行模式匹配後投遞到相應的Queue。

                       

如圖中,假如消息的RoutingKey是American.action.13,這條消息將被投遞到Q1和Q2中。假如RoutingKey是American.action.13.test(注意:此處是四個詞),這條消息將會被丟棄,因爲沒有routingkey與之匹配。假如RoutingKey是Chinese.action.13,這條消息將被投遞到Q2和Q3中。假如RoutingKey是Chinese.action.13.test,這條消息只會被投遞到Q3中,#可以匹配一個或者多個單詞,而*只能匹配一個詞。

@Configuration
public class UserRegisterQueueConfiguration {

    private Logger logger = LoggerFactory.getLogger(UserRegisterQueueConfiguration.class);
    /**
     * 配置用戶註冊主題交換
     * @return
     */
    @Bean
    public TopicExchange userTopicExchange()
    {
        TopicExchange topicExchange = new TopicExchange(ExchangeEnum.USER_REGISTER_TOPIC_EXCHANGE.getName());
        logger.info("用戶註冊交換實例化成功。");
        return topicExchange;
    }

   

    /**
     * 配置用戶註冊
     * 創建賬戶消息隊列
     * 並設置持久化隊列
     * @return
     */
    @Bean
    public Queue createAccountQueue()
    {
        Queue queue = new Queue(QueueEnum.USER_REGISTER_CREATE_ACCOUNT.getName(),true,false,true);
        logger.info("創建用戶註冊賬號隊列成功.");
        return queue;
    }

   

    /**
     * 綁定用戶創建賬戶到用戶註冊主題交換配置
     * @return
     */
    @Bean
    public Binding createAccountBinding(TopicExchange userTopicExchange,Queue createAccountQueue)
    {
        Binding binding = BindingBuilder.bind(createAccountQueue).to(userTopicExchange).with(QueueEnum.USER_REGISTER_CREATE_ACCOUNT.getRoutingKey());
        logger.info("綁定創建賬號到註冊交換成功。");
        return binding;
    }
}

監控頁面

在rabbitmq sbin目錄下執行以下語句開啓指定插件。

rabbitmq-plugins.bat enable rabbitmq_management

訪問http://127.0.0.1:15672,可訪問rabbitmq監控管理頁面

優缺點

優點:

(1)由Erlang語言開發,支持大量協議:AMQP、XMPP、SMTP、STOMP。

(2)支持消息的持久化、負載均衡和集羣,且集羣易擴展。

(3)具有一個Web監控界面,易於管理。

(4)安裝部署簡單,上手容易,功能豐富,強大的社區支持。

(5)支持消息確認機制、靈活的消息分發機制。

缺點:

(1)由於犧牲了部分性能來換取穩定性,比如消息的持久化功能,使得RabbitMQ在大吞吐量性能方面不及Kafka和ZeroMQ。

(2)由於支持多種協議,使RabbitMQ非常重量級,比較適合企業級開發。

Kafka

kafka是Apache公司的,用java編寫,類AMQP協議。必須和zookeeper配合才能使用。

基本概念

1、消費者:(Consumer):主動從Broker拉數據,從而消費這些已發佈的消息

2、生產者:(Producer)  :向broker發佈消息的應用程序

3、AMQP服務端(broker):用來接收生產者發送的消息並將這些消息路由給服務器中的隊列,便於kafka將生產者發送的消息,動態的添加到磁盤並給每一條消息一個偏移量,所以對於kafka一個broker就是一個應用程序的實例

4、話題(Topic):是特定類型的消息流。消息是字節的有效負載(Payload),話題是消息的分類名或種子(Feed)名;

5、分區(Partition):一個Topic中的消息數據按照多個分區組織,分區是kafka消息隊列組織的最小單位,一個分區可以看作是一個FIFO( First Input First Output的縮寫,先入先出隊列)的隊列。kafka分區是提高kafka性能的關鍵所在,當你發現你的集羣性能不高時,常用手段就是增加Topic的分區,分區裏面的消息是按照從新到老的順序進行組織,消費者從隊列頭訂閱消息,生產者從隊列尾添加消息。

                  è¿éåå¾çæè¿°

zookeeper

(1)無論是kafka集羣,還是producer和consumer都依賴於zookeeper來保證系統可用性集羣保存一些meta信息。

(2)Kafka使用zookeeper作爲其分佈式協調框架,很好的將消息生產、消息存儲、消息消費的過程結合在一起。

(3)同時藉助zookeeper,kafka能夠生產者、消費者和broker在內的所以組件在無狀態的情況下,建立起生產者和消費者的訂閱關係,並實現生產者與消費者的負載均衡。

group  partition

group和partition有千絲萬縷的關係,網上關於兩者的介紹很多,但基本上都是提問,回覆也是五花八門,本着嚴謹治學的態度,以下全是實踐之後得出的結論。

                                               è¿éåå¾çæè¿°

一個topic可以分爲多個partition,這麼做的好處是,每個partition(目錄)相當於一個巨型文件被平均分配到多個大小相等segment(段)數據文件中,一個group中的多個consumer加入時,server會進行partition負載均衡,一個group中的consumer可以分配多個partition,也可能一個都分不到。

1)創建topic  aaa,只對應一個partition  aaa-0

2)創建topic bbb,對應6個partition bbb-0  bbb-1   bbb-2  bbb-3  bbb-4 bbb-5

3)先啓動一個服務consumer1,監聽topics={"aaa","bbb"},group-id="test-consumer-group"

                     

默認系統給分配的partition如下,這個consumer1一個人佔了7個partition!!!

4)很明顯,如果讓consumer1一個人佔7個partition,就失去了負載均衡的意義。此時我們再起一個服務consumer2,topics={"aaa","bbb"},group-id="test-consumer-group"

啓動後consumer2重新分配後的partition如下,發現[bbb-3,bbb-4,bbb-5]都歸consumer2所有

同時consumer1那邊重新分配後的partition如下,值得注意的是aaa-0是碰巧還屬於consumer1,即consumer的啓動順序和分配partition是無關的!!!且topic中的partition數量少於同一個group中consumer的數量時,肯定會有consumer沒有分配到partition,即無法接收消息!!!

    

啓動了兩個consumer,只有consumer1擁有aaa-0,所以只有consumer1能接收到topic aaa的消息。

5)啓動producer向topic aaa發送請求,數字0~19,可以預想到只有consumer1能接收到所有消息

                                       

consumer1接收所有消息,consumer2沒有接收到信息

             

6)如果producer向bbb發送消息呢?bbb有6個partitions,consumer1和consumer2能接收到什麼消息呢?

答案是20條消息被隨機分配到了兩個consumer中,

consumer1接收的消息

        

consumer2接收的消息

            

7)現在我們啓動consumer3,topics={"aaa","bbb"},group-id="test-consumer-group2",這裏group-id與其兩者不同。在保證consumer1和consumer2運行的情況下啓動consumer3,分配的partitions如下。可見consumer3擁有7個partition,並沒和consumer1或者consumer2有什麼交集,更談不上負載均衡,自然consumer3能收到所有信息。

總結:

producer向topic發送消息,topic分配給所屬的partitions。

consumer聲明group-id,並監聽topic,啓動後會接收系統分配partitions。一個partition只能綁定同一個group中的一個consumer,但能綁定多個不同group中的consumer,基於這個原理就會發生有趣的事情。

啓動多個不同group-id的consumer監聽topic,所有的consumer都能接收到信息,這是廣播

啓動多個相同group-id的consumer監聽topic,且topic中的partition數量>=consumer的數量,這是負載均衡,一條消息永遠只有一個consumer能消費,下一條消息可能是另一個consumer消費。所有的consumer都會消費到消息。

啓動多個相同group-id的consumer監聽topic,且topic中partition數量<consumer的數量,那麼總會有consumer沒分配到partition,即它永遠接收不到消息。

優缺點

優點:

1)可擴展。Kafka集羣可以透明的擴展,增加新的服務器進集羣。

2)高性能。Kafka性能遠超過傳統的ActiveMQ、RabbitMQ等,Kafka支持Batch操作。

3)容錯性。Kafka每個Partition數據會複製到幾臺服務器,當某個Broker失效時,Zookeeper將通知生產者和消費者從而使用其他的Broker。

缺點:

1)重複消息。Kafka保證每條消息至少送達一次,雖然機率很小,但一條消息可能被送達多次。

2)消息亂序。Kafka某一個固定的Partition內部的消息是保證有序的,如果一個Topic有多個Partition,partition之間的消息送達不保證有序。

3)複雜性。Kafka需要Zookeeper的支持,Topic一般需要人工創建,部署和維護比一般MQ成本更高。

4)不能像Rabbitmq那樣用topic(#,*)匹配方式傳遞信息,只能死板地訂閱topic,且當同一個group內consumer數量>topic中partition數量時,會有consumer接收不到消息,而rabbitmq可以通過fanout模式實現廣播。

搭建流程

kafka的搭建相對較爲複雜,因爲boker、producer、consumer都是通過zookeeper交流的,所以要先搭建zookeeper,再搭建kafka。

1.搭建zookeeper

在新版的kafka中,都會自帶zookeeper,但總歸是配置不夠方便明瞭。一般還是單獨下載zookeeper進行搭建。

zookeeper下載地址:http://mirrors.hust.edu.cn/apache/zookeeper/

1.1 配置zoo.cfg

解壓後,裏面有conf/zoo_sample.cfg,把它複製粘貼後改名zoo.cfg,作爲核心配置類,zookeeper啓動時會讀取zoo.cfg中的配置信息。其中dataDir,dataLogDir,server.x這幾個屬性得重新配置。

# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial 
# synchronization phase can take
initLimit=10
# The number of ticks that can pass between 
# sending a request and getting an acknowledgement
syncLimit=5
# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just 
# example sakes.
#dataDir和dataLogDir可以自行設置,必須是全路徑
dataDir=/usr/local/zookeeper/data
dataLogDir=/usr/local/zookeeper/datalog
# the port at which the clients will connect
clientPort=2181
# the maximum number of client connections.
# increase this if you need to handle more clients
#maxClientCnxns=60
#
authProvider.1=org.apache.zookeeper.server.auth.SASLAuthenticationProvider  
requireClientAuthScheme=sasl  
jaasLoginRenew=3600000 
# Be sure to read the maintenance section of the 
# administrator guide before turning on autopurge.
#
# http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance
#
# The number of snapshots to retain in dataDir
#autopurge.snapRetainCount=3
# Purge task interval in hours
# Set to "0" to disable auto purge feature
#autopurge.purgeInterval=1
#配置集羣地址,第一個端口是master和slave之間的通信端口,默認是2888,
#第二個端口是leader選舉的端口,集羣剛啓動的時候選舉或者leader掛掉之後進行新的選舉的端口默認是3888
server.1=192.168.3.163:2888:3888
server.2=192.168.3.164:2888:3888
server.3=192.168.3.165:2888:3888

zookeeper集羣配置服務器地址,可以像上圖那樣配置,也可以在/etc/hosts中將這些ip配置成具體名稱(zk1,zk2,zk3),這樣配置的好處是如果某個IP地址發生了變化,我們不需要重啓zookeeper,直接修改主機對應的IP地址即可。

[root@localhost zookeeper]# vi /etc/hosts

然後在文件末尾追加以下內容,這樣就可以在zoo.cfg中用server.1=zk1:2888:3888來代替了。

192.168.3.163 zk1
192.168.3.164 zk2
192.168.3.165 zk3

按照上面配置,將zookeeper文件夾scp -r到另外兩個服務器上。

1.2 創建myid

myid文件和zookeeper_server.pid  在快照目錄下(./data)存放的標識本臺服務器的文件,他是整個zk集羣用來發現彼此的一個重要標識。快照目錄需和zoo.cfg中 dataDir=/usr/local/zookeeper/data  一致。

                                                    

myid文件裏面只有一個數字,用於區分集羣中不同節點的zookeeper。分別給幾個zookeeper創建myid。

#server1
echo "1" > /usr/local/zookeeper/data/myid
#server2
echo "2" > /usr/local/zookeeper/data/myid
#server3
echo "3" > /usr/local/zookeeper/data/myid

zookeeper_server.pid是啓動zookeeper server後,自動創建的文件,裏面有數字pid,即啓動zookeeper的進程號pid。

1.3 啓動zookeeper

進入bin目錄下

#啓動服務(3臺都需要操作)
./zkServer.sh start
#關閉服務
./zkServer.sh stop 

#檢查服務器狀態
./zkServer.sh status


#執行命令jps,查看zk進程QuorumPeerMain 
jps
20348 Jps
4233 QuorumPeerMain

#使用客戶端進入zk
./zkCli.sh -server 192.168.3.164:2181  
[zk: 127.0.0.1:12181(CONNECTED) 0] ls /

#顯示結果:[consumers, config, controller, isr_change_notification, admin, brokers, zookeeper, controller_epoch]
'''
上面的顯示結果中:只有zookeeper是,zookeeper原生的,其他都是Kafka創建的
'''
#查看partion
[zk: 127.0.0.1:12181(CONNECTED) 7] get /brokers/topics/shuaige/partitions/0
#查看group,其中console-consumer-xxxxx是控制檯默認生成consumer./kafka-console-consumer.sh
[zk: localhost:2181(CONNECTED) 1] ls /consumers
[console-consumer-24279, console-consumer-87851, console-consumer-74253, console-consumer-91660, test-consumer-group, console-consumer-20304]
#刪除group
[zk: localhost:2181(CONNECTED) 6] rmr /consumers/console-consumer-20304

2 Kafka集羣搭建

下載kafka並解壓,本人下的是版本是  kafka_2.12-1.1.0.tar  ,不同的版本配置文件內容可能不太一致,所以一定要參考相應版本的配置文件。參考 https://blog.csdn.net/l1028386804/article/details/79194929

2.1 配置server.properties

在config目錄下有很多配置文件,其中最重要的就是server.properties,裏面包含了zookeeper集羣的ip地址,監聽本機kafka-server的ip:port。

需要修改的就三個屬性   

broker.id  當前機器在集羣中的唯一標識,和zookeeper的myid性質一樣

listeners  當前機器的ip地址,如果是搭建集羣的話必須寫ip,單機可以寫localhost:9092

zookeeper.connect  zookeeper集羣的所有ip地址

broker.id=0

############################# Socket Server Settings #############################

# The address the socket server listens on. It will get the value returned from 
# java.net.InetAddress.getCanonicalHostName() if not configured.
#   FORMAT:
#     listeners = listener_name://host_name:port
#   EXAMPLE:
#     listeners = PLAINTEXT://your.host.name:9092
listeners=PLAINTEXT://192.168.3.165:9092

...


############################# Zookeeper #############################

# Zookeeper connection string (see zookeeper docs for details).
# This is a comma separated host:port pairs, each corresponding to a zk
# server. e.g. "127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002".
# You can also append an optional chroot string to the urls to specify the
# root directory for all kafka znodes.
zookeeper.connect=192.168.3.163:2181,192.168.3.164:2181,192.168.3.165:2181

2.2 啓動集羣

進入bin目錄,

#後臺啓動kafka服務
./kafka-server-start.sh -daemon ../config/server.properties
#關閉服務
./kafka-server-stop.sh 
#檢查服務啓動成功沒有  
jps
20348 Jps
4233 QuorumPeerMain
18991 Kafka



#查看所有創建的topic,連接的是zookeeper
./kafka-topics.sh --list --zookeeper 192.168.3.164:2181
#創建Topic
./kafka-topics.sh --create --zookeeper 192.168.3.164:2181 --replication-factor 2 --partitions 1 --topic bbb
#解釋
--replication-factor 2   #複製兩份
--partitions 1 #創建1個分區
--topic #主題爲bbb
#修改topic partition
./kafka-topics.sh --alter --topic bbb --zookeeper 192.168.3.164:2181 --partitions 6
#查看topic具體信息
 ./kafka-topics.sh --describe --zookeeper 192.168.3.164:2181 --topic bbb



#創建一個broker,發佈者
./kafka-console-producer.sh --broker-list 192.168.3.164:9092 --topic bbb

#在一臺服務器上創建一個訂閱者,默認創建的consumer group-id=console-consumer-xxxxx
./kafka-console-consumer.sh --zookeeper l92.168.3.164:2181 --topic bbb --from-beginning
#也可以指定consumer.properties,設置group-id
./kafka-console-consumer.sh  --consumer.config  ../config/consumer.properties   --zookeeper 192.168.3.165:2181 --topic bbb 

 

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