最近老大说有个活需要快速撸一个web原型出来,后端随意选择,我就想这么自由的话那我就玩玩golang吧,就简单入坑了一下。其实之前做MIT6.828的时候接触过Golang,不过当时其实写的并没有很爽,加上但是是跟着lab来做,很多golang的特性什么都其实都没有涉及到。这次正好就用golang写写server,正好还涉及到了一些分布式和并发的部分,就想试试golang的并发。

Beego

入坑的server框架我随手选了一个Beego,其实也不算随手,因为是看Beego有完整的中文文档,而且很多大公司都在用,在github上的star也相当之高,而且评价都挺好,属于比较稳定比较全的框架,就先用它入手玩玩。花了2天看了一下Golang的语法,一周时间撸了一个server出来。觉得Golang写得有时候还是挺爽,写并发确实方便。

模块

根据官网的叙述,beego是个HTTP框架,可以快速开发API层,后端逻辑层和数据接口层。和flask比较相似。结构高度模块化,基础的模块比如log,orm,config,cache,httplibs等都是高度解耦,你可以很容易的组织项目的model层,controller层,还有统一的路由模块。很小巧很灵活,比SSH那种大而全的是简单多了。个人感觉controller层封装的还挺方便的。orm层的话,对表的匹配竟然是通过大小写匹配的,比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
type DruidHistorySearch struct {
PressureTestName string
PressureTestVersion string
Author string
CreateTime string
SpendTime int
IsMoreCity bool
IsHaveInterval bool
Qps int
Half int `orm:"column(50p)"`
NinetyFive int `orm:"column(95p)"`
NinetyNine int `orm:"column(99p)"`
Avg int
Max int
Min int
ErrorPercent float64
JsonStr string
Id int
}

默认会把驼峰的命名转成带下划线的,比如这样就会转成这样一张表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
CREATE TABLE `druid_history_search` (
`pressure_test_name` varchar(255) DEFAULT NULL,
`pressure_test_version` varchar(10) DEFAULT NULL,
`author` varchar(10) DEFAULT NULL,
`signal` varchar(50) DEFAULT NULL,
`create_time` datetime DEFAULT NULL,
`spend_time` int(11) DEFAULT NULL,
`is_more_city` tinyint(1) DEFAULT NULL,
`is_have_interval` tinyint(1) DEFAULT NULL,
`qps` int(11) DEFAULT NULL,
`fifty_percent` int(11) DEFAULT NULL,
`ninety_five_percent` int(11) DEFAULT NULL,
`ninety_nine_percent` int(11) DEFAULT NULL,
`avg` int(11) DEFAULT NULL,
`max_qps` int(11) DEFAULT NULL,
`min_qps` int(11) DEFAULT NULL,
`error_percent` float DEFAULT NULL,
`json_str` blob,
`id` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
)

因为我数据库用的是mysql,设置了大小写不敏感,如果设置了大小写敏感,就需要用这样的代码:

1
Half int `orm:"column(50p)"`

来特别指定这个变量在表中代表的字段了。

打包部署

beego有一个好处是提供了一套的tool来帮助开发部署。

1
go get github.com/beego/bee

用这个指令可以获取beego工具。比如bee指令可以有很多功能:
bee工具

用new可以新建beego项目,run可以本地热部署的运行项目,很适合debug。还可以编译到指定平台,比如你在mac上开发,想编译成linux上的可执行文件,就执行:

1
bee -be GOOS=linux GOARCH=amd64

然后把打包编译出来的文件上传的服务器上,就可以直接运行了。

Golang的面向对象

我们用面向对象的语言写了很久的工程,工业界大型系统还是以Java,C++为主。在写Golang的过程中,我发现他很多地方确实很符合软件工程的设计,很适合写工程,有着很多工程经验的约定俗成,比如变量名,代码格式化等等。这里我就讲几个特性。

golang的并发编程

这个是我最喜欢的特性,当然我最开始接触的时候一直用不好。goroutine就是轻量级的线程,基本就是协程,但又有一点不一样。

我曾经一直理解的协程是类似线程的一种。但是又有人理解的是callback的一种。我研究了一下,发现和我一样感觉的都是Java,C++出身,理解为callback的都是写node,lua等出身的。这里正好把这些概念理清楚下然后再和Golang的Goroutine联系起来。

首先我们理清一个概念,并发和并行,就是Concurrency and Parallelism,用《Computer Systems: A Programmer’s Perspective》里面的一张图来解释:

并发和并行

