拉巴力的纸皮箱


  • 首页

  • 标签

  • 归档

  • 关于

  • 搜索

职场生存小小总结

发表于 2020-07-19

不只局限于做需求本身

  1. 可以考虑把原本不合理的东西进行优化(当然要考虑分享和成本以及必要性);
  2. 基于基础通用的角度设计方案;
  3. 只做需求本身和想同时做更多的事,时间成本肯定不一样,如何解决两者之间的矛盾:
    1. 透明!把方案抛出来,由团队共同决策选择;
    2. 提供短期方案和长期优化方案。

及时反馈

  1. 发生故障及时反馈,不独自默默处理,避免由小故障变大故障;
  2. 工作进度和成果及时反馈,避免无用功;
  3. 其实还是要透明。
  4. 凡事有交代,件件有着落,事事有回音。
  5. 在不确定的场景下,尽量可以在多个假设中做多套方案(代价不会特别大),这样在对方案的时候,才不会导致得先重做才能继续讨论。

按流程做事

  1. 大公司的流程虽然繁琐,但很多流程其实有其存在的道理,毕竟来源于很多历史教训;保持敬畏,可以减少错误;
  2. 当然紧急情况除外,但是要保持透明,和坚持底线;

总结

  1. 尽量透明;
  2. 站在对方的角度考虑问题;
  3. 保持底线;

To Be Continued …

扩展

寻帮助 - 《认知觉醒》

  • 原来出现特殊情况时,飞行员的注意力会被巨大的危险所俘获,心智带宽降低,容易陷入单一视角,而此时,指挥员可以给飞行员提供有效的外部视角,帮助他们更好地处置特殊情况。
  • 同理,当我们对情绪问题或工作问题百思不得其解的时候,不要一个人闷头苦想,要学会主动寻求外部帮助,借助他人的多维视角来克服自己单一视角的局限。

监控告警要留意哪些点?

发表于 2020-07-16
  1. 告警信息要标明:机器、业务和环境等信息
  2. 制定合理的告警规则,梳理告警项,优化项目error日志等;避免大量无用的告警导致麻木
  3. 重要的特定业务可以单独配告警
  4. 是不是经常遇到临时打开某个开关,最后又忘记修改回来的事情?其实这种可以通过告警来解决,通过配置常规状态的固定值,定时检查告警即可。

扩展

  • 对 Java 意义重大的 7 个性能指标
  • 对业务系统的监控 No.118
  • 一些好用的开源监控工具汇总

使用“自旋”降低业务失败率

发表于 2020-07-16

业务场景描述

不说废话,直接描述以下两个业务场景

  1. 使用分布式锁控制并发
    请求----------> 服务A
           [1.尝试获取锁]
           [2.获取锁失败]
           [3.返回失败(提示操作太快)]


2. 强依赖服务超时或”请求处理中”

请求----------> 服务A
          [1.强依赖请求]----------> 服务B
                           [2.执行慢(网络抖动等原因)]
          [3.请求服务B超时]   
          [4.返回失败(提示服务繁忙)]

问题描述

  1. 场景1在获取锁失败时直接返回业务失败,可以考虑二次尝试;
  2. 场景2因为服务B执行慢,直接返回上层失败。
    • 服务A重试请求服务B时,可能请求已经执行完,很快就返回成功;或者服务B仍在执行该请求,返回“执行中”错误码。

解决方案

  1. 场景1可以使用“自旋”减少失败率:在抢锁失败时,当前线程“自旋”100m以内的随机数,再二次获取锁;
  2. 场景2同样可以使用“自旋”减少失败率:服务A在请求超时或收到“执行中”错误码时,同样自旋后再次重试,较大概率得到成功结果。

扩展

  • 自旋同样需要考虑“比例”问题,不然大量自旋会影响服务的吞吐量,具体可参考:由灾备工作中引发的对“比例”的思考

canal使用总结

发表于 2020-07-15

前言

本文只讲自己在使用canal之后的一些总结,关于canal的基础用法和介绍,不在这里赘述。

