0%

财务管理

1.财报

财报的全称是财务报告,包括财务报表及附注说明,不仅仅是财务情况的披露,更能体现一个公司的整体经营情况及行业价值链网

  • 例如,财报中会披露公司的Top5业务、Top5客户及供应商等信息,我们就可以看出这个公司的主要业务是什么、收入来源是什么、主要客户及主要供应商是谁,进而分析该公司所在行业的整体经营环境及关联关系。
  • 报表分为凭证、账簿(总账、明细账、科目余额表)、财务三表(资产负债表、现金流量表、利润表)
    • 凭证:业务凭证、损益凭证
    • 帐薄:总帐、明细账
    • 会计报表:资产负债表、利润表、现金流表。

资产负债表

  • 它反映在特定时间点上公司的财务财富状况(类似数据库中的快照)。它告诉你有多少钱可以用于投资,有多少债务要偿还,以及你作为所有者,你的资产值多少钱。查看资产负债表了解更多。它分为3个部分:资产,负债和所有者权益
  • 资产是在当前有一定价值或在未来能提供价值的东西。一般分为流动资产和固定资产两类:
    • 流动资产 包括现金以及其他打算在一年内使用或出售的东西,比如账上的现金或货架上的商品
    • 固定资产 是可以使用一年以上的实物资产,比如车辆,机器或者房产
  • 负债是你的企业欠别人或别的企业的钱,分为:
    • 流动负债 是你在一年内必须付清的债务,比如税费、采购账单、员工薪酬。它也可以包括一年内到期需支付的贷款和租金
    • 长期负债 指一年期以上的债务,它可以包括未偿还的剩余贷款或需支付的租金
    • 资产负债表是的所有者权益表示的是扣除公司负债后归属于公司股东的那部分资产以及其使用去向。
  • 所有者权益由以下构成:
    • 普通股/股本:股份制企业的所有者持有这家公司的股份。非股份制的小微企业所有者可能不持有股票,但享有所有权。在资产负债表上,这一项表示公司的所有者能够主张权利的资金。
    • 未分配利润:当公司赚钱了,它可以把钱分配给股东,也可以留存这些钱 (即保留这些利润)用来增值资产或偿还负债。当公司保留利润的时候,这部分钱应该列示在资产负债表上的所有者权益项下。

损益表

  • 损益表则让你洞察公司在一段时间里的业绩表现类似一定周期内的数据指标);因此可以用损益表来判断在某一期间是盈利或是亏损,也可以构建覆盖不同时段的损益表,比如一个月,一个季度,一年,或者几年。它告诉你公司有哪些开销,为什么赚钱或者亏损。查看损益表篇了解更多。
  • 损益表的公式是:利润(也叫净利润)= 利得(也叫收入)- 费用

现金流量表

  • 现金流是用来度量流进和流出公司的钱。现金流量表将现金流分为三个部分:经营性,投资性和筹资性。它呈现公司账上现金流入和流出的时间点,可以帮助你量入为出。查看现金流量表篇了解更多。
    • 现金流入,即进入你公司的现金,比如销售收入,收到的贷款和投资款等。
    • 现金流出,即离开你公司的现金,比如采购支出,归还贷款,支付工资等。
  • 可以用现金流公式来计算未来某一时点上(或过去某一时点上)
    • 当期现金余额 = 期初现金余额 + 现金流入 – 现金流出
  • 存在透支的情况,就是余额为负数。比如:月初有1000元。这个月中,付了1500元费用,卖了300元的产品并收到现金,另外还有1200元的赊销收入,买方要2个月后才支付。下个月初使用权责发生制的会计计量方法,资产负债表将显示公司的总价值仍为1000元,这是由1000-1500费用 + 300收入 + 1200应收账款得出。余额为-200元。
    阅读全文 »

会计基础

1. 权责发生制和收付实现制

  • 权责发生制和收付实现制是两种相对应的核算制度,由于权责发生制相较于收付实现制可以更好地反映企业实际经营情况,我国要求企业必须使用权责发生制进行核算。

权责发生制

  • 又称应收应付制。凡在本期发生应从本期收入中获得补偿的费用,不论是否在本期已实际支付或未付的货币资金,均应作为本期的费用处理;凡在本期发生应归属于本期的收入,不论是否在本期已实际收到或未收到的货币资金,均应作为本期的收入处理。
  • 简单解释:只要交易行为发生,记账周期内不管有没有收到钱,都确认记账。

收付实现制

  • 凡在本期内实际收到或付出的一切款项,无论其发生时间早晚或是否应该由本期承担,均作为本期的收益和费用处理。

  • 简单解释:只有收到钱了才确认记账,不管这笔交易实际上是发生在哪个周期。

2. 单式记账法和复式记账法

单式记账法和复式记账法是两种不同的记账方法。

  • 单式记账法:我们日常所说的流水账,只记录收入支出的流水,而没有记录流水与账户的对应关系。
  • 复式记账法:要求每一笔流水都记录在两个以上的账簿中,用于反映流水与账户的对应关系,并能够更清晰地看到每个账户的余额,我国企业所用的借贷记账法即是复式记账法的一种。
    • 记账规则:有借必有贷、借贷必相等。

3.资产 = 负债+ 所有者权益

  • 这条等式被称为会计恒等式,是一切会计对应关系和核算的基础。在我们的日常观念中,这一条等式其实是很反人类的,为什么资产会由负债组成?

  • 其实举一个简单的栗子就可以理解,正如贷款购房,房产总价 1000 万,我首付 400 万,剩余 600 万我贷款,那么在这个场景中,我已经购买了房子,1000 万就是我的资产,但这又不完全都是我自己的,只有 400 万才真正是我自己的,剩下的 600 万其实是我的负债,我还需要把负债还清了,因此,所有者权益又被称为净资产。

4. 记账方式

  • 记账方式就是把复式记账法+ 会计恒等式结合在一起,并写出会计的分录,举一些的例子:

    用现金花了100 块买衣服:

    • 借:衣服 100
    • 贷:现金 100

    用花呗花了 100块买衣服:

    • 借:衣服 100
    • 贷:花呗 100

    用花呗花了10块钱吃饭:

    • 借:吃饭 10
    • 贷:花呗 10

    「借」反映的是资产的增加、负债和所有者权益的减少,而「贷」反映的是资产的减少、负债和所有者权益的增加

阅读全文 »

1970年,诺贝尔经济学奖得主弗里德曼(Friedman)提出“企业从事旨在提高利润的活动”,这种“股东至上”主义在很长一段时间被认为是美国企业的使命。1997年,由美国近200家最著名企业的首席执行官组成的协会——商业圆桌会议(Business Round Table,以下简称BRT),他们这样定义企业的使命(Mission):管理层和董事会的首要职责是对企业股东负责,而其他利益相关者是对股东责任的派生物。
2019年8月19日,BRT 发布了一份新的企业使命宣言,其中强调企业要“为客户创造价值、投资于员工、公平且合乎道德地与供应商打交道、支持我们的社区、保护环境”。这样的使命宣言,更加强调企业的社会责任(Social Responsibility),即企业股东利益最大化可以,但是有条件。
由此得出企业存在的意义就是盈利,通过各类经济活动获得利润,并向股东分红;同时为了扩大经营,又会不断获取新的投资。

一、什么是财务

财务是一个很泛的概念,可理解为跟公司资金流动相关的一切活动及关系,按照财务管理的定义,一般会包括筹资、投资、日常管理及利润分配四部分。公司里只要是跟钱相关的,都需要有财务的介入,而当业务进行到一定体量时,财务会深入业务流程以控制成本、提高收益率,同时财务还会负责公司的经营风险评估和控制,影响公司的经营决策。

  • 筹资即是筹措资金的动作,包括外部融资和内部融资两种方式,所谓有钱才能办事,筹资是一切经济活动开展的基础
  • 投资即是把筹到的资金进行合理使用,这里的投资不仅仅包括我们平时常见的对外投资,还包括对内投资,即是将资金用于扩大内部生产经营
  • 日常管理则是财务的日常工作,包括预算、应收应付管理、存货管理、成本控制等等一系列细致而繁杂的工作
  • 利润分配就是大家喜闻乐见的分钱环节,辛辛苦苦一整年,就盼着最后分点钱
  • 对企业日常经济活动进行记录、衡量、出报表的过程。
    • 会计主体:设定会计范围、公司主体范围
    • 持续经营:没倒闭,理论上无限期。
    • 分期:月报、季报、半年报告、年报。
    • 货币计量:设定记账本的货币,本国货币:人民币。
    • 全责发生制:以收取款项的权力,和支付款项的义务为标志来确定本期收入和费用会计核算基础。

财务相关的知识领域