这里可以明确,并发是逻辑架构,非并发的程序就是一根竹竿捅到底,只有一个逻辑控制流,也就是顺序执行的(Sequential)程序,在任何时刻,程序只会处在这个逻辑控制流的某个位置。而如果某个程序有多个独立的逻辑控制流,也就是可以同时处理(deal)多件事情,我们就说这个程序是并发的。这里的“同时”,并不一定要是真正在时钟的某一时刻(那是运行状态而不是逻辑结构),而是指:如果把这些逻辑控制流画成时序流程图,它们在时间线上是可以重叠的。

并行是指程序的运行状态。如果一个程序在某一时刻被多个CPU流水线同时进行处理,那么我们就说这个程序是以并行的形式在运行。(严格意义上讲,我们不能说某程序是“并行”的,因为“并行”不是描述程序本身,而是描述程序的运行状态,但这篇小文里就不那么咬文嚼字,以下说到“并行”的时候,就是指代“以并行的形式运行”)显然,并行一定是需要硬件支持的。

所以我们就知道,进程是有自己的隔离空间的,在CPU中多个进程运行是靠时钟终端来抢占CPU时间的,线程的切换也是如此,而且比进程灵活的是进程的切换调度我们只能依赖操作系统,线程的话我们可以自己控制调度,我们可以通过系统调用来切换到内核进程,然后对整个系统的线程进行调度,所以其实线程调度和并发的逻辑和原理并不复杂,哪个线程抢占CPU全部由内核线程来调度,然后线程通过时钟中断或者系统调用来让内核获得控制权,从而进行调度。

所以从上面的逻辑中,我们可以自己设计一个并发编程的框架,不依赖系统的线程和系统调度,为了描述方便,我们接下来把“代码片段”称为“任务”。
下面通过摘取一篇blog中的片段来解释如何自己设计一个并发编程框架:
http://www.sizeofvoid.net/goroutine-under-the-hood/


>
和内核的实现类似,只是我们不需要考虑中断和系统调用,那么,我们的程序本质上就是一个循环,这个循环本身就是调度程序schedule(),我们需要维护一个任务的列表,根据我们定义的策略,先进先出或是有优先级等等,每次从列表里挑选出一个任务,然后恢复各个寄存器的值,并且JMP到该任务上次被暂停的地方,所有这些需要保存的信息都可以作为该任务的属性,存放在任务列表里。
>
看起来很简单啊,可是我们还需要解决几个问题:
>
(1) 我们运行在用户态,是没有中断或系统调用这样的机制来打断代码执行的,那么,一旦我们的schedule()代码把控制权交给了任务的代码,我们下次的调度在什么时候发生?答案是,不会发生,只有靠任务主动调用schedule(),我们才有机会进行调度,所以,这里的任务不能像线程一样依赖内核调度从而毫无顾忌的执行,我们的任务里一定要显式的调用schedule(),这就是所谓的协作式(cooperative)调度。(虽然我们可以通过注册信号处理函数来模拟内核里的时钟中断并取得控制权,可问题在于,信号处理函数是由内核调用的,在其结束的时候,内核重新获得控制权,随后返回用户态并继续沿着信号发生时被中断的代码路径执行,从而我们无法在信号处理函数内进行任务切换)
>
(2) 堆栈。和内核调度线程的原理一样,我们也需要为每个任务单独分配堆栈,并且把其堆栈信息保存在任务属性里,在任务切换时也保存或恢复当前的SS:ESP。任务堆栈的空间可以是在当前线程的堆栈上分配,也可以是在堆上分配,但通常是在堆上分配比较好:几乎没有大小或任务总数的限制、堆栈大小可以动态扩展(gcc有split stack,但太复杂了)、便于把任务切换到其他线程。
>
到这里,我们大概知道了如何构造一个并发的编程框架,可如何让任务可以并行的在多个逻辑处理器上执行呢?只有内核才有调度CPU的权限,所以,我们还是必须通过系统调用创建线程,才可以实现并行。在多线程处理多任务的时候,我们还需要考虑几个问题:
>
(1) 如果某个任务发起了一个系统调用,譬如长时间等待IO,那当前线程就被内核放入了等待调度的队列,岂不是让其他任务都没有机会执行?
>
在单线程的情况下,我们只有一个解决办法,就是使用非阻塞的IO系统调用,并让出CPU,然后在schedule()里统一进行轮询,有数据时切换回该fd对应的任务;效率略低的做法是不进行统一轮询,让各个任务在轮到自己执行时再次用非阻塞方式进行IO,直到有数据可用。
>
如果我们采用多线程来构造我们整个的程序,那么我们可以封装系统调用的接口,当某个任务进入系统调用时,我们就把当前线程留给它(暂时)独享,并开启新的线程来处理其他任务。
>
(2) 任务同步。譬如我们上节提到的生产者和消费者的例子,如何让消费者在数据还没有被生产出来的时候进入等待,并且在数据可用时触发消费者继续执行呢?
>
在单线程的情况下,我们可以定义一个结构,其中有变量用于存放交互数据本身,以及数据的当前可用状态,以及负责读写此数据的两个任务的编号。然后我们的并发编程框架再提供read和write方法供任务调用,在read方法里,我们循环检查数据是否可用,如果数据还不可用,我们就调用schedule()让出CPU进入等待;在write方法里,我们往结构里写入数据,更改数据可用状态,然后返回;在schedule()里,我们检查数据可用状态,如果可用,则激活需要读取此数据的任务,该任务继续循环检测数据是否可用,发现可用,读取,更改状态为不可用,返回。


