拉巴力的纸皮箱


  • 首页

  • 标签

  • 归档

  • 关于

  • 搜索

谈谈接口调用中的序列化协议

发表于 2024-07-24

接口调用存在于内部服务之间,也存在于客户端和服务端之间
既然涉及接口调用,必然就涉及到数据的序列化

RPC

  • 什么是 RPC?
    • RPC是”远程过程调用”(Remote Procedure Call)的缩写。这是一种计算机通信协议,允许程序调用另一个地址空间(通常是在其它计算机上)的子程序或过程,而程序员就像调用本地程序一样,无需额外地为这个交互作用编程。
  • 其实 RPC 不仅可以存在于内部服务之间,前端和服务端之间的交互也可以用 RPC

序列化

  • 一般情况下,我们习惯于使用 idl 文件定义数据格式(比如 thrift 或 Protocol Buffer),然后使用 RPC(比如 gPRC 等)用于服务之间的调用;而使用 JSON 和 HTTP 作为前端和服务端的交互方式
  • 这里提一点,回看 RPC 的定义,即使使用 JSON 作为序列化协议,也可以使用 RPC 作为接口调用,具体要看 RPC 框架的实现。

使用 RPC 和 IDL文件 的好处

  • 本文只关注序列化方面相关的好处
  • 举个例子,接口调用简单使用 HTTP + JSON;如果后续加字段,而调用方使用不规范,复用接口调用的返回对象用于业务的其他逻辑,而字段名刚好相同,则会冲突从而可能导致逻辑异常,这种低级错误无论是客户端还是服务端都经常发生
  • 理论上每个接口都应该有单独的接口响应类,这样才不会在后续加字段时产生语义冲突,虽然写起来麻烦,但这是最规范最严谨的做法,所以自动化代码工具很重要
  • 其中一种解决方式:使用 IDL 文件定义数据格式,并且通过 RPC 框架限制调用的返回对象不被随意设置,从而解决这种低级又容易忽视的错误
  • 对 RPC 框架的要求:
    1. 限制返回对象不被修改
    2. 排查工具完善,支持将数据转成可阅读格式(比如使用Protobuf二进制传输,将很难排查)
    3. 适配多种语言的客户端 sdk(包括客户端)

高效会议的重要性

发表于 2024-07-18

生命是以时间为单位的,浪费别人的时间等于谋财害命;浪费自己的时间,等于慢性自杀。 - 鲁迅

  • 平常在工作中,有些同事在没有提前发会议主题和相关资料前,突然就拉一群人一起开会,方便了自己,却浪费了别人的时间。
  • 比如一些不专业的产品为了节省自己的时间,也不做充分的会前准备,拉一堆开发给自己做需求完善员,对别人的时间极其不尊重。

高效会议

  1. 能线下小部分人沟通清楚的,不必拉齐人一起开会
  2. 会前发相关资料,让潜在参与者提前了解
  3. 看完资料,可以发相关疑问,有些内容可以提前单独沟通
  4. 真正开会前发会议主题和目标
  5. 会有要有会议纪要,结论,执行人等

Reference

  • 去TMD低效会议
最近和几个大佬朋友聊天,大家不约而同的在吐槽公司开会效率低下的问题。



一提到开会,想必大家都会浮现各种场景,甚至有人可能要开始骂娘了吧?不断延长的会议时间,沉默的参会成员,语无伦次的会议组织者,不断跑偏的主题,一不小心一天就全在开会了。



what? 不仅一天全开会了,还没有任何有用的结论?



作为一个开会无数的互联网老鸟,针对高效开会,我有以下六点建议:



1.能不开会就不要开会



很多会其实根本没有开的必要,不少人平时不沟通,沟通全靠会议!



一旦开会出现几个人相互扯皮,你一句我一句的情况,基本可以断定这几个人平时就不怎么沟通,或者沟通有问题。



