BackOff重试算法

可以使用斐波那契数列计算重试时间,也可以使用指数补偿计算重试时间。

定义一个接口

@FunctionalInterface
public interface ExponentialBackOffFunction <T> {
	T execute();
}

定义重试工具类

import static java.util.Arrays.asList;

import java.net.SocketTimeoutException;
import java.util.List;

import javax.net.ssl.SSLHandshakeException;

import lombok.extern.log4j.Log4j;

@Log4j
public final class ExponentialBackOff {
	
	private static final int[] FIBONACCI = new int[] { 1, 1, 2, 3, 5, 8, 13 };
	private static final List<Class<? extends Exception>> EXPECTED_COMMUNICATION_ERRORS = asList(
			SSLHandshakeException.class, SocketTimeoutException.class);

	private ExponentialBackOff() {
	}

	// E(c) = Math.pow(2, c) - 1
	// 考虑到补偿时间的均匀分布,补偿时间的数学期望是所有可能性的平均值。
	// 也就是说,在c次冲突之后,补偿时隙数量在 [0,1,...,N] 中,其中 [公式] E(c) = Math.pow(2, c) - 1
	// 则补偿时间的数学期望 E(c) = (Math.pow(2, c) - 1)/2
	public static long getWaitTimeExponential(int attempt) {
		double waitTime = (Math.pow(2, attempt) - 1) / 2;
		return Math.round(waitTime * 100);
	}
	
	// 这个地方使用斐波那契数去增加重试时间
	public static long getWaitTimeDefault(int attempt) {
		return FIBONACCI[attempt] * 100;
	}

	public static <T> T execute(ExponentialBackOffFunction<T> fn) {
		for (int attempt = 0; attempt < FIBONACCI.length; attempt++) {
			try {
				return fn.execute();
			} catch (Exception e) {
				handleFailure(attempt, e);
			}
		}
		throw new RuntimeException("Failed to communicate.");
	}

	private static void handleFailure(int attempt, Exception e) {
		if (e.getCause() != null && !EXPECTED_COMMUNICATION_ERRORS.contains(e.getCause().getClass()))
			throw new RuntimeException(e);
		doWait(attempt);
	}

	private static void doWait(int attempt) {
		try {
			long sleepTime = getWaitTimeExponential(attempt);
			System.out.println("attempt " + attempt + " sleep " + sleepTime);
			Thread.sleep(sleepTime);
		} catch (InterruptedException e) {
			throw new RuntimeException(e);
		}
	}
}

测试案例

public class Test {
	public static void main(String[] args) {
		ExponentialBackOff.execute(new Work());
	}
}

class Work implements ExponentialBackOffFunction<String> {

	@Override
	public String execute() {
		int a = 5 / 0;
		return a + "";
	}
}

运行结果

使用指数补偿:
attempt 0 sleep 0
attempt 1 sleep 50
attempt 2 sleep 150
attempt 3 sleep 350
attempt 4 sleep 750
attempt 5 sleep 1550
attempt 6 sleep 3150
使用斐波那契数列
attempt 0 sleep 100
attempt 1 sleep 100
attempt 2 sleep 200
attempt 3 sleep 300
attempt 4 sleep 500
attempt 5 sleep 800
attempt 6 sleep 1300

Goroutines vs Threads

Here are some of the advantages of Goroutines over threads:

You can run more goroutines on a typical system than you can threads.
Goroutines have growable segmented stacks.
Goroutines have a faster startup time than threads.
Goroutines come with built-in primitives to communicate safely between themselves (channels).
Goroutines allow you to avoid having to resort to mutex locking when sharing data structures.
Goroutines are multiplexed onto a small number of OS threads, rather than a 1:1 mapping.
You can write massively concurrent servers withouth having to resort to evented programming.

You can run more of them
On Java you can run 1000’s or tens of 1000’s threads. On Go you can run hundreds of thousands or millions of goroutines.

Java threads map directly to OS threads, and are relatively heavyweight. Part of the reason they are heavyweight is their rather large fixed stack size. This caps the number of them you can run in a single VM due to the increasing memory overhead.

Go OTOH has a segmented stack that grows as needed. They are “Green threads”, which means the Go runtime does the scheduling, not the OS. The runtime multiplexes the goroutines onto real OS threads, the number of which is controlled by GOMAXPROCS. Typically you’ll want to set this to the number of cores on your system, to maximize potential parellelism.

