普通视图

发现新文章,点击刷新页面。
昨天以前子恒的博客

[架构]单体和微服务

作者 子恒
2024年4月15日 19:31
多大的服务 “宜小不宜微”,不宜过大的单体,也不宜滥用微服务,应保持在合适的大小。 (ps. 这里只讨论服务的大小。不讨论服务的手段,拆分解耦、服务发现、mesh、告警、限流、全链路建设,都是好的实践,任何服务都可以做的同样好) 什么样的服务大小属于合理的服务?核心有两点,合理划分服务边界,匹配组织结构。 团队视角:服务的规模应该尽量限制在最小团队,约3人左右的团队为宜 ❎ 过大!跨团队修改同一服务,风险大且不利于基础建设。没有人对整体的架构和质量负责。没有人有全局视角,写代码因为不了解改动范围容易出故障。 ❎ 过大!10人以上的团队不宜修改同一服务。很可能服务定位不清晰,10个人的信息交流会有很多gap,效率会大大降低 ❎ 过小!团队每个人管三四个服务,说明服务数量过多,无用工作会增加,效率也会降低 业务视角:保证产品需求变更如果只在本团队内,修改的服务1-2个即可 ❎ 过小!改一个小小的功能,牵扯4-5个服务。服务太小了,效率降低 ❎ 过大!同时有四五个业务需求,对应数十个研发同时修改一个服务,而这些功能看起来并不密切。这说明服务包揽的功能太多了,已经膨胀的太大了 更微=更好? 我们以一个模块划分合理、大小合适的单体服务为例,看看如果拆分的更微小,对比起来会有哪些点值得关注: 关注点 合理的单体 更微服务 性能 无劣化 约数ms的劣化 复用性 可以仓库内直接引用 不可复用其他仓库代码;所以也不会复用到bug 可读性 容易找代码 需要跨的仓库多,层次深,理解困难 研发成本 编译可能较慢 写代码、编译、调试、发布、部署都需要多仓库并行,成本高 部署成本 单机器 高,需要多台机器 代码合入 公共部分容易冲突 不容易冲突 微服务教 我们看下某网站对单体的批评,一一回应下: 开发效率低:所有的开发在一个项目改代码,递交代码相互等待,代码冲突不断 确实,代码冲突会变多。但在单体内部模块划分合理的情况下,冲突的只会是公共模块;如果这些代码冲突了,使用微服务也会冲突。 代码维护难:代码功能耦合在一起,新人不知道何从下手 耦合是模块设计的问题,不是单体的问题。而且单体断层更少,更容易阅读 部署不灵活:构建时间长,任何小修改必须重新构建整个项目,这个过程往往很长 没发现编译时间有显著区别。部分单体仓库会编译多个产物,建议针对性编译,效果更好。 稳定性不高:一个微不足道的小问题,可以导致整个应用挂掉 绝对意义上,微服务存在同样的问题,而且更难定位。相对意义上,如果说单体的复用代码带来了问题风险的扩大,这属于因噎废食,不可取。 微服务给我们带来了什么样的反思?拒绝盲目创新,提防吹牛皮但不切实际,小心滥用导致过犹不及。 再次回到起点:monorepo 有趣的是,越来越多人意识到了微服务的问题,最终又回归了单一的仓库,并发明了一新的词:monorepo。 Segment.com 提供活动收集和转发服务,每个客户都需要使用一种特殊格式的数据。因此,工程团队最初决定混合使用微服务和多代码库。这一策略效果很好——随着客户基数的增长,他们扩大了规模,没有出现问题。但是,当转发目的地的数量超过 100 个时,事情开始变得糟糕起来。维护、测试和部署超过 140 个代码库(每个代码库都有数百个日益分化的依赖关系)的管理负担太高了。最终,团队发现他们无法取得进展,三个全职工程师花费了大部分时间来维持系统的运行。 对于 Segment 来说,补救办法就是合并,将所有的服务和依赖迁移到一个单一代码库中。他们必须协调共享库并且测试所有内容,虽然花了很大的代价,但迁移非常成功,最终的结果是降低了复杂性,增加了可维护性。 每个团队的精力是有限的,当每个人管理的仓库>10个,团队管理的仓库>100个,会出现非常多的问题: 团队无法集中注意力管理。有很多的仓库大部分人并不知晓。这部分代码不再能被复用,也不再被了解 开发要打开很多IDE并发进行,容易出错。测试需要部署非常多的服务,也不能跨服务debug。发布时需要发布过多的仓库,发布容易遗漏;发布有很复杂的依赖,发布也会花费更久的时间。 无法发起整体性重构。重构一个服务和同时重构10个服务的难度不是一个级别。

