《架构整洁之道》
如果你要成为一名架构师,你需要明确地区分几组词语,否则你不可能成为一名合格的工程师或架构师。这几组词语是简单vs.简陋、平衡vs.妥协、迭代vs.半成品。如果你不能很清楚地定义出其中的区别,那么你将很难做出正确的决定,也就不可能成为一名优秀的工程师或架构师。
什么是SOA?
SOA——解决软件定义汽车中服务间通信的分布式架构
在软件定义汽车中,应用间跨进程或跨核的通信,必然成为软件架构设计中一个需要去解决的问题。SOA在互联网已经应用了很长时间,但在汽车行业中,算是比较新的概念。鉴于汽车的应用场景和通信需求有其特殊性,很多互联网的SOA技术,并不能照搬过来。虽然Adaptive AutoSAR采用了SOA作为通信架构(ARA::COM架构如下图),但是Adaptive AutoSAR的应用可以说还没有普及,应该说整个行业就没什么标准的SOA中间件解决方案,几乎没有专业做中间件研发的公司
SOA,Service-Oriented Architecture(面向服务的架构),是一种架构思想,实施者可以根据实际情况设计SOA的技术实现。为什么要面向服务?以前用得好好的面向信号或者面向消息的通信架构怎么就不香了?面向服务的通信架构,它的优势到底在哪里
本质上SOA就是服务的集合。
以智能座舱域为例(如下图),可以把“服务”分为两类:基础服务和应用服务,基础服务的功能可能包括:总线消息的解析和路由(如车身数据服务)、直接与硬件相关的逻辑处理(如音频服务)、上层应用有共同需求的一些基础设施(如日志服务);应用服务的功能相对复杂些,可能需要由多个基础服务提供数据支撑,也可能需要应用服务之间相互协同,实现业务逻辑(如导航服务)。
SOA分层架构视图(仅作举例)
这只是一个很简单的例子,想表达的是,每个服务将自己的功能,以接口的方式提供,基于这些服务和接口,便可以设计出应用场景,以满足各种用户需求
个人觉得,汽车SOA的设计难点,主要在于以下几点:
-
服务的定义和划分,要把业务需求分析透彻,从中提炼出服务的功能,数据流向理清,定义服务的边界,把握服务的粒度,怎么做到“低耦合,高内聚”,我以前很讨厌研究需求,觉得那些不过就是些业务,没啥技术含量,后来才慢慢认识到,这种想法很危险啊,脱离需求的软件设计不可能很好地满足需求,如果不能很好地服务于产品功能,那么再牛逼的技术都没有机会实现它应有的价值,事实上,能够把需求文档转化为可实施的软件设计,也是一种能力;
-
不同系统中,要实现中间件框架或者底层通信基础设施,Adaptive AutoSAR有ARA::COM组件,Android有Framework,但不能跨域,QNX/Linux就不用说了。要实现一个中间件框架,本身并不是件容易的事,需要比较强的技术实力,一旦出了问题一般都是重大问题;
-
服务接口标准化,接口描述语言化(IDL),能够通过工具自动生成RPC桩的代码(最好能够关联整车通信矩阵,e.g. ARXML->C++ API),能够跨平台,支持多语言,毕竟UI层可能不是C++写的,时至今日,没几个应用愿意去解析原始消息,远程调用接口不香嘛~;
-
如何兼容一些没有与时俱进的设备和模块,如何兼容旧的传输通道,如何尽可能复用以前的业务逻辑,理论上任何兼容都是可以实现的,抽象一层不够,那就再来一层,但兼容得越多,系统就越复杂,出问题的概率就越大,维护起来就越费劲,这意味着成本的升高,质量却不见得变好;
-
评估性能影响,怎么保证安全性,……,如果是基于开源项目,可能还要做二次开发,来满足这些非功能性质的需求~;
所以,汽车SOA真不是SOME/IP,也不是DDS,更不是Adaptive AutoSAR,这些都是汽车SOA技术栈中的一环,并不是全部。
技术需求
四个维度:编程,架构,网络,工具。
编程:多看,多写,多折腾
目前,智能座舱的生态圈主要包括Android、Linux、QNX等操作系统,因此,我认为做SOA开发,至少需要熟练掌握C++和Java两种编程语言。C++是必须要会的,Linux/QNX主要用C++开发,Android其实也涉及C++开发,尤其是对性能要求较高的模块,通常会下沉到Native或HAL实现。Java也是必须要会的:第一,Android的App和Framework是用Java开发的,需要设计和开发SOA接口,提供给位于这两层的应用和服务使用;第二,设计SOA架构,要对系统各层级之间的接口如何设计和调用有足够的了解,代码都看不明白,怎么设计易用的接口;第三,写Demo是架构设计过程中重要的步骤,总不能你设计的方案等着别人写Demo验证吧,更重要的是,通过写Demo可以发现很多想法上的不足,设计文档写得不严谨的地方。
对于C++编程,要掌握的一些基础知识:
语法基础:构造,继承,虚函数,内联,多态,类型转换,STL容器,…;
C++11新特性:时至今日,应该学会使用至少C++11常用的特性,比如:类型推导(auto,decltype),右值引用,智能指针,泛型/模板,type_taits,函数式编程(lambda),C++11多线程,future,promise,…,用它们使代码更简洁,改进代码质量,提升程序性能,不是为了炫技,而是用到实处;
应用开发:Linux/QNX网络编程、多线程编程、进程间通信等编程手法,常用实现如无锁,消息队列、线程池等,常见开源库的使用,选择感兴趣的如Libevent、Muduo等,深入学习其设计和实现;
项目开发:代码结构,接口封装方式,常用几种设计模式的实现(如单例模式、观察者模式等);
对于Java编程,要掌握的一些基础知识:
语法基础:包,类,接口,反射,泛型,异常,集合,注解,常用类,…;
并发编程:原子操作,Exexutor,线程池,阻塞队列,synchronized,锁,volatile,CompletableFuture,…,常见模型如Thread-Per-Message,CopyOnWrite,生产者-消费者,…;
应用开发:Java中的网络编程(JDK API),Android应用开发基础(四大组件,不研究UI,只要能整出个正常点儿的DemoApp就行~),Android进程间通信机制,AIDL、JNI和HIDL接口的设计和开发;
项目开发:常用代码结构,常用几种设计模式的实现(如工厂模式、代理模式、门面模式、观察者模式、策略模式等),注意,这里和前面C++说的都是实现,期望达到的水平:做到真正的理解,谈到某种设计模式,脑子里能够想到其适用的应用场景是什么,落实到代码上,一个大致思路是怎样的,反之亦然;在阅读一些开源项目时,能够看出用了什么设计模式,这样用的好处什么,不用可能会产生什么问题。
架构:保持好奇心,平常心
架构和编程的不同之处在于,编程是容易看见结果和成效的,你写的代码有没有Bug很快能被证明,你写的模块扩展性和稳定性怎么样,经过一些时日,也能看出个一二。架构不是,有多少人能在短时间内落地几种架构方案的,更不用说评价架构设计得好不好了,就拿通信框架来说,很少听应用说满意当前的通信框架的,为什么扩展个接口这么麻烦,为什么不支持同步调用,为什么没有消息缓存,…,能被吐槽的点太多了。即使如此,我们还是要致力于做出好的架构,也许无法得到所有人的满意,但设计和开发出好的SOA架构,可以让系统中每一个应用和模块在交互通信与数据共享的问题上获得不错收益,反之,糟糕的SOA架构可能就是所有人必须面对的一场灾难。
在架构设计这件事上,我也是刚开始有一点点经历,以下所想完全没有参考价值。
设备:跳出单个模块的视野范围,尝试去了解这个通信系统里的每一个设备(可以是车机,仪表,T-Box,网关等等),他们的软件系统,他们的硬件,硬件之间的连接,他们的功能业务,他们的开发模式,他们的数据需求,他们以往采用过的通信方案,…;
车载以太网:了解相关知识,如:CAN,LIN,诊断,标定,CP,AP,…,重点关注整车以太网与智能座舱的对接,或者说智能座舱未来如何接入中央计算单元。至于最近很火的AP,没有计划作深入研究,从编程角度来说,基础是相通的,CP用的C,AP用的C++,很多东西并不是本身难度有多大,而是学习的资源和工具难以获得,人为形成了所谓的技术壁垒,个人开发者想要学习研究,几乎不可能搭建环境。我觉得开源是时代的进步,一定会是大势所趋,1202了,我们都要拥抱开源呀~;
系统:Linux/QNX系统调用(如输入/输出,存储,文件,网络,线程,进程间),Android系统架构,座舱的整体架构,模块的架构,比如,车机中Android原生模块(如BT,WiFi,Audio,Location,…)的数据源有什么不同,车机中这些基础模块都是怎么适配的,车机中较复杂的APP如导航、语音等的架构是什么样的。不要觉得别人负责的模块我了解了干嘛,说白了,SOA就是模块与模块之间的交互,对每个模块了解一下一定是有益的;
中间件:基于几种通信模型(可参考DDS 介绍)的中间件框架,RPC技术的原理和实现,各种开源RPC框架:grpc,brpc,srpc,…,各种中间件架构方案:GENIVI,ROS2,…,如何技术选型,如何验证可行性,如何做性能评估;
设计原则:SOLID,KISS,迪米特,…,结合具体业务的前提下追求原则,知易行难,没有定式,如何做出正确的决策,需要在实践中不断思考和总结;
沟通表达:面向不同受众的架构视图,SOA是需要传播的,“咱们用TCP通信”和“咱们用SOMEIP通信”,对于不熟悉SOMEIP的小伙伴来说,是两码事。正确地传达架构的设计,高效地沟通和解决过程中的冲突,清晰地表述技术实现的细节,画的UML要能指导开发,尽可能说经过验证的结论,即便是预估的,也要有一些数据作为依据,不要说出诸如“应该没问题”这样的结论;
项目+产品:做架构设计,可以是一个人的事,但架构的落地,是需要一群人一起完成的,能否保质保量完成不是项目经理该去操心的事吗?我觉得并不是,能否落地也是架构设计中可以考虑的一个视角,适当了解一些项目管理和产品思维的知识,会使你能更好地理解这个世界,理解一个项目的运作,理解一个产品从无到有所要经历的过程,理解架构不止是一个技术问题,这些都会帮助你设计一个好的架构。
网络:实践出真知,重视细节
如果说架构是宏观,那么网络通信的设计和开发,是件要扣细节的事儿。不要想当然,不要把网络状况想得过于理想,不要把服务端想得过于强大,不要对任何错误掉以轻心,试图找到每个错误背后的根源,解决的过程就是一次收获。
网络基础:OSI,TCP/IP,五层协议,各层作用和常见协议;
开发基础:TCP和UDP建立连接的流程,阻塞与非阻塞Socket API,同步与异步,IO多路复用,epoll的水平触发与边缘触发;
通信协议:SOME/IP,DDS,HTTP/RESTful,SSL/TLS,MQTT,对于SOME/IP和DDS,准备死磕:协议标准,开源项目的应用,源码剖析,二次开发,封装接口;多媒体传输:AVB,RTP/RTCP/RTSP/RTMP;
序列化:JSON,Protobuf,Franca IDL,MsgPack,…,各自优缺点、性能效率、适用场景、开源库选型;
细节:私有通信协议如何设计,如何解决粘包和分片;心跳机制如何设计;收发缓冲区如何设计;如何优雅地关闭连接;断线重连机制如何设计;流量控制如何设计;…;
实践:分析当前网络连接状态;定位通信链路中出错位置,分析其出错原因;抓包,分析数据包;如何设计Benchmark,测试和分析通信架构的各项指标如传输时延、CPU占用、内存消耗、负载压力等;
工具:发现,尝试,积累
写工具,会让人觉得很Low吧,实际上,很多人低估了学会使用工具的重要性。为什么别人花一小时搭建实验环境,而你要一天?为什么别人花半小时定位问题改好代码,而你要一周?为什么别人花半天完成了某个第三方库的平台移植,而你要花几天,甚至搞不定?…?其实不是别人有多厉害,而是你不善于运用各种工具去解决问题,也没有注重积累,举个栗子:一次重装Ubuntu系统时遇到某个问题,查了好几篇CSDN文章,总算给搞定了,可什么都没记下,下次再重装时,又遇到同样的问题,这个问题我好像碰到过!然后,就没有然后了,如果那篇之前帮到你的文章还找不到了,…,总在这样又那样的事情上,浪费了太多的精力和时间,这些时间原本可以用来做更多有价值的事情啊~
开发:VSCode,IDEA,Android Studio;
构建:Shell,CMake,Gradle,Maven;
调试(测试):GDB,UT框架,静态代码检测,内存泄漏检测,tcpdump,Wireshark,MQTT X,JMeter,Docker;
文档:Markdown,PPT,Excel,笔记,Visio,EA,draw.io,录屏,…,;
自动化:一直觉得SOA开发工具是值得研究的,比如API文档自动生成工具、API代码自动生成工具、回环测试工具等(在互联网领域已经有一些类似的工具,看过几个开源的,比如ShowDoc,感觉都不太能用到汽车领域,有好用的欢迎推荐啊~);
终于,写完了,好多没有写进来,比如算法与数据结构、JVM、Linux内核、数据库等,当然不是说它们不重要。但对现阶段的我来说,上面列的这些已经很够我学的了,能把这些搞透彻,我已经非常非常满足了。术业有专攻,先把能做好的事做到最好,不是每个人都能成为技术大牛,也不是每个人的目标都是要成为技术大牛吧,人生的路长着呢,以后的事以后再想好了。有句话不是很扎心么,“以大部分人的努力程度,根本还轮不上拼天赋”,有时候只是态度就足够拉开差距了。
参考
链接:https://blog.csdn.net/xllhd100s/article/details/116851846
链接:https://blog.csdn.net/xllhd100s/article/details/116151933