ZeroMQ通訊訂閱模式--iOS

ZeroMQ在使用模式上支持多種柠衅,有req-reply挡毅,publish-subscribe集歇。訂閱模式是zmq的重頭戲,以鄙人的使用和理解來淺談下訂閱模式想暗,本人也是邊學(xué)邊用,如有問題請(qǐng)大神指出帘不。關(guān)于應(yīng)答模式請(qǐng)閱讀 這篇文章 说莫。

zmq的訂閱模式實(shí)現(xiàn)示例代碼如下

ZMQContext *ctx = [[ZMQContext alloc] initWithIOThreads:1U];
// Socket to talk to server
ZMQSocket *subscriber = [ctx socketWithType:ZMQ_SUB];
if (![subscriber connectToEndpoint:@"tcp://localhost:5556"]) {
    return EXIT_FAILURE;
}

const char *kNYCZipCode = "10001";
const char *filter = (argc > 1)? argv[1] : kNYCZipCode;
NSData *filterData = [NSData dataWithBytes:filter length:strlen(filter)];
[subscriber setData:filterData forOption:ZMQ_SUBSCRIBE];

(void)setvbuf(stdout, NULL, _IONBF, BUFSIZ);

const int kMaxUpdate = 100;
long total_temp = 0;
for (int update_nbr = 0; update_nbr < kMaxUpdate; ++update_nbr) {
    NSData *msg = [subscriber receiveDataWithFlags:0];
    const char *string = [msg bytes];

    int zipcode = 0, temperature = 0, relhumidity = 0;
    (void)sscanf(string, "%d %d %d", &zipcode, &temperature, &relhumidity);

    printf("%d ", temperature);
    total_temp += temperature;
}

訂閱模式的重點(diǎn)在于數(shù)據(jù)分發(fā),數(shù)據(jù)由服務(wù)器主動(dòng)推回來給客戶端寞焙,訂閱的數(shù)據(jù)統(tǒng)一接收储狭,然后在分發(fā)到需要數(shù)據(jù)處理的對(duì)象中。示例代碼只是簡(jiǎn)單的接收數(shù)據(jù)捣郊,它的做法是一個(gè)線程接收一個(gè)訂閱消息辽狈,而且數(shù)據(jù)接收是阻塞當(dāng)前線程的,這個(gè)在實(shí)際的項(xiàng)目應(yīng)用也是不符合呛牲,所以封裝和優(yōu)化數(shù)據(jù)分發(fā)是必須的刮萌。

NSData *msg = [subscriber receiveDataWithFlags:0]; // 阻塞線程直到接收到數(shù)據(jù)

數(shù)據(jù)的處理分發(fā)采用代理來回調(diào),當(dāng)然也可以使用block娘扩,個(gè)人建議使用代理着茸,畢竟數(shù)據(jù)分發(fā)到不同的處理對(duì)象中,使用代理可以很好的避免循環(huán)引用琐旁,避免內(nèi)存泄漏涮阔。我這里是使用一個(gè)兩層Dictionary來存儲(chǔ)代理,dictionary的遍歷速度要比數(shù)組好一點(diǎn)旋膳。第一層dictionary以訂閱碼來做key澎语,第二層dictionary以代理對(duì)象的hash值為key,這樣可以快速找到代理對(duì)象進(jìn)行分發(fā),也可以準(zhǔn)確找到某個(gè)代理對(duì)象并實(shí)現(xiàn)移除操作擅羞。

添加代理對(duì)象的方法

- (void)setCode:(NSString *)code withDelegate:(id<ZMQSubscriptionDelegate>)delegate {
    NSAssert(delegate != nil, @"代理為空");
    NSAssert(code != nil, @"訂閱碼為空");

    // 取出當(dāng)前訂閱碼的代理字典
    NSMutableDictionary *delegateDict = self.delegates[code];

    if (delegateDict == nil) { // 不存在當(dāng)前的訂閱碼代理對(duì)象
        // 創(chuàng)建當(dāng)前訂閱碼的代理對(duì)象的字典
        delegateDict = [NSMutableDictionary dictionary];
        self.delegates[code] = delegateDict;
    }

    // 添加代理對(duì)象
    NSString *key = [NSString stringWithFormat:@"%ld", (unsigned long)[delegate hash]];
    delegateDict[key] = delegate;
}

移除代理對(duì)象的方法

