我做系统架构的一些原则
工作 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 的规范。我觉得是非常重要的,这里给两个我觉得写得最好的参考:Paypal 和 Microsoft 。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 我一样学习,我也知道这些技术的优势,但我不会大规模使用它们。另外,我也尊重保守的决定,这里面没有对和错。但是,我个人觉得对技术激进的态度比起保守来说有太多的好处了。一方面来说,对于用户来说,很大程度上来说,新技术通常都表面有很好的竞争力,而且我见太多这样成功的公司都在积极拥抱新的技术的,而保守的通常来说都越来越不好。
有一些人会跟我说,我们是实用主义,我们不需要创新,能解决当下的问题就好,所以,我们不需要新技术,现有的技术用好就行了。这类的公司,他们的技术设计第一天就在负债,虽然可以解决当下问题,但是马上就会出现新的问题,然后他们会疲于解决各种问题。最后呢,最后还是会走到新的技术上。
这里的逻辑很简单 —— 进步永远来自于探索,探索是要付出代价的,但是收益更大。对我而言,不敢冒险才是最大的冒险,不敢犯错才是最大的错误,害怕失去会让你失去的更多……
(全文完)
(转载本站文章请注明作者和出处 宝酷 – sou-ip ,请勿用于任何商业用途)
《我做系统架构的一些原则》的相关评论
不要过早性能优化。
这句话是不是有点问题呢?
对不起是错别字,已修改。
感谢,获益良多!
读了三遍了,受益匪浅
说到心坎里面了!
不要过早使用Java。
除非你的项目发展到了很大的规模,否则不要一开始就用Java。Java繁琐又冗余,肮脏又丑陋。写Java会加班,写Java会秃头。
你这就是我说的个人喜好了,因为没有什么 数据支撑。我给你一个数据支撑:全中国所有的电商平台,400 家银行,三大电信运营商,所有的保险公司,劵商的系统,医院里的系统,电子政府系统,等等,都是用 Java 开发的,包括 AWS 也是 Java 开发的……
所以我说是项目发展到很大规模了才用Java啊。项目从0-50的时候,坚决不用Java。等到从50-100的时候,再考虑用Java。
不是规模的问题。如果注定要用Java实现,而一开始不用Java,会产生路径依赖。到业务快速发展期,没有一秒钟的空余时间也没有半个人力资源给你用Java重写。
0-50的时候,很多项目都是商业模式尝试阶段,能不能走到50~100甚至更大的阶段是不清楚的.
所以很难说后续是否注定要用java实现.
其实除了人数,程序员的技术能力关系更大,java可以让一般程序员写出不烂的代码,如果让他们写python,可能那些代码永远都没人能维护,稍微大一点的工程就很容易写蹦。要是团队程序员各个厉害的不行,你用basic都能想做什么做什么
我就是写Java的,之余也写过Python、nodejs和go。我觉得做业务最好就一步到位用Java,首先库多,其次便于多人协同。固然使用Python或者nodejs可以很快速的获得一个能用的原型,也能支持一定数量的请求,但后期真的严肃起来了,到底还是得往Java上迁移。而且说起来快速原型,kotlin也挺适合做快速原型的。
医院里的系统,据我所知,基本上是.NET
多学习, 使用更新的版本,Java 还是非常顺滑的。加班是因为技术不行导致的,秃头可能是激素问题。
太牛了,写的
很多时候,负责架构的人不写代码,一线写代码的人决定不了采用什么架构。
你做了架构就会明白,写没有比写代码更简单的事情
但做架构的没写过代码真的说不过去。
那go呢?
Java好,Java香。Java会让一群水平一般的程序员写出还不那么糟糕的代码。
但是Java是真繁琐。
鱼和熊掌可以兼得吗?
Java可以根据场景使简单化,已经简单化的语言无法适用更多场景,反而可能会使场景复杂化
“通过服务编排架构来降低服务音的耦合” ,有个错别字,音-> 间
谢谢,已修正
写的好。大厂还是有必要魔改或者造轮子适应自身业务的,要不也没得一些很好的国产开源框架
原则之间有冲突呢?这几条原则有优先级吗?
java繁琐,用kotlin啊,铁子们
段位不够,只有部分有深刻体会,需要每过一段时间读读
“设计上导致一个慢请拖慢整个应用服务”,慢请 -> 慢请求?慢查?
“更有的技术用好就行了”,更有->现有
谢谢,已更正
无论何种语言、架构,不是满足当前需求即最好吗?
最怕过度设计
想想京东从.NET 到 Java,淘宝从 PHP 到 Java……
如果他们一开始就用java,可能已经死了好多年了。
我文中已经讲了,公司从 0到1用什么 样的语言都行,其成功跟语言没关键,那是商业模式的成功。但是从 1到 10,或是从 10-100,基本上都要往 Java 上走……你 get 到了吗?
那是因为之前.NET 不开源,生态差,京东才会转JAVA,据我所知,京东目前有很多老系统从.NET 转向了 开源的.NET Core。,NET Core 性能上数倍于Java.生态上目前也正在追赶,特别是Dappr 的出现。
耗子叔啥时候出书
请问 原则八里
用消息队列同步数据
为啥是错误的设计?谢谢消息队列是传递消息,最好不要同步数据。收到消息后,调用相应的服务获得数据。大多数情况下,最好不要直接传递数据,否则会造成数据丢失或是数据不一致的情况。
kafka在某种意义上也是消息队列,CDC场景很多情况会用到kafka,这种场景“使用消息队列同步数据”感觉是合理的
看来在Amazon的工作经历对宝酷老师的影响还是很大的。AWS的marketplace就把数据同步分为两步,用户在marketplace的订阅有变更就持久化到SQS里,然后发SNS给ISV,由ISV通过拉取的方式消费订阅变更事件
这里的消息和数据有什么区别
看完了,说的都是大实话,都很朴素,知易行难。感觉真的是有种防御编程的味道了
请问”老旧的 技术:基于 socket的通讯协议” 这个是指不该基于socket自定义通讯协议,而是使用Restful API或者 GPRC等通用协议吗?
这里说的是,自己写的 Sockets 协议。
问一下,消息队列做数据同步为什么不合理,如果有多个下游,订阅的方式不是更合理吗
https://studygolang.com/articles/19958
很多观点和 https://studygolang.com/articles/19958 类似,却没有注明出处。
这篇文章抄袭了我的文章没注明出处,还反过来说我没有注明出处……真是大言不惭啊!
看起来是被培训机构洗稿了。
BS之
我只是看到 Go 想起 openbilibili 事件,想了解事件结果,然后发现的上述文章。
建议你投诉,保护自己的正当权益。
这里面关于最后go语言的描述很多就是原文抄袭耗子叔的左耳听风里面的文章的
WebAssembly算不算rust的杀手级应用呢?不过WebAssembly和rust目前都够复杂的,上手不易啊。
想问一下,有关于 Workflow,Event Driven Architecture 的最佳实践的推荐吗?比如推荐的技术或者文章之类的,谢谢。想想看其他公司是怎么落地的。
我只能说,Amazon 是这类技术的高级玩家,在他们家内部的系统里,遍地都是。
你可以看看temporal cadence,这两个workflow框架在官网和youtube上有挺多分享的
HTTP错误码的那个,扎心了,我还以为我很聪明呢,原来一直用错了。马上改~
所以说“ 自顶向下的,整体规划,统一设计的方式” 在还债的时候 也非常有用,需要一位非常有魄力的人来决策,不然大部分“过客”是搞不定的
错误码是有成因的,一些地方运营商会拦截4xx 5xx,跳转到自己的导航页,赚取流量,导致api调用失败。
当然了,在https的今天,这事已经不存在了。
我想补充一条。
不要只关注系统架构而忽视了代码架构。
勿在浮沙筑高台。
可以看一下这位老哥的文章。
https://michael-j.net/2017/09/24/%E7%BC%96%E7%A8%8B%E5%BF%83%E6%99%BA-%E4%B8%80-%E2%80%94%E2%80%94%E4%BB%A3%E7%A0%81%E6%9E%B6%E6%9E%84%E4%B8%8E%E7%B3%BB%E7%BB%9F%E6%9E%B6%E6%9E%84/
虽然只看懂了部分,但使我醍醐灌顶,而且经常看到耗子叔经常提 x-y问题,最近我也经常遇到,确实这是个值得关注的问题。
原则四:完备性会比性能更重要
印象最深的是, 云风的blog, 做大话西游2的客户端时, 因为游戏的业务逻辑基本都趟过一遍, 美术素材大量复用
然后才能把性能做的很好, 其次还设计了回放机制, 大幅度提高调试分析能力
耗哥,http状态码那里我也困扰很久,我观察到很多公司(不乏大厂)的实践是http协议层面的错误用4xx或者5xx,比如调GET接口使用了POST、body格式不对、服务端线程打满等。但是业务上的错误都是用200并且在body给出具体的业务异常。因为很多前端框架或者Feign都会对4xx 5xx进行拦截,对 web/android/ios不是很友好。
比如账号不存在这种情况,假设返回404,那前端拿到可能就懵了。或者某一个入参不符合业务定义,那也是返回200并且在body里面返回业务错误码和 message
一般公司有点规模都会定制 上下文,比如 header 添加一些东西就可以了。 只是说这样不能直接将开源的监控项目开箱即用。比如要监控业务错误码的统计,一般都只有定制。比如:密码错误次数、余额不足次数、支付失败次数。
我不理解为什么 ,明明是出错,但是返回 200 后前端就不懵了?另外,如果是出错,前端是要重试还是不重试呢?如果你能够知道是调用端出错,你就可以阻止用户在在没有修正错误前重试,但是如果全是成功了,你怎么控制呢?
404 表示资源不存在,帐号不存在返回 404后,你的 Body 里同样可以有 message 啊,前端怎么会懵呢?如果参数有问题,你要返回的是 400 – bad request,然后在 body 里说明一下是为什么 。
如果这些不够的话,你可以自己设计自己的返回码,比如用 跟系统不冲突的456 之类的,但是有一个核心原则是错误码要归类。4xx 就是调用端有问题,而不是服务端有问题。
1、像404这样的处理,很多地方已经被统一标准化了,这样一个404错误,就很难在业务维护上进行区分
2、如果body里带上内容来说,这样也还是需要把所有的网络请求包打开后才知道具体的业务信息
一点愚见,所以我现在也不太强求了。
成功归成功,错误归错误,这么严谨的事还能混用了?如果你不用 404,那么你可以用别的错误码,哪怕自定义的,用 456 也可以, 只要是 4xx 就好。错误码设计的第一原则是错误分类!明白了吗?
业务侧错误码太多。http协议里对的错误码给的range太小了,比如自定义的100001这样的错误码,是非预期的。而类似于这样的错误码,是有业务意义的,往往可以通过错误码直接定位到业务,服务和模块
所以还是倾向于,在内部,只要是用统一标准即可。
http的状态码不是让你放弃你自己的错误码啊……http的状态码是让你归类啊……我上面白说了……
明白,之前我也这么搞过,一方面是业务侧的错误很难有一个标准让大家去归类,所以只能是业务错误和系统错误分开了,如服务治理、RPC框架这些系统错误严格标准,业务侧自己throw的错误,理解为是可服务本身或控,且可预期的,所以还是走200 + error code来做了。
当然,我们怎么搞都能搞得通,甚至不用 HTTP都能搞通,但是如果我们用了 HTTP 就应该遵循 HTTP 的规范。因为这样的可以得到的红利是最大的。
这个是马屁股决定航天飞机的典型案例,可以参考微信的部分api文档。有的时候,只能呵呵,但是还只能屈服
主要是很多前端的框架会去自动拦截处理404、500 这种错误。在框架层面去修改定制的话不如直接在业务代码里处理来的简单。
我干过后端也干过前端,最恶心的就是状态码200里面包自定义错误。大眼喵一下,看不出来哪个接口错,必须一个一个点开才能看。fetch的then写正确的逻辑,error写错误的逻辑,本来设计的好好的,非要搞成then里面再写个判断是不是业务错了,catch里面就变成了网络不通一种情况
意思是在返回4xx 5xx的情况下,框架一拦截,你在自己的业务代码里面已经拿不到 body了,对于大部分公司的大部分非资深程序员来说,处理起来不直观。
1)你告诉我是哪个框架?还能吞掉 body?2)你说——对于大部分非资深程序员来说,处理起来不直观?这个结论怎么来的?什么 叫不直观?
因为按照http规范,有时候很难判断是客户端还是服务端错误,现实比较复杂。比如密码输错,是算400吗?余额不足呢,也是400的话,那比如查询的时候余额是足的,但是转账处理中又不足了,那还是返回400?我觉得这样就很容易引起误解了。像这种业务场景非常丰富,有时候很难说到底是客户端的错还是服务端的错。
密码错误,401,
密码输对,但没有权限,403
余额不够,你可以返回你自定义的错误,但是得归在 4xx,比如:470
我感觉你没有学过错误码设计吧,错误码设计的第一件就是对错误码归类。HTTP 帮我们归好了大类,你要学会遵从标准,不然你就无法得到标准带来的红利。
而且每个人对每个业务场景的理解都不同,比如余额不足,有人可能觉得200,有人觉得是调用方错误,有人觉得查询的时候是足的,那转账的时候不足 了就是服务端的错误,就很难统一。一个系统,不同的人接手之后可能都有不同的理解。
所以才需要一个顶层设计而不是任人乱来啊,一个系统没有标准规范,是一定会搞乱的。参看原则五!
另外,什么叫“很难统一”?!这就是一个简单的错误码,还是自己懒吧,有畏难情情绪吧,所以,就欠债吧。参看原则八!
总的来说还是现实的业务太复杂了,很难简单分类。跟REST标准类似,比如读朋友圈,读过之后小红点就消除掉,如果只用一个接口的话,那算是读(GET)还是写(PUT)呢?
还有就是类似的规范对程序员真的有一定的要求,比如规范的命名REST的资源路径。每个人理解又不同,公司又希望以最低成本找到能够写业务逻辑的程序员就够了。那去判断GET还是PUT,400还是500,就会造成混乱和前后人的不一致。
以上的http或者REST动作的问题,不仅后端团队要达成一致,还需要让前端也买账,让前端也配合来实现,又是一层沟通成本。
现实中肯定还有很多我没想到的更加复杂的业务场景,这些场景下到底该用什么REST动词,什么错误码,本身可能就是模棱两可的,没办法准确说是客户端错还是服务端错,或者是正常返回。
请不要甩锅给前端,如果后端有完整的完善的规范和方案,我是很乐意配合的。
更复杂的场景要等你碰到了再说,提前优化的等你碰到了就能合适吗?
如果能列举出来合适的具体例子,可以在此讨论合适的具体方案,列举不出合适的例子,只能说明你想多了。
楼主说的没错,促使这个发生的诱因是畏难情绪和懒惰,而不是其他的原因。
说的比较多,请耗子哥见谅,如果是我认知不足的话,我只是希望彻底搞清这件事,纠正自己的认识。
最后有一点是,http状态码和REST应该都是PC静态网页时代的标准,但是在移动互联网时代,业务场景复杂了很多倍,交互方式已经面目全非了,我觉得要强行adapt过来确实有困难,也不必要把它们视为圣经。
我覺得 frogman 很實在的把遇到的情況說出來蠻好的, 我也有遇過類似情形
基本上我覺得把http錯誤碼貼在公開的地方, 這樣大家直覺就會改變而不是依照過去可能誤用的經驗了
其实我觉得你说的,第一个原则,本质就一个成本
认同。需要学会从成本的角度分析问题。
我们核心的搜索引擎,业务需要一些自己的功能,所以我们选择“魔改”门槛最低的solr的solr。
行难知易,还需努力。
我对文章中列举rust这个部分持保留意见。因为,现在大部分人的技术背景和相关的工作经历,还无法正确评价这门语言的好处(别锤我~,没恶意)。在现阶段,可以说rust生态“不成熟”(请自行参考https://lib.rs/),但是这仅仅是个时间问题。我的意见是,不用担心技术激进的问题,更不用担心rust学习门槛问题(实际没门槛~),要搞清楚rust究竟解决了什么编程问题(当然要避开x-y陷阱)。java体系刚刚成立的时候,也是有过跟今天rust一样的历史阶段,后期也都是慢慢才成熟。正如作者所说的,如果一个技术债在开始就是错误的,我们应该尽快的修复他。别在jvm的这个gc怪(上千个参数,玄学调优)再投资了,编译器给你提前指出错误,帮你释放,他不香吗?我可以负责任的说,rust写任何服务都不存在任何问题,而且,基本上都是一旦编译,无畏运行。性能上只有c语言能略微领先(当然,在文章的语境下,大家别纠结这个性能)我个人的工作经历,不严谨的说,rust写的服务就没崩溃过。大家可以查查现在aws为啥要控制rust基金会(最近闹得沸沸扬扬的)。实际上,我不希望,我们再走一波aws用java,国内用java,然后aws 用rust,国内再来一轮的场景。大家提前考虑一下,多开开眼界~
Rust 跟 Java 走的是两个方向,一个是是业务开发,rust 干的是 C/C++,不是 Java……在业务开发的领域永远是更为高级的表现力强的语言。像 Rust 这种语言不是这个方向的……对了,Rust 的文章我也是写过的《Rust 编程语言范式》。
首先,我的意思不是说,你不了解rust。我不太认同业务开发限定语言的这个观点的。java之所以好用,是因为过往的生态积累和历史项目经历,才有了今天的局面。Rust未必仅仅适合干c和c++的事情,只不过是他最终编译的结果,最接近c和cpp。如果“纯粹业务”的话,其实DSL或者规则引擎(甚至低代码),往往是表现力会更好。表现力这个因素我认为略带主观,用c的宏,其实也可以写出来“表现力”很好的代码。当然rust社区,后面的重点工作也会在工程编码效率上做优化(这个我确实承认)。最后,我最care的事情,不是质疑作者想要给大家推广的技术判断思维方法。而是,文章在最后部分说rust例子的上下文有些不妥,可能有人会用你的名声拉大旗作虎皮,把rust这条路径给封死。我回复这个帖子,更多的是希望大家多看看,多思考思考。
谢谢留言。以后多多交流。
非常赞同,我刚才就因为这个原因降低了学习rust的优先级!
rust生态还是缺很多,一直都希望能有一个rust的asn.1编解码开源解决方案,收费的解决方案也可以, 类似https://github.com/vlm/asn1c
https://lib.rs/crates/rasn 请查收
关于最后一点,我昨天在公司内部也跟大家说,我们目前采用的技术路线,可能广西这边很多公司都不知道是什么,但他们兜兜转转几年后也会走到这条路上来。
招人不?
rust 感觉很赞呀
我也觉得 Rust 很赞,但是一个技术是否酷炫,和是否能被应用开是两件事。参看原则一。
Rust 很赞,写一个小电商的系统,打包编译成功的文件15M,运行时内存50M不到,比起java的内存大怪兽真的是有优势。
关键rust社区和轮子比其他语言少。
但是编译通过后,写出的系统的确小BUG.
在国内,近几年都是java为主写系统,比较符合些。
有关于“技术债务”的问题,我觉得应该是所有大公司的通病。
一项公司的决策是否可行,需要同时考虑决策成本和实施成本。越大的公司形成新决策的决策成本越高,所以很多实施成本很高但决策成本低的行为(例如维持现状)会被一直执行。
因此想要改变尾大不掉的问题,需要考虑如何降低组织内部的决策成本。
这种10倍效率概念真的很好,只能是慢慢指引方向,One Piece
国内大部分还是以时间内卷忽略效率
耗子叔我太爱你了
原则 6:
这个感觉有点问题
应该是——“因为架构和软件不是写好就完的”
国内大部分开源,感觉都是晋升完就完事儿了,以后再难看见有个什么commit,提issue,也基本没人回
和浩哥有共同的价值观和判断逻辑,特别是最后的人生态度
我还在领导的坚持下用c++写http服务,领导说这个是核心的项目,要用c++,其他非核心的,允许我用java。因为领导只会c++。上面说java繁琐的,请你们用c++写http服务吧。
无法更赞同!尤其感谢耗子叔为一些理念提供了更为正式的学名,例如“应用服务视角”、“工业化技术栈”、“X-Y问题”等,使得谈论这些问题拥有了更确切的名称和抓手。
X-Y问题搜 X-Y Problem。应用服务视角是云原生的概念。都不是耗子叔发明的
他说的 工业化技术栈 就是java ,其它的都是玩具,在他眼里只有java
耗子叔的文章,会多读几遍,每次都有收获