更为可怕的是,有些管理者,不开会就不知道自己要做什么!曾经我招过一个产品leader,热衷于组织各种会议,基本可以在会议室呆着不用出来的那种,但关键事情的推进都没落地。



具体注意点如下:

会议组织者还没完全想清楚之前,坚决不要开会

能定点沟通清楚的问题,坚决不要开会

能召集3,4个人花5分钟说清楚的事情,坚决不要开会



2.开小会,越小越好



会议人数尽量控制在7个人以内,人越多会议效率越低。有时候喊很多人开会,无非是出于显示你的权威或者逃避责任,潜台词是:反正大家都参加会议了,出了问题一起扛。



不知道大家注意过一个现象没有,不少微信群人少的时候,大家都很活跃,如果进来一些不说话的「潜水者」,大家也就慢慢都不说话了。



开会也是一个道理!如果有不太相关的人进来,他肯定会不怎么参与讨论,而其他人的思维活跃度都会受影响。



那么有人会问:如果会议就是需要很多人参与怎么办?可以将一个大的会议拆解成几个议题,分议题开小会。然后再把小会的决策者喊一起开会。



乔布斯就特别重视控制会议人数,在「疯狂的简洁」一书中记载了乔布斯一个小故事:「在一次和广告公司的例行会议上,乔布斯突然发现了一位名叫Lorrie的陌生的参会者,乔布斯指着Lorrie问到:“请问您是哪位?”,Lorrie解释自己需要听这个会议,但最后乔布斯还是礼貌的请Lorrie小姐离开了:“我不觉得你有必要参加这个会议,Lorrie小姐,谢谢。”」



我猜,Lorrie小姐估计也是暗爽的吧,毕竟她估计也是莫名其妙被拉到这个会议。



3.开会前做好充分准备



会议组织者需要把会议主题、会议背景、提前同步给所有参会人员,甚至需要提前进行答疑及相关沟通工作。



比如产品的技术评审会,不少产品经理可能没有提前发出原型做沟通。结果在会议上大家需要先理解原型,而不是上来直奔主题。



充分的准备工作,会让会议更加高效,大家坐下来开会的时候已经清楚所有信息,开门见山展开思辨,而不是一屋子人毫无准备甚至满头雾水。



4.能站着开,就别坐着开



比尔盖茨说过一句话,“当你能站着开会,就不要坐下来”。



会议室的椅子也不能用太舒服的那种,为什么?当人们坐在一个舒适且舒服的椅子上,大脑更多的时候是在放空状态,注意力无法被集中。



坐在那里舒舒服服的探讨,效率远低于站着快速解决。当站着开会,不再有人坐在办公椅上犯困想打瞌睡,不再有人玩手机看电脑。时间大大的减少,不再沉默寡言,而是速战速决。



每日的项目进度之类,特别适合10-20分钟的站会形式。站着开会,大家都不想浪费时间,自然就更能保证高节奏,高效率。



5.盯紧走神的人



开会的过程中,一般会不断有人走神。这点很考验会议组织者,如果发现有人走神,需要盯着他,看他眼睛。如果还不行,就需要提醒下拉回他的注意力。



开会应该是个激烈思辨的过程,走神是要被杜绝的。如果团队中发现开会经常走神的人,那要小心了。他要么是对业务不太了解,说不上话,要么是心灰意冷已经不想说话。「要引起重视了」



6.会后,有结论、有责任



开会过程要对关键结论做会议纪要,在会议结束后,尽早发出会议记录。会议记录可能会有遗漏和错误的地方,尽早将会议记录发给所有相关人,可以让其它参会者检查,提出问题或作出补充。



会议结束后,将任务指派给每一位责任人。这也往往是会议组织者的工作,不仅仅做出决定,更要负责落实决定的执行。如果这一步做不到位,那基本可以说这个会白开了。



最后总结几句:



开会是个大学问,千万不要小看提升的那点效率。10个人开会,浪费2小时,就相当于浪费了一个人一天的生命和一个人的工资。