国内“注册会计师”的六大学科。

  • 《会计学》主要对应“对经济事项的记录、分类和汇总,输出财务报表”这一过程所适用的会计准则和执行规范;
  • 《财务管理》主要对应站在甲方企业的视角,进行资本管理、投融资管理、收入成本管理、盈余管理、预算管理和财务分析等活动;
  • 《公司战略与风险管理》主要对应企业的战略分析、制定和管理,以及风险管理等活动;
  • 《审计学》主要对应审计的一般程序、内部控制、职业道德等内容;
  • 《经济法》和《税法》主要对应我国商业相关法律法规的框架构建、制度要求和执行规范。

岗位划分

常见的财务活动如,财务规划、收入核算、总账与报表、固定资产管理、员工薪酬、税务等

  • 按照岗位划分的话,财务可以被划分为会计、出纳、审计、财务管理等。
  • 除此之外,财务工作和岗位还包括内部控制、风险管理、税务筹划、信息披露、上市筹备等。除去这些基础业务岗位,很多集团型公司尤其是上市公司还会单独设置信用会计、成本会计、资金专员、税务专员、预算专员、财务BP等细分岗位,以满足更为细致的数据出具和财务管理要求。

财务软件

  • 一家集团型公司尤其是上市公司,其财务核算、财报编制、财务管理、税务管理、经营内控等制度要求都是非常严格、标准和精细化的。当一家公司的财务信息化建设到达一定规模时,其财务管理相关的IT投入会逐渐发生边际成本递减,而这一切成本集约化的前提,便是构建一套符合公司业务场景、流程的现状与未来,能够准确进行数据输出并辅助管理决策的自研财务产品体系。

  • 正常情况下,业务系统通常只会记录流水,正确的记录业务流水。通过会计账单引擎进行转换,转换成财务需要的凭证格式。

阅读全文 »

前言

幸得领导信任,负责BOI项目资料整理申报;第一次做跨国项目申报,资料多次修改后最终申请通过,历经4个月申请排队;终于在2023-04得到BOI批准。

集团直接受益

相关企业连续8年企业所得税减免,可购买泰国土地。

BOI是什么?为何泰国和外国投资者想在BOI 开展业务?

与其他国家/地区相比,有很多泰国和外国投资者更注重在泰国做生意。因为泰国有很多优势,无论是气候,地理资源,位子适合作为东盟的中心。尤其是陆上,水上和空中的运输,有很多专业的劳动力,具有数量和质量潜力的农业原料,有各种各样的农产品,例如:大米,橡胶,甘蔗,蔬菜和新鲜水果。所有这些都足以为投资者创造利润和回报。因此,政府建立了投资促进计划(BOI),以支持和鼓励希望在泰国做生意的投资者,并为外国投资者增加投资的吸引力。

BOI 是什么?

  • 泰国投资促进委员会(BOI)是主管投资的部门,或者更准确地说是泰国政府负责制定投资奖励优惠政策并为投资者提供协助服务的组织。主要职责是促进泰国的投资事业、制定投资奖励优惠政策及相关的指导政策、向投资者提供奖励并为投资者提供相关的服务,包括刺激经济计划和可持续发展国家,包括吸引更多的外国投资者在该国投资,可以加入BOI的公司必须通过标准和符合BOI的条件。

在BOI 的业务会得到哪些好处?

1. 税收方面的优惠

  • 免征企业所得税,最长不超过13年(取决于业务类型和条件)
  • 减免50%的所得税 5年(仅适用于投资促进区)
  • 免/减免机器进口税
  • 免/减免原材料进口税
  • 免开发用品进口税

2. 与税收无关的优惠

  • 允许外国人持有100%的股份(具有其他特定法律的公司除外)。
  • 允许土地所有权
  • 允许熟练的工匠/专家的外国人来泰国工作
  • 允许家庭跟随外国雇员进入到泰国(父母,配偶,子女)
  • 允许外国人可以寄钱或汇款到国外

批准要求投资促进有什么标准?

  1. 发展各部门的竞争力农业,工业和服务业
  2. 预防对环境质量的影响
  3. 有最低的投资和项目可行性

其实,将公司进入BOI并不困难,但是需要仔细和周到地准备文件,检查业务是否符合BOI设定的条件,
如果完全符合BOI的要求,并且文件完整则可以申请。向BOI请求支持将帮助您会获得各种好处,对您的业务对有利。

BOI的业务有以下8种类型:

种类 行业类别 例子业务
1 农业及农产品加工业 生产加工淀粉、生产医疗食品、生物肥料等.
2 矿物、陶瓷和基础金属 矿产勘探、生产玻璃或陶瓷制品、生产钢铁上中下游业务等
3 轻工业 生产医疗器械或零件、生产家具、生产玩具、生产纺织品等
4 金属产品、机械设备和运输工具 机械制造、配件、发动机、交通工具部件、摩托车等
5 电子与电器业 生产电器及部件、硬盘驱动器、微电子物质、数码科技等
6 化工产品、塑料及造纸 药品、印刷品、工业化工产品、塑胶包装制品、纸制品等
7 服务业和公用事业 公用行业和基本服务、大众货物运输业务、医疗服务、旅游业等
8 发展科技创新 发展生物技术、纳米技术、先进材料、数字技术等

OI申请流程共有8个步骤

1. 研究咨询基本信息资料

  • BOI申请人可以通过以下网站 www.boi.go.th 或 BOI中央、BOI区域 、外交部研究BOI的信息。

2. 提交投资促进请求(仅在线提交)

  • BOI申请人可以通过 www.boi.go.th 电子投资促进系统提交请求。

3. 项目说明

  • BOI申请人预约负责该项目的BOI官员,在提交请求之日起的10个工作日内澄清项目。

4. 项目分析

  • BOI官员进行分析项目,考虑时间是根据投资规模。

5. 审批结果通知

  • BOI官员将在确认会议决议后7天内通知审批结果。

6. 接受投资审批结果

  • BOI申请人在接受投资委员通知书的1个月内,要通过以下两种方式对投资促进决议做出回应:通过电子投资促进申请系统(e-Investment Promotion )或填写纸质的项目审批结果确认表(表格F GA CT 07)

7. 办理领取投资促进证

  • BOI接收者,要去申请 领取 “投资促进证 “,并向 BOI 提交相关证据。在回复投资决议后6个月内通过纸质形式或者电子投资申请系统(e-Investment )填写递交领取投资促进证申请表(表格F GA CT 08)以及其他相关文件。

8. 颁布投资促进证

  • 在收到促销证书和完整证据的 10 天内,BOI 官员将颁布”投资促进证 “。

故障管理

故障永远只是表面现象,其背后技术和管理上的问题才是根因。技术和管理上的问题,积累到一定量通过故障的形式爆发出来,所以故障是现象,是在给我们严重提醒。在日常运营中,无论什么原因,产品出现服务中断、服务品质下降,导致用户体验下降的情况,都称为故障(故障不包括用户方环境引发的场景)。

  • 在日常运营中,无论什么原因,产品出现服务中断、服务品质下降,导致用户体验下降的情况,都称为故障(故障不包括用户方环境引发的场景)。
  • 为什么会频繁出故障?
  • 为什么一个小问题或者某个部件失效,会导致全站宕机or服务不可用?
  • 为什么发生了故障没法快速知道并且快速恢复?

故障管理规划

  1. 紧急联系人清单:建立一份紧急联系人清单,列出技术团队成员、管理层、供应商以及其他关键利益相关者的联系信息。确保所有人都知道如何联系到这些人,并在必要时可以迅速响应。
  2. 故障分类与级别划分:建立一份故障分类与级别划分表,将故障划分为不同的级别,例如紧急、高、中、低等级别。定义每个级别的响应时间和解决时间,以及分配给哪些人员和团队进行处理。
  3. 值班制度:建立一份值班制度,确保团队可以在任何时间响应故障。值班制度应该包含轮值制度、联系人信息、响应流程和注意事项等方面的细节。
  4. 故障报告和跟踪:建立一份故障报告和跟踪系统,以便团队可以及时记录故障、跟踪解决进度、追踪根本原因,并确保所有人都可以访问这些信息。故障报告和跟踪系统可以是一个内部的工具,也可以是一个第三方工具,如Jira或ServiceNow等。
  5. 预防措施和持续改进:故障管理不仅仅是应急响应,还需要预防措施和持续改进。团队应该分析故障原因,识别重复出现的故障,并采取措施防止它们再次发生。同时,团队应该不断优化流程,提高响应速度和解决效率。
  6. 培训和知识共享:建立一份培训和知识共享计划,确保团队成员具备必要的技能和知识。团队应该定期组织培训,分享最佳实践和经验,并建立一个知识库,以便所有人都可以访问有用的信息。