原理

  • canal的原理是基于mysql binlog技术,所以这里一定需要开启mysql的binlog写入功能,建议配置binlog模式为row

  • 原文:https://blog.csdn.net/liupeifeng3514/article/details/79687130

  • mysql的自带复制技术可分成三步:

    1. master将改变记录到二进制日志(binary log)中(这些记录叫做二进制日志事件,binary log events,可以通过show binlog events进行查看);
    2. slave将master的binary log events拷贝到它的中继日志(relay log),这里是I/O thread线程;
    3. slave重做中继日志中的事件,将改变反映它自己的数据,这里是SQL thread线程。
  • 基于canal&otter的复制技术和mysql复制类似,具有类比性:

    1. Canal对应于I/O thread,接收Master Binary Log;
    2. Otter对应于SQL thread,通过Canal获取Binary Log数据,执行同步插入数据库;
  • 两者的区别在于:

    1. otter目前嵌入式依赖canal,部署为同一个jvm,目前设计为不产生Relay Log,数据不落地;
    2. otter目前允许自定义同步逻辑,解决各类需求;
      a. ETL转化. 比如Slave上目标表的表名,字段名,字段类型不同,字段个数不同等.
      b. 异构数据库. 比如Slave可以是oracle或者其他类型的存储,nosql等.
      c. M-M部署,解决数据一致性问题
      d. 基于manager部署,方便监控同步状态和管理同步任务.
  • canal的工作原理:

    • canal模拟mysql slave的交互协议,伪装自己为mysql slave,向mysql master发送dump协议
    • mysql master收到dump请求,开始推送binary log给slave(也就是canal)
    • canal解析binary log对象(原始为byte流)

使用简述

  1. 对MySQL给canal-server授权
    • CREATE USER 'canal_server' IDENTIFIED BY 'canal_server';
    • GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal_server';
  2. canal使用ZK来做HA,因为涉及多个client时,消费计算的业务较为复杂的问题,所以目前在使用上只是单点部署,基本能满足需求。

使用总结

  1. 可以通过指定binlog文件和position来重放binlog数据:
  2. 如何查看MySQL位点:https://blog.csdn.net/c1052981766/article/details/80604083
    • show master status
    • show binary logs
    • show binlog events in 'binlog.000368' limit 10; //pos 就是位点, 重启 canal
    • 如何查到旧记录的位点?
      1. 由canal-server中meta.dat记录
      2. 通过 mysqlbinlog 命令分析 mysql-bin.xxx 文件内容,确定增量恢复到的时间点(运维协助)。
  3. 除了用pos点的办法进行恢复,也可以通过指定时间区间进行恢复,按时间恢复需要用mysqlbinlog命令读取binlog日志内容,找时间节点。
    • 使用mysqlbinlog工具进行基于位置或时间点的数据恢复
  4. 重跑。修改instance.properties文件设置重跑起始位点,删除meta.dat文件并重启。注意回放数据时视业务情况,可能要忽略第一个位点对应的数据。
  5. 重启之后batchId会变,所以不能使用batchId来做幂等处理
  6. 使用mysql主从同步和使用canal同步的区别? 同步数据为什么不直接用主从同步机制?
    1. mysql版本不一致,不支持主从同步
    2. 并不是自己的库,只需要同步几张表,运维不会做这种定制化
    3. 完成同步目标后,可方便拆卸和控制
  7. client重启如何对应原来的位点
    • canal client和server交互之间的身份标识,目前clientId写死为1001. (目前canal server上的一个instance只能有一个client消费,clientId的设计是为1个instance多client消费模式而预留的,暂时不需要理会)

canal和MySQL相关资料

  1. 下载:https://github.com/alibaba/canal/releases/download/canal-1.1.4/canal.deployer-1.1.4.tar.gz
  2. canal.instance.gtidon 是否启用mysql gtid的订阅模式
    • Mysql GTID 模式详解
  3. https://github.com/alibaba/canal/wiki/QuickStart
    https://github.com/alibaba/canal/wiki/AdminGuide
    https://github.com/alibaba/canal/wiki/ClientExample
  4. MySQL数据库的授权原则
  5. canal 高可用介绍
  6. canal和otter的高可靠性分析

如何接手一个旧项目

发表于 2020-07-14

改变心态,以前会抱怨业务乱,但是业务在发展过程中乱是很正常的,也要有人来主动整理,同时也在考验和锻炼整理人的能力。

  1. 梳理现有业务文档,进行归类(如果有的话?)
  2. 拉代码,服务本地跑起来,并跑通单元测试
  3. 梳理服务现有的所有监控平台和数据
  4. 梳理服务的依赖关系和超时时间
  5. 梳理服务的依赖组件(MQ,Redis,DB等)和部署情况
  6. 数据库表梳理
  7. 了解服务的部署和灾备架构(机器数,机房,流量入口)
  8. 代码核心逻辑阅读(核心流量接口)
  9. 了解服务的监控情况,常用配置和开关,相关告警配置通知人
  10. 待优化任务和遗留问题接手