所以golang的goroutine就是golang自己实现的类似协程的一个并发编程的框架,他就是一个函数的入口,可以在堆上为这个函数分配堆栈,所以很廉价,可以很轻松的创建上万个goroutine,因为他不涉及到操作系统的调度。他们之间通过channel来通信。

终于把休学手续给办下来了,在帝都的房子也安顿好了,gap 成就达成!

一些dream list

这一年时间具体每一步怎么过还没有完全想好,不过应该会一方面沉淀下来,安心做点research,学习Computer Science,另一方面,再看看有什么新的机会,想清楚未来的打算。思索了一下,未来想完成这些:

1.30岁前年薪百万(感觉这个需要历史的进程了)

2.读一个PHD学位(希望有生之年可以达到)

3.加入一个开源组织,参与开源项目

4.进Google工作 once

5.再创一次业

6.找到女朋友

Paxos

Lab3的内容呢就是让你把K/V service实现Paxos。这里就不得不说到大名鼎鼎的Paxos了,所以这里应该会有较大篇幅讲一讲Paxos,包括衍伸出来的CAP理论,两端提交协议,拜占庭将军问题等等。至于lab的内容因为实验的wiki上已经给了伪代码实现了,所以其实并不复杂,不过理解Paxos那可是费了大力气。

CAP理论

首先要说Paxos就要先说下CAP。首先CAP是针对于分布式环境中的,非分布式的就没这问题了。然后是CAP的三个特性:

1、C(Consistency):数据的一致性,是指分布式系统中每个节点中的数据,在同一时刻状态是否是一致的。

2、A(Aviliability):系统的可用性,是指当集群中的部分节点出现故障后,系统是否依旧正常运行。

3、P(Partition Tolerance):分区容忍性,是指当集群中的节点由于网络分区失去联系后,系统是否依旧正常运行。分区主要强调的是无法通信。此处的“正常运行”需要理解下:正常运行指的是,在没有任何因素影响下的理想状态,不是仅仅单指能否访问、能否回应客户端。

CAP理论认为三者不能同时满足。所以很多Nosql系统都不得不在这三样中权衡。在分布式系统中,我们一般认为网络故障是常态,就是分区容忍性是一定要保证的,所以要在一致性和可用性当中做权衡。或者是,是在数据一致性和服务延迟之间的权衡。for example .亚马逊的Dynamo是为它的电商平台服务的,阿里的ODPS也是一样,这些服务有个特点,就是在线服务,延迟不能太久,不然你点个购物车一分钟才返回给你数据,那还怎么秒抢双十一?所以这个就必须保证高可用性。然而这里要保证高可用性,就必须进行数据复制。因为分区容忍性是一定要保证的,所以万一一台数据服务器挂了,你必须去备份的服务器读数据返回给用户才行。那这就出现数据一致性的问题了。

讲到一致性,那又可以分为强一致性,弱一致性

强一致性可以理解为在任意时刻,所有节点中的数据是一样的。同一时间点,你在节点A中获取到key1的值与在节点B中获取到key1的值应该都是一样的。弱一致性包含很多种不同的实现,目前分布式系统中广泛实现的是最终一致性。所谓最终一致性,就是不保证在任意时刻任意节点上的同一份数据都是相同的,但是随着时间的迁移,不同节点上的同一份数据总是在向趋同的方向变化。也可以简单的理解为在一段时间后,节点间的数据会最终达到一致状态。