产研故障等级划分

  1. 一级故障P0:一级故障是指对业务或客户产生严重影响的故障,例如系统崩溃、数据丢失或业务无法正常运行等。一级故障需要立即处理,必须由最高级别的技术人员或高管介入处理,并且立即解决。
  2. 二级故障P1:二级故障是指对业务或客户产生较大影响的故障,例如某些业务功能无法使用或性能下降等。二级故障需要及时处理,由具备高级别技术经验的人员处理,并且需要在30分钟内解决。
  3. 三级故障P2:三级故障是指对业务或客户产生轻微影响的故障,例如某些功能无法正常使用,但业务可以继续运行等。三级故障需要尽快处理,由具备相关技术经验的人员处理,并且需要在2个小时内解决。
  4. 四级故障P3:四级故障是指对业务或客户影响不大的故障,例如某些功能异常或提示信息错误等。四级故障需要在合理时间内解决,由具备基本技术经验的人员处理,并且需要在8个小时内解决。
  5. 五级故障P4:五级故障是指对业务或客户影响较小的故障,例如一些次要功能无法使用或界面样式问题等。五级故障需要在适当的时间内解决,由具备一定技术知识的人员处理,并且需要在8个小时内解决。

产研故障来源分类

  1. 系统故障分类
    1. 基础设施故障:互联网公司的基础设施包括服务器、网络、存储、数据库等,故障可能会导致系统宕机、数据丢失等问题。这些故障通常需要进行设备维护、系统监控、灾备恢复等方式进行处理。
    2. 软件故障:软件故障是指由于软件设计缺陷、编码错误、系统兼容性问题等原因引起的故障。
      1. 集成错误:包括系统集成错误、组件集成错误等。这些错误通常是由于研发人员在系统或组件集成过程中出现的错误导致的,需要通过集成测试、回归测试等方式进行检查和修正。
      2. 性能问题:包括系统性能问题、代码性能问题等。这些问题通常是由于研发人员在编写代码或设计系统时未考虑到性能因素导致的,需要通过性能测试、优化等方式进行检查和修正。
      3. 安全问题:包括系统安全问题、数据安全问题等。这些问题通常是由于研发人员在设计或编写代码时未考虑到安全因素导致的,需要通过安全测试、漏洞扫描等方式进行检查和修正。
      4. 用户体验问题:包括界面设计不当、功能不完善等。这些问题通常是由于软件设计或编写时未考虑到用户需求导致的,需要通过用户反馈、用户调研等方式进行检查和修正。
      5. 界面故障问题:包括用户界面显示不正常、功能按钮无法点击、菜单无法弹出等问题。这类故障通常与用户交互相关,需要进行UI调试和界面优化。
      6. 功能故障问题:包括软件功能无法正常使用、程序崩溃、功能异常等问题。这类故障通常需要进行代码调试和功能重构。
    3. 人为因素:人为因素是指由于员工的失误、操作不当、意外或恶意行为等原因引起的故障。
      1. 编码和文档错误:包括代码错误、文档错误等。这些错误通常是由于研发人员在编写代码或文档时出现的错误导致的,需要通过代码审查、文档审查等方式进行检查和修正。
      2. 系统配置错误:包括系统配置错误、环境配置错误等。这些错误通常是由于研发人员在系统配置或环境配置时出现的错误导致的,需要通过系统配置检查、环境配置检查等方式进行检查和修正。
    4. 环境因素:环境因素是指由于自然灾害、供电故障、通信中断、网络异常等外部因素引起的故障。
    5. 第三方服务故障:第三方服务故障是指由于供应商服务故障、维护计划、升级等原因引起的故障。
  2. 时间分类:白天故障、夜晚故障、周末故障。
  3. 来源分类:用户报告、自动化告警、监视系统等。

故障来源

故障生命周期

故障生命周期流程

故障管理任务

故障管理任务分解

故障前

提前达成共识,上线运营前的问题统一称为bug,上线运营后的问题统一称为故障&事故。

故障预防

产研故障预防流程

处理预案

第一原则:优先恢复业务,而不是定位问题。(在故障组处理的第一原则优先考虑恢复方案)

  • 业务恢复预案
    • 信息通报(工单登记、邮件通知技术团队)。完成上述第一步后,通常会给相关技术和业务团队通报故障初步信息,包括登记、影响面、故障简述以及主要处理团队。
    • 确定故障影响面及等级。故障会通过监控、告警、业务反馈或用户投诉几个渠道反馈过来,这时技术支持会根据故障定级标准,快速做出初步判断,确认影响面,以及故障等级。
    • 组织故障会议。对于无法马上恢复或仍需要定位排查的故障,根据问题紧急程度就近处理。
  • 恢复系统预案
    • 重启;大多情况下适用多数情况,但是重启后容易出现脏数据。
    • 回滚;回滚的条件是,故障与最近发布有关。
    • 降级;暂停出问题的模块,需要与业务方沟通,暂时离线出问题的模块。
    • 限流、扩容;如果是系统扛不住业务流量,可以选择扩容服务。如果不能扩容,可以选择限流,按照一定百分比的流量限流。

故障中

故障应急流程由故障应急小组来主导。对外同步信息,包括大致原因,影响面和预估恢复时长,同时屏蔽各方对故障处理人员的干扰;对内组织协调各团队相关人员集中处理。

故障发现

  • 确认故障的有效性,登记故障缺陷。同步故障信息给对应的技术团队。

故障定位

故障发生后,一定要严肃对待。大多数情况下,会过渡盯着故障本身,而揪着相关责任人不放。进而形成一些负面影响。为此将注意力转到故障背后的技术和处理更为合适。

  • 评估大致原因,影响面和预估恢复时长。组织应急小组处理故障。
  • 在大多数情况下故障定级别都是在处理环境评估,为了提高整体效率,在需求评审和技术评审阶段就应该完成相关业务故障所带来的故障风险。将工作前置化处理,降低运营阶段处理周期。
  • 如果使用到了第三方的服务,如公有云的各类服务,包括 IaaS、PaaS、CDN 以及视频等等,原则就是默认第三方无责。

故障恢复

  • 确定故障处理方案
    • 包括:正常业务流程处理提交数据修改 / 修改配置 / 回滚 / 紧急版本发布
  • 协调推进故障处理
    • 避免扯皮推诿。避免争执不清,甚至出现诋毁攻击的情况。
    • 正视问题,严肃对待。找出自身不足,作为改进的主要责任者,来落地或推进改进措施。
  • 客户沟通
    • 与客户保持联系,

故障后

故障复盘

复盘的目的是为了从故障中学习,找到我们技术和管理上的不足,然后不断改进。

  • 主要针对故障发生时间点,故障影响面,恢复时长,主要处理人或团队做简要说明。

  • 故障处理时间线回顾。技术支持在故障处理过程中会简要记录处理过程,这个过程要求客观真实即可。业务恢复后,与处理人进行核对和补充。这个时间线的作用非常关键,它可以相对真实地再现整个故障处理过程。

  • 确定相关故障原因,故障根因的改进措施进行讨论;就事论事

    • 第一问:故障原因有哪些?
    • 第二问:如何避免再出出现类似故障?
    • 第三问:当时恢复业务方式?
    • 第四问:当前系统中是否存在类似的潜在风险?
  • 故障完结统计,故障完结报告的主要内容包括故障详细信息,如时间点、影响面、时间线、根因、责任团队(这里不暴露责任人)、后续改进措施,以及通过本次故障总结出来的共性问题和建议。这样做的主要目的是保证信息透明,同时引以为戒,期望团队也能够查漏补缺,不要犯同样的错误。

  • 定责的过程是找出跟因,针对不足找出改进措施。落实责任到人。定责的目标,是责任到人。并且让团队成员意识到自己的不足。对故障处理的坚决态度。

  • 高压线,类似酒后不开车,

    对于明知高压线依然触犯导致故障需要处罚。

    • 未经技术主管授权,私自变更线上代码和配置。
    • 未经技术主管授权,私自在业务高峰期进行网络配置变更。
    • 未经技术主管授权,私自在生产环境调试。
    • 未经技术主管授权,私自变更生产数据信息。
    • 未经技术主管授权,私自将未验证代码合并至主分支。

持续改进

无论是理论还是实践,均证明故障只要有发生的可能,它总会发生。所以为了保障业务稳定性,需提前发现、解决风险,及时发现、定位原因、快速恢复故障,同时要确保改进措施有效落地、避免故障重复发生,我们需要建立一个规范可遵循、闭环的故障管理体系。

  • 改进措施:基于复盘信息制定可验的证改进措施,完成时间点,负责人。如果改进措施无效,故障还会重复发生。

故障量化

SRE

Site Reliability Engineer 目前仅实施小部分关键内容,完成最小MVP。SRE-稳定性目标

  • 尽量减少系统故障或异常运行状态的发生,提升系统可用的运行时间占比。
  • 这里需要考虑三个因素:成本因素、业务容忍度、系统稳定程度
