普通视图

发现新文章,点击刷新页面。
昨天以前首页

45 分钟模拟面试(编程、系统设计)+职业发展建议


许多大型科技公司(如 FANG:Facebook/Meta、Apple、Netflix、Google)以及微软等,在发出工作邀请之前,通常会进行多轮面试。这些面试通常包括编程/Coding、系统设计/System Design和行为评估/Behaviour,以考察文化契合度。

我提供 45 分钟的模拟面试,帮助您准备编码和系统设计环节。作为曾在亚马逊(AWS,S3 Object Lambda)担任面试官的我,将通过真实的练习环节为您提供指导。在编码模拟面试中,您将解决一到两个编程问题;而在系统设计模拟面试中,您需要在白板上设计一个可扩展的产品。

此外,我还提供 45 分钟的聊天时间,可以讨论任何话题,包括职业发展和建议。

我目前定价 60 英镑一小时,会员价是 55 英镑,毕竟时间就是金钱。而这也大概和我的时薪差不多。

如果您有兴趣,请点击此处

mocked-interviews 45 分钟模拟面试(编程、系统设计)+职业发展建议 微软 程序员 系统设计 资讯 面试

本人提供模拟面试(编程/系统设计)或/和 职业规划建议

PS:我发现 Buy Me a Coffee 这个创业点子很好,赞赏+集成了网上商店,很是方便,界面也很友好,感兴趣的可以通过这个链接来创建一个专属于你的页面,在为用户创作内容的同时也能很快捷的收到赞赏!

英文:45 Minute Mock Interview (Coding, System Design) + Career Development Advices

system-design-interview 45 分钟模拟面试(编程、系统设计)+职业发展建议 微软 程序员 系统设计 资讯 面试

系统设计面试 System Design Interview

面试经历

面试题

面试技巧

面试其它

软件工程师

本文一共 395 个汉字, 你数一下对不对.
45 分钟模拟面试(编程、系统设计)+职业发展建议. (AMP 移动加速版本)

扫描二维码,分享本文到微信朋友圈
75a5a60b9cac61e5c8c71a96e17f2d9c 45 分钟模拟面试(编程、系统设计)+职业发展建议 微软 程序员 系统设计 资讯 面试
The post 45 分钟模拟面试(编程、系统设计)+职业发展建议 first appeared on 小赖子的英国生活和资讯.

相关文章:

  1. 智能手机 HTC One M9 使用测评 虽然我对手机要求不高, 远远没有像追求VPS服务器一样, 但是怎么算来两年内换了四个手机, 先是三星 S4 用了一年多, 然后 Nokia Lumia 635 Windows Phone, 后来又是 BLU, 半年多前换了...
  2. 按揭贷款(房贷,车贷) 每月还贷计算器 去年给银行借了17万英镑 买了20万7500英镑的房子, 25年还清. 前2年是定率 Fix Rate 的合同 (年利率2.49%). 每个月大概是还 700多英镑. 有很多种还贷的计算方式, 定率/每月固定 是比较常用的. 简单来说就是 每个月交的钱是...
  3. 英国房子的EPC节能报告(Energe/Efficiency Performance Certificate) EPC (Energe/Efficiency Performance Certificate) 是英国房子的节能报告, 法律上规定, 每个房子都必须要有一个EPC报告, 报告的有效期为十年. 房东在把房子出租或者想卖房的时候, 这个EPC就必须有效, 在一些情况下 比如出租房子的时候, 这个EPC报告还必须符合一些最低标准, 比如房子必须满足 F档(类似及格线)...
  4. 同一台服务器上多个WORDPRESS站点的一些设置可以移出去 我自从把所有网站都挪到一处VPS服务器上 就发现很多事情省事很多 可以同时管理多个网站 包括 WORDPRESS博客. 比如我有四个WORDPRESS博客 然后我就把通用的一些资料给移出去 移到 HTTP或者HTTPS都不能直接访问的文件夹里这样就更安全许多. 文件 wp-conn.php 存储了 相同的数据库资料. 1 2...
  5. 拔牙后的注意事项(图, 慎入) Care of Mouth after Extraction 昨天又拔了两颗牙, 初步定在5月4号装牙套. 这是牙医诊所给的术后注意事项: 拔完后需要等3-4小时麻醉失效后才能吃喝. 稍微流点血是很正常的. 但是请不要漱口吐出, 因为这会加速流血. 你只要轻轻的含着口水并咽下即可. 如果一直流血, 请拿着纱布(并不是纸巾)放在拔牙处20分钟. 24小时内请不要运动, 术后几小时内回家静静坐着. 12小时内不要吸烟, 喝酒或者喝热饮, 因为这会让伤口流血....
  6. 公司给配了台高配DELL笔记本 早上例会结束的时候我顺便说了一句 我的笔记本有点慢, 当时我并不知道我的经理远程用电话也参加会议了(他全程在听), senior staff SE 对着电话说, “peter, you hear that? btw, my disks are...
  7. 老婆的配偶签证被拒 郁闷死了, 601镑签证费打水漂,一去不回!费钱费力. 去年12月份我请了律师拿到了永居.老婆是T1G签证的陪工签 (DEPENDENT VISA) 2016年4月份到期. 然后我就想说得趁早把她的签证转成配偶签(SPOUSE)这样她就可以尽快走五年永居的路线. 今天收到拒签信,原因是我没有提供 有工资进帐的那份银行帐单,我提供了我和我老婆的联名帐户, 但是工资并不是直接打到这个帐单上的.所以就这一点被拒了.完全不给解释,不给补材料的机会.601镑就这样再见了. 英国的签证寄出之后是先由另一个部门先收费, 收完费才正式审理,而且不管结果如何是不退钱的.后悔没让律师弄,也不至于到现在浪费这么多时间和金钱,签证还没过.由于原签证还没到期,所以还不能上述.估计只能等搬完家后年底请律师搞定这事. 真是郁闷, 600镑, 我可以再买一个IPHONE6,或者给我的新买的车换四个轮胎....
  8. 花钱让人换汽车钥匙的电池真是个智商税 今天想不到我这么聪明的人也被人狠狠的收了一把智商税. 今天被收智商税了, 去 Tesco 换车钥匙的电池. . 才发现如此的简单, 那人直接2分钟搞定2个, 然后收了我25英镑. . 服了. . 我还以为很复杂…… 网友说 “1....

假装是个设计师

作者 王隐
2018年8月13日 22:25

每个 Web 开发人员都会遇到要做 UI 的情况,无论他喜欢与否。

也许你公司没有全职 UI 设计,所以你得自己来。也或许你在做一个 side-project,但你想让它比 Bootstrap 好看点。

你可以说「我又不是艺术家,别要求我太多」,但事实表明,你还是有很多技巧来提高自己的设计水平,当然了,这并不需要你有很好的 UI 技术。

以下是七个提升设计感的简单实践。

一、 用颜色和粗细来划分重点,而不是字体大小

文本样式的一个常见错误是过度依赖字体大小来划分重点结构。

「这段文字重要吗,字大点。」

「这段文字不太重要,字小点。」

不要总用字体来控制这些,尝试使用颜色和 font-wight 也可以完成同样的工作。比如:

「这段文字重要,加粗。」

「这段文字不重要,细一点。」

用两种或三种颜色划分你的内容:

  • 重要内容用深色(但不是黑色)(如文章标题)
  • 次重要内容用灰色(如文章发布日期)
  • 不重要内容用浅灰色(如页脚的版权声明)

大多情况,两种 font-weight 就够了:

  • 大部分的文字用普通 weight(400或500)
  • 你强调的文字 weight(600或700)

400 以下的 weight 就别用了;大标题下还可以看清,小字体基本看不清。如果你想用更小的 weight 来弱化你的文字,用更淡的颜色或更小的字体吧。

二、彩色背景别用灰色字体

在白色背景下使用浅灰色字体可以弱化内容,但是在彩色背景下就不好了。

这是因为我们在白色背景下看灰色其实是降低了文字的对比度。

相比于使用灰色字体,让文本接近背景色更能突出文字重点。

彩色背景下,你可以通过两种方法降低对比度。

1.降低白字不透明度(opacity)

白色,低不透明度,再让背景色透过来一点,这样可以弱化文字,又不会在彩色背景下显得很违和。

2.根据背景色自己选一个合适的颜色

当你背景是图片或图案时,这种方法比降低不透明度要好一些。而且降低不透明度显得文字太暗了,就像水洗的一样。

选个和背景色相同的颜色,再慢慢调饱和度和亮度,直到看起来还不错。

3.正确使用阴影

相比于使用大阴影(blur)和暗角来让阴影更明显,不如添加垂直偏移 (vertical offset)。

这样看起来会更自然,因为它模拟了光源从上面照下来的效果,这和我们在现实世界看到的一样。

输入框和表格也可以这么用:

阴影设计是门艺术,感兴趣可以看看 Material Design Guidelines

四、少用 border

当你想划分两个元素的边界时,先别考虑 border。

border 确实在划分边界上做的很不错,但这不是唯一的方法,滥用 border 会让你的设计特别杂乱。

你可以试试下面的方法:

1.box shadow

box shadow 不仅可以像 border那样做到划分边界,而且这种方法更精细,也不会太分散你的注意力。

2.两种背景色

相邻元素用不同的背景色就可以划分它们。

3.加空隙

有没有更好的办法划分不同元素的边界,而不只是简单地增加分割部分?当然有,把元素隔得更远就行了。

五、少用大图标,如果它们本身意义不大的话

如果你在设计一些大图标(比如登录页面的「功能」部分),试试 Font Awesome 或 Zondicons 这样的免费图标集,然后加大尺寸直到满足你的要求。

它们都是矢量图,所以不会在放大的过程中丢失细节。

虽然矢量图不会在拉伸时丢失细节,但本来在 16-24px绘制的图标非要拉伸到三倍四倍之大,真的不会显得很好看,他们会缺乏一些细节,比如不成比例的「矮胖」。

如果小图标可以满足你的要求,可以将它们包在另外一个形状中,并且为形状添加背景色。

这可以让实际图标更加接近预期的大小,同时可以填充更大的空间。

如果有预算,可以用大尺寸的高级矢量图,比如Heroicons or Iconic

六、边框特写,给平淡的设计增加颜色

如果你不是 UI,怎么才能从你的用户界面中和其他那些好看的设计中脱颖而出呢。

简单却有效的一个技巧就是为某些部分添加色彩鲜艳的边框,这样不会感到乏味。

举个例子,在提醒信息的左边做文章。

高亮导航栏

顶端都加上也行。。

它不需要任何专业的 UI 设计人员帮你,但可以让你网站看起来更「大气」。

是不是不好选颜色,Dribbble’s color search 可以帮你。

七、不是每个按钮都需要颜色

当用户可以在一个页面有很多的选择时,很容易陷入语义化设计的陷阱。

比如 Bootstrap,无论何时添加新按钮,都可以选择不同的语义。

「积极动作,绿按钮。」

「删除数据,红按钮。」

语义是按钮设计的一个重要部分,但有一个更常见的重要维度被忽略了:重要性。