They let you avoid locking hell
One of the biggest drawback of threaded programming is the complexity and brittleness of many codebases that use threads to achieve high concurrency. There can be latent deadlocks and race conditions, and it can become near impossible to reason about the code.

Go OTOH gives you primitives that allow you to avoid locking completely. The mantra is don’t communicate by sharing memory, share memory by communicating. In other words, if two goroutines need to share data, they can do so safely over a channel. Go handles all of the synchronization for you, and it’s much harder to run into things like deadlocks.

No callback spaghetti, either
There are other approaches to achieving high concurrency with a small number of threads. Python Twisted was one of the early ones that got a lot of attention. Node.js is currently the most prominent evented frameworks out there.

The problem with these evented frameworks is that the code complexity is also high, and difficult to reason about. Rather than “straightline” coding, the programmer is forced to chain callbacks, which gets interleaved with error handling. While refactoring can help tame some of the mental load, it’s still an issue.

Golang 的 goroutine 是如何实现的?

OKR 填写指南

一、OKR概述
OKR是一个目标管理工具。其中O指Objective,是团队或个人的工作目标;KR指Key Result,是一系列可以衡量的关键结果,用来判断Objective是否达成。在作业帮,OKR的制定和共享,是公司、团队和个人制定任务,对齐目标,协调和集中精力的重要手段。

二、OKR执行要点
工作目标设置应该激进,要使自己和团队感受到压力。
关键结果要容易打分衡量,不要模棱两可。
除保密事项外,OKR尽可能的公开,方便互相了解在忙些什么工作。
写好OKR,要求严格的聚焦,只写最重要的目标,不罗列堆砌。
OKR并不是绩效考核工具,但应该是自我检测工作成果的重要工具。
OKR不是一个共享的工作清单或者to-do list,而是一个管理精力,自我规划的重要工具。
没有经过沟通和对齐的OKR,相当于没有写,OKR的对齐方包括你的上级、同事和你承接需求(提出需求)的协同方。

三、设定激进、聚焦的目标

在每个OKR周期中,公司从CEO到全体员工都会制定自己的OKR,通常是三四个Objectives,每个Objective有3个左右的Key Results。这些OKR既有从上到下目标分解而来的,也有从基层收集的各种意见演化而来,所以OKR的制定是一个循环迭代和讨论的过程。

我们希望每一个人都制定激进的目标,这些目标看上去刚好处于 “这个周期完不成” 的边缘。事实证明,制定高目标,有助于我们取得优于普通水准的成果。 每个高目标都需要全情投入。所以OKR制定的另一方面,是严格地聚焦到重点方向,不贪多。但在选定的重点方向上,力求保证目标达成。

一些设定Objective的窍门:

选三五个目标即可,宁缺勿滥。太多目标,工作容易失去焦点,团队也会疲于应付。
描述最终状态,比如“上线xx功能”,“获得10%的市场份额”。
常规动作不要写进OKR,例如“继续推进”、“保持行业地位”。如果确是长期重要工作,要思考如何拆解成合理的周期OKR,而不是不经思考地写“继续做”。
团队leader是团队的大脑,更要注意排优先级和授权。

四、可衡量的Key Result

关键结果(Key Result,或简称KR)是用来评价目标是否达成的。考虑到这个功能,对关键结果最大的要求就是容易衡量,且直接支持目标的达成。

一些设定Key Result的窍门:

每个目标定3个左右KR即可,不多刻意多写。
注意,KR是用来衡量Objective的达成程度的,问一下自己“这个KR和相应的Objective有直接的支撑关系吗”。
要描述产出,而不是动作。产出是指类似于“发表一篇论文”这样的表述,而动作是指“进行研究并撰写论文”这样的表述。所以当一个KR中出现 “参与”、“分析”、“辅助”这样的词汇,或者描述过程的动词特别多时,可能就有问题了。
用客观、外部可观测和不模糊的表述。最好是你的同事也能够准确地对你的KR进行打分,这说明KR足够清晰。
五、常见错误
执行不好的OKR,不但对公司、团队和个人没有帮助,反而可能造成团队安于现状,甚至方向混淆、内耗。所以我们要避免常见的OKR执行错误,比如下面这些:

堆砌不重要的目标
OKR常常被写得很长、很多,很难用两三句阐述其中的重点。往往这样的OKR包含大量的常规工作。常规工作不是不可以写,而是要判断,这项工作需要我和团队付出额外的努力才能达成吗。因为OKR要求激进,“担心有可能完不成”,相当多的常规目标是达不到这个要求的。这里有两点需要澄清:一、OKR不是工作量的衡量工具,并且我们应当主要关注OKR对业务带来的实际效果,所以不必在OKR中堆砌工作量;二、避免堆砌,要求每个人有判断力,自觉抛弃低优先级的目标和低价值的目标,而将精力和资源放到高优先级目标上。可以问自己几个问题,“这件事不做有什么影响吗”,“这件事做了有实际的业务收益吗”。

只写一个单词
写OKR时容易偷懒,只写一个单词,比如“收入” “DAU” “xx项目”。这样的写法,既没有说明最终要达到的状态,也很难客观打分。同时在沟通和对齐的时候,其他同事也很难看懂。类似的,“其他项目”、“重点项目”也是不可取的Objective,因为项目真的重要话,应该明确说出来要达到什么目标;而归为“其他”的目标,通常可能并不重要。

给自己留余量
如果历史上所有的OKR都轻松达成,可能定目标的时候就不够有雄心。

六、沟通、对齐和进度更新
沟通和对齐有几个重要的作用。首先是在重要的方向上,配合团队和上下级形成合力,而不要往不同方向使力。其次,OKR的制定是一个迭代过程,从草稿到定稿,需要方方面面的意见输入。再次,要了解配合团队定OKR时的激进程度,判断如果配合团队的OKR只能完成0.5分,会不会影响自己的工作。另一方面,应该鼓励经常更新OKR进度,既是对自己的提醒鞭策,又是向同事同步信息的好方式。

JAVA 锁优化 笔记

高效并发是JDK一个非常重要的改进,HotSpot虚拟机开发团队花费大量的精力去实现各种锁的优化技术,如适应性自旋(Adaptive Spinning)、锁消除(Lock Elimination)、锁粗化(Lock Coarsening)、轻量级锁(Lightweight Locking)和偏向锁(Biased Locking)等,这些技术都是为了线程间更高效地共享数据,以及解决竞争问题,提高程序执行效率。

自旋锁和自适应自旋
共享数据的锁定状态只会持续很短的时间,为了这一段时间挂起和恢复线程并不值得。如果机器有多个处理器,可以让多个线程同时并行执行,我们可以让后面请求锁的那个线程“稍等一段时间”,但是不放弃CPU执行时间,看看持有锁的线程是否很快就会释放掉锁。为了让线程等待,我们只需要让线程执行一个忙循环(自旋),这个技术就是所谓的自旋锁。

自旋锁在jdk1.4.2中已经引入,只不过默认是关闭状态,可以使用-XX:+UseSpinning参数开启,在jdk1.6中已经默认开启了。自旋等待不能代替阻塞,自旋要求有多处理器,自旋虽然避免了线程切换的开销,但是需要占用处理器时间,所以会白白消耗CPU资源,如果自旋时间非常长,就会带来资源的浪费。所以,自旋等待的时间必须有一定的限度,如果自旋次数超过了限定次数还没有成功获取锁,就应当使用传统的方式去挂起线程。自旋次数默认为10次,用户可以使用参数-XX:PreBlockSpin 配置。

jdk1.6引入自适应自旋锁,自适应意味着自旋的时间不再固定了,而是由前一次在同一个锁上的自旋时间和锁拥有者的状态来决定的。如果同一个锁对象上,自旋等待刚刚成功获得过锁,并且持有锁的线程正在运行,那么虚拟机会认为这次自旋也很有可能再次成功,进而允许自旋等待相对更长的时间。反之,如果对于某个锁,自旋很少获取到过,那么在以后获取这个锁的时候就会省略自旋的过程。有了自适应自旋,随着程序运行和性能监控信息的不断完善,虚拟机对程序锁的状况会预测的越来越准。

锁消除
锁消除的主要判定依据来源于逃逸分析的数据支持,判断堆上的数据都不会逃逸出去从而被其他线程访问,那就可以吧他们当做栈上数据对待,认为数据是线程私有的,加锁操作就可以忽略。具体可以看StringBuffer 在一个方法内部 定义,作用域只在方法内,锁会安全的消除掉,编译后所有的同步操作就会直接执行。