[管理]对新人友好的项目管理手册

作者 子恒
2022年8月15日 15:37
刚毕业的高材生小姜,有着浑厚的知识储备和满怀热情的心脏来到了某厂,在做了一段时间需求后,发现自己对做事靠谱的老司机倍加羡慕;为什么人家有条不紊,好评如潮?自己确手忙脚乱,频频提测delay,加班到深夜? 今天我们来帮帮小姜,看看小姜为什么技术扎实,态度积极确总是使不上劲? (故事仅供参考,切勿对号入座) 需求详评 这周产品新提了一个需求,拉了小姜和老梁一起做,在详评会议上: 小姜:听的云里雾里,昏昏欲睡,听产品讲完后准备下来仔细看看代码哪里要改 老梁:对产品频频犀利发问:这个细节A为什么这样做,我感觉体验并不好?我们这个需求预期什么时候上线?是不是倒排?我看到这个项目需要我们合作方A做开发,定容了吗? 小姜恍然大悟的笔记(详评阶段应该做的): 敲定需求细节,可以给出自己视角的建议 对产品考虑不周全的需求,结合现状进行扩充 对于欠缺价值的需求点,合理挑战 对于破坏系统设计过大的需求点,提早商量 敲定需求整体节奏和预期上线时间,为做方案做准备 对于涉及到前置依赖的需求,一定要尽早确认好工作节奏 UE稿什么时候出 下游是否已经定容、什么时候完成开发,下游依赖接口什么时候给 技术评审 小姜:往文档里贴了下代码截图片段,告诉大家我要改这里,其他人听的一脸懵 老梁:给了一个完整的方案,涉及到合作方的关注点的内容写的非常详细,获得一致好评 小姜复制了一份老梁的模板: 排期概览(开发、联调、测试、上线的准确时间) 业务背景(问题、目标、收益) 技术方案 设计模块和改动点列举 架构图、流程图、状态机、时序图、ER图 异常情况和处理 AB实验方案 老数据兼容 依赖下游方案 风险点(需求自身风险、产品影响风险、可维护性/效率风险) 存储设计 DB/TCC/MQ/Redis/ES的schema变更 数据量级、key、分片、索引、选型、异常处理 接口文档 http接口定义和使用文档 rpc接口定义和使用文档 服务治理 异常兜底 监控报警 工作量评估 工作拆分和估分 进度计划 自测用例 冒烟测试用例(描述、步骤、期望结果) 希望测试测到的地方 发布计划 MR 发布顺序 开发 评审之后大家迅速都加入了如火如荼的开发当中: 小姜:早早地做完了自己的工作,等着联调。联调前一天突然发现下游依赖的接口还没数据,一问原来是有个工作没有对齐漏掉了,心急如焚 老梁:按照之前拆分的开发计划,列出了一个详细的进度追踪表,可以看到工作分配到的人、完成时间、里程碑,还有风险;提前把问题消灭了,大家笑盈盈进入联调 功能点 开发状态 人力预估 开发者 自测完成 风险记录 功能A 未开始 5 小A 是 暂无 功能A适配 进行中 2 小B 否 下游接口延迟一天 功能B适配 已完成 2 小B 否 功能B 已完成 0.

