三国杀武将|手游三国杀边锋版

首頁 >iOS開發

從0到1實現一個模塊間通信的服務組件

2019-05-06 13:11 編輯: suiling 分類:iOS開發 來源:一線搬磚工人

寫在前面

一名一線開發對于App架構和組件化的思考  文章中,我們主要站在了軟件工程的角度上,分析了做App架構和組件化時該如何下手,其中也介紹了路由和服務模塊在組件化中扮演的重要角色。本文,我們將進行實操,一步步實現一個模塊間通信的服務組件。

這里剖出一個微服務的概念,在Java Spring框架中,微服務是個很火的東西。鑒于筆者對于Java一概不知,所以僅僅站在作為一個App開發的角度去認知它。微服務確切的說是某個功能模塊的子集,它把單體架構中的某些功能拆離出來,然后開啟獨立進程來給其他模塊提供服務,通信方式一般是標準REST API來進行。這樣的做的話有幾個好處。

1.獨立進程,獨立部署。不會因為單體架構機器掛掉后,導致所有服務不可用。  

2.避免項目過度臃腫。  

3.擴展性強,可以多個微服務組成集群。

對于Java Spring框架,這里就不做過多贅述了。推薦一個比較形象的描述微服務的漫畫,感興趣的可以看一下,這樣可以對整個系統上下游架構會有更深的理解。

漫畫說:什么是微服務?

服務組件在App里應用場景

舉個栗子。

還是拿登錄模塊舉例子。。。

image.png

在之前的分享中我們知道,登錄模塊一般位于App分層架構中的通用模塊層。假如說A模塊要調用登錄模塊中的獲取登錄態的方法,在沒有服務組件的情況下,我們一般會直接把登錄整個模塊import進來,這樣做難免有點小尷尬(僅僅是獲取個登錄態,我就要把整個登錄模塊import進來,這樣就耦合在一起)。

再打個很形象的比喻。。。

雖說結婚不是兩個人的事情,而是兩個家庭的事情,但是結婚后你老丈人和丈母娘一起打包過來跟你過了,你是什么感受?那肯定是臉上笑嘻嘻,心里mmp啊。我是要跟你女兒過日子的啊,咋都打包給我了???。

image.png

所以通過以上的生動的示例,我們總結出了服務組件在App里的應用場景。

  • 模塊間更小粒度組件間的通信場景。

  • 開放一個模塊中某些特定功能API場景,使模塊中的子組件“微服務化”。

  • 組件化之間進行解耦的應用場景。

從0到1編寫一個服務組件

方案一:通知中心(NSNotificationCenter)

Excuse me?通知不是單向數據傳輸么,A給B發通知,B收到通知后處理,貌似不符合我們這種有返回值的需求啊?

在OC中有個神奇的東西那就是Block,說白了是匿名函數,那我們直接把函數指針傳輸過去不就可以了嘛?而且我們知道在OC中Block本質上是一個對象,恰好發送通知可以攜帶一個對象,豈不美哉。

- (void)postNotificationName:(NSNotificationName)aName object:(nullable id)anObject;

說寫咱就寫!Perfect!你為何如此優秀!!!

登錄模塊:

/*登錄模塊注冊通知*/
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(getCookie) name:@"getCookie" object:nil];  

- (void)getCookie:(NSNotification *)noti {
    void(^callBack)(NSString *) = noti.object;
    /*獲取cookie邏輯*/
    ---this is a long story---
    /*獲取完畢之后,調用block*/
    if (callBack) {
        callBack(@"cookie");
    }
}

調用模塊:

/*創建一個Block*/
void(^callBack)(NSString *) = ^(NSString *cookie) {
     NSLog(@"cookie->%@",cookie);
 };
/*調用方通過發送通知*/
[[NSNotificationCenter defaultCenter] postNotificationName:@"getCookie" object:callBack];

Command+R,完美!滿足需求,我們成功地在模塊中獲取到登錄模塊中的登錄態。

這時候我們停下來仔細想一下通知中心的方案,假如說登錄模塊除了提供獲取登錄態的服務,可能還有獲取用戶信息服務等等。如果服務越來越多,注冊通知就會分散在不同的文件中、不同的代碼邏輯中,服務太分散難以維護!!!

