0%

背景

公司从去年引入TRIZ,以激发产品创新,上半年宗磊带着RELAX几位架构师参加了一个课题。从目前公司的成果来看,还是一个非常不错的创新方法,公司也有非常大的决心去推进这个方法论的落地。

之前认为TRIZ不适用软件,上期陆续有多个软件课题获奖,包括一些持续集成,测试,算法等方面的课题,为了让我们未来的产品更有竞争力,我们将会加大这方面的投入。

学习

当我们遇到实际问题的时候,会产生截然不同的解决问题的思路。如果是使用传统的试错法,通常是根据问题的现状,利用自己的专业知识和行业知识,尝试不同的办法来寻找问题的解决方案。

img

图1 问题导向做法(图片选自网络并修改)

如图1所示,由于并非人人都是专家,都能遇到自己熟悉的问题,所以以往的经验未必对解决问题有太大的帮助。这种从问题出发,典型的问题导向做法往往不知道问题的解决办法在何处,有点类似“脚踩西瓜皮,划到哪里算哪里”的感觉。有时虽然能解决问题,但效率较低,且其解决办法是否是最优的也未必知晓。

如果是采用TRIZ创新方法,比如用到TRIZ的工具——最终理想结果(IFR,Ideal Final Result,也称最终理想解),这种从解决问题所追求的结果出发,目标明确,则是典型的结果导向做法,通过对系统的最终理想结果(IFR)的确定,来判断具体问题的解决方案的空间和解决问题的入手点。

如图2所示,就像在黑夜里航行的船舶看到航灯,其好处是既知晓问题解决的理想方向,在解决问题的时候始终不会迷失方向,又能找到合理的解决问题的入手机会,导向性非常明确,这是以往多数的创新方法所不具备的优点。

img

图2结果导向做法(图片选自网络并修改)

在实际的培训、咨询和辅导解题中,经常会遇到研发人员不太会正确定义最终理想结果(IFR)和合理使用此工具,不能准确锁定问题,严重影响了问题的解决。那么,如何指导研发人员快速、准确的确定最终理想结果(IFR)呢?

首先,我们来看看最终理想结果(IFR)的概念,最终理想结果(IFR)是对发明问题的最好解决方案模型它使系统完全消除了问题,并且没有让系统的参数发生恶化,且对系统的改变最小。它是解决方案的模型,可以指引着我们去解决发明问题。

通过上述概念,可以了解三个信息:

1)IFR是最好的解决方案模型,注意不是之一,可以用来引导和把握问题的解题方向;

2)通过IFR模型,既要解决系统存在的问题,又不能使系统的参数发生恶化,也就是要消除矛盾;

3)是对系统的最小改变,也就是最小问题,不要考虑对系统进行较大的改变,这不符合IFR的理念。

img

其次,最终理想结果(IFR)的定义过程中,要注意系统的最终目标和最终理想结果的区别。

系统的最终目标是指所设计的系统应该达成的结果,即客户之声****(VOC,Voice of Customer)。例如,客户需要清洁衣物,又不能过于劳累,所以设计了洗衣机,则洗衣机的最终目标就是清洁衣物,至于生产商用什么工作原理、什么系统组成来满足客户的需求,则会有不同的系统(产品)产生。

所以,最终目标往往是客户很模糊的需求,需要认真加以甄别,有相应的工具和流程,这里不加赘述。

再次,为了打破研发人员的惯性思维,通常建议定义IFR时把握两个原则:

**
**

1)以系统的作用对象(又称制品、目标)为入手来定义;

2)定义的IFR应用“自身”或“自我实现”的字眼。

img

例如,清洁衣物,可以用灰尘等污物自身脱离衣物来作为IFR。当然,如果能做到这一点,系统就是非常理想的系统,但是,这往往是不可能的,否则系统就没有存在的必要了。

既然IFR是不太容易实现,为什么还要这样定义呢?这里主要是基于以下考虑:

1)在解决问题之初,先抛开各种客观限制条件,把最终理想结果作为终极追求目标

2)针对问题情境,结合最终理想结果设立各种理想模型,即最优的模型结构来分析问题;

3)这样定义的IFR在后续的分析很容易达成共识,不会变来变去,干扰解题过程。

这时,我们就可以在最终理想结果(IFR)与系统问题现状之间不断选择不同的次理想结果,方便找到解决问题的入手点。

还是拿清洁衣物来举例说明,让灰尘等污物自己脱离衣物纤维这一IFR无法实现,那么就尝试改变工具(或执行机构),这样次理想结果就是衣物纤维自己能与灰尘等污物分离。

通常应注意:

1)技术物体比天然物体易于改变;

2)工具比制品易于改变;

3)如果系统中无易于改变的要素,即应指出”外部介质”,可以借助中介物原理。

此时需要考虑的就是衣物纤维能不能像荷叶那样,具有自洁能力,灰尘等污物无法附着在衣物纤维上,故而就可以实现清洁衣物的目的。

还有,当衣物纤维不具备自洁功能时,可以将下一个次生理想结果定义为衣物纤维借助外力实现与灰尘等污物分离,这时问题就转变为利用什么力(或场)能实现两者的分离,能达到清洁衣物的目的。

img

图3 后退收敛定义IFR的过程(图片选自法思诺FASINNO课件)

如此这般,通过逐步后退一小步收敛问题的不同理想结果(次生、次次生……),更易发现解决问题的机会

可以参照如图3所示的收敛过程进行定义,把不明确的问题通过IFR的多次定义、分析,将问题转化为明确的问题,寻找到解决问题的入手点。

  • 什么是TRIZ创新理论

TRIZ意译为发明问题的解决理论。TRIZ理论成功地揭示了创造发明的内在规律和原理,着力于澄清和强调系统中存在的矛盾,其目标是完全解决矛盾,获得最终的理想解。它不是采取折中或者妥协的做法,而且它是基于技术的发展演化规律研究整个设计与开发过程, 而不再是随机的行为。实践证明,运用TRIZ理论,可大大加快人们创造发明的进程而且能得到高质量的创新产品。

专利

TRIZ这个强而有力的工具消除在不同性能测量之间的冲突所引起对妥协和交换的需要,为创新带来了可执行的方法论。

特点

  • 降低尝试次数和错误迭代
  • 降低对共同的资源的需要
  • 使用演化的趋势打开历史,为新产品想法证明方向
  • 利用专利规避,突破竞争对手的防御

优越性

TRIZ 是一套以人为导向的知识系统之系统化创新问题解决方法。它有别于传统的脑力激荡,TRIZ强调发明或创新可依一定的程序与步骤进行,而非仅是随机或天马行空的脑力刺激。TRIZ 发展至今,从 300 万个专利分析中提取了 1000 个样式等,这个广泛的知识库可使用户在开发实际产品时,面对困难的局面和问题得以迅速获得可能的答案或思路。

img

示例

TRIZ具体项目的落地成果,取决于几个关键要素:

1. 能识别出关键问题

2. 能想到创新的方案

3. 能把这些方案做出来

因此,在这个过程中需要几个关键要素:

第一:大家能够整合不同的观点,找到关键问题和关键解决方案;

第二:能死磕,把方案落地过程中的关键问题能够解决掉;

第三:落地过程中的具体工作和资源支持。

在这个过程中,我们需要参与者付出三方面的努力:

\1. 积极参与小组讨论活动,因为协作的力量搭配上TRIZ流程才能发挥比较大的作用;

\2. 积极提出方案;

\3. 积极完成分工布置的主要任务。

那么,如何通过考核与激励来实现这一点呢?

我觉得可以从两个角度来实施:

从业务部门的角度,可以从项目管理的角度来推进落地,考核整个TRIZ小组的项目进度和流程的应用。可以通过定期的项目评审来完成,考核与激励指标都基于项目成果来设置。

从人力资源的角度,可以考核项目组里每个成员的参与度和贡献度,考核与激励指标基于出席率、创意的数量与质量等。

最终,业务考核可以与项目奖金挂钩,人事考核可以与年终评奖、职称等挂钩。

用图片来总结就是:

img

