回归

2019可谓是风云变幻的一年,贸易战打得如火如荼,股市像过山车一样,金融市场暴雷不断,科技巨头不断收缩,房价高企,忽然之间仿佛连苹果都吃不起了,相信所有的人或多或少都能感觉到无形的压力。印证了个人命运和国家命运是紧密相连的。

本来我在公司的“新项目”进展还算不错,和几个合作很好的同事将一艘满是漏洞的船修缮的相对完整,在 Kubernetes 世界里几乎可以自由航行,最近很长一段时间,我都在给内部用户做演示宣讲,渐渐我们也积累了不少用户。就在项目干得如火如荼之际,一场裁员却来的很突然,这次裁员和绩效无关,同一个部门的三位其他同事必须有一位必须离开,两位需要转组,被裁的同事可以拿到补偿。我这位被裁的同事是成都人,海归90后,因为反正在北京也买不起房,平时也很洒脱,不多的工资和假期干脆都拿来周游世界,增长见识。毕业一两年却已去了好几个国家。他恰好正准备过段时间辞职,这也算是种幸运。我问他后面有什么打算,他说已经和妻子决定离开打拼了两年都北京,返回成都创业。在为他们几个办的散伙饭上,我倒是为他感到坦然和释放。有时放弃了才能得到,回归到家乡,换个环境重新开始新的生活,新的起点,所以年轻真好,就在于拿起放下的成本很小,未来总有无限的可能性和广阔性。而我们这些老油条,还得在这里日复一日的重复朝九晚六(晚上还得开会)的非健康生活。

这场裁员对我的项目也是冲击不小,由于人手少了,管理层决定把这个项目停了,项目成员也都被分散到其他更紧急的项目组里。包括我自己,本来计划调整自己的职业生涯往管理方向走一走,现在短期内已不可能,几乎调整好的姿态现在又必须完整地调整回来,回归纯技术。好在我对纯技术还是有热情的,而在大公司做管理就面临大量的会议和办公室政治,做技术就显得存粹多了。所以,我对这样的“回归”,倒也很坦然,我也正需要一点时间来继续调整自己。至于我的 “新项目”,积累了不少用户,项目组成员依然热情高涨,所以我们自然也不会就此放弃,我们会利用自己的时间来做一些事情,包括支持内部客户,等到过一段时间公司的人手又富裕了,我们就又可以“东山再起”了。

所以人的命运常常是和时代关联在一起的,国际政治的“蝴蝶效应”会形成无形的飓风,将我们每个人都裹挟在一起。在无力抗争时,大概能做的,也只有坦然的“回归”了。

196 次阅读

银行网点转型

招行的银行卡到期换卡,最近忙到没有时间,新卡片制好了收到短信通知后,都拖了将近一个月没去取。感谢支付宝和微信,现在真的是不需要现金了。于是周末专门花了半天时间去了北三环的招行换卡,首先是坐地铁,然后再换共享单车骑行一会儿。我本以为银行人不多,只是取一张卡而已,可以很快办完,谁知竟然花了40多分钟。

首先,现在这家招行的网点居然没有取号机了,而是要求我用招行网银App扫一个二维码取号。我在想这分明是为了推广网银App,要是我没用智能手机怎么办?要是我没有安装网银,还得当场下一个不成?取了一个B开头的号之后就开始等了。银行里人的确不多,一个诺大的网点其实只有三、四个客户,开了两个普通高柜窗口,两个普通理财窗口,还有金葵花理财也在开门营业,外加大堂经理一人,迎宾员一人。

一开始前面有两个客户C开头的号,办完了又直接叫了一个C起始的号码,然后很我就等了将近半个小时,最后我实在有点不耐烦了,于是去问了大堂经理为什么不叫 B 开头的号。大堂经理感觉对客户还是比较尊重的,立马就进去协调一下,过了一会儿就来了一个柜员开了个新的窗口叫了B号。我顺便问了问,C开头的号是什么意思,柜员告诉我C开头是普通卡,B开头的是金卡。所以这还真符合我之前来招行网点的印象,好像现在金卡客户好像多于普通卡客户,每次办理业务都需要排队。就这样,我的业务大概4分钟不到就办完了,我却等了将近40分钟。

2012年我就曾经做过银行网点转型的解决方案和项目,例如大堂经理在客户进入网点之后(至少是排队之后),就能够获取到客户的等级级别,从而提供相应等级的服务。特别在人工智能兴起,支付宝的扫脸支付都已经成熟应用的今天,识别到客户并不是有很大的难度,银行网点是应该能够通过技术手段提供更好的服务。当然,从银行角度,是否真的愿意花心思服务好中低端客户,就是另外一回事了。而能否维护好存量客户,并且从存量客户里挖掘出高端客户,则是另外一个仁者见仁,智者见智的问题了。