锁粗化
如果一系列操作都对同一个对象反复加锁和解锁,甚至加锁操作出现在循环体内,就算没有线程竞争,频繁的互斥同步操作也会导致性能损耗。如果虚拟机探测到有这种操作,会把加锁同步的范围扩展到整个操作的序列外,只进行一次加锁和解锁。

轻量级锁
轻量级锁并不是用来替代重量级锁的,本意是在没有多线程竞争的前提下,减少传统重量级锁使用操作系统互斥量产生的性能损耗。主要依赖虚拟机对象头数据,进行CAS操作来实现加锁和解锁操作。[备注:后续文章会详细介绍JAVA 对象的内存布局]。对象头[Mark Word]中有2bit存储锁标志位,1bit固定为0,其他状态(轻量级锁定,重量级锁定,GC标记,可偏向)
01 ——> 未锁定状态 –> 存储 对象哈希码 对象分代年龄
00 –> 轻量级锁定 –> 指向锁记录指针
10 –> 重量级锁定 –> 指向重量级锁的指针
11 –> GC标记 –> 不记录任何消息
01 –> 可偏向 –> 偏向线程ID 偏向时间戳 对象分代年龄

代码进入同步快,如果同步对象没有锁定(01 状态),虚拟机首先在当前线程的栈帧中建立一个名为锁记录(Lock Record)的空间,用于存储对象目前的Mark Word的拷贝(官方把这份拷贝加了一个Displaced 前缀,即 Displaced Mark Word),然后虚拟机使用CAS操作尝试把对象的Mark Word 更新指向 Lock Record的指针。如果操作成功线程就拥有了该对象的锁,并把对象Mark Word 的标志位修改为”00″,表示对象处于轻量级锁定状态,如果更新失败,虚拟机首先检查对象的Mark Word是否指向当前线程的栈帧,如果是说明当前线程已经拥有这个对象的锁,那就可以直接进入同步块进行执行。否则,这个锁对象已经被其他线程抢占了。如果有2个以上线程竞争,那轻量级锁就不再有效,要膨胀为重量级锁,锁标志位变为”10″,Mark Word中存储的就是指向重量级锁(互斥量)的指针,后面等待的线程也会进入阻塞状态。

解锁过程也是通过CAS操作的,如果对象的Mark Word 仍然指向着线程的锁记录,那就用CAS 操作把对象当前的Mark Word 和 线程中复制的Displaced Mark Word 替换回来。如果替换成功整个同步过程就成功了,如果替换失败,说明其他线程正在尝试获取该锁,那就再释放锁的同时唤醒其他被挂起的线程。

轻量级锁提升性能主要依据:“对于绝大部分的锁,整个同步周期内都是不存在竞争的”,这只是一个经验数据。如果存在锁的竞争,出了额外的CAS操作,还有互斥量的开销,轻量级锁会比传统的锁性能差。

偏向锁

轻量级锁是在无竞争的情况下使用CAS操作去消除同步使用的互斥量,那偏向锁就是在无竞争的情况下把整个同步都消除掉,连CAS都不做了。

偏向锁的“偏”,是偏向的意思,锁会偏向于第一个获取到它的线程,如果下面的操作,没有其他线程去获取该锁,则持有偏向锁的线程将永远不会同步。

启用偏向锁:-XX:+UseBiasedLocking jdk1.6默认开启。
第一次获取锁需要把状态标志设置 01 (偏向模式) ,同时使用CAS操作把当前线程ID记录在Mark Word中,CAS操作成功,持有偏向锁的线程每次进入这个锁的同步块时,虚拟机没有任何同步操作(Locking,UnLocking,Update Mark Word)
当其他线程去获取这个锁的时候,偏向模式结束,根据锁对象是否处于锁定状态,撤销偏向后恢复到未锁定(01)或者轻量级锁(00),后续和轻量级锁执行流程一样。

偏向锁可以提高带有同步但是无竞争的程序性能。它同样是带有权衡收益(Trade Off)的优化,也就是说,并不一定对程序有利,如果程序锁竞争比较激烈,那偏向就是多余的,具体问题具体分析,有时候关闭偏向锁性能反而提升了。

关于缓存的反思

目前缓存的解决方案一般有两种:

内存缓存(如 Ehcache) —— 速度快,进程内可用