页面上的每个操作都处于金字塔的某个重要位置。但大多数页面只有一个真正重要的操作,一些不太重要的次要操作,以及一些很少使用的三级操作。

设计这些动作的时候,更重要的是理清这些按钮谁更重要。

  • 重要动作应该突出,加粗,高对比度。
  • 不太重要的动作应该清楚显示,但别太主导。用轮廓线,低对比度背景色。
  • 三级操作可见即可,但不要引人注意。像链接一样处理吧。

「删除按钮呢,应不应该总是红色?」

没必要,如果它们不是页面中的主要操作,处理成二级或三级按钮即可。

保留大、红,粗样式在那些以负面操作为主的页面上,比如确认对话框。

原文链接:7 Practical Tips for Cheating at Design – Refactoring UI – Medium

作者:Adam Wathan & Steve Schoger

编译:@王隐在录音

移动应用设计原则简介

作者 Wayne
2023年4月25日 13:17

随着移动设备的普及,移动应用程序越来越受欢迎,因此越来越多的品牌都在考虑开发自己的应用程序。但是,如何设计一个优秀的移动应用程序呢?以下是几个值得考虑的设计原则。

在这篇文章中,我们将介绍移动应用设计的几个重要原则。这些原则包括关注用户体验、简化设计、遵循标准模式、考虑可访问性、以及保持一致性。我们还将讨论如何将这些原则应用到移动应用程序设计的各个方面,从导航和布局到色彩和图标。如果您正在设计一个移动应用程序,本文将为您提供宝贵的指导,帮助您设计一个成功的应用。

1、简化设计

优秀的移动应用程序设计应该尽可能简洁。设计师应该避免过于复杂或难以理解的界面和功能,这会让用户感到困惑和不适。为了提高用户的体验和满意度,设计师应该使用直观的设计元素,例如大而简洁的按钮和易于识别的图标。

2、用户中心设计

在设计移动应用程序时,需要考虑用户的需求和体验。设计师应该优先考虑用户,确保应用程序易于使用,并且能够满足用户的需求。用户中心设计还包括了考虑如何在设计中融入用户反馈,并在后续版本中不断改进。

3、流畅的用户体验

良好的用户体验应该是设计师的首要考虑因素之一。为了获得良好的用户体验,设计师需要确保应用程序具有流畅的导航、快速响应和直观的界面。流畅的用户体验有助于提高用户的满意度,增加用户的黏性和留存率。

4、可访问性

可访问性是指应用程序可以被更多的用户所访问和使用,包括那些有视觉、听觉或其他残障的用户。为了提高应用程序的可访问性,设计师需要确保应用程序的设计和功能符合相应的可访问性标准,例如 WCAG 2.0 等。

5、数据安全和隐私

数据安全和隐私对于任何移动应用程序都是至关重要的。设计师应该确保应用程序能够安全地存储和处理用户数据,并且严格遵守隐私保护法律和法规。

以上是几个设计移动应用程序时应该考虑的原则,这些原则有助于提高用户的体验和满意度,增加应用程序的留存率和转化率。

原文: www.thinkwithgoogle.com/marketing-strategies/app-and-mobile/principles-of-mobile-app-design-introduction/
翻译: ChatGPT
更多: 移动应用设计原则 PDF版下载

The Performance Design Of OS

作者 Gracker
2023年10月15日 15:28

1 Origin

I am embarking on a new series of articles addressing various considerations in OS architecture design. Indeed, these considerations are not limited to OS but are applicable to the design of any large-scale software.

I am limited by my capabilities and knowledge and bring a highly subjective view, and there are undoubtedly inadequacies. I am eager to hear different thoughts and perspectives, and through the collision of ideas, we can achieve a deeper understanding.

In my opinion, the core differences between Android and iOS from the OS perspective are primarily manifested in:

  1. The IPC mechanism between applications and core services
  2. Platform development environment, including programming languages, IDE tools, and the construction of the developer ecosystem
  3. Application lifecycle management mechanisms and strategies
  4. The runtime organizational structure of the kernel and core services

Why do they adopt different strategic decisions? It relates to the factors considered during architectural design. A software architectural decision is a selection of the most suitable decision for the present and foreseeable future amidst a series of current considerations; it is a collection of decisions. Thus, there’s no absolute right or wrong in architectural design, or rather, rational or irrational. Different projects and decision-makers face different considerations and prioritize different aspects. If architects of similar skill levels switch scenarios, they are likely to make similar decisions. This indicates that architectural design is an engineering process and a technical craft, learnable and following certain patterns.

The challenge in architectural design lies in accurately understanding the environment the organization operates within, current and foreseeable considerations, and finding the most suitable methodologies or tech stacks from existing engineering practices. It is evident that considerations play a significant role. What factors need to be considered in software architectural design? These include, but are not limited to, testability, component release efficiency, development efficiency, security, reliability, performance, scalability, etc. Experienced architects, especially those with operational experience in similar businesses, are better at discerning what to focus on at different times, what must be adhered to, and what can be relaxed or even abandoned. Considering the law of diminishing marginal returns, the consideration of influencing factors and decision-making behavior will permeate the entire lifecycle, introducing another art of decision-making.

We can summarize that:

  1. Under different stages and constraints of various considerations, software architectural designs on different projects may differ.
  2. During the entire cycle of software product iteration, such design decisions are always evolving.

Interestingly, one consideration may conflict with another, leading to a situation where one cannot have the best of both worlds. For example, improving component development efficiency might impact program performance. So, what were the considerations for the designers of Android and iOS in the context of mobile OS design? To answer this question, we first need to explain the relationship between the OS, application programs, and the kernel.

2 Mechanisms and Policies

First, it should be noted that the OS is part of the software stack above the hardware. It, along with application programs, constructs the complete software program stack, utilizing the hardware capabilities to provide services to users. From the hardware’s perspective, regardless of the OS or application programs, both are software; however, the OS has higher privileges, allowing it to operate in the CPU’s high-level modes, for tasks such as direct interaction with hardware and executing interrupt handling routines. From the software developer’s perspective, however, the OS and application programs are entirely different entities. Application programs utilize the capabilities provided by the OS to meet their business requirements or use hardware capabilities for tasks like playing music or storing data.

At a higher level, from the user experience perspective, application programs, OS, and hardware are all part of the same entity. When issues arise in their collaboration, ordinary consumers might simply feel that the device is less than ideal. Therefore, all three parties are obligated to cooperate and facilitate each other; only when the consumer is well-served can these entities sustainably profit.

In discussing the OS, it’s crucial to recognize that the vast majority of modern OSes consist of a kernel and system services.

  • In macOS/iOS, the kernel is Darwin, formed from the combination of XNU and the MACH microkernel. Its system services are provided by an array of daemon services, encapsulating kernel capabilities, data management, and higher-level APIs like display.
  • In Android, the kernel is the Linux kernel, with system services comprising C++ written daemon services (e.g., SurfaceFlinger) and Java written daemon services (e.g., SystemServer). They too encapsulate kernel capabilities, data management, and higher-level APIs concerning display rendering and composition.

Current mainstream operating systems include macOS, Windows, and various derivatives of Linux. Clearly, Android belongs to the Linux derivatives, with another renowned version in the developer community being Ubuntu. Due to the complexity and path-dependent nature of a complete OS, derivatives are often deployed on different hardware and application scenarios. For a while, Linux was lauded for its extensive device radiation and wide application scenario spectrum. However, being able to run an OS and running it well—connecting hardware and application programs to offer an optimal user experience—are two different things.

An increasingly common understanding is that the OS’s mechanisms, policies, and closely associated application programs vary greatly depending on the application scenario.

For instance, even if based on the same Linux kernel, the use and system services built upon it for embedded devices, smartwatches, smartphones, large servers, or even smart cars are distinctly different, as are the operating strategies of application programs. The Linux kernel and device drivers can be seen as the bridge between hardware and system services—a standard bridge—but the vehicles and pedestrians traversing it are entirely distinct.

This leads to another classic OS design concept: mechanism and policy.

The “separation principle” noted in UNIX programming specifies the separation of policy from mechanism and interface from the engine. Mechanisms provide capabilities; policies dictate how those capabilities are used.

In this context, the memory management, process management, VFS layer, and network programming interfaces that Linux provides are mechanisms. Memory allocation and release mechanisms, process schedulers, frequency and core allocators, and different file systems are policies. Going further, identifying which processes interact with users and which are background tasks, synchronizing this information to the scheduler to determine the optimal process for the next scheduling window is fundamentally a policy built upon the Linux process management mechanism. Similarly, notifying future computational task requirements to the CPU frequency allocator for dynamic adjustment (DVFS) involves a policy and a mechanism.

For instance, all modern OSes support memory compression capabilities, but different OSes use this mechanism according to their own characteristics to best meet business needs. iOS exhibits process-level compression, while Android relies on Linux’s ZRAM.

Though OS mechanisms might be similar, policies are diverse. The extent of their differences depends on the OS designers’ understanding of their own business—meaning the application programs running on the OS and the kind of experience and services they aim to provide users. Early Android was essentially a desktop architecture, but modifications, especially by domestic manufacturers, have made it increasingly resemble iOS, aligning more with the system capabilities required by mobile device operating systems.

The OS for smartphones and smart cars probably faces a similar scenario—they cannot be directly transplanted.

One interesting aspect of policy is that implementing one policy often brings up another issue, necessitating the introduction of an additional policy. When one policy compensates for another, a chain forms, eventually creating a closed-loop mechanism. In other words, all policies must be in effect simultaneously to maximize the system’s benefits. When learning about a competitor OS’s policies, remembering this aspect is essential; otherwise, we might only grasp the superficial aspects, leading to “negative optimization” once the features are launched.

Now, narrowing it down to Android and iOS, where do their strategy designs originate?

3 Butts Decide Heads

Apple has released a series of operating systems including macOS, iOS, iPadOS, watchOS, and tvOS. The distinctions between them are not merely in brand names but are characterized by specific strategy variations. While they largely share underlying mechanisms, their strategies are distinctly different. For instance, the background running mechanism on iOS is vastly different from that on macOS. iOS resembles a “restricted version of multitasking,” while macOS offers genuine multitasking. Hence, it’s not that iOS can’t implement multitasking but rather a deliberate design decision.

Android, as we refer to it, is actually a project open-sourced by Google, known as AOSP (Android Open Source Project). Device manufacturers adapt AOSP and integrate their services based on their business models and understanding of target users. Apple is singular, but there are numerous device manufacturers, each with their own profit models and interpretations of user needs. They modify AOSP accordingly, and the market decides which version prevails.

From a technical perspective, AOSP is rich in mechanisms but lacks in strategies. Google has implemented these strategies within its GMS services. Users outside mainland China, like those using Pixel or Samsung phones, would experience Google’s suite of services. Although the ecosystem is considered subpar in China due to the proliferation of substandard apps, the situation is somewhat mitigated overseas, but still not comparable to Apple’s ecosystem.