[杂谈]我怎么选择笔记软件

作者 子恒
2022年8月10日 16:57
折腾了这么久笔记软件,终于有了一个最终的方案,给大家分享下 用过的软件对比 软件 优点 缺点 用途 印象笔记 云/多端 丑,难用,残疾md 手机查看 OneNote 多目录/手写 同步难,没有md 读书、网课笔记 Typora 好看/导出 没有云、收费 离线编辑 马克飞象 比较好看 没有云、收费 离线编辑 Obsdian 比较好看 没有云、不能导出 离线编辑 CmdMarkdown 有云 难看 备份,网页版查看 StackEdit 简单 太卡了 没用 MarginNote 知识图谱 无win 读pdf书 备忘录 多端同步、轻量 格式难用 速记 Notion 多端同步、复杂 md不好用,手写不支持 替代onenote 诉求 支持markdown,且美观 支持Latex,脚注 支持云存储,移动端同步 支持mac/windows/ios/网页版 支持3级目录,支持搜索 支持手写插入 支持网页剪裁 支持离线编辑 可以导出pdf,html 支持代码块,代码块染色,行号 支持大纲 工作流 没有一个通用的工作流,所以我需要根据不同的场景来决定采用哪些软件。 速记 (todo list,check list) 备忘录 写博客 Obsdian -> github -> hugo

[互联网]广告业务的前世今生

作者 子恒
2022年1月4日 17:23
笔者曾在腾讯商业化、字节广告变现承担过多年广告后台相关工作,对业界的广告套路和广告架构比较熟悉。本文旨在以尽量容易理解的方式来分享广告相关的知识,来给对广告业务了解较少的同学形成一个基本的认知: 广告是如何赚钱的? 广告系统的组成是什么样的? 广告系统有哪些值得学习的策略? 商业模型 广告在平台内部的“永动机” 用户产品:把平台拉新进来的用户留存,转化成活跃用户 商业化:在活跃用户的浏览行为中插入广告,获得收入 UG:把部分商业化的收入用来拉新、激励,使得有源源不断的新用户进来 广告做的是下面三个人群的生意 用户:需要使用平台的服务,如新闻、推文、博客、视频 广告主:需要构建自己的品牌价值、推销自己的产品等 平台:需要用自己产品的DAU变现 广告业务 广告形态 常见的广告形态 硬广 开屏广告 原生开屏广告 信息流广告 搜索广告 软广 图文视频软广 非标 锚点 彩蛋 hashtag DOU+ 交易链路 如果广告主想要在多个媒体(网站、广告网络、交易平台)投放广告 ,是一个非常繁琐的过程。因为每一个网站、广告网络、交易平台的媒体购买系统、操作规则不同,需要人工进行调整,费时低效;而且跨渠道媒体购买很可能重复购买同一部分人群。 DSP平台把广告主、代理人员从庞杂的重复手工操作中解放出来。广告主只要在DSP平台投放广告即可,由DSP平台帮助广告在多个媒体投放广告。 同理,如果流量主想在自己的网站流量位置上插入广告,如果自己去招商,也是个非常消耗人力的事情,寻找广告主、和广告主对接、广告主提供的素材匹配自己的广告位都需要很多时间。SSP平台可以快速满足流量主接入广告的需求,帮助流量主快速变现。 名词解释 DSP (Demand-Side Platform): 需求方平台,可以简单理解为需要采买流量的平台(广告主自建或者第三方技术公司) SSP (Supply-Side Platform):供应方平台/媒体平台,管理流量和坑位。可以简单理解为流量(抖火西头) DMP (Data Management Platform):数据管理平台,管理人群,生成定向人群包 ADX (AD Exchange):程序化广告****交易平台,通过技术对接的方式,支持客户(DSP)进行媒体的流量(用户)采买 ADN (Ad Network ): 广告网络,聚合了大量App内的展示广告资源,主要包含中长尾App流量,帮助广告主实现媒体精准、灵活的投放。汇集了很多媒体的余量 各种公司的布局: 行业角色 变现公式 **广告收入 = 活跃用户数 * 人均展示数 * 广告负载 *** 广告点击率 * 广告价格 $$(Ad Revenue = DAU * Avg Imps * AdLoad * CTR * AdPrice)$$