集中式缓存(如 Redis)—— 可同时为多节点提供服务

使用内存缓存时,一旦应用重启后,由于缓存数据丢失,缓存雪崩,给数据库造成巨大压力,导致应用堵塞

使用内存缓存时,多个应用节点无法共享缓存数据

使用集中式缓存,由于大量的数据通过缓存获取,导致缓存服务的数据吞吐量太大,带宽跑满。现象就是 Redis 服务负载不高,但是由于机器网卡带宽跑满,导致数据读取非常慢

在遭遇问题1、2 时,很多人自然而然会想到使用 Redis 来缓存数据,因此就难以避免的导致了问题3的发生。

当发生问题 3 时,又有很多人想到 Redis 的集群,通过集群来降低缓存服务的压力,特别是带宽压力。

但其实,这个时候的 Redis 上的数据量并不一定大,仅仅是数据的吞吐量大而已。

个人觉得: 可以使用2级缓存,优先使用本地,再使用redis或者memcache的缓存,本地防止OOM可以做一些淘汰机制,刷新到redis或者memcahe中,当然这种情况就需要一些精巧的机制保证2级缓存的一致性。

节点间数据同步的方案 —— Redis Pub/Sub 和 JGroups 。当某个节点的缓存数据需要更新时,会通过 Redis 的消息订阅机制或者是 JGroups 的组播来通知集群内其他节点。当其他节点收到缓存数据更新的通知时,它会清掉自己内存里的数据,然后重新从 Redis 中读取最新数据。

为什么不用 Ehcache 的集群方案?

对 Ehcache 比较熟悉的人还会问的就是这个问题,Ehcache 本身是提供集群模式的,可以在多个节点同步缓存数据。但是 Ehcache 的做法是将整个缓存数据在节点间进行传输。如咱们前面的说的,一个页面需要读取 50K 的缓存数据,当这 50K 的缓存数据有更新时,那么需要在几个节点间传递整个 50K 的数据。这也会造成应用节点间大量的数据传输,这个情况完全不可控。

补充:当然这个单个数据传输量本身没有差别,但是 ehcache 利用 jgroups 来同步数据的做法,在实际测试过程中发现可靠性还是略低,而且 jgroups 的同步数据在云主机上无法使用。

订阅和通知模式传输的仅仅是缓存的 key 而已,因此相比 Ehcache 的集群模式,使用通知机制要传输的数据极其小,对节点间的数据通信完全不会产生大的影响。

关于谷歌极客Jeff Dean的冷笑话

在谷歌加州山景城总部,除了拉里·佩奇(Larry Page)和谢尔盖·布林(Sergey Brin),真正的牛人工程师要数杰夫·迪恩(Jeff Dean)了。

下面是谷歌员工 Heej Jones 在 Quora 上发布的关于Jeff Dean的一则故事:

来谷歌上班前一天,一个朋友给Jeff发邮件介绍了我,所以在上班的第一周,我就邀请他共进午餐。

那时候,我并不知道他是谁,也不了解他在谷歌的情况。只是午饭时,我有注意到其他餐桌的人在盯着他看,也有一些人路过我们的餐桌时会窃窃私语。

慢慢认识了更多的朋友,我才知道关于Jeff Dean的一些传奇故事;一位朋友曾经惊呼道:“你和Jeff Dean 一起吃过午饭?!”。

Jeff Dean

谷歌员工都认为谷歌搜索惊人的速度都归功于Jeff Dean,因此他也成了谷歌的名人。

大家对他的崇拜到底有多深?

你有听过关于武术战神查克·诺里斯的一些笑话吗?就像“查克从不洗盘子,盘子会因为恐惧他,自动清洗的”或者“查克被商业航班拒载,因为他的拳头会将飞机击落”等等诸如此类的笑话。

江湖中有一大堆关于Jeff Dean的传奇故事,都是由崇拜他的(前)谷歌员工写的。如果你了解软件工程师,懂得程序员幽默的话,那你会觉得那些故事非常有趣。

有时遇到不理解的,我们也会请求 BI首席架构师Pax Dickinson为我们解释那些笑话。

“编译器从不会给Jeff Dean警告的,Jeff Dean会给编译器警告的。”

解释:当你的代码有误时,编译器会给出警告,但是Jeff比编译器还牛叉。