Given Google’s less-than-ideal strategic implementation, domestic manufacturers in China have carved out space for themselves. The intense competition and the sheer volume of phone shipments in mainland China have led manufacturers to prioritize consumer feedback and innovative adaptations of AOSP.

The most significant difference between iOS and Android stems from their respective strategies, rooted in their initial service objectives and developmental goals.

Books like “Steve Jobs” and “Becoming Steve Jobs” touch upon the development of the iPhone and the discussions around AppStore. Jobs was initially resistant to allowing third-party app development on mobile devices due to concerns about power consumption, performance, and security. The initial intent was to create a device that offered an unparalleled user experience, not necessarily catering to every user demand.

As Apple had written the first batch of apps themselves, they amassed a wealth of insights on designing excellent embedded device applications, leading to the creation of effective API systems. This comprehensive approach from hardware to software was not for the sake of exclusivity, but a necessary path to crafting the best user experience.

Contrastingly, during 2007-2008, Android was focused on getting the system up and running. Android’s initial aim was to accommodate a vast array of app developers, leading to its favoring of Java, a popular language among developers and in the embedded device domain. Although Android later shifted to Android Studio, improving the development experience, it still lagged behind Apple’s Xcode in terms of application development and debugging tools.

Apple’s strong control over its app ecosystem, partly attributed to its powerful IDE tools, aids developers in solving problems rather than imposing constraints. Further, initiatives like LLVM, Swift, and SwiftUI underscore Apple’s commitment to facilitating superior app development to enhance the user experience.

The purpose of designing an OS is profit-oriented, and it should facilitate app developers in crafting quality programs. Apple has showcased that offering quality developer services can be instrumental in achieving optimal device experiences. A summary of insights gleaned from Apple’s approach includes:

  1. Building an OS is a means; delivering a complete and excellent experience is the end goal. Both the OS and device manufacturers may need to put in extra effort to achieve this objective.
  2. Serve app developers well, assist them in improving app quality, and even identify and diagnose app issues.
  3. Provide faster and more user-friendly APIs to efficiently meet the needs of app developers.
  4. An excellent IDE tool can serve developers well, enabling the development of superior apps, and ensuring the OS’s survival.

While Apple exercises absolute control, it also offers software services that are significantly above industry standards. Offering an OS is merely a means; understanding the nature of the relationship with developers and providing developer services, such as IDE, is a more profound consideration at the cognitive level.

4 Strategy of “Overload Protection”

The greatest feature of mobile devices is their portability, enabled by battery power. Besides, as handheld devices, they primarily rely on passive cooling since they don’t have an active cooling mechanism (exceptional cases of gaming phones and attachable fans aside). Currently, there are two trends: one, the transistor fabrication process is inching closer to its physical limit, and two, more functionalities are being integrated into a single chip. This increase in the number of active transistors (or their area) leads to a corresponding rise in heat emission, although it wasn’t a primary concern during the early days of smartphones. Now, the balance between power consumption and performance has become a significant challenge for smartphones.

More active threads mean the CPU remains busy, resulting in reduced CPU time slices allocated to user-related programs, thus impacting performance. Therefore, the design of mobile device OSes naturally leads to restrictions on resource utilization by applications. If left unrestricted like servers or desktop computers, it would be impossible to maintain a balance between performance, power consumption, and heat dissipation. The more constrained a device is in terms of performance and power consumption, the stricter the control over application programs, as is the case with smartwatches.

Both Android and iOS have their resource protection mechanisms. In Android, the most common is the OOM (Out Of Memory) mechanism. When the heap memory usage of a Java application exceeds a certain threshold, the system terminates it. Although Android has a mechanism to detect excessive CPU usage, it is somewhat rudimentary and only monitors the CPU usage of regular applications, not system or native thread (written in languages other than Java).

In contrast, iOS has a plethora of mechanisms ranging from CPU, memory, to even restrictions on excessive IO writes, including:

  • Termination when the device overheats
  • Termination of VoIP class applications when there are excessive CPU awakenings
  • Termination during BackgroundTask execution if CPU use exceeds a threshold
  • Termination if BackgroundTask is not completed within the specified time
  • Termination if a program’s thread exceeds CPU use threshold
  • Termination if a program’s disk write volume exceeds a threshold
  • Termination if program’s inter-thread interactions within a unit time exceed a threshold
  • Termination if a program’s memory usage is exceeded
  • Termination under excessive system memory pressure
  • Termination if a program opens too many files
  • Termination during PageCache Thrashing

iOS outlines these behaviors in developer documentation to clarify the reasons for unexpected application exits.

Google’s lax approach to Android’s design has provided ample room for domestic manufacturers to introduce their overload protection strategies (similar to iOS’s, with minor variations) to ensure phones are not compromised by substandard applications. However, the issue lies in the lack of transparency about system termination behaviors. Developers are often in the dark about why their applications are terminated. Even if they are aware of the reasons, the lack of debugging information during termination impedes improvement efforts since no manufacturer releases this information.

Consequently, application developers resort to various “black technologies” to keep their applications alive and bypass the system’s detection mechanisms. What should have been a collaborative ecosystem building effort has turned into a battleground. In the end, both parties suffer, with consumers bearing the brunt of the damage.

In an ideal world:

  1. Overload protection mechanisms should be documented and explained in application development guides.
  2. Debugging information context should be saved when the system executes overload protection, and developers should have access to this information (with specific permissions, scope, and validity to be determined).
  3. Manufacturers should provide convenient and user-friendly debugging tools for developers to fix issues locally during development.
  4. Developers should be mandated to fix issues when they exceed the quality standards set by the manufacturers, failing which their applications should be delisted.

Manufacturers and developers should be partners. Manufacturers may need to do more to assist developers, as many capabilities are exclusive to them. Blaming developers solely for poor quality is not a competitive approach for manufacturers.

The fault, in this case, is at the cognitive level.

5 Strategy on “Lifecycle Management”

Different device forms pursue varied user experience requirements, leading to diverse OS design necessities. In desktop OS, the lifecycle of an application is entirely under its control, aiming to maximize the program’s potential. This design is viable because desktop computers are not constrained by power consumption and heat dissipation and rarely face performance bottlenecks. Their primary concern is exploiting the machine’s capabilities to the fullest.

On the contrary, smartphones are a different story due to their limitations in power consumption and heat generation. Similarly, smartwatches also suffer from these restrictions but to a more stringent degree. No one desires a watch that heats up their wrist and cannot last a day on a full charge. Moreover, their performance and memory limitations mean that too many apps can’t remain active in the background, necessitating a centralized management module to uniformly implement services for most common applications, known as a hosted architecture. While smart cars aren’t constrained by performance, power, or heat, they require high stability. Unless completely powered down, core system services must remain operational, emphasizing the importance of system anti-aging design.

A core strategy in smartphone OS design revolves around lifecycle management, determining the entire journey of an application from its inception to termination. Android leans towards desktop system design, offering a “looser” strategy and more room for developers to maneuver. In contrast, iOS imposes more restrictions; an application relegated to the background only has about 5 seconds to perform background tasks before entering the Suspend state. In this state, the application is denied CPU scheduling, rendering it “quiet” when in the background.

Chinese manufacturers, after obtaining AOSP code, have replicated a mechanism similar to iOS’s Suspend. However, due to the lack of native support in AOSP, compromises were made, resulting in an implementation not as thorough as iOS. Android interprets this running strategy as the developers’ responsibility to create well-crafted applications – a notion I find naive and impractical. By this logic, human societal development would never have required laws, an idea that contradicts human nature. Fortunately, Google might have realized this issue, gradually enhancing the so-called “freezing” strategy in their annual updates, albeit less effective than improvements made by domestic manufacturers. The progress in AOSP is slow, and substantial changes in this area aren’t expected in the next two to three years.

So, if an application is Suspended in the background on iOS, how can it perform required background computations? iOS introduced the BackgroundTask mechanism, allowing applications to request permission for background task execution, with the system intelligently scheduling these tasks. Hence, iOS offers a strategy for application background operation but places the final decision in the system’s hands. This allows the system to schedule background tasks based on the phone’s current status, avoiding task execution during high system load periods to reduce overall load. The system also assigns daily quotas to each application, incorporating execution frequency and duration as crucial factors. Generally, tasks are allowed about 30 seconds of execution before being terminated by the system.

However, background tasks aren’t limited to computations. How are requirements like playing music or location tracking addressed? Applications needing these services must declare them explicitly in the IDE, with the App Store checking for a match between the application and requested permissions – a mismatch leads to rejection. The App Store is central to iOS’s lifecycle management mechanism, enabling quality control during the application’s listing and operational phases. Applications identified as subpar are flagged for the developers to fix, facing delisting otherwise. Post-Suspend, the system may also terminate applications as part of overload protection. The most common reason is memory reclamation, especially given the expense of memory chips; without opting for larger memory, terminating applications is the only way to free up more memory.

So, if the application isn’t even running, how are background tasks executed, and messages received? Thanks to BackgroundTask design, even if an application is terminated, the system will automatically restart it to execute background tasks when conditions are met. Message reception is achieved through notification mechanisms, with two kinds: one displaying detailed content in the notification bar, activating the application only upon user interaction; the other is for VoIP class applications, capable of actively restarting terminated applications.

Android possesses a similar mechanism but requires the integration of its GMS service. Due to uncertain reasons, this service is inaccessible in China, forcing domestic apps to rely on various “dark arts” and commercial collaborations to keep their programs alive in the background for message reception. This has led to a grotesque scenario where head applications, often used by users, are greenlit by manufacturers, who, upon realizing this trend, keep intensifying various services, treating the phone as their playground and squeezing every bit of system memory. Could manufacturers offer a notification service akin to this? They could, but the construction and operational costs are disproportionately high compared to their sales profits, leading to the only option of increasing memory capacity, passing the price pressure onto consumers. The overall cost of a complete machine has an upper limit; bolstering memory means cutting corners elsewhere. For domestic manufacturers to break into the high-end market, recognizing the issues in the entire loop and co-building the ecosystem with applications is the sole breakthrough.

Looking at iOS’s design, compared to macOS, it restricts application freedom but isn’t a one-size-fits-all solution. It offers various “windows of opportunity” or “unified solutions” to cater to different developers’ needs. The objective is to allow developers to operate within reasonable boundaries, not to drain users’ battery and performance.

Summarizing the principles beyond the technology:

  • Mobile devices have many constraints; therefore, application “freedom” must be restricted but not completely cut off, requiring corresponding solutions.
  • Common tasks among applications should be provided uniformly by the system, saving overall system load, especially crucial for devices with many constraints.
  • The final execution power of a program should be determined by the system, which, after synthesizing various information, schedules uniformly, benefiting the ultimate user experience protection.

At this point, it seems like a clash between two regimes: one valuing freedom and individual priority, and the other advocating unified arrangement and scheduling. Regardless of the regime type, the ultimate objective must be considered. If the aim is to offer the best device user experience, evidently, the latter regime has been proven right by the market.

6 Above Design

