过去,未来

很快,已经大半年没写博客了,这半年内,一直再准备面试,找工作,终于在7月去了自己还认为不错的公司。也发现自己已经工作四年了。如果要给自己前四年以一个词语做总结的话,我想我会选择“混沌”这个词。

过去

在物理学上,混沌(chaos)是指确定性动力学系统因对初值敏感而表现出的不可预测的、类似随机性的运动。又称浑沌。英语词Chaos源于希腊语,原始 含义是宇宙初开之前的景象,基本含义主要指混乱、无序的状态。

而自己曾经的过往也是这样的随机飘荡。没有为自己制定一个人生目标,也不造为何而追求,是我之前很长一段时间的状态。主要体现在我认为的以下几点:

  • 没有制定人生规划。因为没有大方向,所以很多时候更偏向于随遇而安。
  • 工作单位随意性。和人生规划也有关,所以在面临单位的抉择方便没有去更加的努力。没有想好不同的单位能为我带来什么样的背景和能力。
  • 情感的随意性。在情感方面,自己并没有什么优势,所以更需要自己勇于去争取。而为此,需要去提高自己的设计能力,沟通表达能力,自己却没有过多的在意。
  • 到底想要什么。这个问题思考的不够深刻,因为道路上的一些阻碍,也造成前进方向的左右摇摆。
  • 勇敢去争取的心。无论在工作上,情感上,生活上,如果没有一颗勇敢去争取的心,人很容易颓废,然后陷入自我怀疑。社会因为信息的不对等,所以机会更容易给予勇敢去争取的人,而不去争取的人,却只能抱怨“他不如我,为啥是他”这种无聊的问题。
Read more
python协程

这些天,在标签调度上,可预见性的标签调度会越来越多,如果全由调度系统承载,调度系统压力增大,负责调度系统的同事担心会影响其他任务,遂在讨论下,决策开发一个简版的标签执行服务/脚本(相比现有调度系统,只保留任务执行控制,例如池化,并发控制等,任务编排,启动时间都由调度系统控制,整个执行服务就是调度系统的一个task)。而我对此持保持意见,我认为应该增强调度系统能力,2333。

既然决策已定,加上部门现在主要使用python,而调度系统也使用的是airflow,所以主要开发语言也定为python。对于此执行服务/脚本,希望能够并行的执行任务,并且能够控制每次在跑的任务数。调研了下python的并发模型,以及多线程的知识。很多说python多线程是鸡肋,而自己也不想引入第三方并发的库,又看到python支持协程,且相比线程轻量很多,代码易理解,遂最终选定使用协程来实现这个需求。

协程

网上对协程的解释众说纷纭,有说协程是一种用户态的轻量级线程,协程的调度完全由用户控制。wikipedia的定义:
协程是一个无优先级的子程序调度组件,允许子程序在特点的地方挂起恢复。但大家对协程的作用倒挺统一的:

  • 占用资源少,开个协程只需要K级别内存,而新开个线程则需要M级别
  • 线程之间的上下文切换,性能消耗大,而协程间切换非常快
  • 相比事件驱动模型的回调复杂性,协程易于理解,写协程代码就像写同步代码一样。
  • 协程的调度是协作式调度,需要协程自己主动把控制权转让出去之后,其他协程才能被执行到(很难像抢占式调度那样做到强制的 CPU 控制权切换到其他进程/线程)

历史的宿命

在互联网行业面临C10K问题时,线程方案不足以扛住大量的并发,这时的解决方案是epoll() 式的事件循环,nginx在这波潮流中顺利换掉apache上位。同一时间的开发社区为nginx的成绩感到震撼,出现了很多利用事件循环的应用框架,如tornado/ nodejs,也确实能够跑出更高的分数。而且python/ruby 社区受GIL之累,几乎没有并发支持,这时事件循环是一种并发的解放。然而事件循环的异步控制流对开发者并不友好。业务代码中随处可见的mysql/memcache调用,迅速地膨胀成一坨callback hell。这时社区发现了协程,在用户态实现上下文切换的工具,把epoll()事件循环隐藏起来,而且成本不高:用每个协程一个用户态的栈,代替手工的状态管理。似乎同时得到了事件循环和线程同步控制流的好处,既得到了epoll()的高性能,又易于开发。甚至通过monkey patch,旧的同步代码可以几乎无缝地得到异步的高性能,真是太完美了。

Read more
略微糟糕的2018

不在以流水形式记录,而网易云音乐年报体搞笑却不能展示更多。在朋友圈看了别人的发的年终总结,遂借鉴其模板。