如果你对TRIZ感兴趣,并且是产品、研发部门的小伙伴,欢迎扫码添加小助手,发送暗号“TRIZ”,经过验证后,会邀请你进入【研发专属群】,精品直播课+大咖分享+专业咨询… 等着你!

Jest

skip,only,each修饰符

describetest可以连接skiponlyeach修饰符。如describe.skip('something', testFunction),会在测试时跳过这一个describeonly会使测试只运行指定的测试用例,这在某个测试用例出错Debug时非常好用。each修饰符可以执行多次参数不同的测试,它接受一个数组table和一个测试函数,table里的元素会作为参数传入测试函数。具体语法可以参见文档

beforeAll,afterAll,beforeEach,afterAll钩子函数

Jest也支持在执行测试用例之前以及之后执行一些代码来做一些工作,像在测试前设置好测试数据、在测试后清理测试数据。这些工作可以作为beforeAllafterAllbeforeEachafterAll的回调函数。

断言

Jest支持expect式的断言,像expect(1).toBe(1),其中toBe就是断言部分。Jest支持很丰富的断言。

相等断言

断言两个基本类型的值相等使用expect(val1).toBe(val2)。注意toBe断言使用Object.is()判断相等。它与==以及===都有不同。

如果要断言数组或者Object相等,使用toEqual断言。它会递归地判断每个属性/元素是否是相等的。

如果要断言数组或者Object相等,使用toEqual断言。它会递归地判断每个属性/元素是否是相等的。

toStrictEqual(value)

测试对象具有相同的类型和结构

.toEqual不同在于:

  1. 检查具有未定义属性的键。 例如 使用.toStrictEqual时,{a:未定义,b:2}与{b:2}不匹配。
  2. 检查数组。 例如 使用.toStrictEqual时,[,1]与[undefined,1]不匹配
  3. 检查对象类型是否相等。 例如 具有字段a和b的类实例将不等于具有字段a和b的文字对象。
数字大小断言
1
2
3
4
5
6
7
8
9
10
11
test('two plus two', () => {
const value = 2 + 2;
expect(value).toBeGreaterThan(3);//大于
expect(value).toBeGreaterThanOrEqual(3.5);//大于等于
expect(value).toBeLessThan(5);//小于
expect(value).toBeLessThanOrEqual(4.5);//小于等于

//等于
expect(value).toBe(4);
expect(value).toEqual(4);
});

对于浮点数,不能使用toBe或者toEqual进行相等断言。Jest提供了toBeCloseTo断言,可以在忽略一定误差的情况下,断言浮点数相等。

1
2
3
4
5
test('adding floating point numbers', () => {
const value = 0.1 + 0.2;
//expect(value).toBe(0.3); This won't work because of rounding error
expect(value).toBeCloseTo(0.3); // This works.
});
真值断言(Truthiness)
  • toBeNull 用于 null

  • toBeUndefined 用于undefined

  • toBeDefinedtoBeUndefined 相反

  • toBeTruthy 用于值为 true

  • toBeFalsy 用于值为 false

    1
    2
    3
    4
    5
    6
    test('null', () => {
    const n = null;
    expect(n).toBeNull();//pass
    expect(n).toBeDefined();//pass
    expect(n).toBeUndefined();// faild
    });
字符串相关

Jest提供toMatch断言被测试的字符串是否匹配给定正则表达式。

复制

1
2
3
test('but there is a "stop" in Christoph', () => {
expect('Christoph').toMatch(/stop/) // pass
});
数组

要断言数组中包含某个子项可以使用toContain断言。

复制

1
2
3
4
5
6
7
8
9
10
11
const shoppingList = [
'diapers',
'kleenex',
'trash bags',
'paper towels',
'beer',
];

test('购物清单(shopping list)里面有啤酒(beer)', () => {
expect(shoppingList).toContain('beer');
});
抛出异常

要断言对函数的某些操作会抛出异常可以使用toThrow断言。

复制

1
2
3
4
5
test('throws on octopus', () => {
expect(() => {
drinkFlavor('octopus');
}).toThrow();
});
not修饰符

not修饰符可以把所有的断言反向,像expect(1).not.toBe(2)

Jest提供的断言不止上面提到那么多。常用到的还有像断言长度的toHaveLength,断言对象有某个属性以及属性的值的toHaveProperty。更多断言的可以参见Expect文档

背景

最近整理产品中积累下来的工具类,因为要用新框架了要对之前的代码进行移植。当然咱是个追求完美的人,实在是团队小伙伴们一路疯狂操作下来,工具类是茫茫多啊,基本上想要维护是注定两行泪,所以咱们借着这次机会把工具类好好整理一下。

开始

诉求:

  1. 可维护性强。
  2. 能进行包管理。
  3. 每个方法有详细的说明,且能友好的查看。

具象化要求

  • 工具方法需要分门别类,不能一个文件一把梭,一定要控制代码量,不能让人一打开文件,心里就是wc。
  • 更新(删除、新增、修改)某个工具方法时不影响其它方法的使用,达到我改我的,你别瞪我的效果。
  • 不能发生函数名、变量名冲突。
  • 每个方法的注释遵循统一规范,根据统一规范可生产类似swagger那样的api文档(有张好的面皮大家爱看也耐看,不用不断打扰写方法的人)。

解决办法

秉着能抄袭就不自己动手的精神,看了很多star上千的工具库,总结了一下方式方法。

  • 基本上大家都采用的方式是一个方法一个文件,这一个文件暴露出来的就一个方法也只有一个方法,如果该方法需求其他辅助方法,直接通过import引用,而不直接写到该文件中,整个文件特别整洁,如果方法比较复杂则会建立一个文件夹从而存放辅助方法的文件,辅助方法的文件里也只有一个方法。虽然看上去可能这样文件有点多了,但是确实很清晰很整洁,对于维护来说很友好。所以就用这种方式了
  • 看了下生产api的几种库,比如jsdoc、ddoc等,大体是jsdoc还是多一些,用起来也挺简单,所以就选jsdoc了
编码方式

一个方法一个文件

rollup

打包工具用rollup,以为是工具库所以用更简单的rollup。

  • 自动 Tree-shaking(Tree-shaking, 也被称为 “live code inclusion,” 它是清除实际上并没有在给定项目中使用的代码的过程,但是它可以更加高效。)
  • 打包速度快
  • 配置简单
Jest

jest开箱即用挺好,另外我们多用react,jest是Facebook出的,与react具有天然亲和力。

Api Doc

选择了jsdoc,使用简单且较普遍。

实操

目录

关键手法

编码

进行编码后记得在index.js处暴露方法。

单元测试

单元测试时,如果想打断点,记得修改上图中的配置后重新生成库。

dist说明
打包

如下图:

  • 每次修改库时,记得改3处的版本号,以及1处的CHANGELOG对本次修改进行一个简要的说明。
  • 2所示部分根据不同时期的需求可对应进行更改,从而改变打包的内容。
  • 完事之后直接npm publish,如果需要关联到fusion design 则直接iceworks sync就行。
使用说明
1
2
3
npm i @riil-frontend/utils

import { arrayToTree,UnitFormat,Units,getNumberLength } from '@riil-frontend/utils';
重复提交

加节流防止重复提交

防抖和节流

https://juejin.im/post/6844904004850286605

https://www.lodashjs.com/

最后

如果本身已经引入了第三方得工具库,比如lodash、underscore等,先看看它们得API看是否有现成得,如果没有再自己整一个,不然重复造轮子没多大意思,更何况有可能你造的轮子有可能是扁的…

本文引用的内容,如有侵权请联系我删除,给您带来的不便我很抱歉。

背景

因为团队要用fusion design,所以按我的习惯我得先知道他是什么、为什么、能做什么,我才好下手。

开始

先不负责任的下一个结论,fusion design是个撒子?

答案:

一个平台: fusion.design

两个工具:

  1. 开发者工具 Iceworks
  2. 设计师工具 FusionCool

可以看作是下图这样纠缠:

Fusion Design

fusion design = 一套基础组件库 @alifd/next + 主题定制平台 https://fusion.design + 设计师工具 FusionCool+ 物料中心 。