Looking back at the history of electronic consumer products, the development has mainly followed two themes: the democratization of professional equipment and the integration of multifunctionality (N in 1 style). The reliance on CPU computation is gradually being replaced by Domain Specific Architecture (DSA). Upon DSA, domain-specific programming languages and compilers are constructed, with GPU and Shader Language in the graphic processing domain serving as prime examples. The era where software reaps the benefits of CPU performance enhancement is drawing to a close, and DSA appears to be the opportunity for the next “great leap” in the coming decade.

M1 epitomizes the dividends brought by regular microarchitecture and manufacturing process, but its impact is magnified due to the subpar performance of competing products. When a product’s core components are supplied by specific manufacturers, its developmental ceiling is essentially predetermined. This underscores the oft-repeated adage that core technologies must be self-controlled. Besides its CPU capabilities, M1 excels in multimedia processing, especially in video stream processing scenarios, outperforming Intel chips substantially. These performance enhancements are attributed to the processor’s performance uplift in specific scenarios.

However, this doesn’t signify the end of the road for performance enhancements based on CPUs. As CPU performance enhancements stagnate, precise understanding of demands and optimizations of matrices and architectural designs to boost performance on existing CPUs become imperative. Profound insights into hardware, compilers, algorithms, and operating systems (both frameworks and kernels) are increasingly crucial. After optimizing business codes to a certain extent, focus inevitably shifts towards the underlying layers.

Accumulated experience from numerous failures is essential to anticipate issues and design optimal architectures and optimization matrices proactively. An optimization matrix refers to the necessity of an ensemble of complementary technologies, not just an OS, to deliver an exceptional experience. This includes IDEs, cloud collaboration, and accurate cognition. Offering a supreme experience is a daunting task, but the more one learns, the more possibilities become apparent. By the same token, maintaining a perpetual “awareness of one’s unawareness” is equally pivotal.

However, all these are contingent upon the designers’ ability to keep pace with their cognition.

Charlie Munger once articulated that investment isn’t merely about scrutinizing financial statements and trend charts. Psychology, sociology, political science, and even biology are intricately linked to it. Only by dismantling the barriers between disciplines and integrating contents from multiple fields without reservations can one perceive a world invisible to others. While I haven’t attained such an enlightenment, Munger’s insights offer invaluable lessons worthy of our learning. Deliberate cross-disciplinary and cross-field practice, coupled with reflective thinking, significantly augments the learning process.

“I leave my sword to those who can wield it.” - Charlie Munger

About Me && Blog

  1. About Me: I am eager to interact and progress together with everyone.
  2. Follow me on Twitter
  3. Blog Content Navigation
  4. Record of Excellent Blog Articles - Essential Skills and Tools for Android Performance Optimization

An individual can move faster, a group can go further.

OS 设计之性能设计

作者 Gracker
2023年8月21日 18:30

本文是之前星球里 Yingyun 大佬的文章,由于星球已经关闭,所以把这个关于 OS 性能设计的系列文章发到博客上

Yingyun 是资深性能优化专家,他对于系统优化有非常深入的见解,本身在国内各个手机大厂都呆过,他本人的博客还在休整中,等休整好了我再发出来,目前他在我们的微信群里很活跃,对本文有什么建议或者意见,或者说想咨询问题的可以加我们的微信群(加我微信 553000664,备注博客加群,我会拉你进去)

1 缘起

新开系列文章,OS 架构设计中的各种考量因素。其实不止 OS,在设计任何大型软件都涉及到此类内容。

能力与知识面有限,而且还带了非常主观的看法,肯定有不足之处。希望听到不同的思路与观点,通过观点的碰撞进而达到更进一步的认知。

我认为,Android 与 iOS 在 OS 角度看最核心的差异主要表现为:

  1. 应用与核心服务之间的 IPC 机制
  2. 平台开发环境,包括编程语言、IDE 工具、开发者生态建设
  3. 应用生命周期管理机制与策略
  4. 内核与核心服务的运行时组织结构

他们为什么会有不同的策略决策呢?这跟架构设计时的考量因素有关。一个软件架构设计决策是在当前一系列考虑因素中选择了对当前与可见的未来选择的最合适的决策,是一系列决策的集合。所以,架构的设计上没有绝对的对与错,或者说合理与不合理。因为不同项目、不同决策者所面临的的考虑因素、追求的侧重点是都不尽相同。 如果架构师的水平差不多,把他们互换下情境,那大概率上所做出的决策是差不多的。这说明架构设计是一个工程,是一个技术手艺,它是可以被习得且有规律可循的。

架构设计中的挑战,可能更多的是在于准确理解组织所处的环境、当前与可见未来所要满足的考量因素,并从已有工程实践中找出最合适的方法论或者技术栈。由此可见,考虑因素就起了重要作用,在软件架构设计里要考虑哪些因素呢?包括但不限于,可测试性、组件发布效率、组件开发效率、安全性、可靠性、性能、扩容性等等。有经验的架构师,特别是对类似业务有操盘经验,他更能把握好不同时期应该注重什么,哪些是必须坚持的,哪些是适当放开甚至舍弃的。考虑到边际收益递减,影响因素的考量与决策行为会贯穿整个生命周期,这又是另一个「决策艺术」了。

我们可以总结出:

  1. 不同阶段与不同考量因素的限制下,不同项目上软件架构设计可能是不同的。
  2. 软件产品迭代的整个周期内,这种设计决策是随时会发生。

有意思的是一个考量因素可能会与另外一个有冲突,会造成鱼和熊掌不可兼得局面。比如提高了组件开发效率但有可能会影响到程序性能。那针对一个移动 OS 的设计,当初 Android 与 iOS 的设计者们的考量因素是什么呢?为了回答这个问题,首先要解释 OS 与应用程序以及内核之间的关系。

2 机制与策略

首先要说明的是 OS 是硬件之上的软件栈的一部分,它与应用程序一道构建了完整的软件程序栈,通过发挥硬件的能力为用户提供服务。从硬件的角度看,甭管 OS 还是应用程序,它们都是软件,只是 OS 的权限比较高,可以在 CPU 的高级别模式下运行,比如用于直接跟硬件打交道、执行中断处理程序等。但是在软件开发者角度来看,OS 与应用程序,那可是完全不一样的。应用程序利用 OS 提供的能力,实现自身的业务需求、或者使用硬件的能力,比如播放音乐、存储数据等。

再拔高一个层次,在用户体验角度来看,应用程序、OS 以及硬件,都是一回事情。 当他们协作出现问题,普通消费者可能就觉得这台设备就不够理想。因此,这三方都有义务互相配合好、互相为彼此提供便利,只有把消费者伺候舒服了,这三家才有可持续的利润可赚。

当我们说到 OS,绝大部分现代 OS 是由内核(Kernel)跟系统服务组成。

  • 在 macOS/iOS 中,它的内核是 Darwin,而 Darwin 又是由 XNU 与 MACH 微内核组而成。它的系统服务,是由一大堆 daemon 服务提供,他们封装了内核的能力与数据管理、界面显示等级别的 API。
  • 在 Android 中,它的内核是 Linux 内核,它的系统服务既有 C++ 编写的 daemon 服务(如,SurfaceFlinger)、又有 Java 编写的 daemon 服务( 如,SystemServer),他们同样也是封装了内核的能力与数据管理、界面渲染与合成等级别的 API。

现在主流的操作系统有 macOS、Windows 以及基于 Linux 的各种衍生版本。显然,Android 是属于基于 Linux 的衍生版本,当然还有个在开发者圈子中更有名的延伸版本,那就是 Ubuntu。 由于一个完整的 OS 的复杂性与路径依赖特性,往往会将一个延伸版本部署到不同的硬件与不同的应用场景上。有一阵子 Linux 就是以此为标榜,即它辐射到了多少台设备,应用场景有多广之类。能运行一个 OS 跟是否运行得好,即把硬件、应用程序连接起来,提供了最优的用户体验,完全是两码事情。

一个越来越普遍的认知是,不同的应用场景下,所需要的 OS 的机制与策略以及与之紧密配合的应用程序,是完全不一样的。

比如,即使是基于同一个 Linux 内核,用于嵌入式设备、智能手表以及智能手机、大型服务器甚至是智能汽车,它们使用 Linux 的方式与构建在它之上的系统服务均是不一样的,当然应用程序的运行策略也不尽相同。Linux 内核与设备驱动可以理解为硬件与系统服务之间的桥梁,是一个标准的桥梁,但是跑在它上面的车辆与行人,是完全不同的。

这就要引申出另一个非常经典的 OS 设计理念,机制与策略。

UNIX 编程一书中有提到「分离原则:策略同机制分离,接口同引擎分离」,机制提供能力,策略提供能力方法。

其中,Linux 提供的内存管理、进程管理、VFS 层、网络编程接口均是机制。内存分配与释放机制、进程调度器、频率与核分配器以及不同的文件系统,他们均是策略。更进一步,由系统服务识别出哪些进程是跟用户有交互、哪些是后台任务,将此类信息同步到调度器后找出下一个调度周期窗口里的最佳进程,本质上也是基于 Linux 进程管理机制之上的策略。 同样道理,根据未来所需的计算任务需求将其信息通知到 CPU 频率分配器进行动态调整(DVFS),前者是策略后者是机制。

再比如,所有的现代 OS 都支持内存压缩能力,但是不同的 OS 要根据自身的特点来使用此机制,目的是尽可能满足业务特点。iOS 中可以看到针对进程级别的压缩,而 Android 中反倒是依赖 Linux 的 ZRAM。

OS 的机制或许类似,但是策略千差万别,他们之间的差异有多大,取决于 OS 设计者对自身业务的理解。 自身业务,指的是运行在 OS 之上的应用程序,到底要为用户提供什么样的体验与服务。早期的 Android 其实就是桌面机架构,随着国内厂家对它的魔改,反倒是越来越像 iOS 了,越来越符合一个移动设备操作系统所需要的系统能力了。

智能手机、智能汽车的 OS,估计也是同样局面,不可生搬硬套。

策略还有个很有意思的特点,当你实施一个策略的时候会引申出另外一个问题,为此你要引入另一种策略。当一个策略弥补另外一个策略,逐渐会形成一个链条,你会发现你形成了一个闭环的机制。 即,所有的策略同时生效,才能使你的系统发挥出最大的效益。 当我们学习竞品 OS 的策略的时候,一定要记得这一点,否则只学会了皮毛,功能上线后会带来更大的「负优化」。

那具体到 Android 与 iOS,他们的策略设计是从何而来?

3 屁股决定脑袋

苹果推出的操作系统有 macOS,iOS,iPadOS,watchOS 与 tvOS。它们之间并不是单纯的品牌名称差别,而是有具体的策略差异。底层的机制大部分有共享,但是策略却截然不同。iOS 上的后台运行机制与 macOS 就截然不同,iOS 更像是「限制版多任务」,而 macOS 是真正的多任务。所以,iOS 并不是不能实现多任务,而是它的有意为之。

我们所说的安卓,其实是谷歌开源的项目,即 AOSP(Android Open Source Project)。设备厂商拿到 AOSP 与与硬件的相关的代码之后会在此基础上加入自己的各种服务,这是基于它们自身的商业模式、目标用户的理解,所创造出来的。苹果只有一个苹果,但是设备厂家就有很多,它们各自的盈利模式跟对目标用户理解不同,在 AOSP 基础上魔改了一遭,至于哪个好,就让市场先生来做判断吧。