然后就引出了问题,假设一个数据发生了更新,那么我要把更新的信息发给所有的包含这个数据的服务器,并且要等他们全部返回已经更新的信息之后,才能进行之后的操作,这是强一致性。然后再看Paxos,Paxos协议就是一种在保证强一致性前提下把可用性优化到极限的算法。

Paxos协议提出只要系统中2f+1个节点中的f+1个节点可用,那么系统整体就可用并且能保证数据的强一致性,它对于可用性的提升是极大的,仍然假设单节点的可用性是P,那么2f+1个节点中任意组合的f+1以上个节点正常的可用性P(total)=,又假设P=0.99,f=2,P(total)=0.9999901494,可用性将从单节点的2个9提升到了5个9,这意味着系统每年的宕机时间从87.6小时降到0.086小时,这已经可以满足地球上99.99999999%的应用需求。

不过Paxos这个协议该考虑的问题太多,所以只能简单描述,不过我想更多的讲一讲Raft,这个Paxos的简化版本,但是又比Paxos方便的多的协议。

Paxos

这里开始说Paxos,Paxos的具体内容其实在论文《Paxos made Simple》已经说的很清楚了,而且有了非常严格的证明,这里我就略过证明的部分,直接说最终得到的协议的过程。

Paxos协议流程划分为两个阶段,第一阶段是Proposer学习提案最新状态的准备阶段;第二阶段是根据学习到的状态组成正确提案提交的阶段,完整的协议过程如下:

阶段 1:

1.Proposer选择一个提案编号n ,然后向半数以上的Acceptors发送编号为 n 的prepare请求。

2.如果一个Acceptor收到一个编号为n 的prepare请求,且 n 大于它已经响应的所有prepare请求的编号,那么它就会保证不会再通过(accept)任何编号小于 n 的提案,同时将它已经通过的最大编号的提案(如果存在的话)作为响应。

阶段 2:

1.如果Proposer收到来自半数以上的Acceptor对于它的prepare请求(编号为n )的响应,那么它就会发送一个针对编号为 n ,value值为 v 的提案的accept请求给Acceptors,在这里 v 是收到的响应中编号最大的提案的值,如果响应中不包含提案,那么它就是任意值。

2.如果Acceptor收到一个针对编号n 的提案的accept请求,只要它还未对编号大于 n 的prepare请求作出响应,它就可以通过这个提案。

这样一个算法过程就保证了:

(1):任意时刻,就算存在多个Proposer在询问之后提出了不同的值的提案,最终只有其中一个Proposer的提案中的值将会Acceptor接受,即只有一个值能够被决定。

(2)当提案的值被决定为后,假设被决定为c,之后任意的Proposer提出的提案的值也是c

这是学习笔记的第二篇,这篇主要是记录我完成MIT-6.824 lab 2 的思路和想法

Lab2 Overview

这部分的实验主要会实现容错的机制并且完成一个K/V service的雏形。容错的机制其实就是用主备系统来实现。这里用一种叫viewservice的server来完成lab。viewservice会不断监控每一个server是否还alive,如果primary server或者backup server出现问题,就采取对应的措施。如果primary挂了,就会让一个backup取代成为primary。如果一个backup挂了,并且“集群”中海油空闲的server,就会让他变成backup。这时候primary会把自己完整的database内容发送给新的backup,并随后通过Put来验证backup的K/V database是否保持一致性。

因为这个lab是在单机上模拟的分布式的一些特性,所以一些地方需要注意,比如说这个databases的数据是放在内存里而不是放在disk当中的。以及各个server和client等之间的通信只能通过RPC,而不能通过直接的golang对象或者文本IO来实现。

因为这是模拟出来的分布式逻辑,所以下面的一些容错机制和限制会导致真正的分布式系统会too weak,比如

  • view service是很脆弱的,因为没有复制。这其实就是类似hadoop中的HA的问题。相当于master节点只有一个的话,是很危险的,容易过载。
  • 像之前说的,一个空闲的sever变成backup的时候,需要从primary复制databses来完成备份的效果,这个是很慢的。尤其当网络不好的时候。
  • server并没有将K/V databases存储在disk中, so they can’t survive simultaneous crashes (e.g., a site-wide power failure).
  • 如果出现暂时性的问题导致primary和backup间的通讯出现延迟或者中断,那么只有两个办法来解决。第一是改变view来将backup移除,或者keep trying。如果通信问题出现的很频繁,那以上两个方法都不好用。
  • 这个实验的primary/backup结构有些类似 Flat Datacenter Storage和Monge DB,还有Chain Replication,当然他们的设计和实现更详细。