所以更确切的说 Fusion Design算是一套体系,是一种旨在提升设计与开发之间 UI 构建效率的工作方式 ,我认为理解这点很重要,不然可能咱们用半天还以为他就是另一个库就像我们之前用的antd一样,其实完全不是一回事。

Fusion Design能解决哪些痛点:

  1. 【协作成本】内部(UCD和研发)协作问题,不再需要因为对概念、规范、复用性等问题UCD和研发不断沟通。

  2. 用户体验一致性问题 ,不同业务功能或者不同迭代功能,同样功能的交互和组件用户体验不一致。

  3. 【时间成本】重复工作问题,比如不断的review还原度不断的修正、UCD每次都需要对高保真进行规范说明以及关键内容的标注。

    旧模式如下图:红框部分是我们经常重复的内容。

Fusion Design提供了哪些能力来解决上诉的痛点:

  • 物料中心:各种组件、区块、模板(包含官方(Next等)+其它第三方+咱们自研的)

  • UI的可定制能力,设计师根据物料中心的内容定制UI,还可沉淀设计模板。

  • 研发都能配合前端工具(iceworks等),开发模块模板更高效,沉淀业务模板,后续可直接套用模板不用再开发。

  • 快速定制、切换主题。

    应用fusion design之后,产出过程应该就会像下图:

@alifd/next

  • Next 是基于 Alibaba Fusion Design 的设计理念实现的一套骨架DPL(Design Pattern Library)类似于咱们之前使用的antd。配合 fusion.design 使用可以实现换肤的能力。
  • 基于React的组件库。 可以理解**Next**是fusion design的技术实现。

小结一下

所以综上所述,引人fusion design后,理想状态下设计师和研发产出页面(功能)的过程应该会如下面两张图所示:

物料中心

可以理解成一个仓库,类似maven仓库或者npm仓库,里面可包含用开发好的物料(区块、模板、组件),该物料中心与sketch、iceworks是互通的,相互间可上传可下载。

FusionCool

FusionCool:组件分发工具,主要面向所有设计师。当组件构建者完成组件设计发布组件后,每位设计师手上的Fusion Cool都会“自动”接收到构建者的发布的组件样式,确保无缝衔接组件更新。

FusionCool也可以简单理解为是设计端使用的sketch 插件,达到sketch 既能设计页面,又能沉淀已经设计完成的模板。即设计师使用的同一套规范的组件,产出的设计稿都是同一套规范。

IceWorks

飞冰(ICE) 是一套基于 React 的中后台应用解决方案,ICE 包含了一条从设计端到开发端的完整链路,帮助用户快速搭建属于自己的中后台应用,开发者无须关注环境的问题,并且有海量物料可用。目前已经和 Fusion 的物料体系打通,可以轻松使用 Fusion 站点的物料。

Iceworks 是淘宝飞冰团队开发的面向前端开发者的 GUI 工具,开发者无须关注环境的问题,并且有海量物料可用。目前已经和 Fusion 的物料体系打通,可以轻松使用 Fusion 站点的物料。

fusion design的御用开发者工具,基于其开发各种组件丰富fusion design站点的物料中心,当然iceworks也能轻松使用 Fusion 站点的物料,两者互通。

总之

我个人认为fusion design的价值在于提升工作效率,因为它改造了前端(设计师和研发)的工作方式,减少了重复工作的内容,减少了沟通以及甩锅的成本,通过fusion design这个平台,让设计师和研发都能深度参与产品中来且这种参与是互补共赢的,它让设计师和研发之间的一些壁垒或者冲突点慢慢的消失了。

另外

对于角色(设计、研发)来说:可能最大变化就是对于通用性的、沉淀下来的物料,UCD才是老板,这块的样式布局等需要UCD统一把关收口,研发只需要更新包就行。总体上就是UCD的工作内容会增加但是研发时间会减少,协作时间也会减少,同时体验一致性也能达到要求。我估计能达到这种状态应该就可以要自行车了吧?

但是这玩意都是线上的,不知道能不能支持本地搭一套(如果不能搭是不是又不能要自行车了)?

题外篇:iceworks server

如需使用iceworks提供的一些快捷能力,比如新建项目(基于fusion design、react、typescript)、项目管理等。

用于练手刚好。

1.安装iceworks

npm install iceworks -g –registry=https://registry.npm.taobao.org

每个命令大家都可以玩一玩,我下面只介绍start的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
D:\baymax_projects\fusion-design-one>iceworks -h
Usage: iceworks <command> [options]

Options:
-V, --version output the version number
-h, --help output usage information

Commands:
start start and open the iceworks
init [type] [npmName] init project/material/component by template
add [options] [materialType] [npmName] add block to current directory
generate generate material collection data(material.json)
sync [options] sync materials data to Fusion Material Center
use <version> specify the iceworks-core version
config [type] [key] [value] operate iceworks global config

Run iceworks <command> --help for detailed usage of given command.
2.启动 安装iceworks-server

windows:

1
iceworks  start

linux:

1
2
3
#!/bin/sh
# iceworks start
iceworks # open http://localhost:8000/

3.若提示是否安装iceworks-server 直接Enter 默认是 稍等几分钟 自动启动浏览器

iceworks server使用方法

1.打开项目,(首先你要有项目包)

2.安装依赖

如果要切换cnpm源,设置包管理工具cnpm(前提是现状了cnpm),如不需要则跳过此步。

img

img

3.启动服务

当然你也可以本地运行

4.当页面变成这样说明已经启动成功:(会自动跳转到项目页面)

5.打开编辑器

img

感谢:

背景

其实老早就想做监控,之前整理的前端埋点(一)里头有说一些相关的内容。

不痛就不慌,所以说说我们痛的地方:

  1. 因为我们的产品都是部署在客户内网的,所以对于debug及其不友好,客户现场问题排查前端几乎没有任何输入,难弄。
  2. 产品迭代了多个版本,但是没有任何客户现场的用户行为等数据,产品优化少了一些输入。

本来计划是自研,但是由于业务压力突然来袭,所以就搁置了,不过我犹不放弃,觉得自研既然短期不现实,那可以站在巨人肩膀上搞一搞。

找了两个工具Sentry+rrweb,基于两个工具做一些定制化,手里不就有东西了吗。

Sentry

Sentry的应用程序监视平台可为每位开发人员提供帮助诊断,修复和优化其代码的性能。

config

config for JavaScript

React

features

package

@sentry/tracing 性能监控

1
2
# Using npm
$ npm install --save @sentry/react @sentry/tracing