回到技术本身,AOSP 中有大量的机制但是缺乏策略。谷歌把这些策略实现在了 GMS 服务中。 如果你在非大陆地区使用安卓手机,如 Pixel、三星手机,会体验到谷歌的全家桶。大家都会说国内的生态比较烂,垃圾应用比较多,到了海外这可能会缓解。 其实也就那样,谷歌的生态控制跟影响能力跟苹果是没法比的。

连谷歌自身的策略表现不尽人意,那就更为国内厂商发挥拳脚腾出了空间。中国大陆手机出货量累计合是全球最大的,而且竞争尤为激烈。因此它们会非常重视消费者的反馈,各种各样的需求与痛点挖掘,自然不在话下。 简单总结就是,国内厂商更懂消费者的需求,这也是国内厂家对 AOSP 做各种魔改与优化成立的底层逻辑了。

所以当你再次见到有个老板说 “做手机很简单嘛,拿开源安卓跟厂商的方案整合一下就行了”,离他赶紧远一点,跟着他混简直就是枉费青春。

iOS 与安卓之间两者最大的差异来自于策略,他们之间拥有的机制都差不多顶多效率上可能有差异,但更大的差异来自于策略上。

这跟它两刚开始时不同的服务对象与发展目标,导致了技术选型上的巨大差异。

「乔布斯传」与「成为乔布斯」两本书中,关于 iPhone 研发章节中都提到过关于 AppStore 的讨论。起初乔布斯坚持认为移动设备上由于功耗 / 性能与安全的考虑,不允许让三方应用开发程序。由于系统复杂,所以直接采用了 macOS 的内核以此实现多媒体、浏览器,以及播放音频与视频等功能。macOS 内核本身的运行成本较高,在此情况下再让三方应用运行,硬件根本吃不消。他们原本可以基于 iPod 上的系统实现 iPhone,但乔布斯又要实现世人从未见过的智能手机,上马 macOS 也是他不得已的选择。

随着第一代 iPhone 在用户侧的成功(商业上成功还没有开始),大家都在呼吁在 iTunes 上可以下载应用程序。其实第一代黑客们就是这么做的,因为很多 API 是跟 macOS 共享,因此通过一些逆向手段观察到了 iPhone 上编写应用程序的方法。但乔布斯坚决反对,因为还是担心这会破坏安全性跟设备使用体验。从此处就能看出,乔布斯的目的是打造一个拥有完美用户体验的设备,用户的呼吁或者需求,其实是并不是首要的。

但 VP 们不这么想,由于之前的 iTunes + iPod 组合的成功,VP 们私底下开始安排相关的工程研究了。直到后来来乔布斯也没有再坚持反对,只是说自己不再管了。

对 UX 界面的优雅性,特别是图标与界面的一流体验是刻在苹果骨子里的基因。即使是开放出 API,他们也对整个应用运行机制做了修改,从此开始与 macOS 上的程序执行策略有差异了。

由于第一批应用都是苹果自己写的,因此他们积攒了大量在嵌入式设备上良好设计的应用应该是怎样的,也设计出了非常有效的 API 体系。 在这种局面下,苹果有自己的 OS、自己的 IDE、自己的商店系统(当时还是跟 iTunes 共用),自然而然会设计出「最佳应用」应该长什么样。

这里头缺一不可,还记得前面提到过的观点吗? 当你要实施一个策略的时候,可需要另一个策略来解决前一个策略带来的问题,当策略变多的时候就有可能形成了一个环路。

从这儿可看出,为了打造出一个完美的体验,从硬件到软件全部打通,是必然的结果。 这不是为了封闭而封闭,而是为了打造最佳体验而做出的唯一一条路。

反观 07 - 08 年的 Android 阵营,他们还在忙着如何让系统跑起来。

在由 Cheet 著作的 「The Team That Built the Android Operating Systems」书中讲述了安卓从零开始被 Andy Rubin 创建的过程。它是由 Andy Rubin 离职后创业的公司,起初目标是给相机提供 OS。但是随着市场的演变,他们的目标变成了提供给移动设备,特别是手机的移动操作系统。Andy 当初的目标是创建一种可以平衡开发者与设备制造商以及运营商利益的真正意义上的完全开放的操作系统。这就要求它尽可能采用市面上已有的组件,将他们简单改在后适应嵌入式环境后快速部署上线。毕竟是有创业压力嘛,也不可能精雕细琢,只能用快速发展来解决各种体验问题了(主要是这时候 iPhone 还没出来)。再加上从零开始,他们没有配套的 IDE,也只能先提供简单的基于命令行的工具来构建应用程序, 因此从开始他们就缺乏整个应用运行环境的管控能力。不过这也是相对于苹果而言,毕竟两者的目的完全不同。

Android 的目标之一就是有大量应用程序可用,因此照顾到市面上人群最多的开发者是很想当然的思考方式。当时市场上开发者最多的编程语言是 Java,而 Java 在嵌入式领域里也是很受欢迎的。你可能很难相信,07 年的时候嵌入式设备性能很差,但为什么会是受欢迎的编程语言呢? 个人理解原因是,除特殊设备之外大部分设备其实不关心性能,基本维持在能用就行的程度。而且 Java 的可移植性,也大大降低了开发者的负担。为了使 Java 速度更快,Andy 团队还聘请了一位大神重新写一套适合嵌入式设备的虚拟机,这在当时看来都是正确的选择,只是没有 iPhone 出来之前。 不过运气好在,智能手机刚好碰上了黄金的 CPU 单核性能与制程爆发的十年,因此它跟 iOS 相比并没有逊色太多。

在安卓开发的早期,使用的是基于 Eclipse 的构建与开发工具,虽然谈不上非常优秀但是够用。但是痛苦的根源来源于对比,苹果很早之前开始构建自己的 IDE 工具,即 Xcode。这套工具里集成了大量的应用开发与调试以及分发功能,这极大的提高了应用开发工程师的效率。 虽然安卓将开发环境切入到 AndroidStudio 之后相比之前进步了很多,但这主要还是得益于 IntelliJ 本身的优秀,单纯应用开发角度来看跟 Xcode 相比还是弱了一些。Xcode 提供了非常方便的编写与调试程序性能的工具,这会使开发者通过简单的学习就能快速找出程序上低性能的代码段,大大提高了程序的质量而且还使整个过程很愉快,这在安卓上可不是这么一回事了。

由此可见,苹果能够在应用生态管控上的强势,其中一个重要的原因是得益于它的强大的 IDE 工具,提供方便的来帮助开发者解决问题,而不是一味地给他们压力。更进一步,LLVM、Swift 以及 SwiftUI,这些都是苹果了编写出更好地应用而所做的基础工具与语言。 目的当然是为了自身应用生态的发展,为消费者提供最佳的体验。 它的思路是尽可能服务好开发者,让他们编写更能契合系统机制的应用程序,即给你限制又给你解决方案。

设计 OS 的目的是盈利,因此要想办法帮助应用开发者开发好程序。很多 OS 提供了能力之后,应用如何编写就不归他们管了,他们往往会把这个责任放到应用开发者身上,从苹果身上可以看到,这种做法可能不利于整个设备的最优体验,吃亏的还是消费者自己以及厂商,因为把消费者给磨没了。

所以当我看到苹果的强大时候,除了硬件的强大,在软件生态的建设上面的思路是非常值得借鉴的,简单总结就是:

  1. 构建 OS 是手段,提供完整且优秀的体验是目的,双方通力合作才能达到此目的。特别是 OS 与设备制造商,可能要做出更多的努力。
  2. 服务好应用开发者,努力帮助他们写好应用,甚至发现与诊断能出应用的问题,协助开发者改进应用程序。
  3. 提供更快更好用的 API,尽可能高效的满足应用开发者的需求。
  4. 通过优秀的 IDE 工具服务好开发者,让他们在此基础上开发更多更优秀的应用,OS 才能有更好的机会存活下去。

可以看到虽然苹果有绝对的话语权,但同时也提供了远超于行业平局水平的软件服务。 再次强调,提供 OS 只是手段,要认识到与开发者建立怎样的关系、提供怎样的开发者服务(如 IDE),是在认知层面更为有意的事情。

4 策略之「过载保护」

移动设备的最大特性就是可移动,它是由电池供电实现了可移动性。除此之外,由于是手持设备因此散热基本靠被动散热,没有主动散热一说(奇葩的游戏手机与外挂式风扇另说)。现在有两个趋势,其一是晶体管的制作工艺越来越趋近于物理极限,其二是越来越多的功能直接封装在同一颗芯片上。处于活跃状态的晶体管数量变大(或者面积)之后发热量也是蹭蹭往上涨,这在智能手机刚开始普及那一会儿倒不是主要的矛盾。对于智能手机来说,现在更大的矛盾是,功耗与性能的平衡。

活跃的线程多了,就会使 CPU 一直处于工作状态,当然分给用户相关程序的 CPU 时间片也会少一些,性能也就受到影响了。所以移动设备 OS 的设计上,就自然的引申出了要对应用程序的资源使用上的限制,如像服务器、台式机一样完全放开,性能与功耗是无法保证了(当然还有发热)。越是性能跟功耗约束大的设备,对应用程序的管控就越严苛,比如智能手表。

Android 与 iOS 各自均有资源保护机制,Android 中最为常见的当属 OOM 机制了。当 Java 应用的堆内存使用超过一定阈值之后,就会被系统终止。它也有 CPU 使用过度的检测,但从实现上比较简陋,而且只会监听普通应用程序的 CPU 使用量,不监控系统以及由 Native 编写的线程(不用 Java 写)。

iOS 中可谓百花齐放,从 CPU 到内存,甚至 IO 过多写入也有限制,具体为:

  • 设备过热时被终止
  • VoIP 类应用有过多 CPU 唤醒时被终止
  • 执行 BackgroundTask 时使用 CPU 超过阈值时会被终止(备注: BackgroundTask 是 iOS 上后台执行时的状态)
  • 执行 BackgroundTask 时没有在规定时间内完成时会被终止
  • 程序的线程使用 CPU 超过阈值时被终止
  • 程序写数据到磁盘的量超过阈值时被终止
  • 单位时间内程序的线程之间的交互超过阈值时被终止(如两个线程互相唤醒)
  • 程序的内存超标时被终止
  • 系统内存压力过大时被终止
  • 程序打开了过多的文件时被终止
  • 系统遭受 PageCache Thrashing 时被终止

显然不止于此(可见未来,iOS 会增加更多限制),但以上是跟普通应用开发者最为密切的。iOS 将这些行为写在了开发者文档,让开发者知道自己的应用被异常退出时的原因。

