1 1

2015,2016

转眼又是一年,在厦门也已经一年有余,工作平稳顺利,生活按部就班。没有波澜起伏,平稳渡过。不知该庆幸还是遗憾。

工作

还在云朵科技,继续做穿戴式设备的云端数据处理。各种新的需求,然后各种实现和抓虫,以及调优,都是常规的开发事项。随着国家经济形势的不容乐观,互联网行业的虚火也开始消退,小米也卖不出1亿只手机了。

穿戴式设备是很新的概念。技术层面还有很多不够完善和自然的地方,导致消费者的接受程度并不太高。目前卖的较多的穿戴式设备只有手环和手表,记录运动日志。别的方面还没有看到比较成功的例子。

云朵这边也是不温不火。

出租屋

年后搬来了和前同事合租。唯一的缺陷是 广场舞。纵然关上门窗给还是比较清晰。有时扰乱心智,无法安心撸代码,心情不悦。而且我的次卧湿气较大,加上我不会经常收拾,偶尔有不舒服的味道出来。遂决定,到期后另觅别家。其实,也没有搬离这个小区。只是找了一个靠里面的,向阳的单人间住下。

室友同事去深圳拼搏,不过听说路途坎坷,我只能祝福一下了。

代码

2015年很多新奇的东西出现。ReactVue.js 已经体验,感觉良好,Flux 一知半解。比较向往体验 React NativeAtom Electron , 计划中。Golang 也已经发布到 1.5,实现自举,里程碑般的进化可喜可贺。同样的还有 PHP 7Javascript ES6。很多新的写法需要学习,必须跟上时代。

因为工作需要,也用 Go 写了一些机器学习的代码,实践一些 map-reduce 和 分布式程序的开发,也算是入门了。

还有就是这个小站,已经是自己写的静态生成器,有兴趣可以去 pugo.io 体验一下。

她说在本命年遇到了我,是福气。其实也是我的福气。我愿意陪在身边,也希望她一直在我的身边。

12 9

我煞笔的被 bufio.Reader 小坑

最近在用 Go 做一个小型的 gateway 服务。PHP 请求 Go 的 tcp server,然后 Go 根据命令参数开启多个 goroutine 去调度 php-fpm 执行不同的脚本并组合结果返回。 想来只是利用 goroutine 的便利并发执行逻辑,如此简单直接。

不过在测试的时候 PHP 发送 socket 的 json 数据发生了明确的截断,后来发现是我煞笔的被 bufio.Reader 坑了,真是无言以对。

问题重现

因为是长连接,PHP 每次发一段 json ,都会加上换行符\n分割。我就很理所当然的用起了*bufio.Reader.ReadLine()。就像下面的代码:

func handleConn(conn net.Conn) {
    reader := bufio.NewReader(conn)
    for {
        // 读取一行数据,交给后台处理
        line,_,err := reader.ReadLine()
        if len(line) > 0{
            fmt.Printf("ReadData|%d \n",len(line))
            executeBytes(line)
        }
        if err != nil{
            break
        }
    }
    conn.Close()
}

可是当 PHPer 测试发送大json数据的时候,发现了明确的截断:

ReadData|4096|{"ename.com":........,"yunduo.com":{"check":[1
// 又一次
ReadData|4096|{"ename.com":........,"yunduo.com":{"getWhois"

想让 json 还没读完就被截断。我记得默认的 bufio.Reader 的大小是 4096,所以 bufio.Reader 的行为是读满了buffer就return出来啊。。我勒个去。我总不能写个很大的size吧。

    reader := bufio.NewReaderSize(conn,409600)

PHP 发送的测试数据最大可能得到100k左右,平均只有<10k。我服务端每次都开一个100k+的bufio.Reader太浪费啦。最后变成,开小了截断,开大了浪费,我煞笔了。

11 25

阅读 valyala/fasthttp —— 比官方库更快的 HTTP 包

valyala/fasthttp 是号称比官方net/http库更快的 http server 库。就去顺便研究了,发现一些细节的不同。

处理 net.Conn 的 goroutine

处理net.Conn的goroutine的使用方式,和标准库有很大差别。在标准库,net.Listener.Accept() 到一个连接,就会开启一个goroutine:

// Serve accepts incoming connections on the Listener l, creating a
// new service goroutine for each.  The service goroutines read requests and
// then call srv.Handler to reply to them.
func (srv *Server) Serve(l net.Listener) error {
    defer l.Close()
    var tempDelay time.Duration // how long to sleep on accept failure
    for {
        rw, e := l.Accept()
        if e != nil {
            ......
        }
        ......
        c, err := srv.newConn(rw)
        if err != nil {
            continue
        }
        c.setState(c.rwc, StateNew) // before Serve can return
        go c.serve() // 在这里创建一个goroutine处理net.Conn的实际逻辑
    }
}

但是在valyala/fasthttp中使用的是worker的形式,开启固定数量的goroutine处理net.Conn

10 14

PuGo 一次内存泄露的调优

我刚刚写好新的博客程序 Pugo,欢迎试用和体验。这两天我把个站 fuxiaohei.me 迁移到新的博客程序。但是,经过一天的运行,发现内存从启动的 14MB 上升到了 228 MB。显然程序发生内存泄露,所以也开始以下调优过程。

PPROF

pprof 是 Golang 自带的调试工具,有很多可用的工具。pprof 的调试方式有代码的方式和 HTTP 方式。其中 HTTP 调试比较方便,加入很简单的代码:

import _ "net/http/pprof" // pprof 的 http 路由注册在自带路由上

go func() {
    http.ListenAndServe("0.0.0.0:6060", nil) // 启动默认的 http 服务,可以使用自带的路由
}()

更新的 以前的