工作的目标是为了创造价值,而不是摧毁价值。低效会议无疑是摧毁价值的重要帮凶!!!



浪费时间等于谋财害命,高效开会是每个会议组织者必须学会的技能。

看似理科实则文科,看似文科实则理科

发表于 2024-07-17
  • 前段时间在看Rust,所有权部分涉及到很多规则,让我重新反思了传统文科和理科的区别

  • 学习一门编程语言,大家普遍会认为这是一门理科

  • 但像Rust语言的所有权规则,让我想到了英语中的各种时态规则

  • 大家基本会认为英语是文科

  • 但我其实认为Rust中的规则和英语中的规则,其实没啥区别,都是逻辑规则,比较烧脑。

  • 所以无论是学习文科还是学习理科,有些地方,都是需要良好的逻辑能力。

  • 逻辑能力好的人,基本学习文科理科都不会有太大差异。都是聪明的人。

  • 当然中理科中偏理的部分,文科中偏文的部分,这些可能有差异则另说。

基于Saturn定时任务的客户端工具设计

发表于 2024-05-30
  • 基于之前写的这篇谈谈定时任务的原理和应用继续聊。

  • 由于业务进程不是Java编写的,无法使用Saturn提供的Java定时任务,只能使用Shell定时任务

  • 这里选择用UDS实现Shell进程和服务进程之间的通信,实现和使用Java定时任务一样的效果

  • 在上次的实现中,只考虑了如何触发定时任务执行,并没有考虑如何停止

实现对执行中的任务进行停止

  • 通过测试可以知道,当在saturn控制台点击终止任务时,会对shell进程发出terminated信号

  • 如何不是通过saturn控制台触发,直接终端执行shell命令时,操作Crtl+C时,会对Shell进程发出interrupt信号

  • 基于上述研究,提供以下方式停止业务服务的运行中的定时任务

    1. 通过saturn控制台点击终止任务,发terminated信号
    2. 通过操作Crtl+C,发interrupt信号
    3. 通过执行shell命令,指定任务名称和-stop选项,直接发出终止任务请求
  • 具体实现参考:saturncli

扩展

  • 经过测试,saturn的调度不会因为设置的频率太快导致并发运行,只会执行完一个任务再执行下一个

也谈谈feed流的设计

发表于 2024-05-28

几年前也做过feed流相关的服务,虽然网上相关的文章很多,这里也做个人的简要记录和总结
方案并非原创,当时是参考其他业务团队的设计

  • 需求背景:做一个用户动态模块,可以发动态,可以关注别人动态,有推荐列表,有消息提醒(其实就是微博的基本功能)

实现逻辑流程

  • 整个feed流设计都是强依赖Redis来实现的,以下列出key设计和对应的功能模块

    • 粉丝的feed流 - 【feed_list_$userId】
      • member-$id_$userId_$type (score-addtime)
    • 粉丝的feed流为空标志 - 【【feed_list_empty_$userId】】- 防止每次请求都穿透到DB
    • 用户的活跃粉丝列表 - 【active_fans_list_$userId】
    • 用户的活跃属性 - 【active_user_status_${userId}】
    • 动态详情 - 【cache.user.dynamic_${dynamicId}】 - MGet获取
  • 数据库分表设计

    • 用户动态表 - 按用户id分库
    • 评论表 - 按动态id分库(根据业务,一般都是单个动态的所有评论)
    • 点赞表 - 按用户id分库(根据业务,一般都是动态列表中展示当前用户的点赞状态)
  • feed流使用sortset,score是addtime

  • 推模式,通过维护用户的活跃粉丝列表(有数量限制),推到粉丝的feed流

  • 拉模式,通过请求第一页或其他接口进行预加载,查询粉丝的关注人和对应动态(有数量限制),构建粉丝自己feed流

01.feed流 构建 整体流程

  • 用户发动态、删动态、关注和取关事件,都会对feed流有相应操作
  • 通过发布事件的设计进行业务逻辑解耦