“Jeff Dean 提交代码前会编译和运行他的代码,只是为了检验编译器和链接器有没有问题。”

解释:Jeff 的代码从不出错,他编译代码只是为了确保编译器和链接器没有bug。

“Jeff Dean 每次只给一条腿穿裤子,但是如果他有很多腿,你会发现他穿裤子的时间复杂度为O(log n)”

解释:Jeff Dean 穿裤子的算法复杂度是对数级的而不是线性级的,这样的话,如果他有很多条腿的话,就会大大节约穿裤子的时间。

“当 Richard Stallman 听说Jeff Dean的自传专属Kindle平台,他就去买了Kindle。”

解释:Richard Stallman是著名的极力反对非自由软件的人,并且从来不购买和使用Kindle。但是Jeff Dean就是这样神奇,Richard会因为想要阅读Jeff的自传而去违背自己的原则。”

“Jeff Dean 是直接写二进制机器代码的,他写源代码,是为了给其他开发人员作参考。”

解释:所有的代码在执行前都要先编译成二进制机器码,Jeff是直接写二进制机器码的,他写源代码主要是方便其他程序员理解。

“Jeff来面试谷歌时,被问到等式P=NP成立的条件,他回答,P=0 或者N=1时成立。然后在面试官哈哈大笑的时候,他看了一眼谷歌公有证书,就直接在白板上写出了相应的私钥。”

解释:“P与NP一直是计算机科学领域的一个悬而未决的问题,但是 Jeff Dean把它想成了一个代数问题,他直接用大脑根据谷歌的公有证书算出了相应的私有秘钥,这在超级计算机看来,都是不可能的事。

“X86-64 规范有几项非法指令,标志着‘私人使用’,它们其实是为Jeff Dean专用。”

解释:私有的非法CPU指令是不能被任何人使用的,但是Jeff Dean 就可以用。

“Jeff Dean 进行人体工程学评估,是为了保护他的键盘。”

解释:通常评估人体工程学是纠正坐姿,保护你的健康的,但是Jeff 却是为了保护他的键盘。

“所有的指针都是指向Jeff Dean的。”

解释:指针是C编程的核心,但是Jeff Dean 是编程世界的中心。

“在2000年末的时候,Jeff Dean 写代码的速度突然增长了40倍,原因是他把自己的键盘升级到了USB 2.0。”

解释:是键盘和计算机之间接口的速度影响了Jeff Dean 的编码速度。

原文链接: businessinsider 翻译: 伯乐在线 – JingerJoe
译文链接: http://blog.jobbole.com/51607/

DeviceEventEmitter – react-native

关于 DeviceEventEmitter 的文档说明不足,这里通过源码,记录下相关内容。

用法

import { DeviceEventEmitter } from 'react-native';

API

1 addListener – 添加事件监听(常用)

/**
   * Adds a listener to be invoked when events of the specified type are
   * emitted. An optional calling context may be provided. The data arguments
   * emitted will be passed to the listener function.
   *
   * TODO: Annotate the listener arg's type. This is tricky because listeners
   *       can be invoked with varargs.
   *
   * @param {string} eventType - Name of the event to listen to
   * @param {function} listener - Function to invoke when the specified event is
   *   emitted
   * @param {*} context - Optional context object to use when invoking the
   *   listener
   */
addListener( eventType: string, listener: Function, context: ?Object)

demo

 DeviceEventEmitter.addListener('COUSTOM_EVENT_TYPE', (e) => {
    console.log(e)
 });

2 once – 一次性监听,联想 jquery 的 once

/**
   * Similar to addListener, except that the listener is removed after it is
   * invoked once.
   *
   * @param {string} eventType - Name of the event to listen to
   * @param {function} listener - Function to invoke only once when the
   *   specified event is emitted
   * @param {*} context - Optional context object to use when invoking the
   *   listener
   */
  once(eventType: string, listener: Function, context: ?Object)

3 removeAllListeners – 移除所有的事件监听

/**
   * Removes all of the registered listeners, including those registered as
   * listener maps.
   *
   * @param {?string} eventType - Optional name of the event whose registered
   *   listeners to remove
   */
  removeAllListeners(eventType: ?string)

说明。可选参数 eventType是为了移除指定事件类型的监听

4 removeCurrentListener – 在事件回调调用期间移除监听

