• 为何报死锁? at 2020年01月19日

    <p>for ... range... 可以遍历 channel 直到 channel 被关闭并且 channel 里的数据被全部消费完。</p><p>上面代码 report1 是可以很快执行完毕返回的,但是 report2 没有 receiver,ch2 不能被消费,所以一直阻塞,因此 ch1 和 ch2 就不能被关闭。</p><p>下面两个 range 循环,因为 channel 没有被关闭,所以第一个循环不会结束,一直阻塞在那,于是第二个 range 不能被执行,所以上面的 report2 一直无法退出。</p><p>所以造成了死锁。</p><p>而注释的代码中,两个 range 循环分别在两个 goroutine 中,一个阻塞不会影响另一个,所以两个 report 都可以迅速返回,然后 channel 被 close,range 循环退出。</p>

  • go env -w GO111MODULE 设置问题 at 2019年09月30日

    <p>windows 平台:</p><p>set GO111MODULE=auto</p><p><br></p><p>linux/mac 平台:</p><p>export GO111MODULE=auto</p>

  • 我设计 api 接口返回的数据格式包括固定的三部分:

    {
        &quot;code&quot;:0,
        &quot;msg&quot;:&quot;ok&quot;,
        &quot;data&quot;:{
            &quot;name&quot;:&quot;tom&quot;
        }
    }
    

    code 是错误码,msg 是错误信息,data 是真正的数据部分。其中 code 表示业务逻辑中的错误码。而 http 状态码用来表示网络请求的状态。比如说发送一个 delete 请求删除一篇文章,而这篇文章不存在,那返回值可能是:

    {
        &quot;code&quot;:1001,
        &quot;msg&quot;:&quot;article not found&quot;,
        &quot;data&quot;:null
    }
    

    但是 http 的状态码可以是 200。这样就可以把业务逻辑的错误和网络请求的错误区分开来。

    这里还有一个点就是返回的 http 的状态码,虽然说在这种情况下,任何请求的 http 状态码都可以是 200,但是却不利于监控告警,因为 200 一般不会引起重视。如果有个请求的参数是个 int 型,但是前端传了个 string,那么 http 状态码返回 400 可能会比返回 200 更好一点。

  • http.FileServer的坑 at 2019年04月04日

    我看了下,windows 上有 2GB 限制,主要是因为调用了 windows 的系统调用TransmitFile。这个系统调用能够在内核层面将数据写入 socket 而不用经过用户层,提高了效率。TransmitFile的函数原型:

    BOOL TransmitFile(
      SOCKET                  hSocket,
      HANDLE                  hFile,
      DWORD                   nNumberOfBytesToWrite,
      DWORD                   nNumberOfBytesPerSend,
      LPOVERLAPPED            lpOverlapped,
      LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers,
      DWORD                   dwReserved
    );
    

    nNumberOfBytesToWrite是个 DWORD,用 go 的话来说就是个 uint32,那为什么文件大小限制不是 4G 呢? 因为此系统调用可能还会调用其他函数,在有些函数中,负数是有特殊含义的,因此最大值应该为 int32,也就是 2G。(此条待考证,不保证正确性及准确性)

    下面说下怎么解决:(只是个思路,没有具体尝试)

    查看 sendfile 调用链,可以看到只有 net.TCPConn 在 ReadFrom 函数中在调用,那么可以把 ReadFrom 函数重写,不要调用那个系统调用就好了,虽然效率肯定会差,但瓶颈不在这的话就无所谓了。具体大致实现如下:

    package main
    
    import (
        &quot;net&quot;
        &quot;net/http&quot;
    )
    
    func main() {
        var addr *net.TCPAddr
        l, err := net.ListenTCP(&quot;tcp&quot;, addr)
        checkerr(err)
        http.Serve(&amp;listener{l}, nil)
    }
    
    type listener struct {
        *net.TCPListener
    }
    
    func (l *listener) Accept() (net.Conn, error) {
        conn, err := l.TCPListener.Accept()
        checkerr(err)
        return &amp;tcpconn{conn}, nil
    }
    
    type tcpconn struct {
        net.Conn
    }
    
    func (c *tcpconn) ReadFrom() {
        // .....
    }
    
    func checkerr(e error) {
        // ...
    }
    
    
  • 大致看了下源码,说的不一定对,轻喷。

    1. 首先说为啥 http.ResponseWriter 是 interface。因为不光是 http/1.1,还有 http/2,这两者业务逻辑是相同的,但是底层的处理是不同的,所以要抽象出一个 interface 来屏蔽底层实现。在 net/http/server.go:1847 行,ServeHTTP 方法传入的是个*http.response,但在 net/http/h2_bundle.go:5877 行中 ,是个*http.http2responseWriter,因此要是 interface。

    2. 至于为什么 http.Request 为什么不是 interface,一方面是因为没必要,既然使用结构体已经能解决问题,就没必要搞个 interface 来徒增类型转换的成本了;另一方面是有些时候会需要改动 request 里的字段内容,比如说 request.URL.Path,这个时候就需要接口里面多定义很多不一定能用到的方法,这样,接口里要考虑的函数就太多了,所以是个 struct。

  • 每日新闻 能邮箱订阅吗? at 2018年12月22日

    我这边自己写了个爬虫,如果有需要的话可以访问

    https://gocn-news.chenqinghe.com/subscribe?mail=xxxx(你的邮箱)

    进行订阅。(10 分钟爬一次,压力应该不算大:))

  • 关于包名的疑惑 at 2018年12月19日

    可以考虑使用 Go1.11 新出的module,在初始化的时候指定 module 名,然后包路径就是module/pkg1/subpkg这样的,不管移到哪里都无所谓了。

  • 以太坊怎么交叉编译? at 2018年12月19日

    以太坊的源码不了解,不知道有没有用到特定系统的系统调用。不过通用的来说,只要设置 GOOS 和 GOARCH 两个环境变量就可以了。比如说在 linux 上编译 windows 程序:

    GOOS=windows GOARCH=amd64 go build 
    
  • 那就在 session 中另外添加一个过期时间字段,然后手动判断 session 是否过期,如果过期,再手动删除,这样子应该可以吧。

  • 且不说参数是不是错误了,就新手来说, defer 的使用就是有问题的。这里有个坑,只不过运气好没有踩到罢了。

    defer 是函数调用延迟执行,最后执行 c.RetData 的时候,参数还是开始定义的那个,不过这里 map 是引用类型,并且没有对 resp 重新赋值,而是做修改,因此这么写是可以的。但是如果改成下面这样,或者 resp 是值类型,那么就会出错了:

    func foo(){
        var a = make(map[string]interface{})
        defer fmt.Println(a) // output: map[]
    
        m:= make(map[string]interface{})
        m[&quot;1&quot;] =1
        m[&quot;2&quot;] =2
    
        a= m
    }
    

    defer 一般用于异常处理、释放资源、清理数据、记录日志等。如果用在其他地方,除非心里有数,否则还是不要乱用了。