Redis編程實踐【pub/sub】__編程

來源:互聯網
上載者:User
Redis編程實踐【pub/sub】 部落格分類: Redis  

 

    Redis或許已經在很多企業開始推廣並試水,本文也根據個人的實踐,簡單描述一下Redis在實際開發過程中的使用(部署與架構,稍後介紹),程式執行環境為java + jedis,關於spring下如何整合redis-api,稍後介紹吧。

 

前言:下載redis-2.6.2,安裝好redis之後,請在redis.conf檔案中,將如下3個配置屬性開啟(僅供測試使用): Xml代碼   ##用戶端連結的連接埠,也是server端偵聽client連結的連接埠      ##每個client執行個體,都將和server在此連接埠上建立tcp長連結      port 6379      ## server端綁定的ip地址,如果一個物理機器有多個網路介面時,可以明確指定為某個網口的ip地址      bind 127.0.0.1      ##連結中io操作空閑時間,如果在指定時間內,沒有IO操作,連結將會被關閉      ##此屬性和TCP連結中的timeout選項一樣,建議設定為0,很多時候,我們一個應用也只會有一個redis執行個體      ##不過,如果你使用串連池的話,你需要對此參數做額外的考慮。      timeout 0    

    ##用戶端連結的連接埠,也是server端偵聽client連結的連接埠      ##每個client執行個體,都將和server在此連接埠上建立tcp長連結      port 6379      ## server端綁定的ip地址,如果一個物理機器有多個網路介面時,可以明確指定為某個網口的ip地址      bind 127.0.0.1      ##連結中io操作空閑時間,如果在指定時間內,沒有IO操作,連結將會被關閉      ##此屬性和TCP連結中的timeout選項一樣,建議設定為0,很多時候,我們一個應用也只會有一個redis執行個體      ##不過,如果你使用串連池的話,你需要對此參數做額外的考慮。      timeout 0  

 

Pub/Sub: "發布/訂閱",對於此功能,我們將會想到很多JMS實現,Redis提供此功能顯的“多此一舉”;不過這個功能在redis中,被設計的非常輕量級和簡潔,它做到了訊息的“發布”和“訂閱”的基本能力,但是尚未提供JMS中關於訊息的持久化/耐久性等各種企業級的特性。

    一個Redis client發布訊息,其他多個redis client訂閱訊息,發布的訊息“即發即失”,redis不會持久儲存發布的訊息;訊息訂閱者也將只能得到訂閱之後的訊息,通道中此前的訊息將無從獲得。這就類似於JMS中“非持久”類型的訊息。

    訊息發行者,即publish用戶端,無需獨佔連結,你可以在publish訊息的同時,使用同一個redis-client連結進行其他動作(例如:INCR等)

    訊息訂閱者,即subscribe用戶端,需要獨佔連結,即進行subscribe期間,redis-client無法穿插其他動作,此時client以阻塞的方式等待“publish端”的訊息;這一點很好理解,因此subscribe端需要使用單獨的連結,甚至需要在額外的線程中使用。

    一旦subscribe端取消連結,將會失去部分訊息,如果你非常關注每個訊息,那麼你應該考慮使用JMS或者基於Redis做一些額外的補充工作,如果你期望訂閱是持久的,那麼如下的設計思路可以借鑒(如下原理基於JMS):

    1) subscribe端首先向一個Set集合中增加“訂閱者ID”,此Set集合儲存了“活躍訂閱”者,訂閱者ID標記每個唯一的訂閱者,例如:sub:email,sub:web。此SET稱為“活躍訂閱者集合”

    2) subcribe端開啟訂閱操作,並基於Redis建立一個以“訂閱者ID”為KEY的LIST資料結構,此LIST中儲存了所有的尚未消費的訊息。此LIST稱為“訂閱者訊息佇列”

    3) publish端:每發布一條訊息之後,publish端都需要遍曆“活躍訂閱者集合”,並依次向每個“訂閱者訊息佇列”尾部追加此次發布的訊息。

    4) 到此為止,我們可以基本保證,發布的每一條訊息,都會持久儲存在每個“訂閱者訊息佇列”中。

    5) subscribe端,每收到一個訂閱訊息,在消費之後,必須刪除自己的“訂閱者訊息佇列”頭部的一條記錄。

    6) subscribe端啟動時,如果發現自己的自己的“訂閱者訊息佇列”有殘存記錄,那麼將會首先消費這些記錄,然後再去訂閱。

 

--------------------------------------------------------------非持久化訂閱-------------------------------------------------------