/**
   * Provides an API that can be called during an eventing cycle to remove the
   * last listener that was invoked. This allows a developer to provide an event
   * object that can remove the listener (or listener map) during the
   * invocation.
   *
   * If it is called when not inside of an emitting cycle it will throw.
   *
   * @throws {Error} When called not during an eventing cycle
   *
   * @example
   *   var subscription = emitter.addListenerMap({
   *     someEvent: function(data, event) {
   *       console.log(data);
   *       emitter.removeCurrentListener();
   *     }
   *   });
   *
   *   emitter.emit('someEvent', 'abc'); // logs 'abc'
   *   emitter.emit('someEvent', 'def'); // does not log anything
   */
  removeCurrentListener

example

var subscription = emitter.addListenerMap({
     someEvent: function(data, event) {
       console.log(data);
       emitter.removeCurrentListener();
     }
   });

5 removeSubscription – 移除指定的订阅

/**
   * Removes a specific subscription. Called by the `remove()` method of the
   * subscription itself to ensure any necessary cleanup is performed.
   */
  removeSubscription(subscription: EmitterSubscription)

6 listeners – 返回指定事件类型的所有监听者

/**
   * Returns an array of listeners that are currently registered for the given
   * event.
   *
   * @param {string} eventType - Name of the event to query
   * @returns {array}
   */
  listeners(eventType: string): 

7 emit – 触发某个事件 (常用)

/**
   * Emits an event of the given type with the given data. All handlers of that
   * particular type will be notified.
   *
   * @param {string} eventType - Name of the event to emit
   * @param {...*} Arbitrary arguments to be passed to each registered listener
   *
   * @example
   *   emitter.addListener('someEvent', function(message) {
   *     console.log(message);
   *   });
   *
   *   emitter.emit('someEvent', 'abc'); // logs 'abc'
   */
  emit(eventType: string)

demo

emitter.addListener('someEvent', function(message) {
  console.log(message);
});

emitter.emit('someEvent', 'abc'); // logs 'abc'

8 removeListener – 移除指定事件类型的监听

/**
   * Removes the given listener for event of specific type.
   *
   * @param {string} eventType - Name of the event to emit
   * @param {function} listener - Function to invoke when the specified event is
   *   emitted
   *
   * @example
   *   emitter.removeListener('someEvent', function(message) {
   *     console.log(message);
   *   }); // removes the listener if already registered
   *
   */
  removeListener(eventType: String, listener)

Linux and Curl: How to use Bash to Read a File Line by Line and Execute Curl command to get HTTP Response Code

for URL in `cat crunchify.txt`; do echo $URL; curl -m 10 -s -I $1 "$URL" | grep HTTP/1.1 |  awk {'print $2'}; done

-m: Maximum time in seconds that you allow the whole operation to take. This is useful for preventing your batch jobs from hanging for hours due to any network issue

-s: show error message

-I: Show document info only

awk: matches the pattern and prints result

30 Handy Bash Shell Aliases For Linux / Unix / Mac OS X

### Get os name via uname ###
_myos="$(uname)"
 
### add alias as per os using $_myos ###
case $_myos in
   Linux) alias foo='/path/to/linux/bin/foo';;
   FreeBSD|OpenBSD) alias foo='/path/to/bsd/bin/foo' ;;
   SunOS) alias foo='/path/to/sunos/bin/foo' ;;
   *) ;;
esac

alias c='clear'

# if user is not root, pass all commands via sudo #
if [ $UID -ne 0 ]; then
    alias reboot='sudo reboot'
    alias update='sudo apt-get upgrade'
fi

## Colorize the ls output ##
alias ls='ls --color=auto'
 
## Use a long listing format ##
alias ll='ls -la'
 
## Show hidden files ##
alias l.='ls -d .* --color=auto'


## get rid of command not found ##
alias cd..='cd ..'
 
## a quick way to get out of current directory ##
alias ..='cd ..'
alias ...='cd ../../../'
alias ....='cd ../../../../'
alias .....='cd ../../../../'
alias .4='cd ../../../../'
alias .5='cd ../../../../..'


## Colorize the grep command output for ease of use (good for log files)##
alias grep='grep --color=auto'
alias egrep='egrep --color=auto'
alias fgrep='fgrep --color=auto'

alias bc='bc -l'

alias sha1='openssl sha1'

alias mkdir='mkdir -pv'