Part A

好了首先我们来实现这个View Service,就是完成primary/backup结构。
整体思路大概是这样的,client和server通过Ping()方法,也就是RPC请求来通信。client向view server发送的ping带两个参数,me:string,viewnum:int 也就是client的name和viewnum。其中me用来判断是primary还是backup或者是空闲的server。viewnum用来判断是否现在的primary已经被知晓。

从上面的逻辑我们可以很简单的完成client的代码,其实工程中很多的方法和接口已经写好了,核心部分我们只要完成viewservice/client.go 中的Ping()方法就行。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func (ck *Clerk) Ping(viewnum uint) (View, error) {
// prepare the arguments.
args := &PingArgs{}
args.Me = ck.me
args.Viewnum = viewnum
var reply PingReply
// send an RPC request, wait for the reply.
ok := call(ck.server, "ViewServer.Ping", args, &reply)
if ok == false {
return View{}, fmt.Errorf("Ping(%v) failed", viewnum)
}
return reply.View, nil
}

然后是服务端接收到ping并且得到传来的参数后作出的反应。在viewservice/server.go中。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
func (vs *ViewServer) Ping(args *PingArgs, reply *PingReply) error {
id, viewnum := args.Me,args.Viewnum
switch id {
case vs.curr.Primary:
if viewnum == vs.curr.Viewnum {
vs.hasPrimaryAcked = true
vs.lastSeen[vs.curr.Primary] = time.Now()
}else {
vs.promoteBackup()
}
case vs.curr.Backup:
if viewnum == vs.curr.Viewnum {
vs.lastSeen[vs.curr.Backup] = time.Now()
} else {
vs.removeBackup()
}
default :
vs.addServer(id)
}
reply.View = vs.curr
return nil
}

主要就是根据me和viewnum来判断情况。如果me是primary,则判断viewnum是否和现在的viewnum相同,相同则说明primary已经被ack了,记下时间。不同则说明现在的primary可能已经挂了。就要将backup变成primary,执行promoteBackup()函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
func (vs *ViewServer) promoteBackup() bool {
if !vs.hasPrimaryAcked {
return false
}
if vs.curr.Backup == "" {
return false
}
vs.changeView(vs.curr.Viewnum+1, vs.curr.Backup, "")
return true
}

若是me是backup,则判断viewnum和现在的是否相等。不等的话就移除这个backup。

Part B

这部分就是在之前primary/backup的基础上,加上K/V service。未完待续!

Golang RPC

未完待续!

序言

这系列的博客是来记录我的分布式系统的学习笔记。说起来其实在大一就开始接触分布式了,从最开始的hadoop,mapreduce,到spark,storm。然后接触的大数据等等。不过一直都是自己从网上找资料或者在实习当中学习到的知识,一直没有一个系统的知识梳理。前段时间看到了mit的这个课程,简直感慨万千。野路子走惯了,真心需要学院派的系统指导来帮自己梳理计算机的知识体系了。所以这一系列的博客来记录这一阶段的学习历程,就当做是学习笔记了。

目录

一. Mpareduce和Lab 1 思路

二. RPC 和 GO

三. Lab 2 思路(主备和容错)

四.Paxos协议

五.Lab 3 思路

六.未完待续

Lab Overview

因为是学习笔记,所以很多东西不会写的很详细。Mapreduce因为之前用的很多了,这里就不细的展开了,主要说下课程的lab,这也是我觉得最有价值的地方。

MIT-6.824的实验分为5个部分,整个项目用golang来实现,testcase都已经给出。最终会实现一个key/value的 services,支持三个RPC请求,Put(),Append(),Get()。实现一个简单的K/V的分布式数据库

  • Lab 1 要求实现一个Mapreduce的lib,接口已经给出了。这个主要就是根据google的那篇论文来实现。并且还要求实现一个任务的分配系统。也就是简单的hadoop的yarn,并且要能简单的容错
  • Lab 2 分为两部分。最后会做出一个K/V services的雏形,用主从复制来实现。有点hadoop中master/worker的雏形。其中part A会实现一个viewservice。part B会开始实现K/V services
  • Lab 3 会让你把Paxos的内容给你的K/V services给加上。Part A让你实现Paxos的lib,Part B让你实现有Paxos的K/V services
  • Lab 4 这里会让你实现 K/V的存储系统。原理应该就是根据partition和group来切分和归类数据
  • Lab 5 就是让你整合所有的内容,来真正实现这个K/V的分布式数据库,具体的细节可以有很多,自己可以扩展