PrintListener.java:訂閱者訊息處理器 Java代碼   public class PrintListener extends JedisPubSub{           @Override       public void onMessage(String channel, String message) {            String time = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");            System.out.println("message receive:" + message + ",channel:" + channel + "..." + time);            //此處我們可以取消訂閱            if(message.equalsIgnoreCase("quit")){                this.unsubscribe(channel);            }        }    ...    }  

public class PrintListener extends JedisPubSub{@Overridepublic void onMessage(String channel, String message) {String time = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");System.out.println("message receive:" + message + ",channel:" + channel + "..." + time);//此處我們可以取消訂閱if(message.equalsIgnoreCase("quit")){this.unsubscribe(channel);}}...}
 

PubClient.java:訊息發布端 Java代碼   public class PubClient {           private Jedis jedis;//        public PubClient(String host,int port){            jedis = new Jedis(host,port);        }                public void pub(String channel,String message){            jedis.publish(channel, message);        }                public void close(String channel){            jedis.publish(channel, "quit");            jedis.del(channel);//        }       }  

public class PubClient {private Jedis jedis;//public PubClient(String host,int port){jedis = new Jedis(host,port);}public void pub(String channel,String message){jedis.publish(channel, message);}public void close(String channel){jedis.publish(channel, "quit");jedis.del(channel);//}}

 

SubClient.java:訊息訂閱端 Java代碼   public class SubClient {           private Jedis jedis;//                public SubClient(String host,int port){            jedis = new Jedis(host,port);        }                public void sub(JedisPubSub listener,String channel){            jedis.subscribe(listener, channel);            //此處將會阻塞,在client代碼層級為JedisPubSub在處理訊息時,將會“獨佔”連結            //並且採取了while迴圈的方式,偵聽訂閱的訊息            //        }       }  

public class SubClient {private Jedis jedis;//public SubClient(String host,int port){jedis = new Jedis(host,port);}public void sub(JedisPubSub listener,String channel){jedis.subscribe(listener, channel);//此處將會阻塞,在client代碼層級為JedisPubSub在處理訊息時,將會“獨佔”連結//並且採取了while迴圈的方式,偵聽訂閱的訊息//}}

 

PubSubTestMain.java:測試引導類 Java代碼   public class PubSubTestMain {           /**        * @param args        */       public static void main(String[] args) throws Exception{            PubClient pubClient = new PubClient(Constants.host, Constants.port);            final String channel = "pubsub-channel";            pubClient.pub(channel, "before1");            pubClient.pub(channel, "before2");            Thread.sleep(2000);            //訊息訂閱著非常特殊,需要獨佔連結,因此我們需要為它建立新的連結;            //此外,jedis用戶端的實現也保證了“連結獨佔”的特性,sub方法將一直阻塞,            //直到調用listener.unsubscribe方法            Thread subThread = new Thread(new Runnable() {                @Override               public void run() {                    try{                        SubClient subClient = new SubClient(Constants.host, Constants.port);                        System.out.println("----------subscribe operation begin-------");                        JedisPubSub listener = new PrintListener();                        //在API層級,此處為輪詢操作,直到unsubscribe調用,才會返回                        subClient.sub(listener, channel);                        System.out.println("----------subscribe operation end-------");                    }catch(Exception e){                        e.printStackTrace();                    }                                    }            });            subThread.start();            int i=0;            while(i < 10){                String message = RandomStringUtils.random(6, true, true);//apache-commons                pubClient.pub(channel, message);                i++;                Thread.sleep(1000);            }            //被動關閉指示,如果通道中,訊息發行者確定通道需要關閉,那麼就發送一個“quit”            //那麼在listener.onMessage()中接收到“quit”時,其他訂閱client將執行“unsubscribe”操作。            pubClient.close(channel);            //此外,你還可以這樣取消訂閱            //listener.unsubscribe(channel);           }       }  

public class PubSubTestMain {/** * @param args */public static void main(String[] args) throws Exception{PubClient pubClient = new PubClient(Constants.host, Constants.port);final String channel = "pubsub-channel";pubClient.pub(channel, "before1");pubClient.pub(channel, "before2");Thread.sleep(2000);//訊息訂閱著非常特殊,需要獨佔連結,因此我們需要為它建立新的連結;//此外,jedis用戶端的實現也保證了“連結獨佔”的特性,sub方法將一直阻塞,//直到調用listener.unsubscribe方法Thread subThread = new Thread(new Runnable() {@Overridepublic void run() {try{SubClient subClient = new SubClient(Constants.host, Constants.port);System.out.println("----------subscribe operation begin-------");JedisPubSub listener = new PrintListener();//在API層級,此處為輪詢操作,直到unsubscribe調用,才會返回subClient.sub(listener, channel);System.out.println("----------subscribe operation end-------");}catch(Exception e){e.printStackTrace();}}});subThread.start();int i=0;while(i < 10){String message = RandomStringUtils.random(6, true, true);//apache-commonspubClient.pub(channel, message);i++;Thread.sleep(1000);}//被動關閉指示,如果通道中,訊息發行者確定通道需要關閉,那麼就發送一個“quit”//那麼在listener.onMessage()中接收到“quit”時,其他訂閱client將執行“unsubscribe”操作。pubClient.close(channel);//此外,你還可以這樣取消訂閱//listener.unsubscribe(channel);}}

 

--------------------------------------------------------------持久化訂閱-------------------------------------------------------

 

PPrintListener.java Java代碼   public class PPrintListener extends JedisPubSub{           private String clientId;        private PSubHandler handler;                public PPrintListener(String clientId,Jedis jedis){            this.clientId = clientId;            handler = new PSubHandler(jedis);        }                @Override       public void onMessage(String channel, String message) {            //此處我們可以取消訂閱            if(message.equalsIgnoreCase("quit")){                this.unsubscribe(channel);            }            handler.handle(channel, message);        }                private void message(String channel,String message){            String time = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");            System.out.println("message receive:" + message + ",channel:" + channel + "..." + time);        }           @Override       public void onPMessage(String pattern, String channel, String message) {            System.out.println("message receive:" + message + ",pattern channel:" + channel);                    }           @Override       public<

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.