# install  colordiff package 🙂
alias diff='colordiff'

alias mount='mount |column -t'


# handy short cuts #
alias h='history'
alias j='jobs -l'


alias path='echo -e ${PATH//:/\\n}'
alias now='date +"%T"'
alias nowtime=now
alias nowdate='date +"%d-%m-%Y"'


alias vi=vim
alias svi='sudo vi'
alias vis='vim "+set si"'
alias edit='vim'

# Stop after sending count ECHO_REQUEST packets #
alias ping='ping -c 5'
# Do not wait interval 1 second, go fast #
alias fastping='ping -c 100 -s.2'

alias ports='netstat -tulanp'

## replace mac with your actual server mac address #
alias wakeupnas01='/usr/bin/wakeonlan 00:11:32:11:15:FC'
alias wakeupnas02='/usr/bin/wakeonlan 00:11:32:11:15:FD'
alias wakeupnas03='/usr/bin/wakeonlan 00:11:32:11:15:FE'

## shortcut  for iptables and pass it via sudo#
alias ipt='sudo /sbin/iptables'
 
# display all rules #
alias iptlist='sudo /sbin/iptables -L -n -v --line-numbers'
alias iptlistin='sudo /sbin/iptables -L INPUT -n -v --line-numbers'
alias iptlistout='sudo /sbin/iptables -L OUTPUT -n -v --line-numbers'
alias iptlistfw='sudo /sbin/iptables -L FORWARD -n -v --line-numbers'
alias firewall=iptlist

# get web server headers #
alias header='curl -I'
 
# find out if remote server supports gzip / mod_deflate or not #
alias headerc='curl -I --compress'

# do not delete / or prompt if deleting more than 3 files at a time #
alias rm='rm -I --preserve-root'
 
# confirmation #
alias mv='mv -i'
alias cp='cp -i'
alias ln='ln -i'
 
# Parenting changing perms on / #
alias chown='chown --preserve-root'
alias chmod='chmod --preserve-root'
alias chgrp='chgrp --preserve-root'

# distro specific  - Debian / Ubuntu and friends #
# install with apt-get
alias apt-get="sudo apt-get"
alias updatey="sudo apt-get --yes"
 
# update on one command
alias update='sudo apt-get update && sudo apt-get upgrade'

# become root #
alias root='sudo -i'
alias su='sudo -i'


# reboot / halt / poweroff
alias reboot='sudo /sbin/reboot'
alias poweroff='sudo /sbin/poweroff'
alias halt='sudo /sbin/halt'
alias shutdown='sudo /sbin/shutdown'


# also pass it via sudo so whoever is admin can reload it without calling you #
alias nginxreload='sudo /usr/local/nginx/sbin/nginx -s reload'
alias nginxtest='sudo /usr/local/nginx/sbin/nginx -t'
alias lightyload='sudo /etc/init.d/lighttpd reload'
alias lightytest='sudo /usr/sbin/lighttpd -f /etc/lighttpd/lighttpd.conf -t'
alias httpdreload='sudo /usr/sbin/apachectl -k graceful'
alias httpdtest='sudo /usr/sbin/apachectl -t && /usr/sbin/apachectl -t -D DUMP_VHOSTS'


## pass options to free ##
alias meminfo='free -m -l -t'
 
## get top process eating memory
alias psmem='ps auxf | sort -nr -k 4'
alias psmem10='ps auxf | sort -nr -k 4 | head -10'
 
## get top process eating cpu ##
alias pscpu='ps auxf | sort -nr -k 3'
alias pscpu10='ps auxf | sort -nr -k 3 | head -10'
 
## Get server cpu info ##
alias cpuinfo='lscpu'
 
## older system use /proc/cpuinfo ##
##alias cpuinfo='less /proc/cpuinfo' ##
 
## get GPU ram on desktop / laptop##
alias gpumeminfo='grep -i --color memory /var/log/Xorg.0.log'

## this one saved by butt so many times ##
alias wget='wget -c'

## set some other defaults ##
alias df='df -H'
alias du='du -ch'
 
# top is atop, just like vi is vim
alias top='atop'



How to clear journalctl

The self maintenance method is to vacuum the logs by size or time.

Retain only the past two days:

journalctl --vacuum-time=2d

Retain only the past 500 MB:

journalctl --vacuum-size=500M

man journalctl for more information.