系统可用性 故障时间/年 故障时间/月 故障时间/周 故障时间/日
90% 36.5天 72小时 16.8小时 2.4小时
99% 3.65天 7.2小时 1.68小时 14.4分
99.9% 8.76小时 43.8分钟 10.1分钟 1.44分
99.99% 52.56分钟 4.38分钟 1.01分钟 8.66秒
99.999% 5.26分钟 25.9秒 6.05秒 0.86秒
  • NOC(Network Operation Center)或者叫技术支持 ,这个角色主要有两个职责:一是跟踪线上故障处理和组织故障复盘,二是制定故障定级定责标准,同时有权对故障做出定级和定责。

  • 要提高系统的稳定性,就要制定衡量系统稳定性的相关指标,系统的稳定性指标主要有以下两个:

    • MTBF,Mean Time Between Failure,平均故障时间间隔。
    • MTTR,Mean Time To Repair, 故障平均修复时间。
      • MTTI(Mean Time To Identify,平均故障发现时间;故障发现,故障发生到响应)
      • MTTK(Mean Time To Know,平均故障认知时间;故障定位,根因或是根因范围定位出来为止)
      • MTTF(Mean Time To Failure,平均失效前时间;故障恢复,采取措施恢复业务)
      • MTTV(Mean Time To Verify,平均故障修复验证时间;故障恢复验证,故障解决后验证业务恢复所用时间)
  • 核心目标

    • 提升 MTBF,也就是减少故障发生次数,提升故障发生间隔时长。
    • 降低 MTTR,故障不可避免,那就提升故障处理效率,减少故障影响时长。

SLI

SRE体系建设-可用性建设

  • 容量(Volume):服务承诺的最大容量是多少,从QPS、TPS、流量、连接数、吞吐量;
  • 可用性(Availability):服务是否正常,看HTTP状态码2xx的占比;
  • 延迟(Latency):服务响应速度是否够快,rt是否在预期范围内;
  • 错误率(Errors):错误率有多少,看HTTP状态码5xx的占比;
  • 人工介入(Tickets):是否需要人工介入处理,考虑人工修复。

近期在处理公司项目历史债务问题,由于客观历史原因导致。为了快速收益初期不知情况下产生。定期不清理债务导致。

综合债务偿还规划

根据当前情况,除重写策略以外,其他策略同时进行。按阶段将债务问题逐步偿还。根据阶段性成果适当调整下个阶段的重点工作。

  • 达成共识 > 债务可见 > 及时止损 > 不断改善。

债务的体现

  • 代码在不同项目中复制迁移,并随着时间增加。
  • 代码虽然运行稳定,但是回归测试成本在增加。
  • 迭代维护越来越困难。
  • 新的feature开发周期越来越长,ROI降低。
  • 影响开发人员心态。

技术债务的组成

  • 代码债务:代码重复、坏味道、代码风格混乱、魔法值、条件太多、异常处理、代码风险、安全风险、严重耦合。
  • 设计债务:缺少设计、对非功能性认知不足,系统性风险。
  • 测试债务:缺乏测试、测试覆盖率不足和不使用测试。
  • 质量债务:缺乏稳定和健壮性的技术验证,版本控制模糊,环境参数混乱。
  • 文档债务:无技术文档、设计文档或者文档过期。

技术债务利息

技术债务和贷款买房的思维模式一样,如果借技术债务的收益大于利息的时候,大胆借!

  • 开发速度降低:通常项目正常情况下,在相同的时间间隔下,完成的任务是接近的。随着项目的迭代,项目单位时间内能完成的任务数明显下降,那很可能是技术债务太多导致的。
  • 单元测试代码覆盖率低:现在大部分语言都有单元测试覆盖率的检测工具,通过工具可以很容易知道当前项目单元测试覆盖率如何,如果覆盖率太低或者下降厉害,就说明存在技术债务了。
  • 代码规范检查的错误率高:静态代码分析工具提示的代码债务太多,存在大量历史代码就需要处理。
  • 在线bug越来越多:正常情况下,如果没有新功能开发,Bug 数量会越来越少。但是如果 Bug 数量下降很慢,甚至有增多的迹象,额外增加研发成本、不稳定的产品质量、难以维护的代码。
  • 业务共识问题:理解需求需要时间,加入一个新功能需要写 100 行代码,只要花 20 分钟。但是搞清楚这 100 行代码,应该加到哪个文件里,关联那些系统需要超长时间。

债务偿还策略

策略并没有绝对好坏,需要根据当前项目场景灵活选择。有个简单原则可以帮助你选择,那就是看哪一种策略投入产出ROI更好。

  • 重写:推翻重来,一次还清。
  • 维持:修修补补,只还利息。
  • 重构:新旧交替,分期付款。
  • 投资:好的架构设计、高质量代码就像一种技术投资,能有效减少技术债务的发生。
  • 测试:日常能做好代码审查、保障单元测试代码覆盖率,预防技术债务。
  • 还债:因为进度时间紧等客观原因,导致不得不走捷径,那么就应该把欠下的技术债务记下来,放到任务跟踪系统中,安排在后续的开发任务中,及时还债及时解决,就可以避免债务越来越多。

规划共识

防止技术债务产生的主要方法是了解开发团队存在的技术债务。开发团队必须全面了解技术债务,以及债务对项目的影响。合适的流程可以帮助开发团队避免技术债务积累,例如代码、设计、架构和测试的审查等。而且,这些流程必须是务实的,否则事与愿违。

  • PDCA(Plan-Do-Check-Act)它最早来⾃于质量管理领域,意思是做任何事情,都要经过规划(Plan)、执⾏(Do)、检查 (Check) 和⾏动 (Act) 这四个步骤。这四个步骤提供了⼀个简易的思考和做事框架。这个循环并不是运⾏⼀次就结束了,⽽是周⽽复始、螺旋上升的。
  • WBS(Work Breakdown Structure)任务分解。识别依赖及各环节关键路径,定义完成标准,达成共识并公开透明,变更即时调整。
  • MVP(Minimum Viable Product)最⼩化可实⾏产品。来自产品开发,不要⼀下⼦做⼀个「尽善尽美」的产品,⽽是先花费最⼩的代价做⼀个「可⽤」的产品原型,去验证这个产品是否有价值、是否可⾏,再通过迭代来完善细节。
  • ROI(Return on Investment)投资回报率,通常在广告业务作为重要的衡量标准。因为它是衡量广告投放花费的资金是否对于公司业务产生良好的实际影响,例如产生潜在客户、增加销售额、提升品牌知名度或者推动其他有价值的客户活动。
  • 效率先行。开发过程越快越好,越快越有竞争力。这的确是软件开发,尤其是互联网行业软件开发的不二法则。

实施步骤

  1. 团队内债务认知建立,技术债务会直接影响到公司业务的成长,间接影响个人的成长。
    • 技术债务产生的负作用,将直接影响相关业务线的发展。
  2. 采用低成本的方式进行预防。
    • 强化开发人员时间管理认知,帮助开发人员更好的安排时间高效完成工作。
  3. 识别债务并标记。
    • 服务、组件依赖混乱;文件、函数、包内容太多职责不清晰。
    • 魔法值、炫技、复杂设计、无设计。函数、接口、参数过多。
  4. 采用PDCA的方式循环迭代。
    • 标记出存在的负债问题。集中汇总到bug池。关联相关业务线。
    • 梳理业务边界,统一代码风格、完善开发文档、接口文档、接口单元测试。降低开发人员协调共识周期长的问题。
  5. 采用WBS方式标记、处理、公开相关问题。
    • 优先处理服务稳定性问题、线上紧急bug问题。
    • 根据bug与业务的关系程度划分处理级别。
  6. 采用MVP方式逐步迭代项目。
    • 落地持续改进能力;快速的增量交付。
    • 提早集成与测试;让问题得以及时暴露,带来了更快的反馈及应对。
    • 及时规避⻛险;意外仍然会有,但⼤多数情况下,急早发现问题,避免更⼤的影响。
    • 降低发布周期;降低修复试错成本。

经过90天的努力,体重最大相差20KG。按照月均计算,已经减重15KG。

为什么减重

已经造成了生活上的不便利,几年前精神旺盛,白天都不困。最近一年中午吃完饭就犯困。

  • 旅游容易暴饮暴食是真的。

  • 2021-11月初在苏州旅游的时候,因为体重原因不让玩水上项目。

  • 2021-11月11日我把床头坐塌了,我突然意识到问题的严重性了。

  • 2021-11月11日买了体脂秤,各项指标显示我的身体已经不非常不健康了,开始了减重。

减重规划

  • 第一阶段减重20KG。 2022-02-13 已达成目标。
  • 第二阶段减少脂肪。体脂率维持在20%。进行中
  • 第三阶段减少脂肪,体脂率维持在15%。

