Input Parm Abnormal(入参异常)

入参异常这是一律怪类,防御的主意也针锋相对较通俗易懂,也是极度容易查最易并发的。

  基于ZeroMQ的实时报道平台

  上篇:C++分布式实时应用框架 (Cpp Distributed Real-time Application
Framework)—-(一):整体介绍

 

版权声明:本文版权及所用技术归属smartguys团队所有,对于抄袭,非经同意转载等表现保留法律追究的权!

 

  通讯平台作为C++分布式实时应用框架(Cpp
Distributed Real-time
Application Framework)的尽基本模块,承担了分布式实时框架的基本功通讯功能。通讯平台框架具备了因Reactor模式之网络通讯能力,并且依赖让ZeroMQ库,因此支持非持久化的message
queue的成效。基于配置文件来机关建立链接关系的效益,可以同状态为主一块配合,实现无需重新开节点的动态扩容缩容等功能。强大的实时监察能力,可以实时举报每个通讯子节点的TPS和时延等根本特性数据。管控工作经过的力,业务过程的良心跳检测,故障时自动重新开、保证系统正常运作。完善之平台工具,可以经报道平台为事情经过发送各种吩咐,如:调整日志级别,刷新业务参数,启停业务过程等等。下面用相继介绍通讯平台的机能细节。

代码

  • swizzing切面
  • 方防御选型
  • 守护成功申报

次第外要之是代码,这个模块是一旦没有任何侵入性的,所以切面是须的,其次就是尽可能的细化切面颗粒度保证意外状况最好小化!