我們總結了一下,很容易發現通知的方案所存在的問題。

  • 注冊通知太分散,難以維護。

  • 沒有統一的地方來維護通知名稱,調用方需要預先知道通知名才能調用該服務。

  • 傳參數不太方便,雖然系統發送通知函數提供了一個object,但在復雜業務中遠遠不夠。

  • 通知中心存在一定的問題,比如說不支持異步通知(在A線程注冊通知,B線程發送通知,接收到通知后回到A線程進行處理)。

關于通知中心的弊端,這里也不做贅述,推薦一個自己之前寫的一個通知中心解決方案,目前還不太完善。其使用姿勢相當優雅,而且實現了異步通知,感興趣的筒子們可以了解一下。

SmartBlock(一個用Block實現的通知替代方案,并且已實現在不同線程進行發送消息和執行Block,支持多參數傳送,解決回調地獄問題,適用于組件化數據傳輸等。)

方案二:反射機制(NSClassFromString)

名詞解釋:Java反射說的是在運行狀態中,對于任何一個類,我們都能夠知道這個類有哪些方法和屬性。對于任何一個對象,我們都能夠對它的方法和屬性進行調用。我們把這種動態獲取對象信息和調用對象方法的功能稱之為反射機制。以上內容來自于網絡。

在OC中,runtime也提供了類似的機制,我們可以通過runtime提供的函數,在運行時動態地獲取到某個類、方法、屬性等。