减重计划

  • 控制饮食摄入,在吃饱喝足的情况下。控制碳水、糖、反式脂肪摄入。
    • 营养摄入均衡。
    • 早上吃碳水、中午吃蛋白、晚上吃蔬菜。
  • 保持最低运动量,走路、跳绳。
  • 多喝水,每天必须喝2升水。
  • 保持睡眠时长,睡眠能减重是真的。

减重目标达成

再接再厉

体重已经下降很多了,下一步是就是健康。体脂率下降主要减少脂肪。

END

尝试下使用python Matplotlib可视化数据。

演示代码

  • python3.8运行环境
  • 测试数据,这里使用mysql提供的测试数据库。安装步骤
  • jupyter notebook
  • pip3 install pymysql
1
2
3
4
➜  jupyter-example git:(main) python --version
Python 3.8.7
➜ jupyter-example git:(main) jupyter notebook --version
6.2.0
阅读全文 »

尝试下使用python Matplotlib可视化数据。

演示代码

  • python3.8运行环境
  • 测试数据库
  • jupyter notebook
  • pip3 install pymysql
1
2
3
4
➜  jupyter-example git:(main) python --version
Python 3.8.7
➜ jupyter-example git:(main) jupyter notebook --version
6.2.0
阅读全文 »

尝试下使用python可视化数据。

云词

演示代码

  • python3.8运行环境
  • 测试数据库
  • jupyter notebook
  • pip3 install pymysql
  • pip3 install WordCloud
  • pip3 install jieba
1
2
3
4
➜  jupyter-example git:(main) python --version
Python 3.8.7
➜ jupyter-example git:(main) jupyter notebook --version
6.2.0
阅读全文 »

RediSearch 是一个高性能的全文搜索引擎,它可以作为一个 Redis Module(扩展模块)运行在 Redis 服务器上。

RediSearch

  • Redis 哈希中多个字段的全文索引
  • 无性能损失的增量索引
  • 文档排名(使用tf-idf,具有可选的用户提供的权重)
  • 场加权
  • 使用 AND、OR 和 NOT 运算符的复杂布尔查询
  • 前缀匹配、模糊匹配和精确短语查询
  • 支持双变音拼音匹配
  • 自动完成建议(带有模糊前缀建议)
  • 多种语言中基于词干的查询扩展(使用Snowball
  • 支持中文标记化和查询(使用Friso
  • 数字过滤器和范围
  • 使用Redis 地理空间索引进行地理空间搜索
  • 强大的聚合引擎
  • 支持所有 utf-8 编码文本
  • 检索完整文档、选定字段或仅检索文档 ID
  • 排序结果(例如,按创建日期)

官方文档

运行环境

  • docker
1
2
mkdir -p redisearch/data
docker run -p 6379:6379 -v $PWD/redisearch/data:/data -d redislabs/redisearch:latest
  • 检查安装
1
2
3
4
5
6
7
➜  docker-run redis-cli 
127.0.0.1:6379> MODULE LIST
1) 1) "name"
2) "search"
3) "ver"
4) (integer) 20015
127.0.0.1:6379> exit
  • 测试下
1
2
3
4
5
6
7
8
127.0.0.1:6379> FT.ADD idx docCn 1.0 LANGUAGE chinese FIELDS txt "Redis支持主从同步。数据可以从主服务器向任意数量的从服务器上同步,从服务器可以是关联其他从服务器的主服务器。这使得Redis可执行单层树复制。从盘可以有意无意的对数据进行写操作。由于完全实现了发布/订阅机制,使得从数据库在任何地方同步树时,可订阅一个频道并接收主服务器完整的消息发布记录。同步对读取操作的可扩展性和数据冗余很有帮助。[8]"
OK
127.0.0.1:6379> FT.SEARCH idx "数据" LANGUAGE chinese HIGHLIGHT SUMMARIZE
1) (integer) 1
2) "docCn"
3) 1) "txt"
2) "<b>\xe6\x95\xb0\xe6\x8d\xae</b>\xe5... <b>\xe6\x95\xb0\xe6\x8d\xae</b>\xe8\xbf\x9b\xe8\xa1\x8c\xe5\x86\x99\xe6\x93\x8d\xe4\xbd\x9c\xe3\x80\x82\xe7\x94\xb1\xe4\xba\x8e\xe5\xae\x8c\xe5\x85\xa8\xe5\xae\x9e\xe7\x8e\xb0\xe4\xba\x86\xe5\x8f\x91\xe5\xb8\x83... <b>\xe6\x95\xb0\xe6\x8d\xae</b>\xe5\x86\x97\xe4\xbd\x99\xe5\xbe\x88\xe6\x9c\x89\xe5\xb8\xae\xe5\x8a\xa9\xe3\x80\x82[8... "

  • 对中文支持不够好
阅读全文 »

简单优化的下博客。

优化内容

  • SEO优化
  • 增加站内搜索
  • 增加网站地图
  • 添加字数统计和阅读时长
  • GitHub Fork Me
  • 允许复制代码
  • 图片懒加载

SEO优化

hexo 默认生成文章命名方式,在中文标题下很不友好。可以选择生成永久的链接。

  • 使用abbrlink插件
1
npm install hexo-abbrlink --save
  • 配置_config.yml
1
2
3
4
5
6
#permalink: :year/:month/:day/:title/
#permalink_defaults:
permalink: posts/:abbrlink/
abbrlink:
alg: crc32 #support crc16(default) and crc32
rep: dec #support dec(default) and hex
  • 生成的链接将会是这样的(官方样例):
1
2
3
4
5
6
7
8
9
10
crc16 & hex
https://post.x.com/posts/66c8.html

crc16 & dec
https://post.x.com/posts/65535.html
crc32 & hex
https://post.x.com/posts/8ddf18fb.html

crc32 & dec
https://post.x.com/posts/1690090958.html
阅读全文 »

编写第一个CLI小程序练手。

项目需求

  • 访问一个网站,输出页面内容成md文件。

初始化项目

1
cargo new learning-05 
  • 使用vs code 打开项目。

添加依赖

  • 修改Cargo.toml
  • 引入 reqwest 和 html2md
    • reqwest 是一个http客户端
    • html2md 顾名思义就是html转markdown
1
2
reqwest = { version = "0.11", features = ["blocking"]}
html2md = "0.2"%

编写代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
use std::fs;

fn main() {
let url = "https://z201.vip";
let output = "z201.md";
println!("url {} output {}",url,output);
let body = reqwest::blocking::get(url).unwrap().text().unwrap();

println!("Converting html to markdown...");
let md = html2md::parse_html(&body);

fs::write(output, md.as_bytes()).unwrap();
println!("Converted markdown has been saved in {}.", output);
}

  • Console
1
2
3
url https://z201.vip output z201.md
Converting html to markdown...
Converted markdown has been saved in z201.md.

迭代代码

版本一

  • 这里将url 和 output作为参数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
use std::fs;
use std::env;

fn main() {
let args: Vec<String> = env::args().collect();
println!("args {:?} ",args);
// 获取第一个参数
let url = &args[1];
// 获取第二个参数
let output = &args[2];
// 判断参数是否为空,不知道这个是否有效。
assert_eq!(url.is_empty(),false);
assert_eq!(output.is_empty(),false);

println!("url {} output {}",url,output);
let body = reqwest::blocking::get(url).unwrap().text().unwrap();

println!("Converting html to markdown...");
let md = html2md::parse_html(&body);

fs::write(output, md.as_bytes()).unwrap();
println!("Converted markdown has been saved in {}.", output);
}

  • Console
1
2
3
4
5
6
7
➜  learning-05 git:(master) ✗ cargo run https://z201.vip z201.md
Finished dev [unoptimized + debuginfo] target(s) in 0.09s
Running `target/debug/learning-05 'https://z201.vip' z201.md`
args ["target/debug/learning-05", "https://z201.vip", "z201.md"]
url https://z201.vip output z201.md
Converting html to markdown...
Converted markdown has been saved in z201.md.

版本二

  • 换一种方式来处理参数
  • 打包测试
增加依赖
1
2
3
reqwest = { version = "0.11", features = ["blocking"]}
html2md = "0.2"
structopt = "0.3.13"
  • 代码
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
use std::fs;
use structopt::StructOpt; // 使用StructOpt传递参数

#[derive(StructOpt, Debug)]
struct Cli{
url:String,
output:String,
}

fn main(){
let args = Cli::from_args();
// println!("args {:?} ",args);
let url = &args.url;
let output = &args.output;
println!("url {}\n output {}",url,output);
let body = reqwest::blocking::get(url).unwrap().text().unwrap();
println!("Converting html to markdown...");
let md = html2md::parse_html(&body);
fs::write(output, md.as_bytes()).unwrap();
println!("Converted markdown has been saved in {}.", output);
}