另外一些哪怕是切面以后咱们针对原本方法应该采取什么样的守卫,这里虽可以try catch的样式呢得展开逻辑判断形式。
一旦己之代码里之所以逻辑判断,更多之勘察是本着的函数都偏下层都爱采取时外部恰巧又闹各种循环逻辑,那样相较之下try catch当匪刹车的调用性能会时有发生得影响,所以小失效try catch作为防守的手法。
自打外一样角度看其实try catch的使用状况有些计还是于确切的,首先我们当守卫时办法颗粒度已经杀细所以抓住异常都见面做对诺处理不会见出内存泄漏或逻辑遗漏,另外无论try还是catch内的法为未见面尽多,满足了`try
catch的最佳场景,只是各自方法循环利用多少过大或者性能没法到极致仅此而已。

防卫了了crash就是上报,我们保安了序的又为就算表示来地方写的生问题,由于没crash所以没crash
log,这时候就待在安保模块里在报告机制,这时候我之做法虽然是推广有一个商事相当于人口失去贯彻,安保模块就专心处理防御之作业,上报至劳动端的事务交给专门处理这从的模块,我们无非待在守卫成功时告诉协议来诸如此类个事情即可。剩下的饶是私有看景要用详细情况直接[NSThread callStackSymbols]把仓库信息输出一下!

//安保模块上报协议
@protocol SafeObjectReportProtocol

@required
/**
 上报防御的crash log

 @param log log无法抓到Notification的遗漏注销情况
 */
-(void)reportDefendCrashLog:(NSString*)log;

@end

万一落实之协议的不过待针对SafeObjectProxy做个Category实现转即可。

还有即使是扼守之分类开启,这时候枚举就要用各运算的花样,这样才会配合多种模式共存如下只开Array和String的守护

[SafeObjectProxy startSafeObjectProxyWithType: SafeObjectProxyType_Array| SafeObjectProxyType_String]

  一、根据部署文件自动建立通讯链接拓扑关系

  常见的分布式系统通常以经过中、节点内的各种通讯关系写好在事情代码中,这是导致代码复杂难以知晓的来由。我们创新地用装有的报导关系提取到AppInit.json配置文件中,业务代码中不再包含其他和报道连接相关的情,使工作代码可以更在意让业务处理,而休用分心于复杂的分布式节点通讯当中。下面我们用拉动大家看下图所示通讯关系的部署。

 统计 1

  OLC作为数据分发节点,给多个工作处理节点分发消息。业务处理节点内由OCDis接收外部消息,转发给内部的OCPro业务处理进程,并承担处理终结晚的回包。

OLC配置部分:

   "OLC" : {
      "AUTO_START" : "YES",
      "ENDPOINTS" : [
         {  // 用于与SmartMonitor建立心跳
            "name" : "MonitorSUB",   
            "zmq_socket_action" : "CONNECT",  // ZMQ的连接模式
            "zmq_socket_type" : "ZMQ_SUB"     // ZMQ的通讯模式
         },
         { // 下发消息给OCDis,这边存在转发功能,支持业务实现按条件转发
            "downstream" : [ "OCDis2OLC"],
            "name" : "NE2OLC",                // 根据这个名字在业务代码中实现转发
            "zmq_socket_action" : "BIND",
            "zmq_socket_type" : "ZMQ_STREAM" 
         },
         { // OLC到OCDis的链路
            "name" : "OCDis2OLC",
            "statistics_on" : true,
            "zmq_socket_action" : "CONNECT",
            "zmq_socket_type" : "ZMQ_DEALER"
         },
         { // OCDis回OLC的链路,之所以来去分开,主要用于实现优雅启停功能(启停节点保证不丢消息)
            "name" : "OCDis2OLC_Backway",
            "statistics_on" : true,
            "zmq_socket_action" : "CONNECT",
            "zmq_socket_type" : "ZMQ_DEALER",
            "backway_pair" : "OCDis2OLC"
         },
         {  // 用于与SmartMonitor的命令消息链路
            "name" : "OLC2Monitor",
            "zmq_socket_action" : "CONNECT",
            "zmq_socket_type" : "ZMQ_DEALER"
         },
      ],
      "ENDPOINT_TO_MONITOR" : "OLC2Monitor",
      "INSTANCE_GROUP" : [
         {
            "instance_endpoints_address" : [
               {
                  "endpoint_name" : "NE2OLC",
                  "zmq_socket_address" : "tcp://*:6701"
               },
               {
                  "endpoint_name" : "OCDis2OLC",
                  "zmq_socket_address" : [
                     "tcp://127.0.0.1:7201"   // 跨机的IP地址与端口,配合状态中心可实现自动管理,无需人工参与配置
                  ]
               },
               {
                  "endpoint_name" : "OCDis2OLC_Backway",
                  "zmq_socket_address" : [
                     "tcp://127.0.0.1:7202"
                  ]
               },
               {
                  "endpoint_name" : "OLC2Monitor",
                  "zmq_socket_address" : "ipc://Monitor2Business_IPC"
               },
               {
                  "endpoint_name" : "MonitorSUB",
                  "zmq_socket_address" : "ipc://MonitorPUB"
               }
            ],
            "instance_group_name" : "1"
         }
      ]
   },

 OLC程序:

static const char * ENDPOINT_NE2OLC = "NE2OLC";
static const char * ENDPOINT_OLC2OCDIS = "OCDis2OLC";
static const char * ENDPOINT_MONITORSUB = "MonitorSUB";

int main(int argc, char * argv[]) {

    SmartUtilities::Daemonize();
    OLCProxyServer server(argc, argv);

    if (!server.Initialize(logger))
        return -1;
  
    // OLC与OCDis的消息处理
    server.SetCallbackOnReceivingMessage(ENDPOINT_OLC2OCDIS, bind(&OLCProxyServer::ReceiveFromOCDis, &server, _1, _2, _3));

  // OLC与SmartMonitor的消息处理
    server.SetCallbackOnReceivingMessage(ENDPOINT_MONITORSUB, bind(&OLCProxyServer::ReceiveFromMonitorSUB, &server, _1, _2, _3));

  // 解析消息包实现业务功能
    server.SetPacketParserFunction(ENDPOINT_NE2OLC, bind(&OLCProxyServer::ParseStreamCCR, &server, _1, _2, _3));

  // 设置消息转发具体规则
    server.SetDownstreamSelector(ENDPOINT_NE2OLC, bind(&OLCProxyServer::StreamSelector, &server, _1, _2));

    server.Run();
    return 0;
}

NSNotification

这个虽然也是成对以,单比地方的几乎单假设安全有,因为用它们发出[[NSNotificationCenter defaultCenter] removeObserver:self]勤调用或没addObserver且未见面挂,所以可以全局作一下,我以SafeObjectProxy其间纵使只是针对所有NSObject目标上加了个特性做标识,然后hook一下NSNotificationCenter-(void)addObserver:(id)observer selector:(SEL)aSelector name:(NSNotificationName)aName object:(id)anObject方法,只要observer是NSObject靶自我虽标识一下,然后切所有NSObjectdealloc倘若标识了之联结执行[[NSNotificationCenter defaultCenter] removeObserver:self],反正多尽了吗远非问题因此之放心!

唯独如若是成对的,就生出另一个问题,万一真正需要注销的地方是暨逻辑有关,那您对象销毁时注销就晚了,就比如面KVO中涉及的我们做的立即层crash防御其实犯错率并无高能及时发现,而及时发现不了的只能是由此编码规范或者人员分别禁用来缓解。

  二、在线更新链接拓扑能力

  通讯平台支撑在线重新读取更新的布局文件,更新网络拓扑,自动建立新链接、断开旧链接的力。配合状态为主可以兑现无需还开节点的动态扩容缩容等力量。

  统计 2

NSTimer

NSTimer比较特殊,有些时候偏偏不拖欠成为对下,它的成对的逻辑其实是与自己之生命周期有关,毕竟生命周期结束时只要失去成对的停掉timer才会自由,另一些即使是NSTimer精确度并无愈!但它们包裹出来为丁就此底道是ok的亏有单例模式与实例模式简单栽采取。所以自己的提议当然是团结管gcd的timer封装一下,另外拿target这个定义变为weak持有,这样咱们团结包装的timer就可dealloc的时节停掉timer释放了,按照系统NSTimer封装方法即可。这样至少会管timer指定的target释放时timer能住少不见面因跑了其余未安全之逻辑挂掉。其他可能挂掉的图景应当于少。。。

Timer:MSWeakTimer于推荐的一个计时器封装方法就是本身上面说的那种

  三、SmartMonitor进程监控管理工作过程和SmartTool工具进程

  业务经过可以跟SmartMonitor建立通讯联系,SmartMonitor可以检测工作过程的心尖跳,以担保工作经过的可用。SmartMonitor通过AppCount.json来治本节点业务经过,实现合并启停等功效。

{
  "OCPro": {
    "IN":  2,      // 业务进程可以有不同的种类,后面代表进程数
    "PS":  3,
    "SMS": 4,
  },
  "OCDis": 3,
  "SERVER_TYPE":"OCS"  // 节点的类型
}

  还得经SmartTool工具进程,来深受业务过程发送各种吩咐,如:调整日志级别,刷新业务参数,启停业务经过等等。

 

     1. 起动平台

      SmartMonitor

 

      2. 停平台

      SmartTool stop all

   
  停指定进程(停止后会见于SmartMonitor重新拉于)

      SmartTool stop OCPro
停止所有业务的OCPro进程

      SmartTool stop  OCPro.IN
停止IN业务的OCPro进程

      SmartTool stop 4829
停止PID为4829的进程

 

      3. 调整应用层、框架层日志级别

     
其中,日志级别也error,warn,info,debug,trace

      SmartTool log 进程名
level=日志级别,flush=日志级别

      比如: SmartTool log
 OCPro level=debug,flush=debug

KVO

KVO这种crash如果要守护其实只能防御下面3栽情况:
1.观察者或给观察者已经休设有了
2.撤回与长的次数不兼容
3.尚无写监听回调observeValueForKeyPath:ofObject:change:context:

设若立3种状况咱来认真想下开之等级是勿是相似都见面第一时间就让察觉!而且若是未曾经历的程序员写KVO我们是休是都无敢用,会频审查,而发出经历的同时不见面发上面的摩擦。。。。
倘若对端的场面防御也生复杂,而且自尝试以因此过许多老三在,都以我司稍微有硌复杂的种达到悬挂了,不仅没会守护crash还之了crash,这种成对逻辑的八面玲珑非常大,你没法知道系统内人家怎么用正在打的!
说一下守卫上面的情景首先切面add、removeObserve是肯定的,还要在富有的类里再加一个靶,这个目标要负责管理KVO下面就是让KVOController吧,让具备的观察者都变成了受观察者的一个性能,用map记录原来的观察者和keyPath等消息,这样加加要移除观察者就可知看清是不是成对出现的,另外KVOController在dealloc时也得经过map依次移除监听,而出于有的监听回调其实都是出于KVOController的observeValueForKeyPath:ofObject:change:context:通过[originObserver observeValueForKeyPath:keyPath ofObject:object change:change context:context]传送出的自然没有写监听回调的情况也堪判了,但为是能迎刃而解那3独情况!

诚KVO产生的恐怖之crash是移除时机不跟观察者或让观察者销毁有关联,而是同我们的逻辑有关,一旦没有当宜会移除导致的crash排查起来超级讨厌!还有你以监听回调里处理逻辑来无有线程安全问题,这些才是咱们于上线前容易透,排查以坏排查的!

安保体系虽然是若保护上线后能正常运作,然而便比如本人这边说之KVO,如果非以编码期间就做严格规范,上线后产生之问题吧是有史以来不许防御的!

然后再来说说怎么界定我们的自由发挥,KVOController刚才说及之此用的凡将她变形,把回调用block放出来,另外就是是于她发生单例模式以及通常的实例模式,只有创建对象、关联监听与逻辑处理,一个KVOController可以是大局或属于一个靶,相当给可视化了KVO的见效周期,一目了然,这里叫突出逻辑适应我们的标准才是无可非议的安保思路。包括NSTimer在内也也是如此好为个TimerController不过封装最好吧转变用NSTimer精度不高,反正要卷入不如直接gcd,与该设手动保持成对不如我们就算管逻辑封装好,让使用者忘掉成对的概念!但在开之今天全可GitHub搜一波找来封装好的团结再也略包装下,然后叫团队以规范开发即可。。。

KVO:KVOController比较推荐的一个KVO管理

  四、通讯平台性数据 

 统计 3

 

 进程Z负载控制消息流量,进程A负责发、收消息,统计时延数据。进程B收到信息后当转消息。

 统计 4

 性能瓶颈主要以A机,既设担当收发包,又如统计时延数据,还要控制流量。

 

未完待续…

 

 技术交流合作QQ群:436466587 欢迎讨论交流

Unrecognized Selector(找不交方式)

夫找不至方法算比较好惩治的。。。也毕竟比较普遍的好查的,另外处理ok了null对象调用的题目呢会见随着缓解
而摘的方式来一定量种植
Hook这有限单点子
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
- (void)forwardInvocation:(NSInvocation *)anInvocation
还是Hook这一个方式
-(id)forwardingTargetForSelector:(SEL)aSelector

核心思想就是当追寻不顶方法之前创建方法确保继续执行不悬挂,为了尽可能不多余的创造方法,集中的拿创建于到联合之地方。

前端需要以methodSignatureForSelector执行前当初的target里创建没有底艺术,然后用它们调用methodSignatureForSelector归来,而此的target当然要单例弄出来省的以后来回创建。然后在forwardInvocation里用他来调用invokeWithTarget拄到我们新的target上。

膝下为即是我因此底法门,之所以用其最主要是一个方式
就ok!而我辈还要兼任静态方法和实例方法去分别hook才会防住这半种植,而前者为要hook的点子重新多。。。。
若是这里就待切forwardingTargetForSelector方,静态方法返回class,动态方法返回target,当然返回之前我们而上加上未存的点子,值得注意的是OSX上一个神奇之题材,我在认清是否系发生夫艺术的时第一次于甚至respondsToSelector返回false而methodSignatureForSelector产生多少,第二差校验是methodSignatureForSelector才为空,而iOS上则并未立题目首先不好校验就是对之!

Dangling Pointer(野指针)

夫种Crash堪称经典!就是怪最难以排查的,而这里我们会召开的防御工作也杀少!
现实定位看看腾讯这几首雅有救助!
怎么样定位Obj-C野指针随机Crash(一)
何以稳定Obj-C野指针随机Crash(二)
安稳定Obj-C野指针随机Crash(三)
咱俩只好去对都清楚之产出野指针的近乎进行防卫,找到crash的野指针开启Zombie
Objects,加上Zombies工具,然后想艺术不断增强复现率还是得以的一定及之。
俺们的防卫则是hook系统dealloc,判断用举行拍卖的好像非移步系统delloc而是走objc_desctructInstance自由实例之中所负有属性之援和涉及对象,保证对象极其小化。紧接着就是待来波isa swizzling了,因为一般野指针伴随在的还有就是是调用没有底章程,或者出于调用的之时机是休健康的,各种数码的安全性都没有了包,所以dealloc后去掉所有拥有,再管原先的isa指为一个别样的好像,而这类能把具备的调用方法对一个拖欠方法这么即便由至了守之来意。

能够干就从之也罢惟有NSProxy了,利用协议落实methodSignatureForSelectorforwardInvocation主意,统一由及事先处理找不顶方式自动创建的类似吃,也即是以NSProxy内实现者Unrecognized Selector的防御,这样有对野指针的调用就还是拖欠了!
正要因上面的由来一旦打开了之防御,真正释放的时就是还是有,如果当野指针出现前点了真释放的逻辑,crash就还是会有的!
我在SafeObjectProxy里只是用野指针个数控制做真正自由,回头可能会见卷入个block方便复杂情况的论断。

安保系统规划

此处我所认为的安保体系应于代码和标准两独面看,毕竟想拘捕及持有的crash情况是得非容许的,现实中尽管处处try
catch都没法保证办案及独具crash!

规范

另外一个安保模块的构成则应是针对性代码规范之制订同校验,这就是得clang来举行了,不是此处最主要讲的,相当给多了同样种植Build OptionsCompiler for C/C++/Objective-C属性的挑选,用我们付出的Xcode校验插件,检查代码语法上之题材直接报错,这样由源头来规范化编码。

Abnormal Matching(异常配对)

马上同样近似算是不建议召开防守之!成对的章程处理好像KVO,NSTimer,NSNotification都算,需要注册与取消。
这种气象我之建议是联合封闭装独立模块调用统一之方,让丁非欲关怀注册与撤销,主要描写逻辑处理。从效果实现上开严格界定,这样吃人口设想的就算是怎么样将一个现象融入到包的法门中,而非是即兴的刻画!
下面说下由,由于挂号与取消是分离写的
,所以利用状况,解决问题之章程还见面具备非常灵活的操作,这实在生吓人,先用KVO做一个比方顺便说一下眼看仿佛防御如果确实若举行一般的做法是怎开。

UI Refresh Not In Main Thread(UI刷新不以主线程)

刷新UI不以主线程的情这里就是对UIView和NSView的3独道做切面线程判断。分别是setNeedsLayout,setNeedsDisplay,setNeedsDisplayInRect,执行前看是不是于主线程,不以的语就是绝对到主线程执行,但死引人注目这3单主意肯定覆盖不都,而且就是覆盖全了每次都认清一下也是性质浪费,所以这边分别斟酌处理吧,这看似情况小没悟出其他好之处理方式!但好于算有这样个可控方案!

timg.jpeg

常用色入参异常

常见类包括String,Array,Dictionary,URL,FileManager等这些看似空值初始化,越界取值,空赋值等,基本看crash
log统计依次切面对应措施以执行前判断一下即便ok。如objectAtIndex,objectAtIndexedSubscript,removeObjectAtIndex,fileURLWithPath,initWithAttributedString,substringFromIndex,substringToIndex等等。唯一要专注的哪怕是这些使切面的类名可是五花八门同时还iOS版本有异常怪关系,所以这个就是是指crash
log积累了解有怎么样坑。当然代码写的好就算就此非顶了!__NSSingleObjectArrayI夫就是近些年以iOS11上新意识的报错数组类,当然也说不定是新近我司有人形容起了这个相关的bug……
广泛的急需留意的hook的类似产生以下
objc_getClass("__NSPlaceholderArray")
objc_getClass("__NSSingleObjectArrayI")
objc_getClass("__NSArrayI")
objc_getClass("__NSArrayM")
objc_getClass("__NSPlaceholderDictionary")
objc_getClass("__NSDictionaryI")
objc_getClass("__NSDictionaryM")
objc_getClass("NSConcreteAttributedString")
objc_getClass("NSConcreteMutableAttributedString")
objc_getClass("__NSCFConstantString")
objc_getClass("NSTaggedPointerString")
objc_getClass("__NSCFString")
objc_getClass("NSPlaceholderMutableString")
切实产生安方法要切面还是看源码吧,这有凡是没什么难点的。

此外我的防御中没针对NSCache做,可能后会管加点,因为缓存相关的模块我之建议是协调包裹缓存模块或因此第三着,那样对上层使用者来说已经是高枕无忧的了!各种非常处理在缓存模块里便当发生包装。

Thread Conflict(线程冲突)

着力无解的问题,出现以后瞬间懵逼,典型例证就是是死锁,异步调用同一对象造成不安全,基本没有防守手段,排查也只好依靠多加log不断复现,然后猜。。。。
而一般只要代码按照正规的正规写吗非会见那么好碰到这题目,但线程冲突理论及只要保证UI操作都以主线程,其他还gcd不以主线程上,然后有需要线程安全之gcd信号量做锁就可,但不会见有人这样写代码,性能及效率那么将是还要抛开的,现在且恨不得你这出活那有空那样,这类似就得完全不考虑防御之从了!

KVC Crash

KVC归根结底也终于这好像入参异常,一共切面3个地方就是足够防御了!
-(void)setValue:(id)value forKey:(NSString *)key,
-(void)setValue:(id)value forKeyPath:(NSString *)keyPath
空值防御上面2单方法
-(void)setValue:(id)value forUndefinedKey:(NSString *)key
方是就算没的性能做赋值操作时走之回调,如果就此到自身之SafeObjectProxy倘若于定义各个类不同之处理是可不起头起来UndefinedKey防御的!

Crash分类与防御实现

  • Unrecognized Selector(找不交点子)
  • UI Refresh Not In Main Thread(UI刷新不以主线程)
  • Input Parm Abnormal统计(入参异常)
  • Dangling Pointer(野指针)
  • Abnormal Matching(异常配对)
  • Thread Conflict(线程冲突)

怀念如果守crash,首先使召开的便是了解都产生什么样情形会起crash,上边就是作者总结的几种植最广大的场面,不咸的讲话想有人留言补足,毕竟crash的守真正发出发言权开发这种模块的估算只有大柜开发app的,不然用户量不足够没样本采集,没法了解坑爹的状态!

假定者列的6种常见crash,真正会广域控制得矣之或许也唯有生一半勿顶!下面就是相继讲解一下,Hook切面就是非同小可的手腕!

前言

这边要归因于iOS和OSX讲出口crash闪退怎么防御。
里头最新的OSX应用本身即发出肯定闪退防御,但小类似@try @catch当最外层包了一下平凡的越界调用空方法还见面搁浅在操作位置不向下执行,如果无进一步复杂逻辑不会见闪退,只是影响延续之操作。

若果iOS则没有这样好出口了,二话不说直接闪退给您看没地方的那种机制。

于是才发生矣规划一个安保网的含义,来管最好特别程度之健壮性,理想之状态就是未crash且能持续健康运行后的逻辑。

参考了无数网上的素材有矣底的有点成果分享出去,这实则就是安保系统最终的一个环的守卫

https://github.com/heroims/SafeObjectProxy