扩展

  • 软件泥潭真体验
    • “定时炸弹”
附录1: 交接列表示例(设计与开发)

## 项目管理
    - 项目日程 
    - 会议记录
    - 体制图 
## 项目要件
    - 业务功能清单 
    - 业务流程图 
    - 需求变更记录 
    - 操作说明书/用户手册 
    - 常见问题一览 
## 界面设计
    - UE设计稿
    - 高保真画面设计稿
    - 需求变更一览
## 系统设计
    - 系统架构设计图
    - 部署架构图DB关联图(ER图)和DB详细设计
    - 系统间集成关系图
    - 对接系统一览表和对接系统接口清单
## 开发制作
    - 源代码
    - 代码运行说明
## 测试
    - 系统测试用例与系统测试报告书
    - 性能测试用例与性能测试报告书
    - 用户测试用例
    - 用户测试签字

附录2: 交接列表示例(运维相关)

## 上线相关
    - 上线判定表
    - 上线操作记录
    - 历次上线版本说明
    - 临时对应体制
## 基础设施
    - 硬件资源一览
    - 软件资源一览
    - 服务器/系统账号权限
    - 系统工具 - 付费/免费软件
## 运维体制
    - 运维工作一览表
    - 近半年运维工作应对流程
    - 运维体制
    - 故障对应流程
    - SLA(服务水平协议)
## DEVOPS(开发运维一体化)
    - CI/CD工具与使用
    - 监控工具
    - 备份管理
    - 代码库与分支管理
    - 数据库相关配置与策略

单元测试的工具和技巧

发表于 2020-07-14

提高单元测试覆盖率

  • 提高bean类的覆盖率

    1. pojo-tester
    2. lombok [lɒmˈbɒk]
    3. kotlin
  • 定义一些过滤规则

工具

  • mock神器:mockito
  • sql模拟测试:h2
  • 对测试进行参数化: JUnitParams
  • Awaitility: 一个小型的Java领域专用语言(DSL),用于对异步的操作进行同步。
    https://www.ctolib.com/topics-109441.html
  • WireMock: 用于模拟HTTP服务的工具 (对外部服务打桩)
  • Selenium:编写UI驱动的端到端测试

其他

  • 使用 集成测试 覆盖若干个完整的核心流程

  • 测试金字塔(深度好文)

业务重构实践总结

发表于 2020-07-14

业务重构似乎是必然且合理的过程。一个项目刚开始为了快速上线和试错,总会或多或少出现不合理的地方。当项目活下来了,为了寻求下一步的发展,服务的稳定和扩展性提升了优先级,自然就需要有人来重构了,这也是项目历史遗留的技术债务。

项目背景

  • 公司原有的业务几乎都是PHP编写的,并且处于一个很大的项目里维护和运行,可以说是大杂烩。随着业务发展和用户量的提升,渐渐得暴露了很多问题:
    1. PHP运行效率较低,业务未进行隔离,出问题时相互影响;
    2. PHP 过于灵活的语言特性,导致后期较高的后期维护成本;
    3. 基础框架和服务对PHP支持不足;
    4. 实现灾备比较困难。

重构语言选择

  • 我们选择Java作为开发语言进行重构
    1. 静态类型,多人协作开发和维护更加安全可靠;
    2. 内部基础组件的 Java 版生态比较完善;
    3. 学习成本低,且开发效率较 PHP 没有明显降低。

重构的工作是什么?

  1. 最简单直接的主要工作:就是充当PHP到Java语言的翻译,将PHP代码转化成Java代码,并保持逻辑不变,对上层业务是无感知的;
  2. 除此之外,对业务逻辑进行优化和简化,提高后续的可维护性和开发效率;对服务架构进行优化,提升服务的稳定性和可靠性。这是重构的主要意义;
  3. 重构完成之后,制定完善的验证和上线方案。

实施过程

  • Step 1. 用 Java 重构逻辑
    • 对外暴露的协议(HTTP 、RPC 接口定义和返回数据)与之前保持一致(保持协议一致很重要,之后迁移依赖方会更方便)
    • 尽量翻译而不是重构优化(至少第一版重构采取这样的策略)
  • Step 2. 验证新逻辑正确性
    • 当代码重构完成后,在将流量切换到新逻辑之前,我们会先验证新服务的正确性。
    • 考验对业务的熟悉程度和翻译的准确度(代码太久远,最熟悉的可能只有你自己)
    • 单元测试保证
  • Step 3. 灰度放量
    • 当一切验证通过之后,我们会开始按照百分比转发流量。
    • PHP入口对接底层新逻辑(验证逻辑正确性;之所以不直接从流量入口切换,是为了保证稳定性,在出现问题时可以迅速回滚。)
    • 外网代理切到新接口(保持协议一致,无需前端修改,切换彻底)