#[test]
fn check() {
println!("test")
}

打包

1
cargo build --release   
  • 慢慢等待

测试下

1
2
3
4
5
6
cd target/release
./learning-05 https://z201.vip z201.md
url https://z201.vip
output z201.md
Converting html to markdown...
Converted markdown has been saved in z201.md.

END

Rust的核心功能之一 ownership ,运行的程序都需要使用计算机管理内存的方式,比如Java 具备垃圾回收,还有一些语言需要手动去释放内存。而Rust则是第三种方式,通过所有权管理内存,编译器在编译时会根据一些列规则检查,在运行时所有权系统的任务功能都不会减慢程序。

  • 所有权和生命周期是 Rust 和其它编程语言的主要区别,也是 Rust 其它知识点的基础。

所有权

  • 栈是程序运行的基础。每当一个函数被调用时,一块连续的内存就会在栈顶被分配出来,这块内存被称为帧(frame)。

  • 栈以放入值的顺序存储并以相反的顺序取出值。这也被称为 后进先出 (last in , first out) 。添加数据的时候加 进栈 (pushing anto the stack) ,而移出数据叫 出栈 (poping off th stack)。

  • 在调用的过程中,一个新的帧会分配足够的空间存储寄存器的上下文。在函数里使用到的通用寄存器会在栈保存一个副本,当这个函数调用结束,通过副本,可以恢复出原本的寄存器的上下文,就像什么都没有经历一样。此外,函数所需要使用到的局部变量,也都会在帧分配的时候被预留出来。

  • 栈的操作时非常快的,这主要得益于它存取数据的方式,数据的存取位置总时在栈顶,而不需要重新寻找一个位置去存放或者读取。另一个属性就是,栈中所有的数据都必须占据已知且固定的大小。

  • 但是工作中我们依然要避免大量数据存放栈中,避免栈溢出(stack overfow),运行程序调用栈超出了系统运行的最大空间,就无法创建新的。就会出现溢出现象

在编译时,一切无法确定大小或者大小可以改变的数据,都无法安全地放在栈上,最好放在堆上。

  • 堆主要存放大小未知,可能大小变化的数据,堆事缺乏组织的。当向堆中存放数据时,要申请一个空间,操作系统在堆的某处足够大的空间,把它标记为已经使用。并返回一个指针(pointer),这个过程叫做堆上分配内存(allocating on the heap)。
  • 访问堆上的数据比访问栈上的数据慢,因为必须通过指针来访问。现代处理器在内存中跳转越少就越快(缓存)。当代码调用一个函数时,传递给函数的值(包括可能指向堆上数据的指针)和函数的局部变量被压入栈中。当函数结束时,这些值被移出栈。
案例一
1
2
3
4
5
fn say(name : String){
println("name {}" , name);
}
say("tomcat".to_string());
say("jetty".to_string());
  • 字符串的数据结构,在编译器是不确定的,运行期才代码才知道大小。比如tomcatjetty,当say方法执行的时候才知道参数的具体长度。
案例二
1
2
3
let mut arr = Vec::new();
arr.push(1);
arr.push(2);
  • 列表初始化长度是4,而不是2。在堆上内存分配会使用libc提供的mallo函数。系统调用的代价是昂贵的,所以我们要避免频繁地 malloc()。
内存泄漏

如果手工管理堆内存的话,堆上内存分配后忘记释放,就会造成内存泄漏。一旦有内存泄漏,程序运行得越久,就越吃内存,最终会因为占满内存而被操作系统终止运行。

堆越界

如果堆上内存被释放,但栈上指向堆上内存的相应指针没有被清空,就有可能发生使用已释放内存(use after free)的情况。

GC垃圾回收处理
  • 比如Java采用垃圾回收(GC),来自动管理内存,定期标记(mark)找出不在被引用的对象,然后清理(sweep)掉。
  • 但是GC是不确定的,可能引起STW(Stop The World)。
ARC自动引用计数处理方式
  • 在编译时,它为每个函数插入 retain/release 语句来自动维护堆上对象的引用计数,当引用计数为零的时候,release 语句就释放对象。

栈与堆

  • 栈上存放的数据是静态的,固定大小,固定生命周期;堆上存放的数据是动态的,不固定大小,不固定生命周期。
阅读全文 »

函数

函数遍布于 Rust 代码中,Rust 代码中的函数和变量名使用 snake case 规范风格。在 snake case 中,所有字母都是小写并使用下划线分隔单词。

  • Rust 中的函数定义以 fn 开始并在函数名后跟一对圆括号。大括号告诉编译器哪里是函数体的开始和结尾。
  • 可以使用函数名后跟圆括号来调用我们定义过的任意函数。Rust 不关心函数定义于何处,只要定义了就行。

案例

1
2
3
4
5
6
7
8
fn main() {
println!("Hello, world!");
hello();
}

fn hello(){
print!("hello function")
}
  • Console
1
2
Hello, world!
hello function

函数参数

函数也可以被定义为拥有 参数(parameters),参数是特殊变量,是函数签名的一部分。当函数拥有参数(形参)时,可以为这些参数提供具体的值(实参)。

  • 在函数签名中,必须 声明每个参数的类型。这是 Rust 设计中一个经过慎重考虑的决定:要求在函数定义中提供类型注解,意味着编译器不需要你在代码的其他地方注明类型来指出你的意图。
  • 多个参数时,使用逗号分隔。

案例

1
2
3
4
5
6
7
8
fn number(x:i8){
print!("value {} ",x)
}

fn main() {
println!("Hello, world!");
number(1);
}
  • Console
1
2
Hello, world!
value 1
  • number函数声明一个x的参数,x的类型被指定为i8
阅读全文 »

Rust变量与数据类型

Rust 是一种静态类型的语言。 Rust 中的每个值都是某种数据类型。 编译器可以根据分配给它的值自动推断变量的数据类型。

let

使用let关键字声明变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
fn main() {
println!("Rust基础语法!");
let str_ = "String"; // string 类型
let f_ = 1.1; // float 类型
let bool_ = true; // boolean 类型
let char_ = 'a'; // unicode character
println!("string {}",str_);
println!("float {}",f_);
println!("boolean {}",bool_);
println!("icon {}",char_);


}

  • Console
1
2
3
4
5
Rust基础语法!
string String
float 1.1
boolean true
icon a
阅读全文 »

学习下Rust

为什么学习Rust

在知乎上看到一个人对Rust的评论。

首先,Rust 是有点反人类,否则不会一直都不火。然后,Rust 之所以反人类,是因为人类这玩意既愚蠢,又自大,破事还贼多。 你看 C++ 就很相信人类,它要求人类自己把自己 new 出来的东西给 delete 掉。 C++:“这点小事我相信你可以的!” 人类:“没问题!包在我身上!” 然后呢,内存泄漏、double free、野指针满世界飘…… C++:“……”

Java 选择不相信人类,但替人类把事办好。 Java:“别动,让我来,我有gc!” 人类:“你怎么做事这么慢呀?你怎么还 stop the world 了呀?你是不是不爱我了呀?” Java:“……”

Rust 发现唯一的办法就是既不相信人类,也不惯着人类。 Rust:“按老子说的做,不做就不编译!” 人类:“你反人类!” Rust:“滚!”

  • C/C++ 完全相信而且惯着程序员,让大家自行管理内存,所以可以编写很自由的代码,但一个不小心就会造成内存泄漏等问题导致程序崩溃。
  • Java/Golang 完全不相信程序员,但也惯着程序员。所有的内存生命周期都由 JVM 运行时统一管理。 在绝大部分场景下,你可以非常自由的写代码,而且不用关心内存到底是什么情况。 内存使用有问题的时候,我们可以通过 JVM 来信息相关的分析诊断和调整。
  • Rust 语言选择既不相信程序员,也不惯着程序员。 让你在写代码的时候,必须清楚明白的用 Rust 的规则管理好你的变量,好让机器能明白高效地分析和管理内存。 但是这样会导致代码不利于人的理解,写代码很不自由,学习成本也很高。
阅读全文 »

分布式系统

开发了几年的分布式系统,对其原理模模糊糊。大量的时间在研究场景下解决方案。并未深入总结过。本文梳理个人对分布式的认知理解。

  • 在没有系统学习分布式相关知识的情况,通常选择针对局部场景需求进行认知下局部最优解决。处理问题也没有十足的把握,在系统稳定性上只能通过case by case的方式处理。

  • 解决单机性能瓶颈。

  • 解决数据增加运维成本。

阅读全文 »

背景

权限管理是一个几乎所有大中型 B 端系统都会涉及的重要组成部分,其目的是对整个系统进行权限控制,避免造成误操作及数据泄露等风险问题。

权限与权限管理