现在看来,普通的银行网点,离智能智慧还差得远,银行网点人员的服务水平也需要加强,银行网点转型还有很长的路要走。

另外回来的路上试了试青桔单车,还是比较方便的。共享单车企业第一波已经死在了沙滩上,最终谁能胜出还是看资本实力、运营和服务,潮水退去,就知道谁在裸泳了。做什么都是长跑,笑到最后的,才是赢家!

183 次阅读

Shell 中 grep 的副作用

最近在 Jenkins 中编写项目的 Build 脚本时碰到一个关于 grep 的副作用的问题,让人感觉挺费解,解决时需要用一点小技巧,因此在这里记录一下。

先描述一下所碰到的问题的现象,我需要用 Jenkins Shell script 将为我们的项目部署到 Kubernetes cluster 上,在部署之前会进行一些权限的检查和设置,因此在脚本中直接使用了 kubectl 查询资源并结合 grep 的方式来判断资源是否已经创建出来了,例如:


...
is_service_account_exists=$(kubectl get sa --name myns | grep 'my-service-account')
if [ -z "$is_service_account_exists" ]; then
  echo "The service account does not exist, will create it ..."
fi
...

当 Jenkins Job 开始运行时,虽然 service account 并不存在,但是该 Job 却莫名其妙失败了,并没有看到期待的输出 “The service account does not exist, will create it …”。经过一番检查发现问题出在 grep 命令上。当 grep 正确执行,且从标准输入上匹配到期望的内容时,它会返回错误代码 0,例如:


$ kubectl get sa --namespace kube-system | grep tiller
tiller                               1         392d
$ echo $?
0

否则如果没有匹配到任何内容,该管道命令会返回 1,还是以上面的例子为例,当我故意查找并不存在的内容时,返回结果如下:


$ kubectl get sa --namespace kube-system | grep not-exists
$ echo $?
1

而 Jenkins Shell 脚本在执行时默认使用了 -xe 参数,-x 参数将打印每一行执行的命令, -e 则在运行 script 时检查每一行的输出结果,如果结果是非 0 则立即停止脚本执行。在本例子中,我们只想通过获取 grep 结果的输出来判断是否获得期待的结果,因此我们希望 ‘kubectl get sa –name myns | grep ‘my-service-account’ 无论如何都返回 0。解决办法是将 ‘|| true’ 添加到命令的最后,我们知道 shell 脚本通过布尔操作符连接时,会根据命令的返回结果来决定是否运行布尔操作符连接的其他命令。通过 || 操作符,可以保证 true 始终被执行,从而保证该命令的执行始终能够返回 0。例如:


$ kubectl get sa --namespace kube-system | grep not-exists || true
$ echo $?
0

最终将 Jenkins 脚本修改如下,并成功执行。


...
is_service_account_exists=$(kubectl get sa --name myns | grep 'my-service-account || true')
if [ -z "$is_service_account_exists" ]; then
  echo "The service account does not exist, will create it ..."
fi
...

参考:https://stackoverflow.com/questions/22814559/how-when-does-execute-shell-mark-a-build-as-failure-in-jenkins/22816074

257 次阅读

Go 语言中的超时处理

最近在项目里用 Go 语言对一些微服务进行重构,由于 Go 对我来说仍然算是一种新语言,因此在项目过程中碰到的一些比较不同一些写法,将会以 Tips 的方式记录在这里。今天想记录的是 Go 语言里的超时处理。

超时在一些业务场景里非常普遍,例如:

– 数据库访问操作,进行网络连接时通常都有超时时间。
– 本地客户端用阻塞方式异步访问远程,等待一段时间之后,如果远程没有返回结果,则认为超时。
– 微服务在启动时,需要初始化某些数据,例如等待服务注册表返回的总线密码等等,如果在规定的时间内未获得需要的内容,则认为超时,服务启动失败。

在 Java 等传统语言里,通常会首先记录当前时间,然后使用一个无限循环进行资源等待检查,如果需要等待的资源无法获取到,则将当前线程休眠一段时间,当休眠恢复后检查时间是否超过规定时间,如果超时则抛出异常,否则继续进入循环进行等待。用伪代码表述如下:


long startTime = Time.currentTimeMillis();
long EXPIRE = 1000*30; // 30 seconds to expire

while (true) {
    resource = getResourceFromRemote();
    if (resource == null) {
        Thread.sleep(1000); // Sleep 1 second
    }

    long currentTime = Time.currentTimeMillis();
    if (currentTime - startTime > EXPIRE)
        throw new RuntimeException("Expired to get remote resource");
    }

上述 Java 实现中,需要手工处理超时时间以及线程休眠等等,看上去也是非常的清晰,但是在 Go 语言的版本里,整个过程会显得更加简洁:


for resource := getResourceFromRemote(); resource == nil {
    select {
        case <-time.After(time.Second * 30):
            panic("Unable to receive resource within 30 seconds.")
        default:
            logs.Info("Resource is not received, waiting for 1 seconds...")
            time.Sleep(time.Second) // Wait for 1s until the resource is received.
    }
}

首先也是进入一个循环,循环条件是可以获取需要的资源,在循环内部使用 select 语句试图从 case 语句指定的通道里读取数据,如果无法读取到数据则进入下一个 case, <- time.After(time.Second * 30) 表示在 30 秒之后从通道内返回数据,在未到达规定的时间执行 ‘default’ 语句并且使用 time.Sleep(time.Second) 休眠 1 秒。

在这个 Go 语言的版本里,虽然看上去代码更加简洁,更多是利用 Go 语言本身的的一些特性,而不是过多依赖于函数库,这体现了 Go 语言设计时将一些互联网时代常用的功能变成语言本身的实用主义特点。

256 次阅读

新项目

八月底一位资深的同事离职,他手上的一个新项目交给我来带,简单的交接后同事就离职了。九月初开始上手这个项目,由于这个项目实际上是后台从老项目迁移,前端重新开发,其中使用到的技术基于微服务架构非常繁杂,简单列举一下:

后端:

  • 开发语言:Golang, Ruby, Shell
  • 开发框架:Sinatra (Ruby), Beego (Go)

前端:

  • 开发语言:Javascript (ES6)
  • 开发框架:React, Redux, Saga
  • 编译工具:Babel
  • 打包工具:Webpack
  • 包管理器:npm

基础架构部分:

  • 服务发现:etcd
  • 缓存:Redis, Memcached
  • 消息中间件:Redis
  • 容器技术:Docker
  • 容器编排:Kubernates
  • 容器安装:Helm
  • 数据库:Mongo

作为架构师我需要熟悉这一系列的技术,重新梳理架构,了解旧代码。吾生也有涯,而代码无涯,之后整个九月都很痛苦,到目前为止也仍然在不停学习中。由于项目时间紧,任务重,从一开始只有两个人,到目前增加了到将近六个人的小团队,仍然都在加班赶工。我自己有很长时间的Java技术背景,做过解决方案架构,又专门去学了项目管理,比较看重用户体验和项目组的氛围,接手这样一个项目之后渐渐会有些感觉不吐不快。政治上的槽点就不宜多说,单说技术和项目管理方面:

  • 这个项目有很多代码是从旧项目迁移过来的,一份代码被若干人改过,风格迥异。
  • 注释偏少(程序员的通病?),搭配一些奇葩的变量命名方式,让人抓狂。
  • 架构的文档少。
  • 接口更是没有文档,有时想调用某个接口,往往需要去直接看代码。
  • 代码日志输出不规范,后端代码调试困难。
  • 一些配置,例如IP地址, 被写死在代码里,直到碰到错误时才发现。

这些都是我们后面需要逐步去改进梳理的,有时在繁忙的工作之余,我会仔细思考一些问题:为什么我们越来越不快乐了,我们曾经是那么喜欢技术本身带来挑战和成就感?把时间耗费在修复前人因为种种原因而犯下的错误是不是对自己生命的浪费?作为对职业的尊重和责任,我常常感到非常无奈,这也许便是成长的烦恼。对于这个项目相关技术现象的几点看法,或许能解释一点点上述困惑,欢迎大家探讨:

  • 前端开发真的是日新月异,像 React 这样的技术虽然克服了传统前端开发的一些性能问题,但是缺少开发工具的支持,开发效率上未见得到实质的提升。现代前端开发我们实际上看到已经形成了一个非常深的技术栈:从开发语言,框架到打包工具等等。这使得前端开发的学习成本越来越高,这大概也是前端开发现在细分成一些专门的职位方向的原因。
  • 过分追求新技术,异构技术对于团队来说不是好事。从技术栈可以看到,我们使用到的语言,框架,基础软件是不少的,这大大地增加了团队,特别是一些新手的学习负担。在我看来,技术够用就好,最关键的还是能够满足需求,只有当一样技术无法满足需求时,再去考虑更新的方案。很多开源框架演进非常快,意味着一定的不稳定性,做技术跟踪也需要大量的时间。
  • 微服务治理是个很大,但是很现实的话题。虽然在为服务架构中,服务注册和服务发现已经有现成的软件去解决,但是仍然不够。服务之间的互相调用仍然是彼此交织在一起,分布在各种代码逻辑里,编织成错综复杂的调用网络,维护起来十分困难。
  • 产业界仍然有很浮躁的气象,虽然重复造轮子的事情少了,但是凡事都想拿来主义,比如原来可以用单一技术将一件事情做到极致,现在却多使用各种开源代码攒成一件成品,就好比苹果电脑和 DIY PC 一样,后者自然是很难做出精品的。

 

1,071 次阅读