config

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Sentry.init({
dsn: 'http://cb4e9b434f004c53a51af8ab45346635@172.17.162.101:9100/2',
integrations: [new Integrations.BrowserTracing()],//性能监控配置
// beforeSend (event, hint) {
// // Check if it is an exception, and if so, show the report dialog 错误弹窗
// if (event.exception) {
// Sentry.showReportDialog({ eventId: event.event_id });
// }
// return event;
// },
debug: false,// 调试模式
attachStacktrace: false,//附上堆栈信息
tracesSampleRate: 1,// Be sure to lower this in production
environment: 'development',
// logLevel: 2,
release: 'sentryTest-0.1.0'
});
1
2
3
4
5
6
7
8
9
10
11
// 创建一个 全局scope ,可以理解为上报数据的附加信息
Sentry.configureScope((scope) => {
//标签,可用于筛选
scope.setTag("first-tag", "Guide");
//绑定用户信息
scope.setUser({
id: 1,
name: "gamehu",
email: "gamehu@yeah.net",
});
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//局部:自定义上下文,补充信息
Sentry.setContext("zhangsan", {
name: "zhangsan",
age: 19,
attack_type: "zhangsan",
});
// 创建一个零时到 scope ,配置到 context 上面
const scope = new Sentry.Scope();
scope.setTag("section", "articles");
scope.setUser({
id: 2,
name: "zhangsan",
email: "zhangsan@yeah.net",
});

sourceMap

sentry-cli releases -o 组织 -p 项目 files staging@1.0.1 upload-sourcemaps js文件所在目录 –url-prefix 线上资源URI

sentry-cli releases files sentryTest-0.1.0 upload-sourcemaps –url-prefixhttp://172.17.162.101:9100/organizations/sentry/issues/61/?project=2&query=is%3Aunresolved‘ ‘./dist/static/js’

添加一个 EventProcessor 对全局生效

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
29
30
31
32
33
34
35
36

const attachmentUrlFromDsn = (dsn, eventId) => {
const { host, path, projectId, port, protocol, user } = dsn;
return `${protocol}://${host}${port !== '' ? `:${port}` : ''}${path !== '' ? `/${path}` : ''
}/api/${projectId}/events/${eventId}/attachments/?sentry_key=${user}&sentry_version=7&sentry_client=custom-javascript`;
}

//添加一个 EventProcessor 对全局生效
Sentry.addGlobalEventProcessor((event) => {
try {
const client = Sentry.getCurrentHub().getClient();
const endpoint = attachmentUrlFromDsn(
client.getDsn(),
event.event_id
);
const formData = new FormData();
const data = JSON.stringify({ logEntries: ["sentryTest"], message: event.message, logger: event.logger });
formData.append(
'my-attachment',
new Blob([data], {
type: 'application/json',
}),
event.event_id + '.json'
);
fetch(endpoint, {
method: 'POST',
body: formData,
}).catch((ex) => {
// we have to catch this otherwise it throws an infinite loop in Sentry
console.error(ex);
});
return event;
} catch (ex) {
console.error(ex);
}
});

RRWEB/TimeCat

录制回放工具,可单独使用也可搭配Sentry使用,可对用户操作录屏,针对一些现场问题可作为排查问题得输入.

rrweb使用指南

preview

监控理论的记录

要做监控先做设计,根据产品、研发、测试等的输入,整理出监控数据类别:

  • JS 的异常错误;
  • 资源测速;
  • 接口的成功率、失败率;
  • 性能。

img

img

img

本文引用的内容,如有侵权请联系我删除,给您带来的不便我很抱歉。

背景

前面的文章已经说过很多了,因为经常会到服务器上操作,所以记录几个常用的Linux命令。

ls命令

ls命令应该说是我接触的linux的第一批命令之一,属于没事就敲个ls装逼的程度,但是用下来发现也是一个宝藏命令,实用性很强。

ls

直接输入ls,不带任何其它选项,该ls命令提供有关由命令行上给定路径指向的每个文件对象的信息:

  • 只显示路径指向的文件对象的名称;
  • 如果该路径指向目录(或指向目录的符号链接),则该ls命令还会列出该目录的内容;
  • 如果显示多个条目,则将按文件名的字母顺序对其进行排序。

最后,当在命令行上未给出路径时,这些ls命令将采用./–即当前目录。

ls -l: 长格式显示

除文件名称外,亦将日期和时间、权限、拥有者、文件大小等资讯详细列出,我们经常使用的ll其实就是它的别名。

ls -a: 显示所有文件

使用该-a选项时,在显示目录内容时ls包括隐藏文件。但是什么是隐藏文件?

隐藏文件:名称以点开头的文件被视为隐藏文件。此外,每个目录还包含两个特殊的,通常为隐藏的条目:...

在每个目录中:

  • .条目指向目录本身。这种自我指称似乎很奇怪。但这有时很有用,有点像将自己的电话号码存储到智能手机库中。
  • ..条目指向父目录。由于类Unix系统上的文件层次结构严格地组织为一棵树,因此每个目录只有一个父目录。
ls -s: 显示文件分配的大小

这里大小的单位是,在linux中一块可以看作是1024字节,该大小指的不是逻辑大小而是实际大小。如下所示,a、b都是2097152,但是通过块的方式查看b只有1028,因为b目录下由 sparse files

1
2
3
sh:~/ls$ ls -ls a b
2052 -rw-r--r-- 1 sylvain sylvain 2097152 Sep 19 22:18 a
1028 -rw-r--r-- 1 sylvain sylvain 2097152 Sep 19 22:18 b
ls -h: 可读性强的方式显示文件大小

使用-h选项,ls将使用单位后缀显示文件大小,以使其更加用户友好。如图直接使用-h是没用的,结合l和s使用。

ls -d */: 只显示目录(文件夹)
ls -i: 显示文件的索引号

有点我们说的引用地址的意思,该选项在查看文件的硬、软链接时比较有用。比如查看某几个文件是否引用同一基础文件系统对象。

比如下图,切换到根目录,然后ls -ia你会发现... 的索引号都是2,证明指向的是同一个目录,这刚好可以解释根目录的父目录就是根目录自身。

ls ../

查看父目录文件。

ls ~

查看主目录的文件。

排序

ls -t :按修改时间倒序,最近修改的在前。

ls -S: 按文件大小正序,最小在前。

ls -r:反转排序。比如ls -rS,则会变成最大的在前。

ls -R:递归列出子目录,跟find .效果类似。

时间完整显示

ls --full-time:显示完整日期及时间。

OK

结束了,以上列举的80%都是我日常经常用的,希望对大家有帮助。

感谢

背景

我们是做NPMD工具的,但是对自身的产品本身确没有监控,说起来就很惆怅了,当然主要原因还是产品从0到1这个过程,前期放荡不羁的功能造作,导致对基础设施建设这块做的很少。

这是一贯坚持基础建设重要性的我的又一篇分享。关于前端埋点。

所以我当然承认基础建设是体系化的漫长的一个过程,但是因为各种不可抗因素,现没法体系化落地,所以只能先捡最能产生价值的且能引起更多不同之能的同事关注的事开始做,比如前端埋点。

先说痛点:

  1. 整个质量体系监控缺失,前端报错后端报错,全靠经验、人肉日志和用户主动反馈。

    特别是现场问题排查,通常都会先找到前端定位问题,现场又不能远程。

  2. 业务数据的效果无从跟踪。

    如使用某功能的频率无从得知,需要人肉从客户处拿此类数据,且还不准。

  3. 用户的访问行为/设备特征/应用性能信息完全无感知。

    如活跃时间点(避开做升级),软硬件系统和设备比例(做兼容),慢页面优化等无从做起。

前端埋点对我们产品的好处:

  1. 记录访问行为/设备特征/应用性能信息。

    为产品的设计,提供参考数据,对于我们这种从0到1的ToB产品,我个人觉得是特别需要这类用户反馈数据的,避免闭门造车。

    为产品的优化(性能、产品设计…)提供参考。

  2. 记录异常。

    便于问题的排查,我们处理的问题一般是客户现场问题,通常情况下又都是客户的内网,所以是无法远程的,这类异常记录手段刚好能丰富异常记录数据,便于问题的排查。

所以可以看出,前端埋点至少会影响的到产品、UCD、研发、测试,当利益方多的时候,事情才更有推下去的可能性。

埋点

决策源于数据,而数据源于采集,采集源于规则梳理,让这一切发生源于工程师的创造力和执行力。

埋点的方式

  1. 手动埋点

    手动代码埋点比较常见,即需要在采集数据的地方调用埋点的方法。

    优点是流量可控,业务方可以根据需要在任意地点任意场景进行数据采集,采集信息也完全由业务方来控制。这样的有点也带来了一些弊端,需要业务方来写死方法,如果采集方案变了,业务方也需要重新修改代码,重新发布。

    百度统计等第三方数据统计服务商大都采用这种方案;

  2. 可视化埋点

    即通过可视化工具配置采集节点,在前端自动解析配置并上报埋点数据。

    优点业务方工作量少,缺点则是技术上推广和实现起来有点难(业务方前端代码规范是个大前提,比如唯一ID等)。

    代表方案是已经开源的Mixpanel

  3. “无痕埋点”

    无痕埋点则是前端自动采集全部事件,上报埋点数据,由后端来过滤和计算出有用的数据,优点是前端只要加载埋点脚本。缺点是流量和采集的数据过于庞大,服务器性能压力山大,主流的 GrowingIO 就是这种实现方案。

那我们产品现阶段比较适合的方式时什么呢?

手动代码埋点的方案,代码埋点虽然使用起来灵活,但是开发成本较高且对业务代码有强侵入性,并且一旦发布就很难修改,更何况我们这是部署在客户现场的。

可视化埋点也是不太可行的,我们连工程化、规范化都没做到,而且是部署在客户现场的,先天不足,再一个可视化埋点通常基于xpath的方式,只能读取页面上标签元素展示出来的属性,不能够获取上下文(通常内存里)的一些属性。而且页面的结构发生变化时,需重新标记操作。

所以几乎只有考虑无痕埋点,虽然无痕埋点流量消耗和数据计算成本很高,但是因为我们是ToB运维工具,企业内部用的,使用人数也不多,所以也还好,当然这是第一版如果确实在实验局发现压力挺大,我们可以再改进一下,比如采用手动埋点+无痕埋点的方式或者三种结合的方式。

OK先把埋点方式定下来了,我们现在考虑剩下的东西。

埋点需求整理原则

埋点不能乱埋,埋点的原则是基于一系列问题展开,并基于这些问题确定埋点需求,怎么确定埋点需求,可以照下面的问题切入进行梳理

HOW:

  • 怎样证明新功能效果?
  • 需要哪些埋点?我们要采集什么内容,进行哪些采集接口的约定?
  • 我该怎么埋这些点?
  • 部分埋点的计算逻辑是什么?
  • 数据结果怎么看?
  • 通过什么方式来调用我们的采集脚本?
  • 无埋点:考虑到数据量对于服务器的压力,我们需要对无埋点进行开关配置,可以配置进行哪些元素进行无埋点采集

WHO:

  • 我的产品设计面对的用户群里是谁?
  • 用户特征是什么?
  • 这部分特征用户对功能预期的数据结果是什么?
  • 用户习惯是什么?

WHAT:

  • 产品包含哪几个模块?

WHERE:

  • 新功能展示在产品端的哪个位置?
  • 在哪些版本生效?
  • 哪些功能的展示或作用在哪里需要跟服务端等交互?

WHEN:

  • 功能是在用户场景什么时候调用产生?
  • 调用过程中什么时候和服务端交互?
  • 功能调用正常情况下需要大概需要多长时间?
  • 什么情况会影响调用结果?
  • 调用有风险的时候,是否需要加监测?

回答了上面的问题,基本上能知道埋点的意义在哪儿以及需要收集哪些数据等,接下来就得开始指定埋点规范了。

埋点规范

埋点规范就跟编码规范一样,不按照规范就会有很大的隐患比如以下问题:

  1. 埋点混乱

  2. 常常埋错,漏埋

  3. 业务变化后,老埋点数据失去意义

  4. 埋点数据无人使用,浪费资源

  5. 数据团队、研发团队、产品团队协作配合难度大

  6. 很多时候不太重视数据,而是重视业务的快速上线

  7. 埋点语义不明确,同一个按钮存在多种描述,不易检索

  8. 无用/重复埋点太多,干扰了正常埋点数据

  9. 大量存量埋点Owner离职或者转岗,导致大量僵尸埋点信息

    所以为了避免以上问题我们需要建立一个好的规范,比如命名规范和流程规范。

    埋点命名规范

    我们当前的做法是埋点名称只能是由字母、数字、下划线组成,并保证在应用内唯一,比如sw是展示,ck是点击。

    常用规则的举例如下:
    比如行为埋点:{团队|业务|角色}_{组件|页面}_{具体元素}_{动作}
    示例:
    front_alarm_sw : front代表项目,alarm代表功能,sw是展示,ck是点击
    front_alarm1_detail_table_point_ck :front代表项目,alarm1_detail代表功能,table组件,point小圆点组件,ck点击

埋点流程规范

如果你发现每天有大量埋点错误反馈,并导致很多错误的分析结论,一个错误的分析结果还不如没有数据分析结果。造成这个问题的原因包括:

  1. 前端埋点涉及复杂的交互,难以找准埋点位置;
    1. 埋点的验收流程不完善,没有经过测试和产品经理的测试和验收,验证不完备;

好的埋点需求应该和业务功能需求同等重要,也需要经历软件开发的整个生命周期,如果能严格按照一个规范的流程处理埋点,以上问题会得到更好的解决。

规范内容

需求阶段:

确定埋点信息,核心包括五方面信息:事件ID、埋点名称、埋点描述、埋点属性以及截图。

如何落地:

如果不按照规则和流程埋点将不会上报相关数据,制定强制规定。

开发什么功能:

埋点全文检索能力、自动找到重复埋点(语义相近或者数据相近)功能。

开发阶段:

务必和开发沟通,并让开发在完全理解业务语义的情况下,在前端按照埋点代码规范进行埋点。

如何落地:

静态代码检查。

开发什么功能:

开发探测每个埋点对应到的代码文件和代码行,开发根据人均事件量级进行监控报警功能。

测试阶段:

务必和测试沟通,并让测试在完全理解业务语义的情况下,通过CodeReview和埋点测试两种方式对埋点正确性进行验证。

如何落地:

规定如果未经测试的埋点不允许上报埋点数据。

开发什么功能:

提供所见即所得的埋点测试平台。

验收阶段:

确保相关人员在完全理解业务语义的情况下,可以通过与自比较和他比较的方式来进行验证。

举例:

- 他比较验证:前端某业务点数量和对应服务端数据应该基本相当。

如何落地:

规定如果未经验证的埋点不允许上报埋点数据。

开发什么功能:

提供埋点实时数据查询。

清理阶段:

确认完全理解语义的情况下,可对业务发生巨大变化或者不在维护的埋点进行废弃。

如何落地:

3个月以上未被使用的埋点、1个月以上数据为0的埋点自动废弃。3个月后使用次日会自动开启采集。

开发什么功能:

根据规则提供针对未使用埋点的计算、并自动废弃。

可以看出,规范要落地,需要整个公司的共识,也需要从上而下的压力,还有强势的制度。比如针对全公司个部门做评分,评分规则基于埋点和数据分析抽象出来。

另外我们在前端埋点中也遇到过一些注意事项,整理如下,希望对大家有所帮助。

注意事项:

一些埋点安全性问题:

如果你使用普通的http 协议,在数据传输的过程存在被劫持(包括不限于:JS代码注入等)的可能性,造成H5页面中出现诸如:未知的广告或者原网页被重定向等问题。为了降低此类事件发生的可能性,建议最好使用https 协议(倡导全站https),以确保数据传输过程的完整性,安全性。

版本迭代的时候埋点需要注意什么?

  • 如果是公用模块,可以非常放心安全的全量迁移埋点;
    • 如果是新增模块,那就需要注意是否遵循了最新的埋点规范,有没有重复的埋点命名存在,埋点是否符合业务逻辑;
    • 如果是下线模块,那就需要评估下线后对数据的影响范围,而且要第一时间充分周知相关方,让各方参与评估;
    • 如果是更新模块,需要评估在原有埋点上需要修改的埋点内容,是否需要重新埋点,删除不需要的埋点,而且要修改功能的数据承接。

感谢

背景

虽然我们系统有建设一些监控工具比如Grafana、Prometheus等,但是很多时候还是愿意直接先到服务器上去瞧一眼,特别是客户现场的问题,因为客户现场暂时不会装这些工具。因为我们是做NPMD的,流量大的时候资源消耗就比较大,加上前期先铺功能非功能需求的细节待完善,所以有时候会出现客户现场机器在一段时间后会变得比较慢的情况,这个时候那当然就说劈里啪啦一堆装逼的命令敲上去瞅瞅服务器咋了。

用的比较高频的命令就是ps、grep、top。用的倒是还算一般,不过一直没有深深的了解,所以刚好借着这个机会,深入的学习一下。演示用的是安装centos系统的vps。

ps

这命令应该是我使用的命令中的top one了,该命令用于获取正在运行的进程信息。 我们在看一些服务是否运行的时候通常都是用这个命令。我们可以获取任何用户在当前系统上运行的进程之类的信息,例如进程ID(PID)。

ps命令本身是一个扩展工具,ps –help a一下,会发现有很多的命令选项,说是有80多个。

当然我们只说说常用的。

基本用法

如果在Linux中使用不带任何选项的ps命令,它将显示当前shell中正在运行的进程:

1
ps

只会看到ps和bash。

1
2
3
  PID TTY          TIME CMD
2053 pts/0 00:00:00 ps
31585 pts/0 00:00:00 bash
  • PID是进程的唯一ID
  • TTY是已登录终端用户的类型。pts表示伪终端
  • TIME给您进程运行了多长时间
  • CMD是您运行以启动该过程的命令

很明显,我们并没有得到任何真实,有用的信息。

1.查看所有运行过程

如果要查看自己运行的所有进程,可以将ps命令与选项x一起使用,如下所示:

1
ps -x

x选项将显示所有进程,即使它们与当前tty(终端类型)不相关。

“– ”是可选的,但一般的Linux约定是加上“–”选项。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
  PID TTY      STAT   TIME COMMAND
543 tty1 Ss+ 0:00 /sbin/agetty --noclear tty1 linux
544 ? Ssl 0:01 /usr/bin/python2 -Es /usr/sbin/firewalld --nofork --nopid
546 ? S 0:00 [hwrng]
556 ? I< 0:00 [cryptd]
943 ? Ss 0:00 /sbin/dhclient -1 -q -lf /var/lib/dhclient/dhclient--eth0.lease -pf /var/run/dhclient-eth0.pid -H vultr eth0
1009 ? Ssl 0:53 /usr/bin/python2 -Es /usr/sbin/tuned -l -P
1012 ? Ssl 0:59 /usr/sbin/rsyslogd -n
1052 ? Ss 0:00 nginx: master process /usr/sbin/nginx
1265 ? Ss 0:02 /usr/libexec/postfix/master -w
12385 ? R 0:00 [kworker/u2:0-ev]
12684 ? Ss 0:00 sshd: root@pts/0
12688 ? Ss 0:00 sshd: root@notty
12690 pts/0 Ss 0:00 -bash

上面输出中的STAT表示过程状态代码。有兴趣可以查一下详细的说明。其实很少会看到仅使用选项x的ps命令。通常以这种方式伴随选项u:

1
ps -ux

使用选项u,您将获得有关每个进程的详细信息:

1
2
3
4
5
6
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
abhishek 503 0.0 0.4 681580 37516 pts/0 Sl 18:09 0:00 gedit
abhishek 2245 0.0 0.0 11300 1496 ? S 18:37 0:00 /usr/bin/ssh-agent -D -a /run/user/1000/keyring/.ssh
abhishek 3039 0.0 0.0 77344 3508 ? Ss 10:37 0:00 /lib/systemd/systemd --user
abhishek 3040 0.0 0.0 114632 360 ? S 10:37 0:00 (sd-pam)
abhishek 3054 0.0 0.1 517104 11512 ? SLl 10:37 0:01 /usr/bin/gnome-keyring-daemon --daemonize --login

如您所见,现在您获得了每个进程的用户名以及CPU 使用率内存使用率。RSS显示该进程当前在RAM中有多少内存,而VSZ显示该进程总共有多少虚拟内存。

2.使用ps aux命令查看所有正在运行的进程

您可能会一直在Linux教程和文档中看到ps -aux或看到ps aux它们。

使用添加的-a选项,您可以查看Linux系统上所有用户的运行进程。

1
ps -aux

命令输出与ps -ux相同,但是现在您也具有其他用户的进程。由于使用-u选项,您可以识别哪个进程属于哪个用户。

3.在Linux中使用ps -ef命令查看所有正在运行的进程

除了ps -aux以外,您还可以使用-e命令列出所有正在运行的进程。通常的做法是将与“f”结合使用,以获得用于运行进程的命令的完整列表。

1
ps -ef

还可以加上“H”,显示所有的父进程以及其下的子进程一起:

1
ps -efH
4.查看特定用户的所有正在运行的进程

要获取有关特定用户运行的所有进程的信息,可以将“-U”选项与用户名一起使用:

1
ps -U user_name

例如,我可以看到root用户正在运行的所有进程是这样的:

1
2
3
4
5
6
7
8
9
ps -U root
PID TTY TIME CMD
1 ? 00:00:41 systemd
2 ? 00:00:00 kthreadd
3 ? 00:00:00 rcu_gp
4 ? 00:00:00 rcu_par_gp
8 ? 00:00:00 mm_percpu_wq
9 ? 00:00:03 ksoftirqd/0
10 ? 00:01:22 rcu_sched
5.查看小组运行的所有进程

您还可以通过按组名或组ID来对正在运行的进程进行分类:

1
ps -G group_name_or_id

您可以与选项f结合使用以获取完整列表。

6.获取程序的所有出现次数和PID

ps命令的基本用法之一是获取正在运行的程序的进程ID(PID)。通常,当要终止行为异常的程序时,将搜索该程序的所有实例,获取其PID并使用kill命令终止该过程。

1
ps -C program__name

例如,如果我必须找到apt软件包管理器的所有正在运行的实例:

1
2
3
ps -C apt
PID TTY TIME CMD
11425 pts/1 00:00:00 apt

您也可以使用grep命令获得类似的结果。

1
ps aux | grep program_name
7.获取有关PID的过程信息

好的!您有一个PID,但您不知道它属于哪个进程。您可以通过以下方式使用ps命令从其PID查找过程信息,N即PID:

1
ps -pN

您可以通过使用逗号分隔多个PID,以使用多个PID:

1
ps -pN1,N2,N3

grep

其实上面提到了一点,结合ps命令搜索进程,所以可以看出grep命令就像一个过滤器一样的作用,grep在文件中搜索匹配条件的内容,并显示包含该条件的所有行。

基本用法

1
2
3
4
5
[root@vultr ~]# grep --help
Usage: grep [OPTION]... PATTERN [FILE]...
Search for PATTERN in each FILE or standard input.
PATTERN is, by default, a basic regular expression (BRE).
Example: grep -i 'hello world' menu.h main.c
1
2
3
4
5
$cat > geekfile.txt
unix is great os. unix is opensource. unix is free os.
learn operating system.
Unix linux which one you choose.
uNix is easy to learn.unix is a multiuser os.Learn unix .unix is a powerful.

1.不区分大小写的搜索: -i选项允许在给定文件中不区分大小写地搜索字符串。比如“ UNIX”,“ Unix”,“ unix”等词都可匹配。

1
$grep -i "UNix" geekfile.txt

Output:

1
2
3
unix is great os. unix is opensource. unix is free os.
Unix linux which one you choose.
uNix is easy to learn.unix is a multiuser os.Learn unix .unix is a powerful.

2. 显示与条件匹配的文件名

1
2
3
4
5
$grep -l "unix" *

or

$grep -l "unix" f1.txt f2.txt f3.xt f4.txt

Output:

1
geekfile.txt

3. 检查文件中的整个单词 : 默认情况下,即使grep在文件中找到子字符串,它也只会匹配给定的字符串。 grep的-w选项使其仅匹配整个单词。

1
$ grep -w "unix" geekfile.txt

Output:

4.使用grep -n在显示输出时显示行号:要显示匹配行的文件的行号。

1
$ grep -n "unix" geekfile.txt

Output:

1
2
1:unix is great os. unix is opensource. unix is free os.
4:uNix is easy to learn.unix is a multiuser os.Learn unix .unix is a powerful.

5.反转模式匹配:您可以使用-v选项显示与指定搜索字符串模式不匹配的行。

1
$ grep -v "unix" geekfile.txt

Output:

1
2
learn operating system.
Unix linux which one you choose.

6.匹配以字符串开头的行: ^正则表达式模式指定行的开头。可以在grep中使用它来匹配以给定的字符串开头的行

1
$ grep "^unix" geekfile.txt

Output:

1
unix is great os. unix is opensource. unix is free os.

7.匹配以字符串结尾的行:“ $”正则表达式模式指定行的结尾。可以在grep中使用它来匹配以给定字符串结尾的行。

1
$ grep "os$" geekfile.txt

Output:

1
2
[root@vultr home]# grep "system.$" geekfile.txt
learn operating system.

grep正则有兴趣的可以看下regular-expression-grep。通常我是ps和grep结合在一起用,比如:

1
2
3
[root@vultr home]# ps -ef |grep -i "v2ray"
root 1008 1 0 Apr17 ? 00:01:57 /usr/bin/v2ray/v2ray -config /etc/v2ray/config.json
root 29435 28377 0 11:09 pts/0 00:00:00 grep --color=auto -i v2ray

小结一下

Linux grep command options Description
-i 忽略大小写
-w 强制PATTERN只匹配整个单词
-v 反向匹配
-n 输出匹配的行号
-h 在输出中禁止Unix文件名前缀
-r 在Linux上递归搜索目录
-R 就像-r一样,但是遵循所有符号链接
-l 仅打印具有选定行的文件名称称
-c 每个文件仅打印选定行的数量
–color 颜色显示匹配的内容

top

top命令提供系统信息的快速概述,它提供了正在运行的系统的动态实时视图,每3秒刷新一次(默认情况下)。 视图内容分为两部分:

  1. 系统的摘要信息
  2. 当前由Linux内核管理的进程或线程的列表。

一旦运行此命令,它将打开一个交互式命令模式,其中上半部分将包含进程和资源使用情况的统计信息。 下半部分包含当前正在运行的进程的列表。 按q会退出命令模式。

上半部分

如图,在终端的顶部,我们获得了概览数据,包括当前任务数、内存使用率和cpu负载。

第一行:任务队列信息,同uptime命令的结果一样。

4:06:23 — 当前系统时间

up 4 days, 22:36 — 系统已经运行了4天22小时36分钟(在这期间系统没有重启过的吆!)

2 users — 当前有2个用户登录系统

load average: 0.00, 0.00, 0.00 — load average后面的三个数分别是1分钟、5分钟、15分钟的负载情况。例如,负载为1.0表示当前负荷为100%。

load average:平均负载部分表示一分钟,五分钟和十五分钟的平均“负载”,数据是每隔5秒钟检查一次活跃的进程数,然后按特定算法计算出的数值。。 “负载”是系统执行的计算工作量的度量。 在Linux上,负载是在任何给定时刻处于R和D状态的进程数。 “平均负载”值为您提供了一个等待时间,可以用来衡量您需要等待多长时间才能完成工作。

在多核系统上,您应该首先将平均负载除以CPU核数以得到类似的度量。

第二行:Tasks — 任务(进程)

系统进程也称为任务。进程可以以许多不同的方式运行,并使用各种算法确定优先级。 这有助于优化计算机执行任务的方式和时间。

1
Tasks:  83 total,   1 running,  48 sleeping,   0 stopped,   0 zombie

对状态进行简单的说明:

State Description
Running 运行中/待处理(Active / in Queue to be Processed)
Sleeping 休眠(Waiting for a Process to Complete)
Stopped 停止(Interrupted by Job Control Signal )
Zombie 僵尸进程(Made up of “Orphaned” Child Tasks / No Longer Running)

第三行:cpu状态信息

1
%Cpu(s):  0.3 us,  0.0 sy,  0.0 ni, 99.7 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st

CPU使用率部分显示了在各种任务上花费的CPU时间的百分比。

Abbreviation Description
us CPU在用户空间(各个进程使用)中花费在执行进程上的时间
sy 运行内核空间(内核使用)进程所花费的时间
ni 优先级
id CPU保持空闲的时间
wa CPU等待I / O完成所花费的时间
hi 处理硬件中断花费的时间(中断是向处理器发出有关需要立即关注的事件的信号)
si 处理软件中断花费的时间(中断是向处理器发出有关需要立即关注的事件的信号)
st CPU在虚拟机上花费的时间

第四行:内存状态信息

“内存”部分显示有关系统内存使用情况的信息。 标记为“ Mem”和“ Swap”的行分别显示有关RAM和交换空间的信息。 简而言之,交换空间是硬盘的一部分,就像RAM一样使用。 当RAM使用率接近满时,RAM的不常用区域将写入交换空间,以备以后需要时检索。 但是,由于访问磁盘的速度很慢,因此过多地依赖交换会损害系统性能。

1
2
KiB Mem :  1006744 total,   200872 free,   146212 used,   659660 buff/cache
KiB Swap: 0 total, 0 free, 0 used. 660908 avail Mem

“avail Mem”值是可以分配给进程而不会引起更多交换的内存量。

Linux内核还尝试以各种方式减少磁盘访问时间。 它在RAM中维护“磁盘缓存”,在RAM中存储磁盘的常用区域。 另外,磁盘写操作存储在“磁盘缓冲区”中,内核最终将其写出到磁盘。 它们消耗的总内存为“ buff / cache”值。缓存使用的内存将在需要时分配给进程。

下半部分

在终端的下部,我们有一个任务信息的表格,其中包含许多详细信息,先简单解释一下表头。

Abbreviation Description
PID 进程ID(唯一正整数)
USER 用户名
PR 代表任务的优先级
NI 代表任务的价值。 负值表示优先级较高,正值表示优先级较低
VIRT 任务使用的虚拟内存
RES 任务使用的物理内存
SHR 任务使用的共享内存
S 进程状态(正在运行,已停止等)
%CPU CPU 负载
%MEM 物理内存/总内存的百分比
TIME + 自启动以来该进程使用的总CPU时间,精确到百分之一秒
COMMAND 进程名称

基本用法

杀进程

如果您想终止进程,只需在top运行时按“ k”。 这将弹出提示,提示您输入进程的进程ID,然后按Enter。

如下图中的:PID to signal/kill [default pid = 1]

1
2
3
4
5
6

Tasks: 81 total, 2 running, 46 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.0 us, 0.3 sy, 0.0 ni, 99.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 1006744 total, 202708 free, 144288 used, 659748 buff/cache
KiB Swap: 0 total, 0 free, 0 used. 662828 avail Mem
<span style=color:red>PID to signal/kill [default pid = 1]</span>

进程列表排序

使用诸如top之类的工具的最常见原因之一就是找出哪个进程消耗最多的资源。 您可以按以下键对列表进行排序(记住是大写哦):

  • “ M”按内存使用量排序
  • “ P”按CPU使用率排序
  • “ N”按进程ID排序
  • “ T”按运行时间排序

默认情况下,top按降序显示所有结果。 但是,您可以通过按’R’切换到升序。

您也可以使用-o+表头的名称对列表进行排序。 例如,如果要按CPU使用率对进程进行排序,可以使用以下方法:

1
top -o %CPU

查看线程

1
2
3
top -H
//出现的视图中的Tasks会变为Threads
Threads: 103 total, 3 running, 68 sleeping, 0 stopped, 0 zombie

显示完整路径(绝对路径)

默认情况下,top不显示程序的完整路径,也不区分内核空间进程和用户空间进程。 如果您需要此信息,请在top运行时按“ c”,再次按“ c”返回默认设置。内核空间进程周围带有方括号标记。

1
2
3
4
5
2036 root      20   0  158820   8852   7532 S  0.3  0.9   0:00.01 sshd: root [priv]
1 root 20 0 46124 8060 5616 S 0.0 0.8 0:15.58 /usr/lib/systemd/systemd --switched-root --system --deserialize 22
2 root 20 0 0 0 0 S 0.0 0.0 0:00.13 [kthreadd]
3 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 [rcu_gp]
4 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 [rcu_par_gp]

或者运行以下命令

1
top -c

树状显示

如果希望查看进程的子级父级。 按“ V”。比如下图所示systemd为系统启动的第一个进程,该进程又创建了sshd等其它进程…。

1
2
3
4
5
6
7
8
9
10
11
12
13
    1 root      20   0   46124   8060   5616 S  0.0  0.8   0:15.58 systemd
439 root 20 0 37204 3708 3396 S 0.0 0.4 2:00.59 `- systemd-journal
1012 root 20 0 258612 9696 8084 S 0.0 1.0 1:14.57 `- rsyslogd
1052 root 20 0 131160 2188 60 S 0.0 0.2 0:00.00 `- nginx
1053 nginx 20 0 132160 10100 7252 S 0.0 1.0 0:46.42 `- nginx
1265 root 20 0 89716 4836 3804 S 0.0 0.5 0:02.67 `- master
1268 postfix 20 0 89888 6600 5596 S 0.0 0.7 0:00.65 `- qmgr
1608 postfix 20 0 89820 6628 5624 S 0.0 0.7 0:00.01 `- pickup
13138 root 20 0 112936 7732 6708 S 0.0 0.8 0:16.48 `- sshd
28367 root 20 0 159228 10264 8572 S 0.0 1.0 0:03.73 `- sshd
28377 root 20 0 115464 3624 3208 S 0.0 0.4 0:00.13 `- bash
2053 root 20 0 161908 4444 3804 R 0.3 0.4 0:00.21 `- top

列出指定用户的进程

要列出某个用户的进程,请在top运行时按“ u”。

或者用以下命令:

1
top -u root

过滤进程

如果只想查看某些进程,可以使用top的过滤。 要激活此模式,请按“ o” /“ O”。 顶部会出现一个提示,您可以在此处键入过滤器表达式。如下图中的add filter #1 (ignoring case) as: [!]FLD?VAL

1
2
3
4
5
6
7
KiB Mem :  1006744 total,   198520 free,   147308 used,   660916 buff/cache
KiB Swap: 0 total, 0 free, 0 used. 659616 avail Mem
add filter #1 (ignoring case) as: [!]FLD?VAL
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1 root 20 0 46124 8060 5616 S 0.0 0.8 0:15.74 systemd
2 root 20 0 0 0 0 S 0.0 0.0 0:00.13 kthreadd
3 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 rcu_gp

过滤器表达式是指定属性和值之间关系的语句。 过滤器的一些示例是:

  • COMMAND = getty:过滤在COMMAND属性中包含“ getty”的进程。
  • !COMMAND = getty:过滤在COMMAND属性中没有“ getty”的进程。
  • %CPU> 3.0:筛选CPU利用率超过3%的进程。

添加过滤器后,您可以通过添加更多过滤器来进一步简化操作。 要清除添加的所有过滤器,请按“ =”。

更改CPU和内存统计信息的默认外观

如果觉得top显示CPU和内存统计信息的默认方式不喜欢。 可以按“ t”和“ m”来更改CPU和内存统计信息的样式。

保存设置

如果您对top的输出进行了任何更改,则可以按“ W”将其保存以备后用。 top将其配置写入主目录中的.toprc文件。

感谢

最近两周被平台组指名道姓拉去当了两周的苦力,写业务层代码,因为逻辑比较复杂数据输入比较多样,所以导致使用集合的概率很高,且常常伴随着过滤、排序等操作,继而用到了很多Streams提供的方法,遂做个简单记录。

Streams

Stream是Java 8中引入的新的抽象层,它提供了一些类似SQL语句的声明性方式处理数据。

流操作分为中间操作和最终操作,元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。 流管道由一个源(例如Collection,数组,生成器函数或I / O通道)组成; 随后是零个或多个中间操作,例如Stream.filter或Stream.map; 以及诸如Stream.forEach或Stream.reduce之类的终端操作。

即将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道中插入节点上进行处理, 比如筛选, 排序,聚合等。

当然为什么喜欢用它还是因为Stream API 借助于同样新出现的 Lambda 表达式,极大的提高编程效率和程序可读性。会让代码看起来更加简洁当然通常也会更加高效。

API

如上图api很多,其中又可以按照最开始说的分为中间操作、最终操作两类,中间(Intermediate)操作是可以零个或者多个但是最终(Terminal)操作只能有一个,能力有限我就列举一下我常用的。

可能我们需要注意的一个概念:因为一个 Stream 可以进行多次中间操作,那是不是就会对 Stream 的每个元素进行转换多次,即时间复杂度就是 N(转换次数)个 ?其实不是这样的,转换操作都是 lazy 的,多个转换操作只会在 Terminal 操作的时候融合起来,一次循环完成。

中间操作(intermediate operation)

API 说明
filter 用于按指定条件过滤元素
map map方法将每个元素映射到其相应的结果,通常用于list转换为map
limit limit返回流中的前N个元素,同SQL的limit
sorted 对Stream中的元素进行排序
distinct 删除重复项

最终操作(terminal operation)

API 说明
forEach 迭代Stream中的元素
sum 对Stream中的元素求和
collect 可以接受各种参数并将流元素累加成集合
reduce 这个方法的主要作用是把 Stream 元素组合起来。它提供一个起始值(种子),然后依照运算规则(BinaryOperator),和前面 Stream 的第一个、第二个、第 n 个元素组合。其实sum等这类也可以说是reduce。
max 获取Stream中符合条件的最大值
findAny 这是一个 termimal 操作,它总是返回 Stream 的符合条件的元素,或者空。注意它的返回值类型是Optional(为了避免空指针)。
anyMatch Stream 中只要有一个元素符合传入的 条件。

示例

列出几个工作中实践的例子

1
2
3
// 用于按指定条件过滤元素并且把符合条件的添加到指定的集合
List<CiStrategy> sorted = new ArrayList<>(cis.size());
cis.stream().filter(it -> it.getType() == StrategyType.GLOBAL).forEach(sorted::add);
1
2
3
4
5
6
7
8
9
10
11
// 拿输入的id到stream中比较是否存在,如果不存在则返回null
final List<String> agentEnableIds = getEnableAgenIdsByApp(query.getAppId());

String enableId = agentEnableIds.stream()
.filter(id -> agentId.equals(id))
.findAny()
.orElse(null);
//未开启xx
if (isNullOrEmpty(enableId)) {
return Collections.emptyList();
}
1
2
// 两个集合中,集合B中找到符合集合A中的数据,最终得到符合条件的元素集合
agentCmsList.stream().filter(ag -> agentEnableIds.contains(ag.getId())).collect(Collectors.toList());
1
2
3
4
5
6
// checkList的元素作为IpV4Ranges中toRange方法的参数,最终把toRange返回值转换为集合
List<String> checkList = splitter.splitToList(scopesToCheck);
List<IpRange> rangesToCheck = checkList.stream().map(IpV4Ranges::toRange).collect(toList());

// failed集合中,去重后的类型失败的有哪些
List<String> types = failed.stream().map(Quality.Metric::getType).distinct().collect(toList());
1
2
3
4
// Alarm的list集合,转换为map,key为Alarm的appId,value为Alarm
List<Alarm> dealingAlarms = dealingAlarmPage.getList();
Map<String, Alarm> dealingAlarmMap = dealingAlarms.stream().collect(Collectors.
toMap(Alarm::getAppId, Function.identity()));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 根据状态排序,如果状态一样按照名称排序
public IBoardAppDataList sortByAppStatus(String[] statusOrder) {
return new BoardAppDataList(this.stream().sorted(new Comparator<BoardAppData>() {

private int findStatus(String[] a, BoardAppData target) {
return IntStream.range(0, a.length)
.filter(i -> String.valueOf(target.getStatus()).equals(a[i]))
.findFirst()
.orElse(-1); // return -1 if target is not found
}

@Override
public int compare(BoardAppData o1, BoardAppData o2) {
int c = findStatus(statusOrder, o1) - findStatus(statusOrder, o2);
if (c == 0) {
return o1.getAppName().compareTo(o2.getAppName());
} else {
return c;
}
}
}).collect(Collectors.toList()));
}
1
2
3
// 判断输入参数里是否存在任意一个满足haveXssCondition
Set<String> keys = paramsObj.keySet();
return keys.stream().anyMatch(key -> haveXssCondition(uri, paramsObj, key));

最后

很显然我这篇仅仅是一个简单的记录文档,如果需要深入了解,还是系统的看相关的文档和源码。而且我主要用的是stream其实还有parallelStream,有兴趣的大家可以看看。

感谢:

搬运DevOps实施手册

  1. 建立愿景与方向
  2. 度量:组织、系统现状
  3. 准入条件。查看是否满足实施 DevOps 的准入条件。
  4. 探索可行方案。即 MVP 尝试
  5. MVP。一次快速的 DevOps 过程和结果的 showcase。
  6. 精细化 DevOps 实施
  7. 回顾优化
  8. 规模化 DevOps 落地

对于技术债务,它的利息表现为系统的不稳定性,以及由于临时性手段和缺乏合适的设计、文档工作和测试带来的不断攀升的维护成本。 —— 《架构师应该知道的 97 件事》