名词定义

权限相关的基本概念:

  • 权限:用户可操作行为的最小单位。
  • 用户:每个用户都有唯一标识,并被授予一个或多个角色。
  • 角色:由不同的权限组合而成,最终分配给具体用户。
  • 权限管理:控制用户的权限,只能访问授权内容。

模型选择

  • ACL(Access Control List):基于用户级别的权限控制。
    • 将系统的各种权限直接授予具体的用户。抽象来说,为每个用户维护了单独的权限列表,当需要分配权限、收回权限时,需要修改对应用户的权限信息。
  • RBAC(Role Base Access Control):基于角色级别的权限控制。
    • 与 ACL 对比,RBAC不用给用户单个分配权限,权限与用户之前通过角色关联。通过给不同的角色分配不同的权限,只需要将用户指向对应的角色就会有对应的权限。分配权限、收回权限只需要通过修改用户的角色即可。
  • ABAC(Attribute Base Access Control):基于属性级别的权限控制。
    • 不同于常见的将用户通过某种方式直接关联到权限的方式,ABAC 是通过动态计算一个或一组属性来是否满足某种条件来进行权限判断。属性一般分为四类:用户属性(自然人属性,如年龄、性别等),环境属性(物理环境,如时间、地点、气候),操作属性(读、写)和对象属性(操作对象,如资金、某张图片、某个特定的页面,又称资源属性)。
  • 因此理论上能够实现灵活的权限控制、将在权限与用户之前通过一组或多组属性实现关联,几乎能满足所有类型的需求。

权限管控

抽象来看权限体系可以分为如下两类:功能权限 与 数据权限 两部分。

  • 功能权限指的是在系统中的功能可否使用,通常我们将功能权限分为查看、编辑、删除等,同时编辑、删除权限又包含了查看。通过小的权限点拆分更精细的赋予了员工能否进入某个页面查看信息、编辑信息的能力。
  • 数据权限指数据中存在的数据是否能查看,是一个更细粒度的权限。比如一个页面,不同角色查看不同的数据就需要通过数据权限控制。
    从管理对象维度又可以分为:企业能力 与 员工能力。
  • 企业能力店铺维度的权限,比如开通某服务,可以通过企业能力去体现。
  • 企业能力赋予用户的权限,比如收电子发票、资金管理等。
  • 企业能力优先级绝对高于员工能力,所有场景的权限判断,店铺能力必须先于员工能力。简单地说,企业能力决定了“企业能做什么”,员工能力决定了“用户能做什么”。

jvm调优是日常工作中经常会使用的技巧,整理下。

JVM调优

为什么要调优,当默认配置参数不能很好的发挥程序性能的时候。

  • Heap内存(老年代)持续上涨达到设置的最大内存值。
  • Full GC 次数频繁。
  • GC 停顿时间过长(超过1秒)。
  • 应用出现OutOfMemory 等内存异常。
  • 应用中有使用本地缓存且占用大量内存空间。
  • 系统吞吐量与响应性能不高或下降。

常用方式

  • 上线之前,应先考虑将机器的JVM参数设置到最优。(启动参数)
  • 大多数导致GC问题的原因是代码层面的问题导致的(代码层面)。
  • 减少创建对象的数量(代码层面)。
  • 减少使用全局变量和大对象(代码层面)。
  • 优先架构调优和代码调优,JVM优化是不得已的手段(代码、架构层面)。
  • 分析GC情况优化代码比优化JVM参数更好(代码层面)。

启动参数

调优的最终目的都是为了令应用程序使用最小的硬件消耗来承载更大的吞吐。jvm调优主要是针对垃圾收集器的收集性能优化,令运行在虚拟机上的应用能够使用更少的内存以及延迟获取更大的吞吐量。

  • 延迟:GC低停顿和GC低频率。
  • 低内存占用。
  • 高吞吐量。
  • 堆内存 = Old + Eden + S0 + S1
  • 年轻的 = Eden(新生代) + S0 + S1
  • 标准参数(-),所有JVM都必须支持这些参数的功能,而且向后兼容
  • 非标准参数(-X),默认JVM实现这些参数的功能,但是并不保证所有JVM实现都满足,且不保证向后兼容。
  • 非稳定参数(-XX),此类参数各个JVM实现会有所不同,将来可能会不被支持,需要慎重使用。
  • Heap 内存使用率 <= 70%;
  • Old generation内存使用率<= 70%;
  • avgpause <= 1秒;
  • Full gc 次数0 或 avg pause interval >= 24小时 ;
1
-XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=128m -Xms1024m -Xmx1024m -Xmn256m -Xss256k -XX:SurvivorRatio=8 -XX:+UseConcMarkSweepGC
  • 简单介绍下参数
1
2
3
4
5
6
7
8
9
-XX:MetaspaceSize=128m (元空间默认大小)
-XX:MaxMetaspaceSize=128m (元空间最大大小)
-Xms1024m (堆最大大小)
-Xmx1024m (堆默认大小)
-Xmn256m (新生代大小)
-Xss256k (棧最大深度大小)
-XX:SurvivorRatio=8 (新生代分区比例 8:2)
-XX:+UseConcMarkSweepGC (指定使用的垃圾收集器,这里使用CMS收集器)
-XX:+PrintGCDetails (打印详细的GC日志)
  • 虽然有了介绍但是依然不清楚具体是干啥的。并且Java虚拟机提供了非常多的参数命令。下面代码可以输出支持的参数数量
1
2
3
4
5
java -XX:+PrintFlagsFinal -XX:+UnlockDiagnosticVMOptions -version | wc -l
openjdk version "1.8.0_275"
OpenJDK Runtime Environment (AdoptOpenJDK)(build 1.8.0_275-b01)
OpenJDK 64-Bit Server VM (AdoptOpenJDK)(build 25.275-b01, mixed mode)
838
  • 堆内存大小配置

建议 -Xms = 最大内存 * [0.6. ~0.8] 这里需要考虑系统损耗内存、和实际物理内存。

  • 堆内存与堆外内存

    • 堆内存

    必须是1024的倍数,且不能低于2M。

    32位机器,最大1G/4G 64位机器最大可以超过 32G/64G

    • 堆外内存

    堆外内存一般指 Direct Memory ,不受GC控制,JVM、Netty都可能使用堆外内存。

1
-XX:MaxDirectMemorySize 限制
如何理解这些参数的含义?

首先我们需要理解java是如何运行的,为什么需要java虚拟机?

我们常用方式一般是安装java运行环境(jre)用命令行的方式启动或者直接双击jar运行。jre包含的java运行的必要环境。

Java 作为一门高级程序语言,它的语法非常复杂,抽象程度也很高。编译出来的也不是机器可以直接直接运行代码。所以使用面向Java语言的虚拟机运行Java编译以后的特定代码。这里的特定代码指的是Java字节指令码。

JVM 内存分配性能问题
  • 在应用服务的特定场景下,JVM 内存分配不合理带来的性能表现并不会像内存溢出问题这么突出。如果没有深入到各项性能指标中去,是很难发现其中隐藏的性能损耗。
  • JVM 内存分配不合理最直接的表现就是频繁的 GC,这会导致上下文切换等性能问题,从而降低系统的吞吐量、增加系统的响应时间。

分析 GC 日志

  • 在进行压测的时候,我们需要对GC日志进行分析。
1
-XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:/log/heap.log
  • -XX:PrintGCTimeStamps:打印 GC 具体时间;
  • -XX:PrintGCDetails :打印出 GC 详细日志;
  • -Xloggc: path:GC 日志生成路径。
  • JVM 内存调优通常和 GC 调优是互补的,基于以上调优,可以对年轻代和堆内存的垃圾回收算法进行调优。
阅读全文 »

开发过程中经常会出现批量写入数据库的操作,特别是后台系统,在导入数据的场景下会对表性能造成一定影响。

Mybatis-ONE-SQL

SQL插入主要使用INSERT语句,有两种常见的用法。

逐条插入

  • 如果插入的记录过多,比如大于20条记录,性能损耗非常严重。
1
INSERT INTO `table_data` ('field1','field2') VALUES ('data1','data2');

批量插入

  • 在日常开发中使用最多的方式,但是数量大比如大于100条的时候插入性能非常差。
1
INSERT INTO `table_data` ('field1','field2') VALUES ('data1','data2'),('data1','data2');

Mybatis-Foreach-SQL

插入数据有两种实现,foreach、batchExecutor。