因为早上6点多有高铁要回家,所以果断决定2015的最后一天就不睡了吧。好久没写东西了,发现自从上了大学,没有了语文课,又误入coding这个坑之后,文笔是越来越差了。好歹我也是当年可以靠作文拉分的存在啊!而且当年引以为傲的一手好字,也是退不了不少。

写东西可以让人思考,深感觉最近脑子CPU用的太少了,只有IO进出。遂决定以此为证,重新开启blog的记录。

2015

一直没有写总结的习惯,然而既然马上就要2016了,就写写2015的总结吧。这一年的我,正好是20岁,这一年的我,感觉眼前的世界越来越大,未来的路越来越长,这一年,我一直在成长。

细数了下这一年走过的城市。长沙,北京,杭州,上海,深圳,福州。不算太多,但每一个都很重要。其实算了下20年的人生,走过的城市虽不算多,但也不是偏居一偶,只在家里的小山村不问世事。但是真的要说记忆,似乎在2015之前,除了福州,除了家乡,给我留下深刻印象的城市,可以让我有归属感,想待在那里的城市,好像并没有。细思了一下,2015之前,除了福州之外,待得最久的城市竟然是东京!而在高中之前的我,却又是很讨厌日本这个国度的(小时候那莫名的爱国情怀吧)。这样想来也是无奈,明明向往自由的我,自己的足迹竟然如此匮乏。

不过这一年倒是有了转折,准确的说应该是2014的7月开始吧。2015,有归属感的城市,应该要多了半个,杭州。算起来,我的2015,似乎有一半是在杭州度过的了。

长沙,焦糖,那群可爱的人,那间记忆中的出租屋。杭州,阿里,一堆优秀的可爱的人,一群可以聊通宵的伙伴。这两个地方,两段记忆,占据了我的大部分2015.

创业的那群焦糖er

一年前的今天,应该是在阳光100三期2栋的7层度过的。犹记得,那时候的我,即将跨年的时候还在给投资人发BP,记得发完应该已经是晚上8点了,我一个人在那间出租屋里,突然窗外绽放了烟火。我才记起来又是橘子洲放烟花了,才猛然惊醒已经要跨年了。那时候望了下空荡荡的出租屋,尽管自己不矫情,但还是有种孤单和失落感。不过这种感觉并没有持续多久,10点的时候,伯霖和炜燊从酒吧回来,11点的时候,家园和伽玛也来了,当场就打开投影仪开始放电影,12点到,几个人干了纸杯中的RIO,那一刻,只有一种感觉:认识你们真好。

这就是第一次创业之后的感觉,再回首,抛开当初的雄心壮志,抛开项目本身,抛开各种O2O,抛开BP,收获最大的,就是这群可爱的人啊!可爱的焦糖er。至于为何叫焦糖er呢,因为最早的时候一群人是学校里面的一间咖啡馆度过了一个个通宵。正好当初公司的名字也是在那里取的,思来想去也没有想好叫什么,看了看桌子上的咖啡,说就叫焦糖科技公司好了。。。所以公司名字就这么随意的被定了下来=。=

阿里的那群小伙伴

三月份的时候误打误撞投了个简历,没想到面试一切顺利。原本并没有想拿到offer的,因为那时候还在创业。反正就这样让我拿到了蚂蚁的实习机会,也算是阴差阳错吧。之后逐渐退出创业圈,开始接触了校招圈(姑且这么称呼他吧)那时候建了个微信群,让拿了阿里offer的小伙伴加入,原本以为只是把身边的几个小伙伴拉进来,到了实习的时候照应下,没想到群越来越大,最后已经500人满了。正是如此,让我在杭州的生活精彩了不少。

刚到杭州的时候为了安家,不得不租个房子了,当时的想法是微信群里那么多小伙伴,我一个人住实在太无聊了,就租了个三室的房子,然后再群里招租,最后竟然是6个小伙伴,4男2女一起住。也是让实习下班之后的生活精彩了不少。阿龟,周楠,华杰,青青,宏钊。还有曾经借宿的宏钊女票和周楠学弟。想着每天8点半,每个人都睡眼朦胧的起床,阿龟和周楠默默挤公交去西溪,剩下的四只轮流打Uber去支付宝。每天晚上不出意外华杰肯定第一个到家,剩下的人基本都是10点以后回家。然后开始洗漱之后的喝酒宵夜聊天吹逼。吐槽阿里,聊各个BU八卦,聊群里的网红,聊各自的人生经历,聊各自的未来,聊周末一起去哪里浪。那时候的日子,虽然每天加班到死,但依旧很开心,很精彩。