经验总结

  1. PHP代码太复杂,项目又不容易跑起来怎么办?
    • 默默睁大眼睛翻译还能怎样。。。
    • 如何验证是否翻译正确?
    • 找个在线PHP执行平台把复杂的代码片断跑起来,按照执行结果编写测试用例
  2. PHP代码太狗血太久远,万一翻译到半死发现这段逻辑根本已经没用了怎么办?
    • 把你的服务当nginx用,参数不符合条件的转发到旧服务,记录入参和返回结果,打日志和监控观察流量

最后

  1. 不能相信自己
  2. 不能相信别人
  3. 只能相信自己
  4. 乖乖覆盖测试用例

由灾备工作中引发的对“比例”的思考

发表于 2020-07-13

前言

  • 关于文章的题目我想了很久,暂时没想到合适的题目,姑且先这样吧
  • 之前在做灾备工作的时候,经常涉及服务的强弱依赖,超时时间,降级,以及业务折中方面的思考,下面将通过一些例子简要进行描述

场景描述

  • 从服务所依赖的重要程度上来区分有“强依赖”和“弱依赖”。

    1. 通常上,针对“弱依赖”我们会设置较小的超时时间(比如100ms以内),并且在失败率较高是进行降级;
    2. 而针对“强依赖”我们会设置一个较大的超时时间,当接口失败时返回“服务繁忙“,表示暂不可用(当然可以产品层面进行合适的引导)
  • 我们在灾备演练中,通过停其中一个机房,触发以下的问题

    1. 强依赖超时问题(停机房瞬间)
    2. 弱依赖多变长的问题(停机房期间)
  • 从而导致停机房瞬间接口失败率高,停机房期间接口时延上涨严重。由此引发我对“比例“问题的思考。

    1. 强依赖超时时间设长,可以减少失败率,但也有可能导致服务worker线程池被占满而拒绝请求。
    2. 弱依赖虽然每个都设置100ms以内,但是当依赖的服务超过10个时(真实业务真的有),可能导致总时长超过一秒(弱依赖慢但都没熔断,所以没降级;可异步并发的弱依赖的另说)

解决方案

  1. 对强依赖,可设置超时时间的比例(比如只有20%的请求可以超过1s,其他的控制在100ms以内),解决停机房瞬间,线程池占满拒绝请求的问题
  2. 对弱依赖,可控制多个弱依赖的总超时时间,解决停机房期间跨机房调用导致时延一直上涨的问题
    • 单个调用超时时间可分级设置比例;多个调用(可熔断)可聚合在一起设置总超时时间比例
    • 降级优先级排序,控制总时间。熔断线程池,控制比例超时。允许一定的时间超时。流量控制。物理隔离。

扩展

  1. 针对使用回调机制提高接口安全性所讲的,某些网络场景可以考虑针对大金额的请求才回调,减少RPC次数,这其实也是一个“比例”问题。

使用回调机制提高接口安全性

发表于 2020-07-12

前言

  • 接口回调在平常的业务开发中并不罕见。做过渠道推广业务,对接过广告平台接口的同学应该比较熟悉。
  • 在业务的作用主要是两个
    1. 异步通知
    2. 业务查询

本文主要讲的第二种,并通过一个业务例子来展开描述

业务描述

有以下业务场景,用户通过充值加金币:

充值请求----------> 服务A(充值服务)
             [1.验证用户已经支付成功]
             [2.发起加金币请求]------------>服务B(金币服务)
  • 服务B如何验证加金币操作的请求是合法,从而保证资金安全呢?

    • 有一种方法是通过分配业务对应的秘钥和服务端签名对比,验证请求的合法性
  • 通过秘钥和验签的方式无法避免以下问题

    1. 其他业务方误用秘钥
    2. 因误操作而发加币请求
    3. 业务方有bug一次充值多次加币

解决方案

  • 其实以上描述的业务有一个特点:每一次操作都有对应的前置操作,比如
    1. 充值加金币的前置操作是:充值支付成功
    2. 活动类加金币的前置操作是:用户达到活动要求