- (void)removeDelegate:(NSString *)code withDelegate:(id<ZMQSubscriptionDelegate>)delegate {
    NSAssert(delegate != nil, @"代理為空");
    NSAssert(code != nil, @"訂閱碼為空");

    // 取出當(dāng)前訂閱碼的代理字典
    NSMutableDictionary *delegateDict = self.delegates[code];

    if (delegateDict == nil) return; // 不存在當(dāng)前的訂閱碼代理對(duì)象

    NSString *key = [NSString stringWithFormat:@"%ld", (unsigned long)[delegate hash]];
    [delegateDict removeObjectForKey:key];
}

注意尸变,當(dāng)對(duì)象不需要處理訂閱消息時(shí),一定要調(diào)用removeDelegate方法减俏,因?yàn)榇韺?duì)象是存儲(chǔ)到字典中的召烂,持有代理對(duì)象的強(qiáng)引用。

zmq要訂閱什么消息就發(fā)對(duì)應(yīng)的訂閱碼娃承,如果要全部訂閱奏夫,就發(fā)一個(gè)空字符串,在子線程使用一個(gè)while循環(huán)來接收數(shù)據(jù)历筝,定義一個(gè)標(biāo)志位酗昼,讓while循環(huán)可以控制,更改后的代碼如下

_context = [[ZMQContext alloc] initWithIOThreads:1];
_socket = [_context socketWithType:ZMQ_SUB];
_socket.loadingtime = 3000; // 超時(shí)時(shí)間梳猪,單位毫秒

NSString *endpoint = @"tcp://:41204"; // 服務(wù)器IP地址
if (![_socket connectToEndpoint:endpoint]) {
    NSLog(@"訂閱失敗");
    return;
}

NSData *filterData = [@"" dataUsingEncoding:NSUTF8StringEncoding];
[_socket setData:filterData forOption:ZMQ_SUBSCRIBE];

(void)setvbuf(stdout, NULL, _IONBF, BUFSIZ);

closeSocket = NO;
while (!closeSocket) {
    
    @autoreleasepool {
        NSData *recieveData = [_socket receiveDataWithFlags:0];
        if (recieveData == nil)  continue; // 訂閱消息超時(shí)返回nil
        
        // 數(shù)據(jù)處理
        NSString *dataStr = [[NSString alloc] initWithData:recieveData encoding:NSUTF8StringEncoding];
        NSRange range = [dataStr rangeOfString:@"{" options:NSCaseInsensitiveSearch];
        NSString *subStr = [dataStr substringFromIndex:range.location];
        NSString *codeKey = [dataStr substringToIndex:range.location];
        
        /*** 數(shù)據(jù)分發(fā) ***/
        // 取出當(dāng)前訂閱碼的代理字典
        NSMutableDictionary *delegateDict = self.delegates[codeKey];
        
        if (delegateDict == nil) continue; // 沒有對(duì)當(dāng)前訂閱碼的代理對(duì)象
        
        [delegateDict enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
            
            [self handleData:subStr delegate:obj code:codeKey];
        }];
    }    
}

zmq訂閱返回的數(shù)據(jù)格式是訂閱碼拼接上推送的數(shù)據(jù)麻削,當(dāng)接收的數(shù)據(jù)為nil,那就是接收超時(shí)了春弥,zmq訂閱只要接收到數(shù)據(jù)呛哟,肯定不為空,必然有訂閱碼匿沛。這里的數(shù)據(jù)處理方式是因?yàn)槲翼?xiàng)目服務(wù)器返回的是訂閱碼拼接json的數(shù)據(jù)扫责,要根據(jù)服務(wù)器定義的數(shù)據(jù)格式來處理數(shù)據(jù)。注意逃呼,這里是有一個(gè)坑鳖孤,要在循環(huán)內(nèi)使用@ autoreleasepool {}。一個(gè)對(duì)象只要出了作用的使用區(qū)域抡笼,就會(huì)自動(dòng)釋放了淌铐,可是當(dāng)使用區(qū)域是一個(gè)while循環(huán)時(shí),系統(tǒng)會(huì)認(rèn)為你的對(duì)象還要使用就不會(huì)釋放對(duì)象蔫缸。所以,在循環(huán)里加上autoreleasepool標(biāo)記际起,不管數(shù)據(jù)傳遞到哪個(gè)對(duì)象中使用拾碌,只有沒有對(duì)象牽引著數(shù)據(jù)對(duì)象,就會(huì)釋放了街望。