[后台]服务端高性能架构之道(系统和服务篇)

作者 子恒
2021年9月28日 21:13
如果你在服务端的工区,常常会听到同学们激烈的讨论,包括能不能扛得住xx流量?能不能P99达到x毫秒?某操作能不能立即生效?某服务CPU飙升了,某服务OOM了,某服务超时率暴涨了? 这些灵魂的质问,其实就是在保障服务端的高并发、高性能、高可用、高一致性等等,是我们服务端同学必备的扎实基本功。 克服系统瓶颈 服务端的代码都跑在各种版本的Linux之上,所以高性能的第一步要和操作系统打交道。我们的服务需要通过操作系统进行I/O、CPU、内存等等设备的使用,同时在使用各种系统调用时避免各种资源的开销过大。 零拷贝 认识零拷贝之前,我们先要对Linux系统I/O机制有一定的了解。当我们执行一个write(2)或者read(2)的时候(或者recv和send),什么时候操作系统会执行读写操作?什么时候又最终会落到磁盘上? 以一个简单的echo服务器为例,我们模拟下每天都在发生的请求和回包: 1 2 3 4 5 6 sockfd = socket(...); //打开socket buffer = new buffer(...); //创建buffer while((clientfd = accept(socketfd...)){ // 接收一个请求 read(clientfd, buffer, ...); //从文件内容读到buffer中 write(clientfd, buffer, ...); //将buffer中的内容发送到网络 } 看一下这段代码的拷贝流程(下图): 数据包到达网卡,网卡进行DMA操作,把网卡寄存器的数据拷贝到内核缓冲区 CPU把内核缓冲区的数据拷贝到用户空间的缓冲区 用户空间处理buffer中的数据(此处不处理) CPU把用户空间的缓冲区的数据拷贝到内核缓冲区 网卡进行DMA操作,把内核缓冲区的数据拷贝到网卡寄存器,发送出去 整个过程触发了4次拷贝(2次CPU,2次DMA),2次系统调用(对应4次上下文切换) (注:DMA(Direct Memory Access), I/O 设备直接访问内存的一个通道,可以完成数据拷贝,使得CPU 不再参与任何拷贝相关的事情,现在几乎所有的设备都有DMA) 使用mmap mmap可以把用户空间的内存地址映射到内核空间,这样对用户空间的数据操作可以反映到内核空间,省去了用户空间的一次拷贝: 应用调用mmap,和内核共享缓冲区(只需一次) 数据包到达网卡,网卡进行DMA操作,把网卡寄存器的数据拷贝到内核缓冲区 CPU把接收到的内核缓冲区的数据拷贝到发送的内核缓冲区 网卡进行DMA操作,把内核缓冲区的数据拷贝到网卡寄存器,发送出去 整个过程触发了3次拷贝(1次CPU,2次DMA),2次系统调用(对应4次上下文切换) 使用sendfile/splice Linux 内核版本 2.1 中实现了一个函数sendfile(2): 他把read(2)和write(2)合二为一,成为一次系统调用,实现了把一个文件读取并写到另一个文件的语义 系统调用中不再切换回用户态,而是在内核空间中直接把数据拷贝过去(2.4 之后这一步支持了DMA拷贝,实现了CPU零拷贝) 我门看下使用sendfile之后的流程: 整个过程触发了3次拷贝(0次CPU,3次DMA),1次系统调用(对应2次上下文切换) Linux 内核版本 2.6 中实现了一个函数splice(2),类似sendfile,但是接收/发送方必须有一个文件是管道,通过管道的方式连接发送方和接收方的内核缓冲区,不再需要拷贝(0次CPU,2次DMA,1次系统调用)