用户发动态(推模式)

  1. 用户发布动态(写入DB)
  2. 查询用户的活跃粉丝列表(Redis)
  3. 写入粉丝的Feed流(Redis)
  4. 写入消息表

粉丝拉关注列表(拉模式)(请求第一页或其他接口,预加载)

  1. 获取feed流(Redis)
  2. 获取关注用户列表(RPC)(限制2000个粉丝)
  3. 获取关注人的动态数据(DB)(限制没人100条动态)
  4. 写入个人的Feed流(Redis)
  5. 成为活跃用户(Redis)

02.动态消息交互

  • 消息表 和 消息读取时间表(用于控制用户红点提示和读取消息列表的范围)
  • 消息表保存7天定时清除

03.feed流预加载策略(pull模式)

  • 。。。。
  • 将用户加入为活跃粉丝

04.动态新增、删除(push模式),活跃粉丝处理

  • 用户的活跃粉丝列表逻辑
    1. 拉取过feed流或发布过动态等操作的用户,都会设置成有活跃粉丝属性的用户
    2. RPC获取用户的粉丝列表,过滤掉非活跃用户,保存到用户的活跃粉丝列表
    3. 关注和取关事件,也更新活跃粉丝列表

05.关注、取关 事件 对feed流的处理

06.获取feed流

  • 有了前面的推拉模式,用户直接从redis获取feed流即可

07.动态类型版本设计

  • 随着动态类型的新增,客户端旧版本不支持新类型动态,需要过滤;

  • 目前feed流是存在redis的,直接过滤可能会导致旧版本数据为空,可以通过多版本动态来区分

  • 实现描述

    1. 用户获取feed流,根据客户端的版本号等信息,判断出客户端支持的feed版本,并构建对应的feed流
    2. 用户发布动态,构建用户存在的活跃动态版本的feed流(为了用户的新旧版本都兼容)

扩展

  • 很久之前做的一次实验
    • 取消了微信黑名单,没看到之前的人的朋友圈。说明微信朋友圈也是feed持久化的方式实现
    • 微博也是这样,之前关注过,取关后再恢复,内容也能恢复,说明feed表只是逻辑删(应该是存数据库表了)
  • 微博feed实现(猜测)
    1. 为每个用户保存他所有关注的用户的feed表数据
    2. 假如取关,则重写关注能恢复数据(逻辑删除)
    3. 但如果之前没关注的用户,则他之前的动态不会出现在feed表中

聊聊配置中心

发表于 2024-05-09

第一次接触配置中心,是快十年前了,那时的配置中心还没像现在那么多成熟的开源项目,很多公司都需要结合自身业务自研。

使用配置中心的好处

  1. 快速发布变更配置,实时生效
  2. 对于不复杂的系统或业务配置,可以避免写繁琐的后台,但又具备快速变更的功能
  3. 读取配置性能高,一般配置后,都是以内存变量的方式常驻在服务中,可以说读取配置几乎零成本

配置中心的核心-推送原理

这里主要讲一下个人实际接触的三种原理,其实从宏观本质上看,三种方案原理是一致的

  1. 第一家公司的配置中心自研方案,借助ZooKeeper的临时节点变更事件,从而实现实时推送

  1. 第二家公司的服务治理中心的实现方案,使用Netty使服务中心和各服务之间建立TCP长链接,从而实现配置推送

  2. 第三家公司使用Apollo开源项目作为配置中心,原理是使用HTTP的Long polling方案

  • 从原理的本质上看,三者都是通过保持TCP连接不断开,从而复用这个通道进行数据推送
  • 除了推送的实现很重要之外,后台管理系统的设计和客户端的SDK也很重要。因为光能实时推送是不够的,要让接入配置中心方便,以及能方便管理和发布配置也是很关键的。在这一点上,Apollo这个开源项目是很不错的。

业务开发中的术与道

发表于 2024-05-07

从事IT行业若干年,有时回首,不禁感叹,很多技术方案和日常生活息息相关,可以相互借鉴。