那么作为底层的金币服务,可以使用定义一种同样的回查机制,由各业务方实现查询逻辑,金币服务通过回查来检验请求的合法性,从而提高安全性。

  • 基础服务(金币服务)接收回调链接地址有两种方式:
  1. 通过后台配置,每个业务配置对应回调链接和相应的参数 (相比可能比较安全可控,但不灵活)
  2. 通过参数获取,如callback参数(灵活,但是要设计完善的校验机制保证安全)
    • 可结合公司的基础服务实现,如可以通过服务发现获取服务对应的地址,只需传uri即可
充值请求----------> 服务A(充值服务)
             [1.验证用户已经支付成功]
             [2.发起加金币请求]------------>服务B(金币服务)
                                <----------[3.回调验证] [4.加金币] < pre>

  • 统一回调接口返回格式
回调接口返回格式
{
    "code": 0,
    "message": "success"
}
0 代表成功;其他:失败

回调用于查询业务订单是否存在,检验是否非法订单
一般实现逻辑建议业务方直接查主库
  • 回调无法绝对解决以上提出的问题,只能说一定程度上提升接口的安全性级别。

扩展

  • 使用非对称加密,是不是可以替换回调验证?(客户端使用公钥解密,服务端使用私钥加密,前提是私钥不泄漏)

分布式限流简单总结

发表于 2020-07-11

基本描述

  • 限流分类

    1. 单机限流
    2. 分布式限流
  • 限流的指标

    • 每秒处理的事务数 (TPS),每秒请求数 (hits per second)
    • 使用 hits per second 作为限流指标
  • 限流规则包含三个部分:时间粒度,接口粒度,最大限流值。

  • 选择单机限流还是分布式限流

    1. 单机限流一般针对服务负载,防止突发流量压垮服务器
    2. 分布式限流一般针对在业务侧做细粒度限流

限流算法

  1. 固定时间窗口
    • 首先需要选定一个时间起点,之后每次接口请求到来都累加计数器,如果在当前时间窗口内,根据限流规则(比如每秒钟最大允许 100 次接口请求),累加访问次数超过限流值,则限流熔断拒绝接口请求。当进入下一个时间窗口之后,计数器清零重新计数。
    • 限流策略过于粗略,无法应对两个时间窗口临界时间内的突发流量。
  2. 滑动时间窗口
    • 滑动时间窗口限流算法可以保证任意时间窗口内接口请求次数都不会超过最大限流值,但是仍然不能防止在细时间粒度上面访问过于集中的问题
  3. 令牌桶算法(Token Bucket)
    • 接口限制 t 秒内最大访问次数为 n,则每隔 t/n 秒会放一个 token 到桶中;桶中最多可以存放 b 个 token
    • 令牌桶大小为 b,所以是可以应对突发流量的
    • 没有提前预热的令牌桶,如果做否决式限流,会导致误杀很多请求
  4. 漏桶算法(Leaky Bucket)
    • 漏桶算法稍微不同与令牌桶算法的一点是:对于取令牌的频率也有限制,要按照 t/n 固定的速度来取令牌,所以可以看出漏桶算法对流量的整形效果更加好,流量更加平滑,任何突发流量都会被限流。

分布式限流

  1. 采用redis+lua的方案做分布式限流 (参考业界实现方案即可,需注意原子性)
  2. 由于使用中心存储计数的方式性能较差,在业务允许的情况下可以考虑将限制的数量分摊到每个服务(服务数通过服务发现接口获取),间接使用单机限流提升性能。

扩展

  1. Google的Guava包中的RateLimiter
    • 令牌桶算法的解决方案,假设1S需要限流5次;也就是1S会往桶里面方5个Token;如果在这1S内桶满了则不再加请求,如果空了则表示达到限制的上线了,会阻塞,直到有数据加入再次处理。
  2. [ratelimiter4j] (https://github.com/wangzheng0822/ratelimiter4j)
    • 分布式限流算法的性能瓶颈主要在中心计数器 Redis,从我们开源的 ratelimiter4j 压测数据来看,在没有做 Redis sharding 的情况下,基于单实例 Redis 的分布式限流算法的性能要远远低于基于内存的单机限流算法,基于我们的压测环境,单机限流算法可以达到 200 万 TPS,而分布式限流算法只能做到 5 万 TPS。所以,在应用分布式限流算法时,一定要考量限流算法的性能是否满足应用场景,如果微服务接口的 TPS 已经超过了限流框架本身的 TPS,则限流功能会成为性能瓶颈影响接口本身的性能。

Reference

  1. 微服务接口限流的设计与思考
<1…13141516>

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