谷歌对 Android 设计上的” 放松 “ ,可就给国内厂家留出了很多发挥空间。各家都有各自的资源过载保护策略(与 iOS 的类似,仅有或多或少的差异),它们尽可能保护手机不会被垃圾应用给搞坏了。但问题也恰恰出在这部分,由于系统的查杀行为没有明文化,开发者不知道自己的应用为什么会被终止。 即使知道了原因,也无法获取被终止时的调试信息,也就没办法做改进,因为没有哪家会把这类信息开放给应用开发者。

这导致的结果是,应用开发者不得不想出各种各样的所谓黑科技来使自己保活、绕过系统的各种检测机制。本应该由开发者一起共建的生态,现在变成了两家的攻防战了。到最后,是两败俱伤,而其中最受伤的就是消费者。

理想中的世界:

  1. 明文化资源过载保护机制,写在应用开发文档上。
  2. 当系统进行过载保护时将上下文调试信息保存下来,开发者可以查阅此类信息(具体权限、范围、有效期可以另定。总之要有方法,可以由开发者拿到此类调试信息)。
  3. 厂商提供方便好用的调试工具,可以由开发者在本地进行开发时修复问题使用。
  4. 当超出厂商制定的质量标准水位线,责令开发者进行修复,若不修复则下架应用。

厂商跟开发者应当是合作关系,可能厂商要做更多的事情用于帮助开发者,因为很多能力只有厂家才有。只是一味地责怪质量差是开发者的问题,这种厂商我觉得不太会有竞争力。

在认知维度上,就已经错了。

5 策略之「生命周期管理」

不同的设备形态所追求的体验要求不同,因此对 OS 的设计要求也不尽相同。在桌面机 OS 中一个应用程序的生命周期完全是由自己掌控的,目的是尽可能发挥程序的能力。能这么设计的原因是桌面机里没有功耗跟散热的限制,很少也会有性能上的瓶颈。对它来说首要考虑的问题是如何把机器的能力榨干。

而在智能手机上却是另一种情况,原因是它有功耗跟发热的限制。与此类似,智能手表上也有功耗与散热的限制,但是它比手机设备更为严苛。谁都不希望手腕里戴着一个会发热的表,而且续航一天都撑不了。除此之外,由于它的性能跟内存受限,不能驻留太多的程序在后台,因此需要有一个集中式管理的模块来统一实现绝大部分常见应用的服务,即所谓的托管式架构。智能汽车虽然没有性能、功耗以及发热的限制,但是它对稳定性的要求非常高。除非是汽车彻底断电,核心系统服务需要保持一直运行,因此对系统防老化的设计尤为重要。

智能手机 OS 设计中一个核心策略是关于生命周期管理的,它决定了应用程序的由生到死的整个过程。Android 的设计上更偏向于桌面机系统,因此策略的设计上比较「宽松」,留给开发者的发挥空间比较多。而 iOS 上限制比较多,一个应用退到后台之后只有 5 秒左右的时间用于执行后台任务,随后便进入到 Suspend 状态。在这种状态下应用程序是得不到 CPU 调度执行,因此在后台的时候应用会比较「安静」。

国内厂家拿到 AOSP 代码之后实现了类似 iOS 的这种 Suspend 机制,不过碍于 AOSP 原生的不支持,因此做了很多让步,这导致了整体效果上不如 iOS 来的彻底。Android 把这种运行策略解释为把应用写好应用开发者的责任,而我觉得这个想法是幼稚且不切实际的。如果这个逻辑成立的话,人类发展历史上都不需要有法律了,这是违背人性的事情。 不过好在谷歌可能意识到了问题,每年的更新中也逐步完善了俗称「冻结」的策略。不过能力奇差,远不及国内厂商所做的改进。AOSP 的进步也是比较缓慢,目测未来两三年内,这部分的进步速度也会一直缓慢,没有实质性的改变。

如果应用在后台被 Suspend 住了,那在 iOS 上如何实现需要后台计算的任务呢? 它引入了 BackgroundTask 的机制,让应用程序申请后台执行任务权限,由系统智能的调度执行应用程序的后台任务。所以,iOS 是给了一套策略让应用程序执行后台任务,但是决定权是交给系统执行的。这有利于系统根据当时手机的不同的状态调度后台任务,比如系统负载比较高的时候就不会执行后台任务了,目的是降低系统整体的负载。系统给每个应用还设置了每日配额的概念,比如一天之内允许你最多执行多少次等等,当然执行时间也是其中一个很重要的考量因素。一般允许执行 30 秒左右,超过之后就会被系统终止了。

但是后台任务不止于计算一种,播放音乐、位置定位,这种需求如何处理? 应用想要使用此类服务,需要在 IDE 中显示声明之后才可以使用,App Store 会检查应用与所申请权限是否匹配,不匹配时不给予通过。App Store 是 iOS 能够实现这套生命周期管理机制的很核心的一环,通过它实现了在上架期与运行期间的质量管控。当发现一个应用的质量较差的时候,会通知开发者让其修复,否则就会下架应用。 继 Suspend 之后系统也会随之终止应用程序,目的是过载保护。最常见的理由是回收内存,毕竟内存芯片非常贵,不上大内存的前提 下只能通过终止应用来腾出更多的内存了。

那我连应用程序都不执行了,又怎么执行后台任务?接收消息呢? 得益于 BackgroundTask 的设计,即使应用被终止了,当条件满足的时候系统还会自动的拉起你的程序执行后台任务。至于消息接收,则是通过消息通知机制来实现。分为两种,一种是在通知栏里能看到具体的内容,只有用户当点击此通知的时候才会唤醒应用程序。另一种则是 VoIP 类应用,可以主动拉起被终止的应用程序。

其实 Android 也有类似的机制,但是需要配合它的 GMS 服务使用,由于不确定的原因这个服务在国内是无法使用。因此国内的 App 不得不又要上各种各样的黑科技、商业合作等手段,使自己的程序在后台保活用于接收消息。这就造成了一个非常畸形的局面,头部应用由于是用户常用的,因此厂商也对它一路开绿灯。而厂家也发现这个现象之后,不断加码各类的服务,把手机当做自己的来用,尽可能榨干系统内存。 那有没有可能厂商自己提供类似的通知服务呢? 有,但是由于建设与运营成本跟自己的销售利润完全不成比例,大家也就只能加大内存容量了,可以把价格压力传递到消费者身上。整机的成本是有上限的,在内存这部分加大那就要在其他地方减弱。国内厂家要突破高端,首先要意识到整个环路的问题所在,与应用共建整个生态才是唯一的破局之道。

纵观 iOS 的设计,相比于 macOS, 限制了应用的自由度,但也不是一刀切方案。而是尽可能的提供了各种各样的「窗口期」或者「统一的解决方案」,以满足开发者的不同需求。目的是尽可能让开发者在合理的范围内做事情,而不是榨干用户的电量与性能。

总结下技术之上的设计原则

  • 移动设备受限因素多,因此要对应用「自由度」给予限制但不能一刀切,需要给予对应的解决方案。
  • 应用之间的共性任务要由系统统一提供,可节省系统整体负担,这对限制因素较多设备尤为重要。
  • 程序最终执行权交由系统而定,由他综合各类信息之后统一调度,这有利于保护最终的用户体验。

写到此处,似乎是两个体制的碰撞,一个崇尚自由,个体优先、一个崇尚统一安排与调度。无论是哪种体制,要看最终目的是什么,如果是要提供最佳的设备用户体验,显然后者体制被市场证明是正确的。

6 设计之上

纵观电子消费品的发展史,以专业设备平民化、 N in 1 式的功能整合,两个主旋律方向发展。纯靠 CPU 的计算也会被专用硬件代替,比如 DSA (Domain Specific Architecture)。在 DSA 之上,会构建领域专用的编程语言与编译器,与之最接近的就是图形处理领域,如 GPU 与 Shader Language。 软件吃 CPU 性能提升的红利已经接近了尾声,下一个十年的「大飞跃」,目前看来也就这 DSA 机会点了。

M1 本身代表了正常的微架构与制程工艺带来的红利,只是友商阵营太拉跨,把这种差距拉大了。当一个产品的核心部件由某几个特定供应商提供的时候,基本上也判定了其发展上限。这也就是常说的核心技术要掌握自己手里,是同样的道理。M1 中除 CPU 能力外,它在多媒体处理,特别是视频流的处理场景下相比 Intel 芯片性能指标超出一大截,能实现这些性能提升的得益于处理器的特定场景下的性能提升。

但这不代表基于 CPU 的性能提升已经走到尽头,正是因为 CPU 性能提升的停滞,通过对需求的准确理解,优化矩阵与架构设计来实现在已有 CPU 上的性能提升,变得更为紧迫。对硬件、编译器、算法以及操作系统(框架与内核)的理解,变得越来越重要。因为当你优化到一定层度的业务代码之后,注意力必然会往底层走。

只有相当多的失败的经验,才能未雨绸缪,以高屋建瓴的方式设计出最佳的架构与优化矩阵。 优化矩阵是指为了提供一个极佳的体验,并不是由一个 OS 就能搞定,他要有相配套的其他技术一起支撑才能做好。比如 IDE、比如云端配合、以及正确的认知。 想要提供一个极致的体验,是非常难的事情,但也正因为如此,你会发现了解越多可做的事情就越多。同样道理,也有别样的说法 → 使自己始终处于「知道自己不知道」的状态。

不过以上成立的前提,是设计者的认知要跟得上。

查理芒格说过,投资不是看看财报,看看走势图就能做好的。除经济学与金融学外,心理学、社会学、政治学、甚至生物学都有关系。只有当你踏平学科间的隔阂,不设边界地把多个学科内容融合在一起之后,才能看到别人看不到的世界。 我当然也没达到这种境界,芒格给世人的经验是我们值得学习的宝贵经验。努力跨学科、跨领域的刻意练习,如果能在此基础上做到思考输出,那对学习更有帮助。

我的剑留给能够挥舞它的人 - 查理 芒格

关于我 && 博客

  1. 关于我 , 非常希望和大家一起交流 , 共同进步 .
  2. 博客内容导航
  3. 优秀博客文章记录 - Android 性能优化必知必会

一个人可以走的更快 , 一群人可以走的更远

微信扫一扫

我做系统架构的一些原则

作者 陈皓
2021年12月21日 15:46

工作 20 多年了,这 20 来年看到了很多公司系统架构,也看到了很多问题,在跟这些公司进行交流和讨论的时候,包括进行实施和方案比较的时候,都有很多各种方案的比较和妥协,因为相关的经历越来越多,所以,逐渐形成了自己的逻辑和方法论。今天,想写下这篇文章,把我的这些个人的经验和想法总结下来,希望能够让更多的人可以参考和借鉴,并能够做出更好的架构来。另外,我的这些思维方式和原则都针对于现有市面上众多不合理的架构和方案,所以,也算是一种“纠正”……(注意,这篇文章所说的这些架构上的原则,一般适用于相对比较复杂的业务,如果只是一些简单和访问量不大的应用,那么你可能会得出相反的结论)

原则一:关注于真正的收益而不是技术本身