1
2
3
4
5
6
7
<insert id="batchInsert">
INSERT INTO `table_data`(`create_time`)
VALUES
<foreach collection="tableDataList" item="item" index="index" separator=",">
(#{item.createTime})
</foreach>
</insert>

Mybatis-Batch-SQL

  • 配合foreach执行
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
@Component
@Slf4j
public class BatchDao {

@Autowired
private SqlSessionTemplate sqlSessionTemplate;

public <T,M> boolean batchSave(Collection<T> entityList, Class<M> mapper, BiConsumer<M,Collection<T>> fuc) {
SqlSessionFactory sqlSessionFactory = sqlSessionTemplate.getSqlSessionFactory();
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
M entityMapper = sqlSession.getMapper(mapper);
try {
fuc.accept(entityMapper, entityList);
sqlSession.flushStatements();
sqlSession.clearCache();
return true;
} catch (Exception e) {
log.error("{}",e.getMessage());
sqlSession.rollback();
} finally {
sqlSession.close();
}
return false;
}
}

测试结果

  • 10条数据 foreach > batch > insert
  • 100条数据 foreach > batch > insert
  • 1000条数据 foreach > batch > insert
  • 5000条数据 batch > foreach (数据量大,batch优势就出来了)
阅读全文 »

SpringBoot测试解决方案

  • mock

    • Mock 的意思是模拟,它可以用来对系统、组件或类进行隔离。
    • 验证组件级别正确性的一大难点在于关于组件与组件之间的依赖关系,这里就需要引出测试领域非常重要的一个概念,即 Mock(模拟)。

  • mvc-mock 测试Controller

  • service-mock 测试 Service

  • repository-mock 测试 Data

  • remote-mock 测试 远程接口

Spring-Boot-Start-Test

Spring Test & Spring Boot Test:为 Spring 和 Spring Boot 框架提供的测试工具。

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
28
[INFO] +- org.springframework.boot:spring-boot-starter-test:jar:2.4.5:test
[INFO] | +- org.springframework.boot:spring-boot-test:jar:2.4.5:test
[INFO] | +- org.springframework.boot:spring-boot-test-autoconfigure:jar:2.4.5:test
[INFO] | +- com.jayway.jsonpath:json-path:jar:2.4.0:test
[INFO] | | \- net.minidev:json-smart:jar:2.3:test
[INFO] | | \- net.minidev:accessors-smart:jar:1.2:test
[INFO] | | \- org.ow2.asm:asm:jar:5.0.4:test
[INFO] | +- jakarta.xml.bind:jakarta.xml.bind-api:jar:2.3.3:test
[INFO] | | \- jakarta.activation:jakarta.activation-api:jar:1.2.2:test
[INFO] | +- org.assertj:assertj-core:jar:3.18.1:test
[INFO] | +- org.hamcrest:hamcrest:jar:2.2:test
[INFO] | +- org.junit.jupiter:junit-jupiter:jar:5.7.1:test
[INFO] | | \- org.junit.jupiter:junit-jupiter-params:jar:5.7.1:test
[INFO] | +- org.mockito:mockito-core:jar:3.6.28:test
[INFO] | | +- net.bytebuddy:byte-buddy:jar:1.10.22:test
[INFO] | | +- net.bytebuddy:byte-buddy-agent:jar:1.10.22:test
[INFO] | | \- org.objenesis:objenesis:jar:3.1:test
[INFO] | +- org.mockito:mockito-junit-jupiter:jar:3.6.28:test
[INFO] | +- org.skyscreamer:jsonassert:jar:1.5.0:test
[INFO] | | \- com.vaadin.external.google:android-json:jar:0.0.20131108.vaadin1:test
[INFO] | +- org.springframework:spring-test:jar:5.3.6:test
[INFO] | \- org.xmlunit:xmlunit-core:jar:2.7.0:test
[INFO] +- org.junit.jupiter:junit-jupiter-api:jar:5.7.1:test
[INFO] | +- org.apiguardian:apiguardian-api:jar:1.1.0:test
[INFO] | +- org.opentest4j:opentest4j:jar:1.2.0:test
[INFO] | \- org.junit.platform:junit-platform-commons:jar:1.7.1:test
[INFO] \- org.junit.jupiter:junit-jupiter-engine:jar:5.7.1:test
[INFO] \- org.junit.platform:junit-platform-engine:jar:1.7.1:test
  • JUnit:JUnit 是一款非常流行的基于 Java 语言的单元测试框架,在我们的课程中主要使用该框架作为基础的测试框架。
  • JSON Path:类似于 XPath 在 XML 文档中的定位,JSON Path 表达式通常用来检索路径或设置 JSON 文件中的数据。
  • AssertJ:AssertJ 是一款强大的流式断言工具,它需要遵守 3A 核心原则,即 Arrange(初始化测试对象或准备测试数据)——> Actor(调用被测方法)——>Assert(执行断言)。
  • Mockito:Mockito 是 Java 世界中一款流行的 Mock 测试框架,它主要使用简洁的 API 实现模拟操作。在实施集成测试时,我们将大量使用到这个框架。
  • Hamcrest:Hamcrest 提供了一套匹配器(Matcher),其中每个匹配器的设计用于执行特定的比较操作。
  • JSONassert:JSONassert 是一款专门针对 JSON 提供的断言框架。
阅读全文 »

Redis 中不能直接使用布隆过滤器,Redis 4.0 版本之后提供的 modules(扩展模块)

布隆过滤器的原理

  • 数据结构使用一位的数组,每次存储键值的时候,不是直接把数据存储在数据结构中。而是经过hash运算。将此元素的 hash 值均匀的存储在位数组中。把这些位置设置成 1 就完成了添加操作。
  • 判断元素是否存在时,经过运算判断对应的位置是否为全部1即可。因此布隆过滤器查询此值存在时,此值不一定存在,但查询此值不存在时,此值一定不存在
  • 但是存在一定的误差,并且当位数组存储值比较稀疏的时候,查询的准确率越高,而当位数组存储的值越来越多时,误差也会增大。

  • 空间占用
    • 布隆过滤器有两个参数,第一个是预计元素的数量n,第二个是误判率p。公式根据这两个输入得到两个输出,第一个输出是位数组的长度m,也就是需要的存储空间大小(bit),第二个输出是hash函数的最佳数量k。hash函数的数量也会直接影响到误判率,最佳的数量会有最低的误判率。
  • 计算公式
1
2
k≈0.7*(m/n)    
p≈0.6185^(m/n)
  • 位数组m越长,误判率p就越低;
  • 位数组m越长,hash函数最佳数量k就越多,影响计算效率;

演示代码

安装Redis

演示情况直接使用docker来安装,免去下载编译过程。

1
docker run -p 6379:6379 -d redislabs/rebloom:latest
  • 使用redis-cli或者客户端工具进入
1
2
3
4
5
6
➜  docker-run redis-cli 
127.0.0.1:6379> bf.add 1 1
(integer) 0
127.0.0.1:6379> bf.add 1 2
(integer) 1
127.0.0.1:6379>
  • 如果没有提示错误则表示安装成功。
阅读全文 »

记录最近在Centos7上面部署jenkens。安装的方法有很多,下面采用最简单的方式安装。

参考文献

jenkins 是一个协调者的身份,管理和协调了代码库,代码仓库,代码运行环境等。

jenkins 简介

Jenkins是一个开源软件项目,旨在提供一个开放易用的软件平台,使软件的持续集成变成可能。持续集成(CI)已成为当前许多软件开发团队在整个软件开发生命周期内侧重于保证代码质量的常见做法。它是一种实践,旨在缓和和稳固软件的构建过程。并且能够帮助您的开发团队应对如下挑战:

  • 持续、自动地构建/测试软件项目。
  • 监控一些定时执行的任务。
  • 高度可扩展性、全球大量团队使用,并共享代码。
  • 基于插件、可以做出非常多有趣的事情。

安裝部署

阅读全文 »

WebClient

Spring 有两个web客户端的实现,一个是RestTemplate另一个是spring5的响应代替WebClient。

WebClient是一个以Reactive方式处理HTTP请求的非阻塞客户端。

  • RestTemplate是阻塞客户端

    • 它基于thread-pre-requset模型。
    • 这意味着线程将阻塞,直到 Web 客户端收到响应。阻塞代码的问题是由于每个线程消耗了一些内存和 CPU 周期。当出现慢速请求的时候,等待结果的线程会堆积起来,将导致创建更多的线程、消耗更多的资源。频繁切换CPU资源也会降低性能。
  • WebClient是异步、非阻塞的方案。

    • WebClient将为每个事件创建类似于“任务”的东西。在幕后,Reactive 框架会将这些“任务”排队并仅在适当的响应可用时执行它们。

    • WebClient是Spring WebFlux库的一部分。因此,我们还可以使用具有反应类型(Mono和Flux的功能性、流畅的 API 作为声明性组合来编写客户端代码。

    • 底层支持的库

      • Reactor Netty - ReactorClientHttpConnector
      • Jetty ReactiveStream HttpClient - JettyHttpConnector
  • 关于IDEA开启 Reactive Streams DEBUG

演示代码

阅读全文 »