太久没回来了,今天计划把后台升级一下,然后重新出发...
Category: GIGI
Spark学习笔记
简述
Apache Spark是一个开源集群运算框架,最初是由加州大学柏克莱分校AMPLab所开发。相对于Hadoop的MapReduce会在运行完工作后将中介数据存放到磁盘中,Spark使用了存储器内运算技术,能在数据尚未写入硬盘时即在存储器内分析运算。Spark在存储器内运行程序的运算速度能做到比Hadoop MapReduce的运算速度快上100倍,即便是运行程序于硬盘时,Spark也能快上10倍速度。[1]Spark允许用户将数据加载至集群存储器,并多次对其进行查询,非常适合用于机器学习算法。
Hadoop 和 Spark 的关系
- Hadoop Google 在 2003 年和 2004 年先后发表了 Google 文件系统 GFS 和 MapReduce 编程模型论文. 基于这两篇开源文档,2006 年 Nutch 项目子项目之一的 Hadoop 实现了两个强有力的开源产品:HDFS 和 MapReduce. Hadoop 成为了典型的大数据批量处理架构,由 HDFS 负责静态数据的存储,并通过 MapReduce 将计算逻辑分配到各数据节点进行数据计算和价值发现.之后以 HDFS 和 MapReduce 为基础建立了很多项目,形成了 Hadoop 生态圈.
- Spark Spark 则是UC Berkeley AMP lab 所开源的类Hadoop MapReduce的
通用并行框架
, 专门用于大数据量下的迭代式计算.是为了跟 Hadoop 配合而开发出来的,不是为了取代 Hadoop.
Spark 运算比 Hadoop 的 MapReduce 框架快的原因是因为 Hadoop 在一次 MapReduce 运算之后,会将数据的运算结果从内存写入到磁盘中,第二次 Mapredue 运算时在从磁盘中读取数据,所以其瓶颈在2次运算间的多余 IO 消耗. Spark 则是将数据一直缓存在内存中,直到计算得到最后的结果,再将结果写入到磁盘,所以多次运算的情况下, Spark 是比较快的. 其优化了迭代式工作负载.
Spark 的主要特点还包括:
- (1)提供 Cache 机制来支持需要反复迭代计算或者多次数据共享,减少数据读取的 IO 开销;
- (2)提供了一套支持 DAG 图的分布式并行计算的编程框架,减少多次计算之间中间结果写到 Hdfs 的开销;
- (3)使用多线程池模型减少 Task 启动开稍, shuffle 过程中避免不必要的 sort 操作并减少磁盘 IO 操作。(Hadoop 的 Map 和 reduce 之间的 shuffle 需要 sort)
Spark计算框架
伯克利大学将 Spark 的整个生态系统成为 伯克利数据分析栈(BDAS),在核心框架 Spark 的基础上,主要提供四个范畴的计算框架:
- Spark SQL: 提供了类 SQL 的查询,返回 Spark-DataFrame 的数据结构(类似 Hive)
- Spark Streaming: 流式计算,主要用于处理线上实时时序数据(类似 storm)
- MLlib: 提供机器学习的各种模型和调优
- GraphX: 提供基于图的算法,如 PageRank
系统架构
Spark遵循主从架构。它的集群由一个主服务器和多个从服务器组成。
Spark架构依赖于两个抽象:
- 弹性分布式数据集(RDD)
- 有向无环图(DAG)
- 应用程序(Application): 基于Spark的用户程序,包含了一个Driver Program 和集群中多个的Executor;
- 驱动(Driver): 运行Application的main()函数并且创建SparkContext;
- 执行单元(Executor): 是为某Application运行在Worker Node上的一个进程,该进程负责运行Task,并且负责将数据存在内存或者磁盘上,每个Application都有各自独立的Executors;
- 集群管理程序(Cluster Manager): 在集群上获取资源的外部服务(例如:Local、Mesos或Yarn等集群管理系统);
- 操作(Operation): 作用于RDD的各种操作分为Transformation和Action.
底层详细细节介绍:
使用spark-submit提交一个Spark作业之后,这个作业就会启动一个对应的Driver进程。根据你使用的部署模式(deploy-mode)不同,Driver进程可能在本地启动,也可能在集群中某个工作节点上启动。而Driver进程要做的第一件事情,就是向集群管理器申请运行Spark作业需要使用的资源,这里的资源指的就是Executor进程。 YARN集群管理器会根据我们为Spark作业设置的资源参数,在各个工作节点上,启动一定数量的Executor进程,每个Executor进程都占有一定数量的内存和CPU core。 在申请到了作业执行所需的资源之后,Driver进程就会开始调度和执行我们编写的作业代码。Driver进程会将我们编写的Spark作业代码分拆为多个stage,每个stage执行一部分代码片段,并为每个stage创建一批Task,然后将这些Task分配到各个Executor进程中执行。Task是最小的计算单元,负责执行一模一样的计算逻辑(也就是我们自己编写的某个代码片段),只是每个Task处理的数据不同而已。一个stage的所有Task都执行完毕之后,会在各个节点本地的磁盘文件中写入计算中间结果,然后Driver就会调度运行下一个stage。下一个stage的Task的输入数据就是上一个stage输出的中间结果。如此循环往复,直到将我们自己编写的代码逻辑全部执行完,并且计算完所有的数据,得到我们想要的结果为止。 Spark是根据shuffle类算子来进行stage的划分。如果我们的代码中执行了某个shuffle类算子(比如reduceByKey、join等),那么就会在该算子处,划分出一个stage界限来。可以大致理解为,shuffle算子执行之前的代码会被划分为一个stage,shuffle算子执行以及之后的代码会被划分为下一个stage。因此一个stage刚开始执行的时候,它的每个Task可能都会从上一个stage的Task所在的节点,去通过网络传输拉取需要自己处理的所有key,然后对拉取到的所有相同的key使用我们自己编写的算子函数执行聚合操作(比如reduceByKey()算子接收的函数)。这个过程就是shuffle。 当我们在代码中执行了cache/persist等持久化操作时,根据我们选择的持久化级别的不同,每个Task计算出来的数据也会保存到Executor进程的内存或者所在节点的磁盘文件中。 因此Executor的内存主要分为三块:第一块是让Task执行我们自己编写的代码时使用,默认是占Executor总内存的20%;第二块是让Task通过shuffle过程拉取了上一个stage的Task的输出后,进行聚合等操作时使用,默认也是占Executor总内存的20%;第三块是让RDD持久化时使用,默认占Executor总内存的60%。 Task的执行速度是跟每个Executor进程的CPU core数量有直接关系的。一个CPU core同一时间只能执行一个线程。而每个Executor进程上分配到的多个Task,都是以每个Task一条线程的方式,多线程并发运行的。如果CPU core数量比较充足,而且分配到的Task数量比较合理,那么通常来说,可以比较快速和高效地执行完这些Task线程。
在实际编程中,我们不需关心以上调度细节.只需使用 Spark 提供的指定语言的编程接口调用相应的 API 即可.
在 Spark API 中, 一个 应用(Application) 对应一个 SparkContext 的实例。一个 应用 可以用于单个 Job,或者分开的多个 Job 的 session,或者响应请求的长时间生存的服务器。与 MapReduce 不同的是,一个 应用 的进程(我们称之为 Executor),会一直在集群上运行,即使当时没有 Job 在上面运行。
而调用一个Spark内部的 Action 会产生一个 Spark job 来完成它。 为了确定这些job实际的内容,Spark 检查 RDD 的DAG再计算出执行 plan 。这个 plan 以最远端的 RDD 为起点(最远端指的是对外没有依赖的 RDD 或者 数据已经缓存下来的 RDD),产生结果 RDD 的 Action 为结束 。并根据是否发生 shuffle 划分 DAG 的 stage.
RDD
RDD(弹性分布式数据集)是Spark的核心抽象。它是一组元素,在集群的节点之间进行分区,以便我们可以对其执行各种并行操作。
Transformation 和 Action
Spark 的设计思想中,为了减少网络及磁盘 IO 开销,需要设计出一种新的容错方式,于是才诞生了新的数据结构 RDD. RDD 是一种只读的数据块,可以从外部数据转换而来,你可以对RDD 进行函数操作(Operation),包括 Transformation 和 Action. 在这里只读表示当你对一个 RDD 进行了操作,那么结果将会是一个新的 RDD, 这种情况放在代码里,假设变换前后都是使用同一个变量表示这一 RDD,RDD 里面的数据并不是真实的数据,而是一些元数据信息,记录了该 RDD 是通过哪些 Transformation 得到的,在计算机中使用 lineage 来表示这种血缘结构,lineage 形成一个有向无环图 DAG, 整个计算过程中,将不需要将中间结果落地到 HDFS 进行容错,加入某个节点出错,则只需要通过 lineage 关系重新计算即可.
Transformation 操作不是马上提交 Spark 集群执行的,Spark 在遇到 Transformation 操作时只会记录需要这样的操作,并不会去执行,需要等到有 Action 操作的时候才会真正启动计算过程进行计算.针对每个 Action,Spark 会生成一个 Job, 从数据的创建开始,经过 Transformation, 结尾是 Action 操作.这些操作对应形成一个有向无环图(DAG),形成 DAG 的先决条件是最后的函数操作是一个Action
RDD 主要特点
- 1.它是在集群节点上的不可变的、已分区的集合对象;
- 2.通过并行转换的方式来创建(如 Map、 filter、join 等);
- 3.失败自动重建;
- 4.可以控制存储级别(内存、磁盘等)来进行重用;
- 5.必须是可序列化的;
- 6.是静态类型的(只读)。
创建RDD
有两种方法可以用来创建RDD:
- 并行化驱动程序中的现有数据
parallelize
- 引用外部存储系统中的数据集,例如:共享文件系统,HDFS,HBase或提供Hadoop InputFormat的数据源
Spark SQL & DataFrame
Spark SQL是Spark用来处理结构化数据的一个模块,它提供了两个编程抽象分别叫做DataFrame和DataSet,它们用于作为分布式SQL查询引擎。
DataFrame
DataFrame(表)= Schema(表结构) + Data(表数据)
DataFrame是组织成命名列的数据集。它在概念上等同于关系数据库中的表,但在底层具有更丰富的优化
DataFrame相比RDD多了数据的结构信息,即schema。RDD是分布式的对象的集合。DataFrame是分布式的Row对象的集合。DataFrame除了提供了比RDD更丰富的算子以外,更重要的特点是提升执行效率、减少数据读取以及执行计划的优化
创建DataFrame
- 方式1:使用case class定义表
- 方式2:使用SparkSession对象创建DataFrame
spark.createDataFrame
- 方式3:直接读取格式化的文件(json,csv
shuffle 和 stage
shuffle 是划分 DAG 中 stage 的标识,同时影响 Spark 执行速度的关键步骤.
RDD 的 Transformation 函数中,又分为窄依赖(narrow dependency)和宽依赖(wide dependency)的操作.窄依赖跟宽依赖的区别是是否发生 shuffle(洗牌) 操作.宽依赖会发生 shuffle 操作. 窄依赖是子 RDD的各个分片(partition)不依赖于其他分片,能够独立计算得到结果,宽依赖指子 RDD 的各个分片会依赖于父RDD 的多个分片,所以会造成父 RDD 的各个分片在集群中重新分片, 看如下两个示例:
// Map: "cat" -> c, cat val rdd1 = rdd.Map(x => (x.charAt(0), x)) // groupby same key and count val rdd2 = rdd1.groupBy(x => x._1). Map(x => (x._1, x._2.toList.length))
第一个 Map 操作将 RDD 里的各个元素进行映射, RDD 的各个数据元素之间不存在依赖,可以在集群的各个内存中独立计算,也就是并行化,第二个 groupby 之后的 Map 操作,为了计算相同 key 下的元素个数,需要把相同 key 的元素聚集到同一个 partition 下,所以造成了数据在内存中的重新分布,即 shuffle 操作.shuffle 操作是 spark 中最耗时的操作,应尽量避免不必要的 shuffle.
宽依赖主要有两个过程: shuffle write 和 shuffle fetch. 类似 Hadoop 的 Map 和 Reduce 阶段.shuffle write 将 ShuffleMapTask 任务产生的中间结果缓存到内存中, shuffle fetch 获得 ShuffleMapTask 缓存的中间结果进行 ShuffleReduceTask 计算,这个过程容易造成OutOfMemory.
性能优化
缓存
Spark中对于一个RDD执行多次算子(函数操作)的默认原理是这样的:每次你对一个RDD执行一个算子操作时,都会重新从源头处计算一遍,计算出那个RDD来,然后再对这个RDD执行你的算子操作。这种方式的性能是很差的。
首先要认识到的是, .Spark 本身就是一个基于内存的迭代式计算,所以如果程序从头到尾只有一个 Action 操作且子 RDD 只依赖于一个父RDD 的话,就不需要使用 cache 这个机制, RDD 会在内存中一直从头计算到尾,最后才根据你的 Action 操作返回一个值或者保存到相应的磁盘中.需要 cache 的是当存在多个 Action 操作或者依赖于多个 RDD 的时候, 可以在那之前缓存RDD.
shuffle优化
- 当进行联合的规约操作时,避免使用 groupByKey
- 当输入和输入的类型不一致时,避免使用 reduceByKey
- 生成新列的时候,避免使用单独生成一列再 join 回来的方式,而是直接在数据上生成.
- 当需要对两个 RDD 使用 join 的时候,如果其中一个数据集特别小,小到能塞到每个 Executor 单独的内存中的时候,可以不使用 join, 使用 broadcast 操作将小 RDD 复制广播到每个 Executor 的内存里 join.
资源参数调优
MacOS上 Virtualbox 设置CentOS 网络
MacOS 安装Virtualbox,CentOS,如何配置网络?
mac上不像windows平台Virtualbox安装后可以自动创建两个虚拟网卡vmnet1 vmnet8,如果需要虚拟机连接互联网,并且可以在mac中来访问虚拟机网络,简单的办法就是创建两个网卡:一个NAT Network,一个HOST only Adpater.
vm界面都提供了直接创建入口,点击创建就可以了,在虚拟机中Adpater1,Adpater2 分别设置好这两个网络.
开机进入CENTOS,看是否能识别到这两个网卡.如果识别到,确认一下网络地址,获取直接配置一个静态地址,一个自动获取:
/etc/sysconfig/network-scripts/
ifcfg-enp0s8 ifcfg-enp0s3
TYPE=Ethernet
PROXY_METHOD=none
BROWSER_ONLY=no
BOOTPROTO=static
IPADDR=192.168.56.100
NETMASK=255.255.255.0
NETWORK=192.168.56.0
DEFROUTE=yes
IPV4_FAILURE_FATAL=no
IPV6INIT=yes
IPV6_AUTOCONF=yes
IPV6_DEFROUTE=yes
IPV6_FAILURE_FATAL=no
IPV6_ADDR_GEN_MODE=stable-privacy
NAME=enp0s3
DEVICE=enp0s3
ONBOOT=yes
重启下网络:
systemctl restart NetworkManager
Git提交信息规范Git Commit Message
目前规范使用较多的是引用或衍生 Github Angular开发中<提交信息准则>章节(Commit Message Guidelines).以下为规范译文:
关于如何格式化git commit消息,我们有非常精确的规则。这样会有更具可读性的消息,在查看项目历史记录时易于遵循。而且,我们使用git commit消息生成AngularJS更改日志。
可以使用典型的git工作流程或使用CLI向导(Commitizen)添加提交消息格式。要使用该向导,yarn run commit
请在对git进行更改后在终端中运行。
提交消息格式
每个提交消息均由信息头(header),正文(body)和页脚(footer)组成。信息头(header)具有一种特殊的格式,包括type(类型),scope(范围)和subject(主题):
<type>(<scope>): <subject>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>
所述信息头(header)是必须的,而信息头范围(scope)的是可选的。
提交消息的任何一行都不能超过100个字符!这使得该消息在GitHub以及各种git工具中更易于阅读。
Revert
如果提交还原了先前的提交,则应以开头revert:
,后跟还原的提交的标头。在正文中应该说:This reverts commit <hash>.
,其中哈希是要还原的提交的SHA。
类型(type)
必须为以下之一:
- feat:一项新功能(feature)
- fix:一个bug修复
- docs:仅文档更改
- style:风格,不影响代码含义的更改(空白,格式,缺少分号等)
- refactor:重构,既不修正错误也不增加功能的代码更改
- perf:改进性能的代码改动
- test:添加缺失或更正现有测试
- chore:更改构建过程或辅助工具和库,例如文档生成
范围(scope)
范围可以是指定提交更改位置的任何内容。例如$location
, $browser
,$compile
,$rootScope
,ngHref
,ngClick
,ngView
,等...
当更改影响的范围不止一个范围时,可以使用*(星号)标识。
主题(Subject)
主题简要描述了更改:
- 使用祈使句式和现在时:“change”而不是“changed”或“changes”
- 不要大写第一个字母
- 末尾没有点(。)
正文(Body)
就像在主题中一样,使用祈使句式现在时态:“change”而不是“changed”或“changes”。正文应包括改变的动机,并将其与以前的行为进行对比。
页脚(Footer)
页脚应包含有关Breaking Changes的所有信息,也是参考此提交关闭的GitHub问题的位置 。
重大更改应以BREAKING CHANGE:
带有空格或两个换行符的单词开头。然后,将其余的提交消息用于此目的。
详细说明可以在本文档中找到。
如何将一台Chromebook打造成办公主力?
自从Google 将Android 集成进Chrome OS, 今年年初宣布将支持Linux,而最近的系统更新又有多款Chromebook设备陆陆续续支持了Linux apps,Chrome OS已经不再缺少apps。
在开发办公上使用chromebook已经没有太多的障碍。
前段时间入手了一台Chromebook,是Acer Chromebook 14(CB3-431).而不是大名鼎鼎PixelBook,因为穷,没有dollar预算。配置嘛就不说了,对于不了解的可以认为就是一坨shit吧。
不过,我觉得这台Chromebook使用起来是相当OK的。使用顺手,价格便宜,还要什么自行车?
办公?开发?使用Chromebook?没错,Office 办公,修个图,或者跑个Python程序,编译个Java... 在行的!总有你需要的App等着你。没有Chrome App 可以选择Android App,没有Android Apps 可以选择Linux Apps!
注意这是在一定范围的。具体还是取决于你的具体工作,对于我或许也个ssh客户端 App 就足够了呢?
首先,我拿到了Chromebook,第一件事是激活使用,众所周知的原因,我要借助某些工具就如同我的手机第一次开机时,借助一台windows上使用一个工具(名字大家都知道),局域网共享代理方式,登录Google账号,完成激活。
第二,开启Google play store,设备支持的话目前直接都可以用。下载必备上网工具,就是上条中说的大家都知道名字的App。这个app的图标是这样的:
当然这个app 也有chrome 版的,相对Android版体验还是差很多,已经好久而且已经不再更新了,linux 版,qt版本就没再尝试,应该也可以用的。
第三,打开 chrome://flags/ ,打开 ARC VPN integration #arc-vpn,这样就能使用那个android app来使用互联网了。
接下来就自由发挥了,chrome app, Android app 还有Linux 的该用什么就安装什么。其实chrome app算是体验最好,但是Google已经给App判了死刑,一个个chrome app 都会慢慢的消失。
这里推荐几款很赞的开发工具吧,可以离线使用,Google系列的就不列了,好找。码字太累。
1.Termius - SSH Client (好用,已停更),这里直达Termius - SSH Client
2.Secure Shell App (官方出品,必备)
3.Caret (文本编辑,IDE)
4.Postman (停更)这里直达:Postman
5.Chrome MySQL Admin
6.Evernote
7.Polarr Photo Editor
Linux Mint cinnamon Crashed...
Cinnamon crashed, running in fallback mode...
Cinnamon 喜欢崩溃这是出了名的,刚刚从ubuntu切换到LinuxMint系统,正在为其简单快速而窃喜。我正想这个系统真棒,集成了这个多软件,操作如此简单。没想到...
当我在安装ibus中文输入法是遇到问题: 安装完成后配置界面打不开,症状是点击无效,终端输入:
ibus-setup
会提示
File "/usr/share/ibus/setup/main.py", line 31, in <module> from gi.repository import GLib ModuleNotFoundError: No module named 'gi'
确认发现本机python2.7 python3.6 都没有安装gi库。那就安装了gi, 而貌似gi.repository 并不包含在gi中,就目前版本而言。我安装了pgi 。。。
然后就悲剧了。 而应该是:
sudo apt reinstall python-gi sudo apt reinstall python3-gi sudo apt install python3-pyside sudo apt install python-pyside
安装vmplayer :
https://communities.vmware.com/thread/568089
diff -Naur vmnet-only.orig/bridge.c vmnet-only/bridge.c
--- vmnet-only/bridge.c 2017-06-26 22:08:39.148034785 +1000
+++ vmnet-only/bridge.c 2017-07-16 11:37:01.325802125 +1000
@@ -636,7 +636,7 @@
unsigned long flags;
int i;
- atomic_inc(&clone->users);
+ clone = skb_get(clone);
clone->dev = dev;
clone->protocol = eth_type_trans(clone, dev);
UEFI模式下RemixOS+Win10 启动项修改
主题:UEFI模式下RemixOS+Win10 启动项中顺序及超时修改
如果win10 是HDD UEFI安装的而非U盘安装,使用Remix官方的安装工具安装后,默认启动Remix OS,菜单超时时间30s. 由于RemixOS 启动使用grub.cfg,而该文件安装在引导分区。这里给出最简单最快的方法,不依赖第三方软件:
命令行参数-Command-Line Flags
原文: [Go by Example:Command-Line Flags] --注意正确的上网姿势..
命令行标识(参数)是一种常用的指定命令行程序选项的方法,例如,在`wc -l`中 “-l” 就是一个命令的参数。
Go提供了一个支持基本的命令行参数解析的flag package(包)。我们将使用这个package来实现我们的命令行程序示例。
package main import "flag" import "fmt" func main() { // 基本的标识可以声明为string,integer以及bool类型。 // 这里我们什么了一个字符串参数word,它有一个默认值“foo”, // 还有一个简短的描述。 // flag.String函数返回一个字符串指针(不是字符串值); // 下面将看到如何来使用这个指针。 wordPtr := flag.String("word", "foo", "a string") // 类似于前边的'word' flag,这里声明了numb和fork标识. numbPtr := flag.Int("numb", 42, "an int") boolPtr := flag.Bool("fork", false, "a bool") // 还可以使用在程序成任何地方声明过的var变量来声明一个选项。 // 注意,我们需要为函数传递指向这个flag的指针。 var svar string flag.StringVar(&svar, "svar", "bar", "a string var") // 一旦所有的声明完成,还需要调用flag.Parse()来执行命令行解析。 flag.Parse() // 接下来,我们打印输出解析的选项还有其它紧接位置的参数。 // (注:Trailing positional arguments怎么翻译呢?) // 还要注意,我们需要用形如*wordPtr的指针指向值,来获取参数实际值。 fmt.Println("word:", *wordPtr) fmt.Println("numb:", *numbPtr) fmt.Println("fork:", *boolPtr) fmt.Println("svar:", svar) fmt.Println("tail:", flag.Args()) }
最好先编译下,然后直接运行生成二进制程序来测试这个带参数的命令行程序。
$ go build command-line-flags.go
首先用所有flag都有值来测试下这个编译好的程序。
$ ./command-line-flags -word=opt -numb=7 -fork -svar=flag word: opt numb: 7 fork: true svar: flag tail: []
注意,忽略的flag参数将被自动赋予他们的默认值。
$ ./command-line-flags -word=opt word: opt numb: 42 fork: false svar: bar tail: []
紧随参数可以为提供给任意flag
$ ./command-line-flags -word=opt a1 a2 a3 word: opt ... tail: [a1 a2 a3]
注意,flag package需要所有的flag都要在位置参数之前出现(否则这些flag将会被位置参数中断调掉)
$ ./command-line-flags -word=opt a1 a2 a3 -numb=7 word: opt numb: 42 fork: false svar: bar trailing: [a1 a2 a3 -numb=7]
使用 -h 或者 --help 参数可以自动为命令行程序生成帮助信息
$ ./command-line-flags -h Usage of ./command-line-flags: -fork=false: a bool -numb=42: an int -svar="bar": a string var -word="foo": a string
如果提供的flag不是falg package中指定。程序将打印出错信息并显示帮助文本。
$ ./command-line-flags -wat flag provided but not defined: -wat Usage of ./command-line-flags: ...
接下来我们将看到环境变量-另一种常用参数化程序方法。
关于某支付公司近日某些风波的思考
以下吐槽内容均不代表个人观点,一切风险和责任本人概不承担。
传言有时候真是略显荒唐。事件的原因就暂且不提了。某吧我是从不去的,渣浪也基本不感兴趣,所以对于那些传言从Boss的言行以及近日惨绝人寰的加班中读出四五分的真实~
事件具体产生成了什么样的影响?社会和企业以及个人,前两者自有人琢磨,对于个人,对资深苦逼的印象是深刻和值得深思的。
因此,结合本次事件反思下,主要分析项目管理上的问题。
首先,作为一个大的金融交易系统项目,没有做出具体详尽的需求分析,建设中没有业务参与,管理层的急功近利,顾此失彼,盲目乐观…有限的工期内每一环节出问题都是致命的,我认为导致系统建设问题的根本是管理层的失误。
其次,忽略服务。第三方支付系统是面向商户,给商户提供服务的,商户要的不是一个简单交易成功 ,交易的承兑和资金的通畅才是商户关心的。忽略了个性化服务而盲目追求原型中的稳定或成熟,这本身就是舍本逐末。不同于银行,商户在第三方支付中要个性的多。没有充分评估商户清算环节的重要性,未作必要的评审分析,导致清算问题突出。
第三,系统设计存在问题,不同系统之间差异天壤之别。报文+通信协议构造的联机交易系统可能大同小异,但清算系统的个性不可同日而语。联机中计费或个性处理本质没有问题,但如果这一环节出现问题,补救的难度将是巨大的。
过分依赖中间键和原型系统势必限制新系统的灵活和创新,导致建设中举步维艰,架构设计多次推翻,浪费大量时间。
引入账户体系一直作为Boss自豪的资本,可是,它居然没有用!这几乎成为一个笑话。商户个性多元的服务诉求,这个账户系统似乎不够完备暂时无法满足,于是,账户系统对于线下商户作为摆设还显得刺眼。某种意义上这样不完备的账户体系成了一块巨大的包袱。打脸-它真的没用!还是再考虑考虑完善吧~
四,沟通。管理层和各成员,信息并不能真实的被传递。过分的乐观完全压过下面反对的声音。当然领导有自己的压力,压力传递到各成员后,只有加班~没有意见~没有建议~疯狂
五,风险。一刀切,要么生,要么死,结果残了。
码到这里累了,无力吐槽。个人拙见,但愿来日不再像今日,新年后的良心发现。
个人拙见,谢绝转载。
Install Scrapy on OSX|mac OSX 上安装Scrapy[SYN:gejoin.com]
注:文章同步自我的[SYN:gejoin.com]
顺利的话,一句命令搞定:
sudo pip install Scrapy
OSX还是需要额外一些包或升级的,包括:
cssselect, queuelib, six, w3lib, lxml, Twisted, characteristic, pyasn1, pyasn1-modules, service-identity
幸运的是pip或者easy_install 可以自动为你安装,但是其中的问题:
Found existing installation: six 1.4.1
DEPRECATION: Uninstalling a distutils installed project (six) has been deprecated and will be removed in a future version. This is due to the fact that uninstalling a distutils project will only partially uninstall the project.
Uninstalling six-1.4.1:
…
OSError: [Errno 1] Operation not permitted: ‘/tmp/pip-qeBchm-uninstall/System/Library/Frameworks/Python.framework/
Versions/2.7/Extras/lib/python/six-1.4.1-py2.7.egg-info’
显然升级six是出错了,使用sudo
pip install six --upgrade
或者 pip uninstall six
都会遇到同样的错误。无论什么用户,多大权限。想必下载包安装也会同样问题。我只能这么认为:这都是pip的错。
好吧,那就easy_install试试吧。
sudo easy_install –upgrade six
Searching for six
Reading https://pypi.python.org/simple/six/
Best match: six 1.10.0
Downloading https://pypi.python.org/packages/source/s/six/>six-1.10.0.tar.gz#md5=34eed507548117b2ab523ab14b2f8b55
Processing six-1.10.0.tar.gz
Installed /Library/Python/2.7/site-packages/six-1.10.0-py2.7.egg
Processing dependencies for six
Finished processing dependencies for six
继续使用easy_install 安装Scarpy吧:…
sudo easy_install Scrapy
看起来挺顺利..
可是….
In file included from src/lxml/lxml.etree.c:323:
src/lxml/includes/etree_defs.h:14:10: fatal error: ‘libxml/xmlversion.h’ file not found
#include “libxml/xmlversion.h”
^
1 error generated.
Compile failed: command ‘cc’ failed with exit status 1
/tmp/easy_install-U7v3Lb/lxml-3.5.0/temp/xmlXPathInitxO27oS.c:1:10: >fatal error: ‘libxml/xpath.h’ file not found
#include “libxml/xpath.h”
^
1 error generated.
看来libxml2 需要额外安装的
xcode-select --install
以下就顺利完成安装
sudo pip install Scrapy
OR
sudo pip easy_install Scrapy