对于软件架构来说,我觉得第一重要的是架构的收益,如果不说收益,只是为了技术而技术,而没有任何意义。对于技术收益来说,我觉得下面这几个收益是非常重要的:

  • 是否可以降低技术门槛加快整个团队的开发流程。能够加快整个团队的工程流程,快速发布,是软件工程一直在解决的问题,所以,系统架构需要能够进行并行开发,并行上线和并行运维,而不会让某个团队成为瓶颈点。(注:就算拖累团队的原因是组织构架,也不妨碍我们做出并行的系统架构设计)
  • 是否可以让整个系统可以运行的更稳定。要让整个系统可以运行的更为的稳定,提升整个系统的 SLA,就需要对有计划和无计划的停机做相应的解决方案(参看《关于高可用的架构》)
  • 是否可以通过简化和自动化降低成本。最高优化的成本是人力成本,人的成本除了慢和贵,还有经常不断的 human error。如果不能降低人力成本,反而需要更多的人,那么这个架构设计一定是失败的。除此之外,是时间成本,资金成本。

如果一个系统架构不能在上面三个事上起到作用,那就没有意义了。

原则二:以应用服务和 API 为视角,而不是以资源和技术为视角

国内很多公司都会有很多分工,基本上都会分成运维和开发,运维又会分成基础运维和应用运维,开发则会分成基础核心开发和业务开发。不同的分工会导致完全不同的视角和出发点。比如,基础运维和开发的同学更多的只是关注资源的利用率和性能,而应用运维和业务开发则更多关注的是应用和服务上的东西。这两者本来相关无事,但是因为分布式架构的演进,导致有一些系统已经说不清楚是基础层的还是应用层的了,比如像服务治理上的东西,里面即有底层基础技术,也需要业务的同学来配合,包括 k8s 也样,里面即有底层的如网络这样的技术,也有需要业务配合的 readniess和 liveness 这样的健康检查,以及业务应用需要 configMap 等等 ……

这些东西都让我感觉到所谓 DevOps,其实就是因为很多技术和组件已经分不清是 Dev 还是 Ops 的了,所以,需要合并 Dev和 Ops。而且,整个组织和架构的优化,已经不能通过调优单一分工或是单一组件能够有很大提升的了。其需要有一种自顶向下的,整体规划,统一设计的方式,才能做到整体的提升(可以试想一下城市交通的优化,当城市规模到一定程度的时候,整体的性能你是无法通过优化几条路或是几条街区来完成的,你需要对整个城市做整体的功能体的规划才可能达到整体效率的提升)。而为了做到整体的提升,需要所有的人都要有一个统一的视角和目标,这几年来,我觉得这个目标就是——要站在服务和 对外API的视角来看问题,而不是技术和底层的角度。

原则三:选择最主流和成熟的技术

技术选型是一件很重要的事,技术一旦选错,那会导致整个架构需要做调整,而对架构的调整重来都不是一件简单的事,我在过去几年内,当系统越来越复杂的时候,用户把他们的  PHP,Python, .NET,或 Node.js 的架构完全都迁移到 Java + Go 的架构上来的案例不断的发生。这个过程还是非常痛苦的,但是你没有办法,当你的系统越来越复杂,越来越大时,你就再也不能在一些玩具技术上玩了,你需要的更为工业化的技术。

  • 尽可能的使用更为成熟更为工业化的技术栈,而不是自己熟悉的技术栈。 所谓工业化的技术栈,你可以看看大多数公司使用的技术栈,比如:互联网,金融,电信……等等 ,大公司会有更多的技术投入,也需要更大规模的生产,所以,他们使用的技术通常来说都是比较工业化的。在技术选型上,千万不要被——“你看某个视频公司也在用这个技术”,或是一些在论坛上看到的一些程序员吐槽技术的观点(没有任何的数据,只有自己的喜好)来决定自己的技术,还是看看主流大多数公司实际在用的技术栈,会更靠谱一些。
  • 选择全球流行的技术,而不是中国流行的技术。技术这个东西一定是一个全球化的东西,不是一个局域化的事。所以,一定要选国际化的会更好。另外,千万不要被某些公司的“特别案例”骗过去了,那怕这个案例很性感,关键还是要看解决问题的思路和采用的技术是否具有普世性。只有普世性的技术有更强的生命力。
  • 尽可能的使用红利大的主流技术,而不要自己发明轮子,更不要魔改。我见过好些个公司魔改开源软件,比如有个公司同魔改mesos,最后改着改着发现自己发明另一个 kubernetes。我还见过很多公司或技术团队喜欢自己发明自己的专用轮子,最后都会被主流开源软件所取代。完全没有必要。不重新发明轮子,不魔改,不是因为自己技术不能,而是因为,这个世界早已不是自己干所有事的年代了,这个时代是要想尽方法跟整个产业,整个技术社区融合和合作,这样才会有最大的收益。那些试图因为某个特例需要自成一套的玩法,短期没问题,但长期来说,我都不看好。
  • 绝大多数情况下,如无非常特殊要求,选 Java基本是不会错的。一方面,这是因为 Java 的业务开发的生产力是非常好的,而且有 Spring 框架保障,代码很难写烂,另外,Java 的社区太成熟了,你需要的各种架构和技术都可以很容易获得,技术红利实在是太大。这种运行在JVM上的语言有太多太多的好处了。在 Java 的技术栈上,你的架构风险和架构的成本(无论是人力成本,时间成本和资金成本)从长期来说都是最优的

在我见过的公司中,好些公司的架构都被技术负责人个人的喜好、擅长和个人经验给绑架了,完全不是从一个客观的角度来进行技术选型。其实,从 0 到 1 的阶段,你用什么样的技术都行,如果你做一个简单的应用,没有事务处理没有复杂的交易流程,比如一些论坛、社交之类的应用,你用任何语言都行。但是如果有一天你的系统变复杂了,需要处理交易了,量也上来了,从 1 到 10,甚至从 10 到 100,你的开发团队也变大了,需要构建的系统越来越大,你可能会发现你只有一个选择,就是 Java。想想京东从.NET 到 Java,淘宝从 PHP 到 Java……

注,一些有主观喜好的人一定会对我上述对 Java 的描述感到不适,我还用一些证据说明一下——全中国所有的电商平台,几百家银行,三大电信运营商,所有的保险公司,劵商的系统,医院里的系统,电子政府系统,等等,基本都是用 Java 开发的,包括 AWS 的主流语言也是 Java,阿里云一开始用 C++/Python 写控制系统,后面也开始用 Java ……你可能会说 B站是用 go语言,但是你可能不知道 B 站的电商和大数据是用 Java……懂着数据分析的同学,建议上各大招聘网站上搜一下 Java 的职位数量,你就知道某个技术是否主流和热门……

原则四:完备性会比性能更重要

我发现好些公司的架构师做架构的时候,首要考虑的是架构的性能是否能够撑得住多大多大的流量,而不是考虑系统的完备性和扩展性。所以,我已经多次见过这样的案例了,一开始直接使用 MongoDB 这样的非关系型数据库,或是把数据直接放在 Redis 里,而直接放弃关系型数据库的数据完备性的模型,而在后来需要在数据上进行关系查询的时候,发现 NoSQL 的数据库在 Join 上都表现的太差,然后就开始各种飞线,为了不做 Join 就开始冗余数据,然而自己又维护不好冗余数据后带来的数据一致性的问题,导致数据上的各种错乱丢失。

所以,我给如下的一些如下的架构原则:

  • 使用最科学严谨的技术模型为主,并以不严谨的模型作为补充。对于上面那个案例来说,就是——永远使用完备支持 ACID 的关系型数据库,然后用 NoSQL 作补充,而不是完全放弃关系型数据库。这里的原则就是所谓的“先紧后松”,一开始紧了,你可以慢慢松,但是开始松了,以后你想紧再也紧不过来了。
  • 性能上的东西,总是有很多解的。我这么多年的经历告诉我,性能上的事,总是有解的,手段也是最多的,这个比起架构的完备性和扩展性来说真的不必太过担心。

为了追求所谓的性能,把整个系统的完备性丢失掉,相当地得不偿失。

原则五:制定并遵循服从标准、规范和最佳实践

这个原则是非常重要的,因为只有服从了标准,你的架构才能够有更好的扩展性。比如:我经常性的见到很多公司的系统既没有服从业界标准,也没有形成自己公司的标准,感觉就像一群乌合之众一样。最典型的例子就是 HTTP 调用的状态返回码。业内给你的标准是 200表示成功,3xx 跳转,4xx 表示调用端出错,5xx 表示服务端出错,我实在是不明白为什么无论成功和失败大家都喜欢返回 200,然后在 body 里指出是否error(前两年我在微信公众号里看到一个有一定名气的互联网老兵推荐使用无论正确还是出错都返回 200 的做法,我在后台再三确认后,我发现这样的架构师真是害人不浅)。这样做最大的问题是——监控系统将在一种低效的状态下工作。监控系统需要把所有的网络请求包打开后才知道是否是错误,而且完全不知道是调用端出错还是服务端出错,于是一些像重试或熔断这样的控制系统完全不知道怎么搞(如果是 4xx错,那么重试或熔断是没有意义的,只有 5xx 才有意义)。有时候,我会有种越活越退步的感觉,错误码设计这种最基本最基础的东西为什么会没有?并且一个公司会任由着大家乱来?这些基础技能怎么就这样丢掉了?

还有,我还见过一些公司,他们整个组织没有一个统一的用户 ID 的设计,各个系统之间同步用户的数据是通过用户的身份证 ID,是的,就是现实世界的身份证 ID,包括在网关上设置的用户白名单居然也是用身份证 ID。我对这个公司的内的用户隐私管理有很大的担忧。一个企业,一个组织,如果没有标准和规范,也就会有抽象,这一定是要出各种乱子的。

下面,我罗列一些你需要注意的标准和规范(包括但不限于):

  • 服务间调用的协议标准和规范。这其中包括 Restful API路径, HTTP 方法、状态码、标准头、自定义头等,返回数据 JSon Scheme……等。
  • 一些命名的标准和规范。这其中包括如:用户 ID,服务名、标签名、状态名、错误码、消息、数据库……等等
  • 日志和监控的规范。这其中包括:日志格式,监控数据,采样要求,报警……等等
  • 配置上的规范。这其中包括:操作系统配置、中间件配置,软件包……等等
  • 中间件使用的规范。数据库,缓存、消息队列……等等
  • 软件和开发库版本统一。整个组织架构内,软件或开发库的版本最好每年都升一次级,然后在各团队内统一。

这里重要说一下两个事:

  • Restful API 的规范。我觉得是非常重要的,这里给两个我觉得写得最好的参考:PaypalMicrosoft 。Restful API 有一个标准和规范最大的好处就是监视可以很容易地做各种统计分析,控制系统可以很容易的做流量编排和调度。
  • 另一个是服务调用链追踪。对于服务调用链追踪来说,基本上都是参考于 Google Dapper 这篇论文,目前有很多的实现,最严格的实现是 Zipkin,这也是 Spring Cloud Sleuth 的底层实现。Zipkin 贴近 Google Dapper 论文的好处在于——无状态,快速地把 Span 发出来,不消耗服务应用侧的内存和 CPU。这意味着,监控系统宁可自己死了也不能干扰实际应用。
  • 软件升级。我发现很多公司包括 BAT,他们完全没有软件升级的活动,全靠开发人员自发。然而,这种成体系的活动,是永远不可能靠大众的自发形成的。一个公司至少一年要有一次软件版本升级的review,然后形成软件版本的统一和一致,这样会极太简化系统架构的复杂度。