NSClassFromString(<#nsstring _nonnull="">)

NSSelectorFromString(<#nsstring _nonnull="">)

既然方案一注冊通知太分散,那我們可不可以對于每個服務創建一個類,然后暴露方法,通過runtime反射機制去調用?

  • 第一步:針對獲取登錄態的服務單獨創建類文件。

  • 第二步:在類文件中開放一個方法供調用方調用。

  • 第三步:調用方通過NSClassFromString獲取到登錄態的Class。

  • 第四步:調用方通過NSSelectorFromString獲取到登錄態提供的selector。

  • 第五步:調用該方法- (id)performSelector:(SEL)aSelector withObject:(id)object;完成該服務的調用。

登錄模塊:

/*我們在登錄模塊創建一個GetLoginCookie類*/
/*.h和.m如下*/
#import NS_ASSUME_NONNULL_BEGIN

@interface GetLoginCookie : NSObject

- (id)getLoginCookieWithObjc:(id)obj;

@end

NS_ASSUME_NONNULL_END
#import "GetLoginCookie.h"

@implementation GetLoginCookie

- (id)getLoginCookieWithObjc:(id)obj {
    return @"cookie";
}

@end

調用模塊:

/*在模塊中獲取到GetLoginCookie的Class*/
Class cookieCls = NSClassFromString(@"GetLoginCookie");

/*通過Class,生成一個GetLoginCookie實例*/
id cookieInstance = [[cookieCls alloc]init];

/*通過方法名生成一個SEL*/
SEL selector = NSSelectorFromString(@"getLoginCookieWithObjc:");

/*調用performSelector并獲取返回值*/
NSString *cookie = [cookieInstance performSelector:selector withObject:@"it's me!"];
NSLog(@"cookie->%@",cookie);

Command + R,完美運行,我們也得到了我們想要的結果。

對比方案一和方案二,方案二的確解決了服務分散不好管理的問題,但是依然存在幾個問題。

  • 依然沒有一個配置的地方讓調用者一下就能看到類名或者sel名,方便進行調用。

  • 還有個問題,我們很容易發現這兩個方案都是“去中心化的”。也就是說,消息的發送和消息的接收處理都是直接點對點的。去中心化帶來了很多問題,如果登錄態的服務出現問題,而我們又沒有一個統一收口的地方統一處理,不可控。

這就好比區塊鏈技術去中心化雖然帶來了很多技術變革,但同樣也帶來了一些隱患。如果沒有上面的的監管,那很多black money可以通過區塊鏈手段洗到國外。想想很多貪官拿著我們辛辛苦苦繳納的稅,把貪來的錢都洗到了國外,然后老婆孩子在國外逍遙自在,自己在國內做luo官。而我們依然活在水深火熱之中,百姓民不聊生,苦不堪言,我們內心該是何等氣氛!!!。

扯多了,我們回到正題。

方案三:引入中間件(IQService)

通過對比前兩個方案,我們大概對于服務組件應該滿足哪些要素有了更加清晰的認識。

  • 服務組件要易于管理,統一分布在模塊中的某個地方。

  • 服務組件最好通過配置文件去管理,方便業務方查閱調用等。

  • 服務組件去Model化,徹底解除、還有支持同步異步調用等。

  • 服務組件最好用中間件方式,有統一收口的地方,發生問題可控。

  • 服務組件最好支持靜態注冊、動態注冊等,擴展性高。

我們來簡單畫一下,服務組件架構圖。

image.png

  • 首先為了解決服務易于管理問題,我們這里使用plist來維護業務服務列表和具體服務名與服務的對應關系。

image.png

如圖所示,IQService.plist維護了業務list,一般IQService主工程維護一份即可。

image.png

LoginModule.plist中維護了該組件為外部提供的所有服務列表(服務名和實現類的對應關系)

  • 去model化,我們這里用多參數來解決(也可以通過NSDictionry解決)。

/**
 同步、異步調用
 
 @param sevice 微服務名
 */
+ (void)invokeMicroService:(NSString *)sevice,...;

/**
 同步調用
 
 @param service 微服務名
 @return 同步調用返回值
 */
+ (id)invokeMicroServiceSync:(NSString *)service,...;

我們再來看下具體的使用姿勢。

登錄模塊:

首先創建LoginModuleCookieService類,并將該類注冊到LoginModule中。
.h聲明
#import NS_ASSUME_NONNULL_BEGIN

@interface LoginModuleCookieService : NSObject

- (NSString *)getCookieWithSignature:(NSString *)signature;

@end

NS_ASSUME_NONNULL_END
.m實現
#import "LoginModuleCookieService.h"

@implementation LoginModuleCookieService

- (NSString *)getCookieWithSignature:(NSString *)signature {
    return [NSString stringWithFormat:@"%@->cookie",signature];
}

@end

調用模塊:

同步調用
NSString *cookie = [IQService invokeMicroServiceSync:@"GetCookieSyncService",@"我是同步調用",nil];
NSLog(@"%@",cookie);
異步調用
void (^callBack)(NSString *) = ^(NSString *cookie){
        NSLog(@"%@",cookie);
    };
[IQService invokeMicroService:@"GetCookieAsyncService",@"我是異步調用",callBack,nil];

分析到現在,方案三基本能滿足大部分業務需求。具體實現代碼已經開源到GitHub -----> IQService,一個iOS端模塊間通信的解決方案。喜歡的筒子可以來波Star,也歡迎大家提交PR和ISSUE。

騙大家刷完Star,現在再潑盆冷水。。。

我們再仔細思考一下方案三,貌似有幾個問題依然沒有解決

1.編譯時依然無法進行參數正確性校驗,attribute?宏定義?

2.目前只有靜態注冊,不支持動態注冊。

上面兩個問題,歡迎大家進行頭腦風暴。有好的解決方案可以留言分享,也可以提交PRs or Issues。https://github.com/Lobster-King/IQService

在這里提示一點,沒有一個方案是100%OK的,只有適合自己的才是最好的。

架構和組件化系列文章預告:說說MVVM,會一步步跟大家一起寫一個輕量的view和viewModel進行數據綁定的框架。

文章首發GitHub https://github.com/Lobster-King/AppArticles

作者:一線搬磚工人

鏈接:https://www.jianshu.com/p/3ed4151d928c

搜索CocoaChina微信公眾號:CocoaChina
微信掃一掃
訂閱每日移動開發及APP推廣熱點資訊
公眾號:
CocoaChina
我要投稿   收藏文章
上一篇:GitHub優秀三方庫推薦以及日常知識積累分享
下一篇:iOS系統的底層通知框架庫

相關資訊

我來說兩句
發表評論
您還沒有登錄!請登錄注冊
所有評論(0

綜合評論

相關帖子

sina weixin mail 回到頂部
三国杀武将