我在使用zmq訂閱模式的時(shí)候發(fā)現(xiàn)兩個(gè)問題校翔。zmq訂閱模式有自動(dòng)重新連接的機(jī)制,比如斷網(wǎng)重連灾前,服務(wù)器斷開重連防症,但是超過一分鐘好像就重連不了,我測(cè)試了很多次,可是安卓組的同事使用zmq訂閱不會(huì)遇到重連不了的問題蔫敲。因?yàn)橹剡B不了饲嗽,我要關(guān)閉訂閱socket,重新開啟訂閱socket奈嘿,發(fā)現(xiàn)一關(guān)閉socket就馬上開啟socket貌虾,zmq底層庫就報(bào)錯(cuò),然后程序直接崩潰了裙犹,所以我的啟動(dòng)訂閱的方法要判斷是否socket的關(guān)閉尽狠。

- (void)start {
    if (closeSocket) {
        [self performSelector:@selector(startConnect) withObject:nil afterDelay:1.0];
        return;
    }

    [self startConnect];
}

zmq的訂閱模式使用起來還是很穩(wěn)定的,相對(duì)來說zmq的應(yīng)答模式會(huì)出現(xiàn)請(qǐng)求丟棄的問題叶圃。因?yàn)楸救说捻?xiàng)目使用zmq通訊庫袄膏,后期還會(huì)繼續(xù)研究。

示例代碼都放在 github 掺冠。

參考

zeroMQ使用指導(dǎo) http://zguide.zeromq.org/page:all

zeroMQ的示例程序 https://github.com/imatix/zguide.git

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末沉馆,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子赫舒,更是在濱河造成了極大的恐慌悍及,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,284評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件接癌,死亡現(xiàn)場(chǎng)離奇詭異心赶,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)缺猛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門缨叫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人荔燎,你說我怎么就攤上這事耻姥。” “怎么了有咨?”我有些...
    開封第一講書人閱讀 164,614評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵琐簇,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我座享,道長(zhǎng)婉商,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,671評(píng)論 1 293
  • 正文 為了忘掉前任渣叛,我火速辦了婚禮丈秩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘淳衙。我一直安慰自己蘑秽,他們只是感情好饺著,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著肠牲,像睡著了一般幼衰。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上埂材,一...
    開封第一講書人閱讀 51,562評(píng)論 1 305
  • 那天塑顺,我揣著相機(jī)與錄音,去河邊找鬼俏险。 笑死严拒,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的竖独。 我是一名探鬼主播裤唠,決...
    沈念sama閱讀 40,309評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼莹痢!你這毒婦竟也來了种蘸?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,223評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤竞膳,失蹤者是張志新(化名)和其女友劉穎航瞭,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體坦辟,經(jīng)...
    沈念sama閱讀 45,668評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡刊侯,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了锉走。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片滨彻。...
    茶點(diǎn)故事閱讀 39,981評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖挪蹭,靈堂內(nèi)的尸體忽然破棺而出亭饵,到底是詐尸還是另有隱情,我是刑警寧澤梁厉,帶...
    沈念sama閱讀 35,705評(píng)論 5 347
  • 正文 年R本政府宣布辜羊,位于F島的核電站,受9級(jí)特大地震影響词顾,放射性物質(zhì)發(fā)生泄漏只冻。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評(píng)論 3 330
  • 文/蒙蒙 一计技、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧山橄,春花似錦垮媒、人聲如沸舍悯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽萌衬。三九已至,卻和暖如春它抱,著一層夾襖步出監(jiān)牢的瞬間秕豫,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工观蓄, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留混移,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,146評(píng)論 3 370
  • 正文 我出身青樓侮穿,卻偏偏與公主長(zhǎng)得像歌径,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子亲茅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 拯救世界 略 開始的假設(shè) 我們假設(shè)你使用ZeroMQ 3.2以上的版本回铛。我們假設(shè)你使用Linux或者類似的操作系統(tǒng)...
    lakerszhy閱讀 11,287評(píng)論 1 14
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,139評(píng)論 25 707
  • ZeroMQ API Reference 創(chuàng)建一個(gè)ZMQ的上下文環(huán)境,是ZMQ一切的開始克锣。 線程安全茵肃,不需要自己加...
    分享放大價(jià)值閱讀 3,163評(píng)論 0 3
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)袭祟,斷路器验残,智...
    卡卡羅2017閱讀 134,657評(píng)論 18 139
  • 從7月25日到今天10月12日,我在樂刻的回憶就只有這兩個(gè)半月了榕酒。 時(shí)至今日胚膊,到此時(shí)此刻我都很不清楚到底是怎么走到...
    學(xué)生阿豪閱讀 384評(píng)論 1 1