2016 你好

今年妈妈的农历生日正好是在元旦,就回家一趟。想来大学以来回家的次数也是屈指可数,似乎每个假期都没怎么在家。尽管元旦回来就要考试了,不过现在似乎考试已经让我波澜不惊了=。=

今年的跨年难得的可以在家里过,可能会和爹娘好好聊聊天吧。毕竟这两年多的大学让我成长了很多,见得多了,思考的就多。

想想2016,自己应该还是会不断成长吧。思来想去,可能就两点:

  • 去更远的地方,看更大的世界,让自己的世界观变得更大
  • 看更多的书,学更多的知识,让自己的内心不断充足

2016 加油!

我的大学

想来一晃已经大三了,若是不准备继续求学的话,大学生涯已经过去一半了,
仔细想来,我一直在寻找着大学的意义,或者这四年高等教育对我的意义。这下这篇文章,主要是因为看了吴军博士的《大学之路》。里面的内容,似乎让我之前的一些疑惑和不解之处,豁然开朗。让我对自己之后的路,有种拨云见日的感觉。

初入

大三才过去一个月,想来回忆的也只有之前两年的时间了。让我把时间退回到两年前的9月初,那是我第一次踏上长沙的土地。想在想来,当初还是有点略微兴奋,略微不甘,略微骄傲的。因为高考之前确实没有想过,我接下来的四年会在这里。当然接下来确实证明了我不会安心的呆在这里。

大学伊始,也是和大家一样,军训,熟悉班级,带着一种所谓的新鲜感,看着这个和高中不同的世界。昏昏欲睡的上课内容已经完全不能吸引我们了。这时候的我们,是充满活力希望,但也是一张白纸,只是一个刚进入新世界,还在盲目乱撞的新人。我也不例外。想来大一的时候,因为高中就有很多社团的缘故,所以对社团学生组织这种,也并没有那种见到新世界的新鲜感。反而因为自己性格不愿受束缚,告诉自己,一切官方的学生组织,自己都不参加。想过一次,自己的大学。

最后大一的时候我加入了学社联,参加了街舞社,玩了玩B-box。还加入了一个叫蓝杰的地方,这个对我之后影响挺深的地方。想在想来,或许只有当时在学社联的那段时间,算是自己当学弟,体验所谓大学生活的时间了吧。毕竟之后的我在学校的日子也是越来越少了。那时候的自己还是一个充满激情的小干事,在新闻部熬夜做视频,给部长们各种过生日。参加各种晚会,过着和大家相似的丰富多彩的大一生活。这时候的我,还没有完全接触到互联网圈,完全还想不到一年后的自己会在哪里。

大一的下学期,印象中是一直泡在蓝杰,应该是13年的冬天的时候吧,现在让我形容蓝杰,应该叫做一个互联网圈子的集中地吧。记得是那年的圣诞吧。每年蓝杰都有圣诞聚会的习俗。所以每年的圣诞,都可以看到各种学长学姐到蓝杰来。也就是在那一天,我看到了一个不一样的世界。原来大学里不止有各种社团各种组织,原来大学里还有这样的一群人在做着这样有趣的酷炫的事情。原来这样一群人,才是站在浪潮之巅。那时候的我才知道了,原来大学创业是这样的,原来有个团队一起做项目是这样的,就像是给我开了另一扇世界的门一样。所以大一的下学期,印象中就已经推掉了几乎所有的活动,真正开始了编程,开始了了解这个世界。而且就像一个黑洞一样,不断的吸引我,让我发现东西越来越多,越来越有趣。

猎豹移动

印象中是搞了一学期吧,当时的我仅仅算是入门了吧,隐约记得是14年的3月份,当时我和胡总说,我想搞云计算。其实当时的我还不知道云计算是什么东西。只是有种预感,似乎这东西代表了未来,值得自己去研究。当时就二话没说,当场开始组团队,热火朝天的开始研究。从google三大论文,到hadoop,到MapReduce。然而之前的自己还是只会写点java小程序,连linux都不会操作的小菜鸟。到最后把集群搭起来,跑通分布式的计算框架。当时的感觉,真的是比任何时候都有成就感。