2018年让你印象深刻的5件事情是什么?

  1. 定级答辩,完全暴露了自己的弱点,当大领导说自己的ppt很渣时,很懵逼。自己确实没太上心,做的少。虽然后来又重改了,但最终其实效果都不是很满意,包括答辩。表达能力以及技能包装能力都还需要多加练习。最后感谢领导的细心指导。
  2. 不应该老拿别人的弱点说事或者开玩笑。A之前说了个毫无依据的话被大家笑话,然后就竟然被大家拿出来说,以至于大家一旦把焦点给与他,他便有了种不舒服,即便知道大家也是寻乐。自己也会一直拿别人的弱点寻乐,虽然是玩笑,但别人的感受未必是玩笑。当然自己也会被拿着弱点一直攻击你,当然也是寻乐,但自己确实也有不爽,将心比心,也许有时候会意的鼓励会更好。以后自己应该多注意,少娱乐他人的弱点,多提升自己的弱点。
  3. 接近年底,回家办理贷款手续,正好外婆过生,一大家人和和睦睦,开开心心的氛围真好。希望明年能带外婆来北京玩,外婆那些年一路过来不容易,现在也该享享清福了。

你做的引以为傲的5件事?

  1. 终于还是借着低价买的域名,开通的博客,开始记录技术,生活的感悟,以及看书,电影。零零散散的也写了将近20篇文章,虽然内容还有待提供,以及有些是拼凑的。但还是希望自己沉淀一些知识,以及顺便提高自己的写作能力。
  2. 认知的提升。网络上的人云亦云,股市的追涨杀跌,以及自媒体带节奏的十万加,以及各种app推送的信息。让我意识到现在虽然信息变多了,但都是他们为了满足自己的利益而塞给观看者已经加工过的信息。对此,自己的认知的提升,能够对此消息进行独立思考,而不是跟着他人的情感走是非常重要的。而群体更能泛滥此情绪,相比独立思考,会显得更智力低下。
Read more
spark streaming

之前讲解了Google的Dataflow模型。而spark streaming是以micro batch方式来近似streaming数据进行处理。Spark Streaming从实时数据流接入数据,再将其划分为一个个小批量供后续Spark engine处理,所以实际上,Spark Streaming是按一个个小批量来处理数据流的。

spark streaming

离散数据流(DStream)是Spark Streaming最基本的抽象。它代表了一种连续的数据流,要么从某种数据源提取数据,要么从其他数据流映射转换而来。DStream内部是由一系列连续的RDD组成的,每个RDD都是不可变、分布式的数据集。每个RDD都包含了特定时间间隔内的一批数据,如下图所示:

任何作用于DStream的算子,其实都会被转化为对其内部RDD的操作。例如,在代码

1
val words = lines.flatMap(_.split(" "))

中,我们将lines这个DStream转成words DStream对象,其实作用于lines上的flatMap算子,会施加于lines中的每个RDD上,并生成新的对应的RDD,而这些新生成的RDD对象就组成了words这个DStream对象。其过程如下图所示:

Read more
Dataflow模型

Dataflow模型:是谷歌在处理无边界数据的实践中,总结的一套SDK级别的解决方案,其目标是做到在非有序的,无边界的海量数据上,基于事件时间进行运算,并能根据数据自身的属性进行window操作,同时数据处理过程的正确性,延迟,代价可根据需求进行灵活的调整配置。

DataFlow模型核心

和Spark通过micro batch模型来处理Streaming场景的出发点不同,Dataflow认为batch的处理模式只是streaming处理模式的一个子集。在无边界数据集的处理过程中,要及时产出数据结果,无限等待显然是不可能的,所以必然需要对要处理的数据划定一个窗口区间,从而对数据及时的进行分段处理和产出,而各种处理模式(stream,micro batch,session,batch),本质上,只是窗口的大小不同,窗口的划分方式不同而已。Batch的处理模式就只是一个窗口区间涵盖了整个有边界的数据集这样的一种特例场景而已。一个设计良好的能处理无边界数据集的系统,完全能在准确性和正确性上做到和“Batch”系统一样甚至应该更好。

Read more
livy指南

Livy是一个基于Spark的开源REST服务,它能够通过REST的方式将代码片段或是序列化的二进制代码提交到Spark集群中去执行。它提供了以下这些基本功能:

  • 提交Scala、Python或是R代码片段到远端的Spark集群上执行;
  • 提交Java、Scala、Python所编写的Spark作业到远端的Spark集群上执行;
  • 提交批处理应用在集群中运行。

livy安装

livy安装很简单,从官网下载zip包。
解压,设置spark和hadoop的环境。

1
2
export SPARK_HOME=spark的路径
export HADOOP_CONF_DIR=/etc/hadoop/conf (hadoop配置路径)

然后在livy的bin目录下执行:
livy-server start

livy配置

在conf/livy.conf文件中有配置,常用的配置:

配置信息 含义
livy.server.host = 0.0.0.0 livy服务启动的host
livy.server.port = 80 启动端口号
livy.spark.master = yarn livy执行spark master
livy.spark.deploy-mode = client spark启动模式
livy.repl.enable-hive-context = true 启动hiveContext
livy.ui.enabled = true livy ui页面

还有其他的配置,用户可以根据自己需求自行配置。
另外,当集群开启了kerberos验证,执行spark任务时报gss错误,需要在配置中加上:

1
2
livy.server.launch.kerberos.keytab = /conf/xxx.keytab
livy.server.launch.kerberos.principal = xxx@HADOOP.COM

Read more
airflow使用指南

