178网站建设,被国家禁止访问的网站怎么打开,企业信息填报官网,南京有哪些做网站的公司前言 
作为脉脉和前端技术社区的活跃分子#xff0c;我比较幸运的有了诸多面试机会并最终一路升级打怪如愿来到了这里。正式入职时间为2021年1月4日#xff0c;也就是元旦后的第一个工作日。对于这一天#xff0c;我印象深刻。踩着2020年的尾巴接到offer,属实是过了一个快乐…前言 
作为脉脉和前端技术社区的活跃分子我比较幸运的有了诸多面试机会并最终一路升级打怪如愿来到了这里。正式入职时间为2021年1月4日也就是元旦后的第一个工作日。对于这一天我印象深刻。踩着2020年的尾巴接到offer,属实是过了一个快乐的元旦。不知不觉已经两年多了,细细回想起来更多的是岁月推移并没有回头看看现在的自己和两年前的自己有什么差别。 
决定写文章记录一下还要感谢那个离职前在飞书上和我告别的老哥他说已经学到了想学的。 
那我呢似乎还没有。 
和优秀的人做有挑战的事不止是简单的一句话。 
在字节停留时间越久越是能感觉到身边人的优秀也正是这份优秀推动着我不断前进。 
本文将会从思维方式、问题排查、技术思考三个方面以回顾自我成长的视角展开叙述欢迎阅读。 
思维方式 
思维方式指的是看待事物的角度、方式和方法。放到工作当中来看我逐渐摸索出了几个具体的点。 
工作优先级 
曾很长一段时间里我在工作上没有刻意区分优先级或者说有优先级但是区分度不是那么明显。这意味着只要不是恰好有紧急事情处理基本上业务方提过来的合理需求我都会第一时间安排。不论需求大小也不问紧急程度都默认当作紧急处理。 
诚然在交付后得到业务方肯定的那一刻是有成就感的。但我逐渐意识到这真的是有点本末倒置。由于我负责的这部分工作和底层数据相关可能很多需求直接或间接的都会找到我。事实上完成对齐过的工作才是我更应该高优做的事剩下时间用来完成这些零散需求才更为合理。 
起初我觉得有些小需求可能就是一两行代码的事顺手一个分支就带上去了。但仔细想想这好像引发了蝴蝶效应。一件事仅仅完成是不够的该有的环节要有。 开发测试上线周知业务方验收。这样一个小流程走下来耗费的时间可不仅仅是一两行代码占用的时间可比。更何况可能还不止一个零散需求。时不时被打断自然就会导致原有工作安排非预期delay。 
在意识到这个问题后来自业务方的需求我会主动问一下优先级。如果不是特别紧急的事情将不会安排在当前周期的工作计划里。此外优先级判定上我会和业务方确认完使用场景后有自己的思考。对接次数多了发现有些紧急并不是真的紧急只是单纯的性子急。后来对于这种零散需求我会在项目管理平台写好描述和需求提出人方便后续沟通。 
这个记录还是很有意义的深感好处明显。 
可以起到一个备忘录的作用定期查看提醒自己有todo要处理业务方(需求提出人)可能因业务场景变更或有了其他解决方案不再需要后续支持原业务方(需求提出人)转岗或离职不再需要后续支持 
等到决定去做的时候如果发现时间间隔较久不要急着写代码先和业务方二次确认这个需求是否有必要继续做。试想如果耗时耗力做完最后邀请业务方验收时候对方又反馈用不到了。什么心情那肯定满脸黑人问号啊实惨如我曾有过这样的经历。深感前置确认真的很有必要这样能有效避免打黑工的场景。 
在有意识对工作优先级进行划分后原定对齐的工作进展基本都可以得到保障。等到工作周期结束进行总结的时候看到比较高的完成度我觉得这份成就感更高。 
ROI考量 
ROI 全称为 Return On Investment指的是投资回报率。我是在完成一个比较重要的功能模块迁移后才更加认识到这个东西的重要性。在做数据迁移的时候我写脚本进行的全量迁移。为了兼容新旧平台的格式差异我做了好几处的格式转换过程中还遇到好几个bad case需要手动处理总之并不是那么顺利。等到一切准备就绪我开始拉群周知用户并以表格形式逐个进行使用情况的回访。结果很尴尬实际使用的用户远低于历史存量用户。量少到我完全可以采用更快的手动迁移省去做格式转换和写脚本的时间。 
对于那些实际没人用的数据我后来又进行了删除处理。这一波操作下来真的投入产出比就不高了。算是吃一堑长一智吧在对一个功能模块进行迁移的时候前置工作除了搞清楚历史背景实现原理更应该确定实际使用人群。尤其是对于一个存在年头比我入职时间还久的功能更应该花时间在这个点上好好调研下。确定目标人群才好对症下药,这样才有可能是多人的狂欢而非仅仅是一个人单纯完成迁移工作的孤独玩耍。 
有心和无意真的是两种不同的感觉。 实际上在经历这个事情之前我对自己研发的模块也会有很多想法。有较长一段时间里我脑海中冒出来的小想法会连同某个分支功能带上去改动不大但是可能要思考的点会比较多。现在回想起来大多数属于ROI比较低的。而现在不论是业务方提出的需求还是我自己的小想法我都会优先考虑ROI的问题。时间是很宝贵的在有限时间内产生更高价值带来的成就感和自我认同感绝对是翻倍的。 
技术与业务关联 
在来字节前我很喜欢花大把的时间去钻研一些自己喜欢但可能实际未必会用到或者说使用场景比较局限的东西。比如我曾跟着视频教程鼓捣过一段时间的Angular 1.x 。当时觉得ng-xx这个指令写起来倍感新奇有种发现新大陆的小激动。也曾跟风学过一段时间的php,被其数量庞大的内置函数所震惊。等转回到业务上发现花费大量时间研究的东西和业务根本不沾边或者说没必要为了尝试而去强切技术栈。如此一来割裂就产生了。我曾好长一段时间困在这个技术和业务二选一的局面走不出来。 
等入职字节并工作了一段时间后我发现当业务形态开始变得复杂对技术的考验也会随之而来。善于运用技术恰到好处地解决业务痛点远远比单纯研究技术有意义。 自嗨终究是自嗨没有实际落地场景过一段时间就会忘记。如果还没想清楚技术服务于业务这个关键点那就会陷入【钻研技术-长久不用-遗忘-钻研技术】这个循环。保持技术热情是好事但是对于一个几乎没有业务落地场景的技术投入大把时间研究又有什么用呢知识是检索的当需要时自然会朝着这个方向靠近有具体落地场景才能更好地巩固。 
进一步让我体会到技术与业务是相辅相成的契机是对图数据库bytegraph的相关技术调研和最终的投入使用。业务场景需要我这边会涉及不同类型数据之间关联关系的管理(CRUD操作)。这个关联有层级的概念全部关联建立数据量已到千万级别。从设计角度和实践角度综合考量已经不是MySQL擅长的场景。细想一下层层关联铺开不就是一张图吗自然是图数据库存储更为合适。 
在我看完bytegraph相关文档并使用Gremlin图数据库语言写了几个符合自我预期的基础语句后突然又找回了曾经独自钻研技术的快乐。在使用过程中很自然的就和业务关联起来了。比如如何设计点和边如何提高关联图查询速度我曾写过一篇关于图数据库bytegraph介绍和基本使用的文档有同学在看过后就着某个具体业务场景下点该如何设计这个话题和我进行了语音交流最后我结合实际使用场景给出了有效结论被肯定的瞬间同样是成就感满满。此外在工作中对bytegraph的使用诉求还推动了bytegraph NodeJS SDK 的诞生。有幸成为第一个吃螃蟹的人真的很有纪念意义。 
寻求长期方案 
很多时候解决问题的方案都不止一个。绝大多数情况下选择临时解决方案是最快最省力的。当然也不排除某些极限情况下足够的临时趋近于长久。但临时终归是临时这意味着中后期规划可能会有变更从而导致现有的方案不再适用所以说寻求长期稳定的解决方案才是最终目的。尤其是当系统稳定性和切换成本冲突时更应攻坚克难去破局。近期完成了权限平台相关接口的升级替换由于历史包袱沉重旧的权限接口越来越不稳定已经影响平台侧权限的正常使用。在这种情况下真的是不得不换。好处还是很明显的虽然过程艰难但稳定性上确实得到了保障。 
相信字节内很多平台都是对权限系统强依赖的这意味着一旦权限系统服务出了问题其他的下游服务都会受牵连。这种权限问题感知相当明显最简单的一个例子为什么自己创建的东西在操作时提示没权限 
为了降低权限系统不可用对自身业务的影响我用redis对所有涉及权限读数据的地方做了缓存(如用户权限列表)。每次刷新页面会在获取用户信息的同时查询最新的权限信息当检测到返回结构非预期时则不再更新直接返回缓存数据。一般来说读权限场景比写权限场景更多有这样一层缓存来兜底还是很有价值的。 
此外为了避免自己创建的东西在操作时提示没权限的尴尬局面我进行了业务自身数据库优先权限系统接口查询的处理。这个很好理解写到自己数据库再读取往往比写到权限系统数据库再读取来的方便后者可能会有延迟。完成整体权限系统接口升级替换再结合redis缓存数据库优先权限系统接口读取这两个策略在业务侧整体权限稳定性上可以看作是一个长期稳定的方案了。 
直面问题 
对于一个开发来说出现问题在所难免。解决问题固然重要但是摆正心态也同样重要。工作中基本都是多人协作开发当收到线上报警消息时如果能确定和自己的某些操作有关应及时和相关同学说明避免其他人一同跟着排查。有句话听起来很矛盾但是语境还挺合适的“我知道你很慌但是先别慌。” 出现问题排查清楚后及时修复就好切莫讳疾忌医。 
此外有些问题隐藏比较深复现链路较为隐晦甚至可能除了开发自身其他人几乎不会有感知。我曾遇到过一个这样的case,代码写完过了一年也没有人反馈最后还是我自己在某次调试时候发现并修复的。随着编码经验的积累思维发散性也会更广不同阶段考虑的点自然也有差异。没必要过多纠结当时为什么没有考虑到这个场景更应该思量的是下次遇到类似情况如何避免。亡羊补牢为时未晚。 
问题排查 
问题排查可以说是一个开发人员必备的能力。个人感觉保证开发永远不出bug的方式就是不去开发。当然这并不现实。在字节这两年多的时间里我踩过好多的坑也出过事故逐渐摸索出了一些问题排查的经验。 
环境一致性校验 
工作中我这边常用到的是本地环境、测试环境(boe)生产预览环境(ppe)和正式生产环境(prod)。每个阶段都有可能会引发问题在开始排查问题前需要先确定自己的调试环境与引发问题的环境一致。乍一看可能感觉这句话是废话但是有过相关经验的人都知道这一条真的很重要。 
说来惭愧我有过本地调试半天发现死活不生效最后意识到看的是生产环境页面的尴尬经历真的是又气又无奈。 
优先保证这一点能少走很多弯路。 
格式一致性校验 
格式一致性校验指的是确认原始数据在有意格式处理或漏处理后是否和后续程序要接收的数据格式保持一致。 
一般来说编码粗心或者测试不够充分都有可能引发格式相关的问题。 
有意处理的场景 
const list[1,2,3]
// 有意处理
const formatList list.map(d({id:d
}))
// 省略一大段代码// 此处错误传入了list应使用formatList
getData(list)function getData(list){// do something...return xxx
}在前端操纵数据store也有可能存在类似的问题原始数据格式在某个组件里被修改导致另一个组件无法预期解析。 
漏处理的场景 
// sequelize findAll查询 限定只返回id属性
const ids  await modelA.findAll({attributes: [id],
});await modelB.findAll({where: {id: ids,//这里漏掉了对ids的处理 },
});如图使用了sequelize model方法中的findAll查询并限定只返回id属性且变量命名为ids。 
实际上返回的结构是对象数组{id:number}[]而不是数字数组number[]。 
请求响应一致性校验 
服务里定义的路由地址和前端请求时的地址对不上导致请求404。 
可能是因为单词拼写错误username or ursename? cornjob or cronjob? 或者cv后没有改全。 
前置条件确认 
这个偏向于涉及事件触发的场景要先满足其前置条件。 
下面列举几个有代表性的场景: 
如果想在群里接收某个机器人推送的消息需要先把机器人拉进群如果想在eventbus消费生产者产生的数据需要确保消费者是开启状态如果想使用sdk正常解析hive数据需要先申请表权限 
分区间排查 
这种方式适用于排查由程序代码引起但尚不确定具体代码位置的场景。 
我将其划分为三段式 
给怀疑会出问题的代码圈定一个区间非怀疑区间代码直接注释(前端更有效)或return掉(后端更有效)添加相关打印并重新运行程序观测输出和程序运行结果是否符合预期收缩区间重复12步骤直至发现问题 这里举一个我在使用bytegraph过程中亲身遇到的一个cpu暴涨的例子。 
最初bytegraph并不支持全图查询所以在获取某个点所在的整张关联图谱时拆分成了以下三个步骤 
查询某个点在整张图上的关联点遍历每个点查询入边和出边根据边的指向拼出完整的图谱 
伪代码如下 
function getGraph(vertex:Vertex){// 查询某个点在整张图上的关联点const nodesawait getNodes(vertex);console.log(get nodes)// return  分割区间一后续直接return// 遍历每个点查询入边和出边。const edgesawait getEdges(nodes)console.log(get edges)// return  分割区间二后续直接return // ... other
}async function getEdges(vertexs: Vertex[]) {let res: any  [];for (let i  0; i  vertexs.length; i) {const vertex  vertexs[i];// 根据点查询入边和出边const itemEdgesawait findEdge(vertex);res  [ ... res, ... itemEdges];}// return res 分割区间三不执行uniqWith返回res// 深度去重return uniqWith(res, isEqual);
}采用分区间排查问题的思路在关键节点添加打印日志触发调试。 
查看打印信息发现每次都是在获取所有边那里卡住。 
此时可以进到getEdges里边查看发现内部有一个去重操作。 
试着去掉这个过程再重试问题未复现。ok,定位问题。 针对这个问题我写了一个可复现的最小demo感兴趣的可自行尝试。 
结论是lodash的uniqWith和isEqual方法对大数据 量且重复率不高的数据进行深度去重会导致cpu暴涨。 
const { uniqWith, isEqual }  require(lodash);
const http  require(http);
http.createServer(async (req, res)  {const arr  [];for (let i  0; i  10000; i) {arr.push({n: Math.random() * 20000,m: Math.random() * 20000,});}console.log(uniqWith(arr, isEqual));res.end(hello world);}).listen(3000);请求溯源 
对于有提供Open API 给其他业务方使用或者说当前服务存在开放性接口(未设置权限)的情况下都有可能存在非预期调用其中最典型的是参数错误和session信息缺失。 
我有过类似经历某个已经线上稳定运行过一段时间的接口突然开始报错从错误信息来看是参数错误。随后我仔细查找了代码里的调用点只有可能在平台使用时触发。进一步查看确认是开放性接口没有权限管控。意识到应该是某个用户手动触发的,因为平台侧正常使用的请求参数符合预期。如果能定位到具体的人自然最好如果找不到人就需要在代码层面做一个参数校验如果传递过来的参数不符合预期直接return掉。类似的平台侧调用一定可以拿到session信息但是接连几次报错都是拿不到session导致的怀疑是非常规调用直接return。 
安全日志记录 
我负责的工作中涉及很多底层数据这些数据属性变更有可能会引发非预期的安全卡点。开启卡点的资产越多类似问题感知就会越明显。内部定时任务外部平台配置变更扫描任务人工变更都可以导致资产属性发生变化。因此究竟是哪一环节发生的变更显得尤为重要这能有效缩短问题排查链路。 
通过在每个变更节点添加一条安全日志记录可以有效辅助排查。此外还可以作为业务方溯源的一个途径。比如解答某个资产卡点什么时候开启的卡点开启同步自哪个部门 
审查数据库字段 
在某些业务场景里会在数据库中存储JSON 字符串此时需要对实际可能的JSON大小做一个预判之后再设定与之匹配的字段类型和数据大小。否则当实际长度超过数据库设定字段长度时JSON字符串就会被截断导致最后的解析环节出错。 
超时归因 
开发中遇到网络超时问题太常见了大多数情况下都可以通过添加重试机制延长timeout的方式解决。这里我想说的是一个比较特别的场景海外国内跨机房通信。 绝大多数海外和国内的通信都是存在区域隔离的调用不通表现上可能就是网络超时这种情况下重试也没用。解决途径也比较直观要么直接避免这种情况海外调海外国内调国内要么申请豁免。 
善用工具 
argos观测诊断平台 
在问题排查上观测诊断平台能起到有效的辅助作用。除了报错日志还可以看到所在服务psm集群机房。这些都是缩短问题排查链路的有效信息在服务实例比较多的情况下表现尤为明显。此外还可以配置报警规则命中后会有报警机器人进行推送可及时感知线上问题的发生。 
飞书机器人 
真心觉得飞书机器人是一个很好用的小东西。用它可以干很多事比如按时提醒该喝水了。在报警感知上也可以通过机器人搞点事情。例如在某个装饰器里对核心接口请求地址(如包含/core/)进行识别随后在catch代码块里捕获错误最后将error message or error stack 推送到指定的飞书群里这样团队其他成员也能及时感知。 
飞书表格 
个人精力有限不可能时时刻刻盯着报警信息其他什么都不干。对于一些看起来影响不大不用紧急修复的报警可以先通过飞书表格记录下来等有时间后当成待办事项逐一解决。亲测这种先收集后集中处理的方式比发现一个处理一个更省时间。 
技术思考 
规范 
很长一段时间里我对技术的理解是运用掌握的知识完成开发仅此而已。但事实上开发流程不应仅局限于开发环节还有其他很多有价值的事情需要关注,比如一些规范。团队协作和独立开发还是有明显区别的没有规矩不成方圆。既然是协作就要有达成一致的规范。 
我曾写过一篇关于lint的文章并在小组内和其他同事对齐共同商讨缩进风格哪些规则要开启哪些规则要禁用。项目编码风格统一的管控实现上依赖husky和lint-staged,在提交代码时进行lint检测不符合检测规则无法提交这样可以有效避免个人编码风格差异导致的格式change。 
在代码提交上由组内另一个同学制定了git工作流规范共同约定了不同功能分支如何命名分支间如何检出与合并commit 应该如何编写。这种规范形成文档后作用明显不论是日常开发还是线上部署都有了更清晰的操作流程。此外见名知意的commit message也更有助于查找具体功能点。试想一下如果简写一个fix,或fix err ,等过段时间再看哪里还记得到底fix了个什么 
类似的小组内还有需求迭代上线部署等相关规范这些规范站在开发的全局视角来看都是很有价值的。 
质量 
研发质量问题是一个非常值得重视的点开发完成并不意味着整个研发环节就结束了质量过关才是最后的收尾节点。简单来说上线后功能平稳运行无bug和性能问题这样才算是合格。虽说百密一疏但反复踩同样的坑或者踩不应该踩的坑就有些说不过去了。我印象比较深刻的踩坑点在于数据格式处理这个在上文报警排查处有提到不再赘述。还有一点对于跨越大版本的sdk升级一定要认真且足够详细的审查是否存在break change。有些break change是比较隐晦的乍一看可能察觉不到玄机切记想当然在项目代码中搜索看看总比自我回忆要可信的多。想要收获一批忠实用户研发质量一定是排位比较靠前的。 
稳定性 
这里特指研发的系统稳定性初期我这边涉及到的系统架构比较简单所有功能模块共用一个服务。这样好处是很多代码可以复用开发和上线也比较方便。祸福相依但是一旦服务崩溃除了影响自身业务正常使用还会朝着下游其他业务辐射。具体表现上来看一般是OEPN API不可用。为避免类似问题再发生我和小组内其他同事一起完成了服务架构升级将不同子模块拆分成不同的服务接口层面根据重要等级和业务类型并借助负载均衡能力分散至各自所在服务的不同集群。架构升级完成后即使某个子模块出现问题也不至于牵动整个服务崩盘。在此次架构升级中更深刻体会到了不同类型数据库在特定场景下的使用Redis,MySQL,MongoDB,bytegraph都有涉及收获颇多。 
文档先行 
对于一些偏复杂的模块先找个文档梳理一下逐步拆解清楚后再开始编码属于磨刀不误砍柴工。以前我的习惯是想一个大概然后投入开发写着写着发现之前想错了然后删掉代码再写新的这个过程可能会反复好几次。冷静下来好好想想真不如先写清楚文档更省时省力。实测让思维在文档上交锋远比在编辑器里打架轻松的多。 
沉淀总结 
我始终觉得有输入就应该有输出。不论是日常基础搬砖还是攻坚克难了某个业务痛点又或者加深了自己对某项技术的理解都应该有所展现。并不是说非要落笔成文但至少应该在一个属于自己的小天地里留些痕迹。如果实在懒得打字不妨试试拍照式记忆。亲测这个是科学中带有点玄学的方法。 
先找到想要记住的画面可以是控制台的数据打印也可以是bug调试截图又或者某段关键代码然后想一个主题与之进行关联重复思考几次。好的记住了。 
还是那句话有心和无意是不一样的。有心留意这份记忆就会更为深刻。当下次遇到类似场景近乎是条件反射的思维反应。比如我现在每次写删除语句一定会检查是否加上了where条件。这是有特殊意义的一段经历不堪回首。 
落地统计 
辛辛苦苦搬砖究竟产生了怎样的价值呢究竟有哪些人在用这同样是一个比较关键的点。我曾梳理了一个关于OPEN API 业务落地情况的表格里边记载了哪些业务方在用什么场景下会用对接人是谁。这样除了价值考量还可以在接口变更或下线时及时联系使用方避免造成非预期的影响。 
总结 
不知不觉洋洋洒洒写了几千字梦回毕业论文。曾觉得自己属于有所成长但是成长算不上快那种。写完这篇文章后再回首竟也方方面面很多点。不错经过一番努力终于从一棵小葱茁壮成长为一棵参天大葱了。 
回到最初的问题上时至今日我仍然觉得还有很多东西要学。距离把想学的都学到大概还有很长一段路要走。 
好在这一路不算孤独能和身边优秀的人一起做有挑战的事。 
前方的路仍然值得期待。 
完结撒花