以下只是草稿,以后有缘再整理

以下每项并不是独立,而是相互重叠的

  • 从日常的工作中总结常用的套路,勤记录,提升后续的效率
  • 多开发一些能提高效率的小工具,逐步优化,并可以给其他同事使用
  • 抽时间回顾和总结
  • 对用户的业务数据感兴趣,并思考哪些技术优化值得做

技术追求

  • 提升自身和团队的水平、服务架构合理性和性能的考虑、提高开发效率和体验

定时任务使用UDS作为触发运行的通知方式

  • 基于架构的完全性考虑:不暴露非本机触发的机会
  • 基于架构的合理性考虑:复用已有服务的逻辑和资源,无需新开进程
  • 具体实现看:谈谈定时任务的原理和应用 - 使用域信号通知通知服务

代码质量

使用代码扫描工具

  • 比如Java的Sonar、Golang的golint等

单元测试

  • 及时发现新代码逻辑问题,以及发现避免误改动等
  • 提交代码检查并及时阻断

代码Review

  • 上面的事项自动化,比如和gitlab pipeline结合,在开发阶段就及时修正;
  • 后续代码review可以避免审核低级问题,提高效率

开发效率

脚本工具

  • 开发快速生成curl脚本的工具,便于快速定位问题,以及反馈时提供复现实例;

代码复用

通用组件抽象

  • apollo灰度工具
  • bi数据上报组件
  • 消息通知组件

业务组件抽象

  • 目前都流行微服务,但有很多上游的逻辑是可以复用的,可以抽象出来,通过代码库的方式复用

服务稳定

灰度工具

  • 为保证改动一旦出问题,降低影响,开发基于配置中心的灰度工具,可以根据业务属性进行灰度

性能优化

  • 使用wrk、pprof等工具分析优化

文档化

常规事情文档化

复杂事情文档化

业务开发中使用BI的海量数据处理能力

发表于 2024-04-25
  • BI的数据统计跑数结果一般是T+1生成的
  1. 实时性要求不高的功能,用于推荐等其他业务功能,可用于统计和监控等
  2. 对实时性要求高的功能,做后置的监控,及早发现逻辑错误

从Java转Go的个人体会

发表于 2024-04-07

这里仅从一个业务开发的角度谈谈体会,格局较低,仅做个人记录
编程语言是程序员接触和使用最多的工具
后端技术基础的深度是快速学习和适应新语言的关键

  • 基于对工作中接触过的几种编程语言及其部署架构的看法继续聊。

  • 截至目前,职业生涯五分之四的时间都是在使用Java。出于个人意愿和兴趣,以及行业的发展形势,也系统的自学了Go语言,并在个人的小项目实践。在一次意外的组织变动中,我转岗到了新团队,并开始使用Go,由于之前自己的自学基础,基本是无缝切换到新语言。可能是由于团队人员大部分是从Lua或PHP开始转Go的,在那里甚至感觉自己稍稍领先。

  • Go抽象业务比Java麻烦,代码不美观,但是它原生高并发,而且微服务下很多时候就一个后端服务,业务足够小,不需要复杂的设计模式等,并不需要像以前写大型Java应用这样做非常多的抽象,还能打二进制包,甚至还能保证一个团队所有人代码都是相同风格

  • 以下讲一些个人看法,比较乱甚至不正确,仅做个人记录

    1. 小型项目省内存
    2. 写命令行程序方便简单
    3. 现在绝大部分功能都有相应的官方库和开源库
    4. 运行无需环境依赖,直接打包成二进制可执行程序(Java现在也可以了,可能大部分业务场景下体积还较大)
    5. 支持交叉编译,不要特定平台
    6. 打包体积小(根据代码实际使用情况打包-这点是我一直苦苦寻找的)
      • Go语言中有未用代码消除和可执行文件瘦身机制。只有在程序执行路径上被调用的函数才会进入最终的可执行文件,未被调用的函数会被消除
      • Go未用代码消除与可执行文件瘦身
  • 为什么现在大多数人都会认为Java启动慢占内存呢?

    • 首先不是Java自身的原因,而是跟实际使用场景有关
    • 使用Java大多数用来做业务开发,也习惯引入很多依赖库,首当其冲就是SpringBoot等框架
    • 多数都是spring相关类、proxy/cglib,以及各种bean配置
    • 而且很多类都是在启动的时候初始化的
  • 微服务下的编程语言

    • 在K8S流行之前,Java通常是使用SpringCloud
    • 其实微服务相关技术,在K8S已经实现了
    • 无论是使用Go还是Java,目前都应该向K8S靠近
    • 还有一点,K8S本身就是用Go实现的