我一直相信,机会是给有准备的人的,很多时候当你准备好了,只要静静等待,机会来了,抓住它。当时的我,已经感觉到,互联网,科技圈,在长沙的平台已经不够了,一定要去北京看看。机缘巧合之前,猎豹的VP钢总那时候正好来到蓝杰,猎豹刚成立云事业部,需要带一批人去猎豹。我就机缘巧合的过去了。也就是这次的机会,我的第一次实习,让我的世界观,真正的提升的一个层次。那两个月的实习,
猎豹的休息吧
猎豹的休息吧
猎豹的图书角

这是当时猎豹的工作的地方,当时还是在呼家楼的复星大厦,听说最近搬了栋楼,新的工作环境直接和硅谷接轨更有B格,有机会再去看看。当时是第一次实习,第一次自己去搞定租房,当时的15个小伙伴住在东三环的5间小破屋里,第一个月每天都加班到深夜,然后赶最后一班地铁回家,早上又早起,挤着传说中的北京地铁。虽然辛苦,但是内心一直充满了干劲,已经不记得多少次在公司通宵了。

至今还是很怀念当时的时间,怀念当时的小伙伴,现在想来,也是那次的实习把我第一次带上了CS的世界,带上了互联网的世界。

阿里

写下这篇文章的时候,自己还在杭州,在灯火通明的支付宝大楼里。算起来,这时候的我,已经是刚刚进入大三的本科生了。算起来也已经在阿里实习三个月了。回顾之前的两年,阿里应该是我的第三次实习了。
阿里西溪园区
阿里西溪园区
早上6点的支付宝大楼
这次的实习结束之后,自己应该会有一段时间的沉淀一下了。不管是技术上的,还是思想上,还是选择上的。

阿里的实习很棒,基本上自己被当成full stack来使唤了。而且阿里的实习真的是让我认识了好多好多优秀的小伙伴。而且这次的实习,我是在杭州租了间3室的房子,和6个天南地北的小伙伴一起住的。每到周五晚上,只要周末没被拉去加班,就在考虑着周末肿么浪,约哪些小伙伴一起出来玩耍。也因此结识了一群无比优秀的小伙伴。

还有一个多月阿里的实习就结束了,因为我本身就是大二来实习的,所以不像那些大三研二的人本身就是冲着实习转正找工作的目的。所以我的实习更多的就像是一种学习的过程。就当是来阿里上了几个月的大学。仔细想来,在阿里的这几个月,像这样安静的坐下来写点东西的时候,真的好少。更多的时候,上班时间有各种业务需要交流,各种会议需求需要讨论。下班的时候,各种小伙伴聚会约聊,回到家里也有那几只家里人在啤酒零食的吹逼。所以让自己感觉到,需要一点时间来沉淀下自己了。

阿里的事情,感觉有很多可以说,可以聊得,最重要的,是这几个月里,遇到的那些人。其实实习这件事,我本身是不抱着学习技术来的。技术,在哪里都可以学,真正难得的,是这几个月,和你一起经历加班通宵喝酒唱歌吹逼的人。我是个挺外向的人,可以和形形色色的人玩到一起。我想,和他们交流,从他们身上学到的,远比自己一个人在路上向前走,要多得多。毕竟,你是和国内最优秀的一波人,一波同龄人,一波年轻人在一起做着一件伟大的事情,change the world。

“教”与“学”

我不知道自己有没有资格来说这个问题,不过下面的只是自己的一些思考。我的大学经历算是非典型的。但自觉每一步都是经过自己考虑的,一些决定也是自己思考之后才做出的。

现在回想起来,好像刚进入大学的时候,我就没有认真的研读过,学校给我们的培养方案到底是什么,到底适不适合自己。现在想来,这肯定是有问题的。因为根据现在看来,培养方案只有一套,但是学生却是多样的。孔子在几千年前就得出了因材施教的结论,然而现在看来,大学并不能因材施教。

所以既然不能因材施“教”,那就只能自己去“学”。既然培养方案不适合自己,那就只能自己去找一条适合自己的路了。当然可能这条路会比较难找,过程中会有很多的迷茫和无助。但是我相信,这样的路,才是最适合自己的。

今天是5.21,昨天把gitpages+hexo的博客给搭建完成。最近过的挺颓废的,需要重新整理下自己。因为六月底要去阿里实习了,这段时间要做的事情其实还挺多的,稍微列举下。
1.把蓝杰的云计算教程写完(我会同步到博客上)
2.把手头的外包接得活全部理清
3.把学校的各种杂事安排好
4.算法导论,刷刷刷!!!
5.github上的自己的distributed Filesystem继续coding继续维护

之前的一些文章大家可以看 http://linhaoxiang.iteye.com/
ps:大半年前写的文章了,那是还是个大一的渣渣。。。。