原则六:重视架构扩展性和可运维性

在我见过很多架构里,技术人员只考虑当下,但从来不考虑系统的未来扩展性和可运维性。所谓的管生不管养。如果你生下来的孩子胳膊少腿,严重畸形,那么未来是很难玩的。因为架构和软件不是写好就完的,是需要不断修改不断维护的,80%的软件成本都是在维护上。所以,如何让你的架构有更好的扩展性,可以更容易地运维,这个是比较重要的。所谓的扩展性,意味着,我可以很容易地加更多的功能,或是加入更多的系统,而所谓可运维,就是说我可以对线上的系统做任意的变更。扩展性要求的是有标准规范且不耦合的业务架构,可运维性要求的则是可控的能力,也就是一组各式各样的控制系统。

  • 通过服务编排架构来降低服务间的耦合。比如:通过一个业务流程的专用服务,或是像 Workflow,Event Driven Architecture , Broker,Gateway,Service Discovery 等这类的的中间件来降低服务间的依赖关系。
  • 通过服务发现或服务网关来降低服务依赖所带来的运维复杂度。服务发现可以很好的降低相关依赖服务的运维复杂度,让你可以很轻松的上线或下线服务,或是进行服务伸缩。
  • 一定要使用各种软件设计的原则。比如:像SOLID这样的原则(参看《一些软件设计的原则》),IoC/DIP,SOA 或 Spring Cloud 等 架构的最佳实践(参看《SteveY对Amazon和Google平台的吐槽》中的 Service Interface 的那几条军规),分布式系统架构的相关实践(参看:《分布式系统的事务处理》,或微软件的 《Cloud Design Patterns》)……等等

原则七:对控制逻辑进行全面收口

所有的程序都会有两种逻辑,一种是业务逻辑,一种是控制逻辑,业务逻辑就是完成业务的逻辑,控制逻辑是辅助,比如你用多线程,还是用分布式,是用数据库还是用文件,如何配置、部署,运维、监控,事务控制,服务发现,弹性伸缩,灰度发布,高并发,等等,等等 ……这些都是控制逻辑,跟业务逻辑没有一毛钱关系。控制逻辑的技术深度会通常会比业务逻辑要深一些,门槛也会要高一些,所以,最好要专业的程序员来负责控制逻辑的开发,统一规划统一管理,进行收口。这其中包括:

  • 流量收口。包括南北向和东西向的流量的调度,主要通过流量网关,开发框架 SDK或 Service Mesh 这样的技术。
  • 服务治理收口。包括:服务发现、健康检查,配置管理、事务、事件、重试、熔断、限流……主要通过开发框架 SDK – 如:Spring Cloud,或服务网格Service Mesh等技术。
  • 监控数据收口。包括:日志、指标、调用链……主要通过一些标准主流的探针,再加上后台的数据清洗和数据存储来完成,最好是使用无侵入式的技术。监控的数据必须统一在一个地方进行关联,这样才会产生信息。
  • 资源调度有应用部署的收口。包括:计算、网络和存储的收口,主要是通过容器化的方案,如k8s来完成。
  • 中间件的收口。包括:数据库,消息,缓存,服务发现,网关……等等。这类的收口方式一般要在企业内部统一建立一个共享的云化的中间件资源池。

对此,这里的原则是:

  • 你要选择容易进行业务逻辑和控制逻辑分离的技术。这里,Java 的 JVM+字节码注入+AOP 式的Spring 开发框架,会带给你太多的优势。
  • 你要选择可以享受“前人种树,后人乘凉”的有技术红利的技术。如:有庞大社区而且相互兼容的技术,如:Java, Docker,  Ansible,HTTP,Telegraf/Collectd……
  • 中间件你要使用可以 支持HA集群和多租户的技术。这里基本上所有的主流中间件都会支持 HA 集群方式的。

原则八:不要迁就老旧系统的技术债务

我发现很多公司都很非常大的技术债务,这些债务具体表现如下:

  • 使用老旧的技术。比如,使用HTTP1.0, Java 1.6,Websphere,ESB,基于 socket的通讯协议,过时的模型……等等
  • 不合理的设计。比如,在 gateway 中写大量的业务逻辑,单体架构,数据和业务逻辑深度耦合,错误的系统架构(把缓存当数据库,用消息队列同步数据)……等等
  •  缺少配套设施。比如,没有自动化测试,没有好的软件文档,没有质量好的代码,没有标准和规范……等等

来找我寻求技术帮助的人都有各种各样的问题。我都会对他们苦口婆心地说同样的一句话——“如果你是来找我 case-by-case 解决问题,我兴趣不大,因为,你们千万不要寄希望能够很简单的把一辆夏利车改成一辆法拉利跑车,或是把一栋地基没打好的歪楼搞正。以前欠下的技术债,都得要还,没打好的地基要重新打,没建配套设施都要建。这些基础设施如果不按照正确科学的方式建立的话,你是不可能有一个好的的系统,我也没办法帮你 case-by-case 的解决问题……”,一开始,他们都会对我说,没问题,我们就是要还债,但是,最后发现要还的债真多,有点承受不了,就开始现原形了。

他们开始为自己的“欠的技术债”找各种合理化的理由——给你解释各种各样的历史原因和不得以而为之的理由。谈着谈着,让我有一种感觉——他们希望得到一种什么都不改什么都不付出的方式就可以进步的心态,他们宁可让新的技术 low 下来迁就于这些技术债,把新的技术滥用地乱七八糟的。有一个公司,他们的系统架构和技术选型基本都搞错了,使用错误的模型构建系统,导致整个系统的性能非常之差,也才几千万条数据,但他们想的不是还债,不是把地基和配套设施建好,而且要把楼修的更高,上更多的系统——他们觉得现有的系统挺好,性能问题的原因是他们没一个大数据平台,所以要建大数据平台……

我见过很多很多公司,包括大如 BAT 这样的公司,都会在原来的技术债上进行更多的建设,然后,技术债越来越大,利息越来越大,最终成为一个高利贷,再也还不了(我在《开发团队的效率》一文中讲过一个 WatchDog 的架构模式,一个系统烂了,不是去改这个系统,而是在旁边建一个系统来看着它,我很难理解为什么会有这样的逻辑,也许是为了要解决更多的就业……)

这里有几个原则和方法我是非常坚持的,分享给大家:

  • 与其花大力气迁就技术债务,不如直接还技术债。是所谓的长痛不如短痛。
  • 建设没有技术债的“新城区”,并通过“防腐层 ”的架构模型,不要让技术债侵入“新城区”

原则九:不要依赖自己的经验,要依赖于数据和学习

有好些人来找我跟我说他们的技术问题,然后希望我能够给他们一个答案。我说,我需要了解一下你现有系统的情况,也就是需要先做个诊断,我只有得到这些数据后,我才可能明白真正的原因是什么 ,我才可能给你做出一个比较好的技术方案。我个人觉得这是一种对对方负责的方法,因为技术手段太多了,所有的技术手段都有适应的场景,并且有各种 trade-off,所以,只有调研完后才能做出决定。这跟医生看病是一样的,确诊病因不能靠经验,还是要靠诊断数据。在科学面前,所有的经验都是靠不住的……

另外,如果有一天你在做技术决定的时候,开始凭自己以往的经验,那么你就已经不可能再成长了。人都是不可能通过不断重复过去而进步的,人的进步从来都是通过学习自己不知道的东西。所以,千万不要依赖于自己的经验做决定。做任何决定之前,最好花上一点时间,上网查一下相关的资料,技术博客,文章,论文等 ,同时,也看看各个公司,或是各个开源软件他们是怎么做的?然后,比较多种方案的 Pros/Cons,最终形成自己的决定,这样,才可能做出一个更好的决定。

原则十:千万要小心 X – Y 问题,要追问原始需求

对于 X-Y 问题,也就是说,用户为了解决 X问题,他觉得用 Y 可以解,于是问我 Y 怎么搞,结果搞到最后,发现原来要解决的 X 问题,这个时候最好的解决方案不是 Y,而是 Z。 这种 X-Y 问题真是相当之多,见的太多太多了。所以,每次用户来找我的时候,我都要不断地追问什么是 X 问题。

比如,好些用户都会来问我他们要一个大数据流式处理,结果追问具体要解决什么样的问题时,才发现他们的问题是因为服务中有大量的状态,需要把相同用户的数据请求放在同一个服务上处理,而且设计上导致一个慢函数拖慢整个应用服务。最终就是做一下性能调优就好了,根本没有必要上什么大数据的流式处理。

我很喜欢追问为什么 ,这种追问,会让客户也跟着来一起重新思考。比如,有个客户来找我评估的一个技术架构的决定,从理论上来说,好像这个架构在用户的这个场景下非常不错。但是,这个场景和这个架构是我职业生涯从来没有见过的。于是,我开始追问这个为什么会是这么一个场景?当我追问的时候,我发现用户都感到这个场景的各种不合理。最后引起了大家非常深刻的研讨,最终用户把那个场景修正后,而架构就突然就变成了一个常见且成熟的的模型……

原则十一:激进胜于保守,创新与实用并不冲突

我对技术的态度是比较激进的,但是,所谓的激进并不是瞎搞,也不是见新技术就上,而是积极拥抱会改变未来的新技术,如:Docker/Go,我就非常快地跟进,但是像区块链或是 Rust 这样的,我就不是很积极。因为,其并没有命中我认为的技术趋势的几个特征(参看《Go,Docker 和新技术 》)。当然,我也不是不喜欢的就不学了,我对区块链和 Rust 我一样学习,我也知道这些技术的优势,但我不会大规模使用它们。另外,我也尊重保守的决定,这里面没有对和错。但是,我个人觉得对技术激进的态度比起保守来说有太多的好处了。一方面来说,对于用户来说,很大程度上来说,新技术通常都表面有很好的竞争力,而且我见太多这样成功的公司都在积极拥抱新的技术的,而保守的通常来说都越来越不好。

有一些人会跟我说,我们是实用主义,我们不需要创新,能解决当下的问题就好,所以,我们不需要新技术,现有的技术用好就行了。这类的公司,他们的技术设计第一天就在负债,虽然可以解决当下问题,但是马上就会出现新的问题,然后他们会疲于解决各种问题。最后呢,最后还是会走到新的技术上。

这里的逻辑很简单 —— 进步永远来自于探索,探索是要付出代价的,但是收益更大。对我而言,不敢冒险才是最大的冒险,不敢犯错才是最大的错误,害怕失去会让你失去的更多……

(全文完)

(转载本站文章请注明作者和出处 酷 壳 – CoolShell ,请勿用于任何商业用途)

The post 我做系统架构的一些原则 first appeared on 酷 壳 - CoolShell.
❌
❌