扩展

  • 体验Graalvm+SpringBoot+Java21构建原生程序

  • 在内存利用效率上,Go语言确实比Java做得更好,在4个不同的角度来总结

    • Golang与Java全方位对比总结
    1. Java的JIT策略比Golang的AOT策略
      • Java在运行时相比Golang多占用了一些内存。原因在于:
        • Java运行态中包含了一个完整的解释器、一个JIT编译期以及一个垃圾回收器,这会显著地增加内存。
        • Golang语言直接编译到机器码,运行态只包含机器码和一个垃圾回收器。
      • 因此Golang的运行态相对消耗内存较少。
    2. 内存分配和垃圾回收器
      • Java确实在起步占用上偏多,毕竟jvm需要更多内存做jit,默认的gc算法对内存要求偏高,但这不能代表后续占用仍然线性增长。如果目标是启动成百上千个内存需求较少的进程,那Java确实不擅长。
    3. 并发
      • 协程模型比线程模型更加节省内存。
    4. 反射
      • Golang的反射更加简单,导致框架的内存消耗Golang程序比Java程序优秀。主要是因为: Java的框架实现中大量使用反射,并使用hashmap缓存信息,这2个都是极度消耗内存的行为。 Golang的框架中也使用reflect、map。但是Golang是面向interface和值类型的,这导致Golang的反射模型要比Java的反射模型简单非常多,反射过程要产生的对象数量也少非常多。
  • 为什么一些已经选择 Java 的公司,现在又开始考虑使用 Go?

    • 为什么要用Go重写Dubbo?
    • 相较于 Java,Go 在启动速度、编译速度、内存使用和高并发(如协程)方面都有明显优势。所以,那些已经采用 Java 的公司现在也在考虑引入 Go。但要注意的是,目前这样的公司仍然是少数。另外,一些公司并没有严格规定技术栈的选择,因此新成立的部门或新业务团队可以自由选择,而他们可能更倾向于选择 Go 作为开发语言。
    • 小结: 总的来说,无论是选择 Java 还是 Go,都是有其合理性的。一家公司同时选择这两种语言也同样合理。尽管这样的公司占比不大,但 Java 与 Go 之间的交流需求仍然存在。

记录限免业务的经验

发表于 2024-04-06
  • 由于业务需要,比如歌曲,我们需要对部分歌曲开放限免的功能,而且需要限制开放限免的场景。比如这首歌在场景A免费,在场景B收费。

  • 先说正确的做法,下发另外一个字段比如free_token,通过这个字段判断业务来源,和判断是否可以免费。free_token是随着对应的场景查询时下发的,是变化的。即同一个歌曲的free_token不是唯一的。

  • 现在有另外一种情况,想对现有的业务做限免(前端业务是不认free_token的)。这时可以把歌曲id使用free_token的值来下发,后续通过歌曲id来判断是否符合限免。

    • 这样做有个前提,就是前端业务没有使用歌曲id来做唯一性的业务(因为这是歌曲id是变化的),比如收藏。所以使用这个方案,要充分测试,而且可能还有其他不兼容的场景。
<1…345…16>

160 日志
176 标签
RSS
© 2025 Kingson Wu
由 Hexo 强力驱动
|
主题 — NexT.Pisces v5.1.4