[Redis]Redis 应用篇

作者 子恒
2020年9月24日 15:57
分布式锁 简单加锁 1 2 3 4 5 // 思想:利用setnx检测有没有set过,如果set过就表示没有抢到锁 > setnx locker true OK // ... do somthing ... > del locker 处理set之后进程崩溃的死锁问题 1 2 3 4 5 6 // 思想:给锁加上过期时间,即使set之后进程挂掉,也不会死锁 > setnx locker true OK > expire locker 5 // ... do somthing ... > del locker 处理非原子性问题 setnx之后,expire之前,进程挂了,也会死锁。怎么处理这种情况? 使用redis事务吗?事务里没有if else,要么全部执行,要么全部不执行。需求是setnx成功才执行expire,有依赖关系,没法用事务 使用新的原子命令,如下 1 2 3 4 > set locker true ex5 nx OK // ... do somthing ... > del locker 处理超时问题 上面的方案设定了超时时间。但是如果少数操作的时间超过了超时时间怎么办?有两个问题:

[后台]负载均衡 (三)限流篇

作者 子恒
2020年7月27日 17:28
限流 限流能力是高并发系统中,对于服务提供方的一种保护手段。通过限流功能,我们可以通过控制QPS的方式,以避免被瞬时的流量高峰冲垮,从而保障系统的高可用性。 考虑的问题 完成一个限流系统, 我们可以结合场景的需要做下面的考虑 多规则匹配:是否会存在有多重规则的限流?比如有的规则限制每天1000次,有的规则限制每分钟1次?是同时生效还是优先生效某个? 资源类型:能限流什么?QPS,连接数,并发数 全局限流/单机限流:多个服务的实例共享一个全局的流量限额,比如所有机器共享1000QPS。或者单个实例的限流,比如被调限定每台机器不超过1000QPS 限流阈值:单位时间内的最大配额数。是按照每秒种一次,还是按照每分钟60次? 限流处理:客户端如何处理超出限额的请求?超额后直接拒绝,还是超额后进行排队? 抽象出一个方案 接口级别限流:每个接口分配一个appid和key,各自计算各自的配额 多维度限流:支持每秒N次、每分钟N次、每天N次等维度 匀速防刷:假设配置了每分钟60次,依然可能出现第一秒访问了60次用光了配额。匀速防刷可以匀速消耗配额,解决这个问题 多级限流:支持不同的限流规则,并有采用的优先级,采用优先级最高的方案进行限流 限流算法 固定窗口 固定窗口是在一段时间内可以限制访问次数的方法。 将时间划分为多个窗口 在每个窗口内每有一次请求就将计数器加一 如果计数器超过了限制数量,则本窗口内所有的请求都被丢弃。当时间到达下一个窗口时,计数器重置 这样有一定的限流效果,但是限制住的流量可能是有毛刺的。比如1000次/分钟,可能00:59的时候有1000流量,01:00的时候也有1000流量,这样这两秒内就有2000流量! 具体实现:用一个变量C标记访问次数,一个事件定时过期,并在过期时把变量C清零: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 type FixedWindowCounter struct { TimeSlice time.Duration NowCount int32 AllowCount int32 } func (p *FixedWindowCounter) Take() bool { once.Do(func() { go func() { for { select { case <-time.

[后台]负载均衡(二)能力篇

作者 子恒
2020年5月21日 17:18
名字服务 基础设计 名字服务考虑的基本设计 客户端发现: 服务提供者的实例在启动时或者位置信息发生变化时会向服务注册表注册自身,在停止时会向服务注册表注销自身,如果服务提供者的实例发生故障,在一段时间内不发送心跳之后,也会被服务注册表注销。 服务消费者的实例会向服务注册表查询服务提供者的位置信息,然后通过这些位置信息直接向服务提供者发起请求。 服务端发现: 第一步与客户端发现相同。 服务消费者不直接向服务注册表查询,也不直接向服务提供者发起请求,而是将对服务提供者的请求发往一个中央路由器或者负载均衡器,中央路由器或者负载均衡器查询服务注册表获取服务提供者的位置信息,并将请求转发给服务提供者。 这两种模式各有利弊,客户端发现模式的优势是,服务消费者向服务提供者发起请求时比服务端发现模式少了一次网络跳转,劣势是服务消费者需要内置特定的服务发现客户端和服务发现逻辑; 服务端发现模式的优势是服务消费者无需内置特定的服务发现客户端和服务发现逻辑,劣势是多了一次网络跳转,并且需要基础设施环境提供中央路由机制或者负载均衡机制。目前客户端发现模式应用的多一些,因为这种模式的对基础设施环境没有特殊的要求,和基础设施环境也没有过多的耦合性。 主调调用被调时,根据被调的名字从服务注册中心获取服务实例列表,包括节点ip、端口、权重、地理位置等;一般采取分钟级别的定时任务去拉取,本地做缓存,异步更新。 实现方式 DNS,传播速度太慢,没法发现端口。SkyDNS解决了这个问题,在k8s里大量使用 zookeeper或者etcd,如SmartStack,能保证强一致,但是要做很多开发 Eureka。Netflix的java生态里的优秀方案 Consul,提供服务配置、服务的内存和磁盘监测等 服务注册信息 IP和端口 一个服务端要接入名字服务,必须要先提供自己的IP和端口信息。 IP的获取方法: 通过遍历网卡的方式去获取,找到第一个不为本地环回地址的 IP 地址。dubbo就是这种方法 指定网卡名interfaceName,来获取IP 直接与服务注册中心建立 socket 连接,然后通过socket.getLocalAddress() 这种方式来获取本机的 IP 端口的获取方法: 一般的RPC服务或者Web服务监听的端口都在配置中写好,可以直接获取上报。 扩展设计 除了IP和端口,可能还有一些常用的服务信息需要注册上来,提供更高级的功能: 1.支持TLS:想知道某个 HTTP 服务是否开启了 TLS。 2.权重:对相同服务下的不同节点设置不同的权重,进行流量调度。 3.环境分配:将服务分成预发环境和生产环境,方便进行AB Test功能。 4.机房:不同机房的服务注册时加上机房的标签,以实现同机房优先的路由规则。 无损注册/下线 虽然服务注册一般发生在服务的启动阶段,但是细分的话,服务注册应该在服务已经完全启动成功,并准备对外提供服务之后才能进行注册。 1.有些 RPC 框架自身提供了方法来判断服务是否已经启动完成,如 Thrift ,我们可以通过 Server.isServing() 来判断。 2.有一些 RPC 框架本身没有提供服务是否启动完成的方式,这时我们可以通过检测端口是否已经处于监听状态来判断。 3.而对于 HTTP 服务,服务是否启动完毕也可以通过端口是否处于监听状态来判断。 下线也是一样的,可以注册服务下线的回调,或者监听服务下线的信号,或者做健康检查 健康检查 客户端主动心跳上报健康: 客户端每隔一定时间主动发送“心跳”的方式来向服务端表明自己的服务状态正常,心跳可以是 TCP 的形式,也可以是 HTTP 的形式。 也可以通过维持客户端和服务端的一个 socket 长连接自己实现一个客户端心跳的方式。 客户端的健康检查只能表明网络可达,不能代表服务可用。服务端的健康检查可以准确获得服务的健康状态: 服务端调用服务发布者某个 HTTP 接口来完成健康检查。 对于没有提供 HTTP 服务的 RPC 应用,服务端调用服务发布者的接口来完成健康检查。 可以通过执行某个脚本的形式来进行综合检查,覆盖多个场景。
❌
❌