airflow 是一个编排、调度和监控workflow的平台,由Airbnb开源。airflow 将workflow编排为tasks组成的DAGs,调度器在一组workers上按照指定的依赖关系执行tasks。同时,airflow 提供了丰富的命令行工具和简单易用的用户界面以便用户查看和操作,并且airflow提供了监控和报警系统。

airflow 核心概念

  • DAGs:即有向无环图(Directed Acyclic Graph),将所有需要运行的tasks按照依赖关系组织起来,描述的是所有tasks执行的顺序。
  • Operators:可以简单理解为一个class,描述了DAG中一个具体的task具体要做的事。其中,airflow内置了很多operators,如BashOperator 执行一个bash 命令,PythonOperator 调用任意的Python 函数,EmailOperator 用于发送邮件,HTTPOperator 用于发送HTTP请求, SqlOperator 用于执行SQL命令…同时,用户可以自定义Operator,这给用户提供了极大的便利性。
  • Tasks:Task 是 Operator的一个实例,也就是DAGs中的一个node。
  • Task Instance:task的一次运行。task instance 有自己的状态,包括”running”, “success”, “failed”, “skipped”, “up for retry”等。
  • Task Relationships:DAGs中的不同Tasks之间可以有依赖关系,如 TaskA >> TaskB,表明TaskB依赖于TaskA。

通过将DAGs和Operators结合起来,用户就可以创建各种复杂的 workflow了。

operators

下面讲解下常见的operator,以及如何使用,注意点。

Read more
HBase case之scan batch

标签数据存储在Hbase中,为了加速标签探索功能,会每天导出一份全量数据表。有时候用户也会进行特定标签勾选导出需求。

导出遇到的问题

导出后,出现部分用户,同一个用户多条数据(两条为主),见下图:

从图中可以看出,出现了重复用户uid。

Read more
HBase系列之snapshot

snapshot(快照)基础原理

snapshot是很多存储系统和数据库系统都支持的功能。一个snapshot是一个全部文件系统、或者某个目录在某一时刻的镜像。实现数据文件镜像最简单粗暴的方式是加锁拷贝(之所以需要加锁,是因为镜像得到的数据必须是某一时刻完全一致的数据),拷贝的这段时间不允许对原数据进行任何形式的更新删除,仅提供只读操作,拷贝完成之后再释放锁。这种方式涉及数据的实际拷贝,数据量大的情况下必然会花费大量时间,长时间的加锁拷贝必然导致客户端长时间不能更新删除,这是生产线上不能容忍的。

snapshot机制并不会拷贝数据,可以理解为它是原数据的一份指针。在HBase这种LSM类型系统结构下是比较容易理解的,我们知道HBase数据文件一旦落到磁盘之后就不再允许更新删除等原地修改操作,如果想更新删除的话可以追加写入新文件(HBase中根本没有更新接口,删除命令也是追加写入)。这种机制下实现某个表的snapshot只需要给当前表的所有文件分别新建一个引用(指针),其他新写入的数据重新创建一个新文件写入即可。

snapshot流程主要涉及3个步骤:

  1. 加一把全局锁,此时不允许任何的数据写入更新以及删除

  2. 将Memstore中的缓存数据flush到文件中(可选)

  3. 为所有HFile文件分别新建引用指针,这些指针元数据就是snapshot

snapshot作用

  • 备份:通常情况下,对重要的业务数据,建议至少每天执行一次snapshot来保存数据的快照记录,并且定期清理过期快照,这样如果业务发生重要错误需要回滚的话是可以回滚到之前的一个快照点的。
  • 迁移:可以使用ExportSnapshot功能将快照导出到另一个集群,实现数据的迁移
Read more
HBase系列之region-split上

Region自动切分是HBase能够拥有良好扩张性的最重要因素之一,也必然是所有分布式系统追求无限扩展性的一副良药。那么region又是如何自动切分的呢,触发的条件又是什么?

Region切分触发策略

在最新稳定版(1.2.6)中,HBase已经有多达6种切分触发策略。即RegionSplitPolicy的实现子类共有6个,如下类图:

当然,每种触发策略都有各自的适用场景,用户可以根据业务在表级别选择不同的切分触发策略。常见的切分策略如下图:

ConstantSizeRegionSplitPolicy

0.94版本前默认切分策略。这是最容易理解但也最容易产生误解的切分策略,从字面意思来看,当region大小大于某个阈值(hbase.hregion.max.filesize)之后就会触发切分,实际上并不是这样,真正实现中这个阈值是对于某个store来说的,即一个region中最大store的大小大于设置阈值之后才会触发切分。另外一个大家比较关心的问题是这里所说的store大小是压缩后的文件总大小还是未压缩文件总大小,实际实现中store大小为压缩后的文件大小(采用压缩的场景)。ConstantSizeRegionSplitPolicy相对来来说最容易想到,但是在生产线上这种切分策略却有相当大的弊端:切分策略对于大表和小表没有明显的区分。阈值(hbase.hregion.max.filesize)设置较大对大表比较友好,但是小表就有可能不会触发分裂,极端情况下可能就1个,这对业务来说并不是什么好事。如果设置较小则对小表友好,但一个大表就会在整个集群产生大量的region,这对于集群的管理、资源使用、failover来说都不是一件好事。

Read more