• erlang 那种 umount 旧的 保持的老会话在旧实现里,同时 mount 新的处理新的会话才能算热部署吧?

    php 生产环境依赖 php 的那种"热部署"简直在拷问自己的 RP,不仅修改多个物理文件之间不能保障原子性更新,同时也无法保障 opcode cache 会同步更新 (看重性能的生产环境下启用各种 opcode cache 都是鼓励 never 刷新,或者根据 ttl/访问计数刷新), 依赖语言特性的那种"热部署"每次修改前都得去烧柱香。

  • Rails 在高并发下的性价比 at 2015年11月10日

    改了语言不考虑改架构是耍流氓啊,也无非是把语言层先跑死换成后端数据库一类的服务先跑死。

  • 看见就不舒服的缩写 at 2015年10月27日

    缩写特定环境下能一眼看懂就好了,以前 mozilla 负责 firefoxOS 相关的宫力博士现在自己搞的 firefoxOS 分支可实打实的叫 H5OS 啊,要不去吊打他一下看看?

  • 关于加密和 URL 的冲突 at 2015年09月11日

    用 SSL 就不说了。

    建议隐藏的 form 自动 post 一下,或者通过加自定义的 http token 字段。业务上要是不能保障不会产生由用户引用的来自外站的资源,如图片视频甚至外链,在 URL 里直接带 token 或者 session id 都会有安全问题。比如用户 img 自己网站一张图,就可以去日志里数 referer 里的 token 了; 再比如用户 A 发了个自己网站的外链,B 点了,A 又可以去日志里看 referer 里的 token 了

  • 茴香豆有幾種寫法之 Loop at 2015年02月17日
    `seq 1 15`.split.map(&:to_i).each {|number| puts "The number inside the loop is #{number}" }
    

    😄 作弊的方法算不

  • #8 楼 @Los Martini 说明不了 Go 的性能,这东西为了酷炫黑魔法用太多,性能超烂,甚至都被 PyPy 下的 tornado 甩老远,Martini 的作者都拆了一个叫 negroni 的 middleware 包,然后自己跑去玩 mux 了。

  • #16 楼 @xds2000

    第一,明确观点请用事实和代码说话,用感觉说话会带来思维盲点的。

    我不再重复我在15#说的两个等同了,事实上拿加密 cookie 作为默认 session 选项也不是 rails 一家异想天开弄出来的,比如 php 的 codeigniter,java/scala的play, python 的 flask, go 的 gorilla 默认的 session 方案都是加密 cookie.

    淘宝分享用加密 cookie 作为 session 方案(请搜"淘宝 session 框架")已经实施了四五年了,请问这几年出现的几次安全问题哪次和这方面有关?

    第二,谁说服务端 session 就不需要验证 sign 了?

    比如,你用手机和电脑同时登录同一个账户,分别是两个 session, 其中一个改了密码就必须让另一个失效对吧?

    你的方案是另外通过 sql db 或者 redis 的 set 维护 id 和 session 的对应表,实现连锁销毁。

    但是正常的通用 session 方案里也是通过 current_user 的 password 和 token 做一次 hash, 和 session 里的 sign 验证一下来判断是否有效。

    一方面后者不用特意额外搞个 sql db 或者 redis set 维护变更,维护成本小,二来也不需要重度依赖对应表,scale 成本也低。

  • #12 楼 @xds2000

    第一点不是很一致么 (1) 无论是 cookie store 还是传统的 cookie(session id)<->server session, 就算再通过 secret key 加密,劫持到 cookie 时已经算完了,这方面除了 ssl 是没办法保障的。 (2) 不考虑劫持现象,传统的 cookie(session id)<->server session 无法伪造,但是 cookie store 的 session 在服务端 secret key 不泄漏的情况下,同样无法伪造。 所以安全系数上是完全一致的。

    第二点针对登出失效,这是设计问题,不是安全问题 cookie store 的 session 检测用户时同样需要存在一个 sign 做验证 (比如根据用户的密码和一个 token 字段),用户登出完全可以重设生成这个 sign 所需要的 token, 也就是说当用户登出或者修改密码时,原先保存在 session 里的 sign 已经失效了,当然也就未登录了。

  • #9 楼 @xds2000 服务端 session 也依赖 cookie, 劫持保存在客户端的 session id 照样能产生中间人攻击,没区别,cookiestore 保存好 secret key 安全性不会比传统 session 差。mysql 内存表坑很多,比如不支持大字段,就算是 varchar 照样按定长算,没特殊要求尽量别用这东西。

  • #3 楼 @luikore 他要的那种开挂式 count 必须得是 MyISAM 的表,且查询不能带条件。

  • ruby 的性能问题。 at 2014年05月19日

    就这三个来讲,Ruby(MRI) 和 (C)Python、PHP 性能是半斤八两,属于同一梯队的。

  • 踩坑,不掉坑里不长记性,掉坑多了痛怕了经验值刷多了就知道怎么对付了。

  • #8 楼 @betterthornbird 字符窜类型用 blob/tinyblob 这类有副作用,比如 string 想返回 char_length, varchar/char都是根据编码返回字数(就是ruby里的.size), 但是 blob/tinyblob 返回的是字节长度 (就是 ruby 里的.bytesize)

  • 有没有建议性安装 at 2014年05月16日

    crul -> curl

  • 想要固定字段大小写敏感的话只要把该字段的 collation 从 utf8_general_ci 改成 utf8_bin.

  • #14 楼 @i5ting

    没搞电商经验或者金融业务出身的 DBA,纯后端上手在线交易的 db 设计,不是死在 decimal 和浮点数的乱用,就是错用少用多用事务和各种锁,还得避免滥用索引这种职业病。

    后端要是就一个人,测试覆盖率再高,总有没考虑齐全的地方,和钱打交道的业务总得谨慎点吧。

    UI 和前端就算后端能兼,一会儿切图,一会儿刷 js 和 css 改善可用性,一会儿测各种浏览器和设备的兼容性,一会儿再切回去写后端,效率直接被拖爆。

    后端兼网管可以,但是一旦线上出问题,自身程序和各种中间件先扫哪头?还不包括要去调 syslog 看看是不是撞上 kernel 或者硬件出错这种小概率事件。

    要说全栈,没流行框架前的 php 程序员个个多面手,那么多电商程序光注意数据库的事务和锁的就死剩一个 magento,但是 magento team 的人少说也得 2 位数吧。

    当然,肯定有人能点亮上面大部分甚至全部的技能树,不过一难找,二难挖,三不一定比多招几个各方面熟手便宜。

  • 我估了下最少也得

    搞过电商或者金融业务的资深 DBA*1 能互相 review 代码的后端熟手*2 UI*1, 前端*1 熟悉虚拟机、haproxy、zookeeper、lvs 等的资深网管*1 搜索、推荐这些预算足也可以找专人做,不足先让后端用 solr、mahout 这些顶着. 移动 app 有预算再找专人,没预算先拿 webapp 凑和着。

  • #23 楼 @ane

    不用定义函数,我指的

    readable = (IO.select(sockets) || []).first
    

    或者

    readable = IO.select(sockets).to_a.first
    

    其实不怕污染的话简单粗暴定义

    def nil.first; nil end
    

    就可以直接

    readable = IO.select(sockets).first
    
  • #21 楼 @ane 不是,代指 IO.select(sockets) 这种可能返回 Array 也可能返回 nil 的变量。

  • result = (array_or_nil || []).first

    result = array_or_nil.to_a.first 😄

  • 关于 Martini 的路由问题 at 2014年05月13日

    #9 楼 @july_12

    试了下,确实,不过要近 3000 条路由 martini 才能反超 mux, 比较贴近实际的 (500 条以内), mux 还是能压 martini 近一倍。而且 mux 路由匹配应该是能优化的,martini 为了仿动态性依靠注入机制导致运行时大量反射操作加速应该很难吧。

    package main
    
    import (
        "encoding/json"
        "fmt"
        "github.com/go-martini/martini"
        "io"
        "net/http"
    )
    
    const (
        N = 1000
    )
    
    func json2writer(w io.Writer, result interface{}) error {
        if resp, ok := w.(http.ResponseWriter); ok {
            resp.Header().Set("Content-Type", "application/json")
        }
        encoder := json.NewEncoder(w)
        if err := encoder.Encode(result); err != nil {
            return err
        }
        return nil
    }
    
    func main() {
        m := martini.New()
        r := martini.NewRouter()
        for i := 1; i < N; i++ {
            r.Get(fmt.Sprintf(`/%d/(?P<id>[1-9]\d*)`, i), func(i int) func(params martini.Params, w http.ResponseWriter) {
                return func(params martini.Params, w http.ResponseWriter) {
                    json2writer(w, map[string]interface{}{
                        "result": true,
                        "i":      i,
                        "params": params,
                    })
                }
            }(i))
        }
    
        m.Action(r.Handle)
        http.Handle("/", m)
        http.ListenAndServe(":5000", nil)
    
    }
    
    
    package main
    
    import (
        "encoding/json"
        "fmt"
        "github.com/gorilla/mux"
        "io"
        "net/http"
    )
    
    const (
        N = 1000
    )
    
    func json2writer(w io.Writer, result interface{}) error {
        if resp, ok := w.(http.ResponseWriter); ok {
            resp.Header().Set("Content-Type", "application/json")
        }
        encoder := json.NewEncoder(w)
        if err := encoder.Encode(result); err != nil {
            return err
        }
        return nil
    }
    
    func main() {
        m := mux.NewRouter()
        for i := 1; i < N; i++ {
            m.HandleFunc(fmt.Sprintf(`/%d/{id:[1-9]\d*}`, i), func(i int) func(w http.ResponseWriter, r *http.Request) {
                return func(w http.ResponseWriter, r *http.Request) {
                    json2writer(w, map[string]interface{}{
                        "result": true,
                        "i":      i,
                        "params": mux.Vars(r),
                    })
                }
            }(i))
        }
    
        http.Handle("/", m)
        http.ListenAndServe(":5000", nil)
    }
    
    
    ab -n 10000 -c 100 http://localhost:5000/(N/2)/10000
    

    N = 500

    martini => Requests per second:    5286.49 [#/sec] (mean)
    mux => Requests per second:    9058.32 [#/sec] (mean)
    

    N = 1000

    martini => Requests per second:    3949.45 [#/sec] (mean)
    mux => Requests per second:    6380.75 [#/sec] (mean)
    

    N = 1500

    martini => Requests per second:    3288.06 [#/sec] (mean)
    mux => Requests per second:    4732.49 [#/sec] (mean)
    

    N = 2000

    martini => Requests per second:    2673.03 [#/sec] (mean)
    mux => Requests per second:    3229.62 [#/sec] (mean)
    

    N = 2500

    martini => Requests per second:    2224.42 [#/sec] (mean)
    mux => Requests per second:    2472.72 [#/sec] (mean)
    

    N = 3000

    martini => Requests per second:    1807.92 [#/sec] (mean)
    mux => Requests per second:    1750.74 [#/sec] (mean)
    
  • 关于 Martini 的路由问题 at 2014年05月13日

    #6 楼 @july_12

    而且我之前栽坑的经验,实际开发用到 martini-contrib 的 render 和 martini 本身 router 的.Group 这些特性,性能还会直线下降。

  • 关于 Martini 的路由问题 at 2014年05月13日

    #6 楼 @july_12

    你那偏旧,martini 这几个月 router 之类的改动蛮大的,而且 Go 1.3 的性能拉升对除了 martini 这种黑魔法玩嗨的框架都提升极大。

    我这两个例子,为了避免干扰,没用 martini-contrib 的 render 输出 json, 也没用 Classic 一些 serve 静态文件和日志打印的 middleware

    运行环境是 1.3 开发版,做的是

    ab -n 100000 -c 1000 http://host/ 和 ab -n 100000 -c 1000 http://host/helloworld.json

    martini 当前版本是

    / => Requests per second:    9463.05 [#/sec] (mean)
    /helloworld.json => Requests per second:    7103.08 [#/sec] (mean)
    

    mux 当前版本是

    / => Requests per second: 17820.39 [#/sec] 
    /helloworld.json => helloworld.json Requests per second: 17788.92 [#/sec]
    

    测试代码是

    package main
    
    import (
        "encoding/json"
        "fmt"
        "github.com/go-martini/martini"
        "io"
        "net/http"
    )
    
    func json2writer(w io.Writer, result interface{}) error {
        if resp, ok := w.(http.ResponseWriter); ok {
            resp.Header().Set("Content-Type", "application/json")
        }
        encoder := json.NewEncoder(w)
        if err := encoder.Encode(result); err != nil {
            return err
        }
        return nil
    }
    
    func main() {
        m := martini.New()
        r := martini.NewRouter()
        r.Get("/", func(w http.ResponseWriter, r *http.Request) {
            fmt.Fprint(w, "helloworld")
        })
    
        r.Get("/helloworld.json", func(w http.ResponseWriter, r *http.Request) {
            json2writer(w, map[string]interface{}{
                "message": "helloworld",
                "okay":    true,
            })
        })
    
        m.Action(r.Handle)
        http.Handle("/", m)
        http.ListenAndServe(":5000", nil)
    
    }
    
    
    
    package main
    
    import (
        "encoding/json"
        "fmt"
        "github.com/gorilla/mux"
        "io"
        "net/http"
    )
    
    func json2writer(w io.Writer, result interface{}) error {
        if resp, ok := w.(http.ResponseWriter); ok {
            resp.Header().Set("Content-Type", "application/json")
        }
        encoder := json.NewEncoder(w)
        if err := encoder.Encode(result); err != nil {
            return err
        }
        return nil
    }
    
    func main() {
        m := mux.NewRouter()
        m.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
            fmt.Fprint(w, "helloworld")
        })
    
        m.HandleFunc("/helloworld.json", func(w http.ResponseWriter, r *http.Request) {
            json2writer(w, map[string]interface{}{
                "message": "helloworld",
                "okay":    true,
            })
        })
    
        http.Handle("/", m)
        http.ListenAndServe(":5000", nil)
    }
    
    
  • 老板...最起码也得投资人...

  • 关于 Martini 的路由问题 at 2014年05月13日

    #4 楼 @july_12 Go 归 Go, martini 黑魔法玩太多,性能不高,pypy 下的 tornado RPS 不低于它,mux 可以超它一倍。

  • 看看这张照片 at 2014年05月11日

    #2 楼 @moioo 当时看到 RMS 被偷的新闻惊呆了,回头发现又是一台又一次惊呆了

    #3 楼 @chunlea 我也团了一台 😃

    #4 楼 @wcc526 龙芯本

  • 国企要的发票,单离岸公司开不了。

    15 万的劳务个税绝对不止 20 个点; 不过公司 6 点也不止,6 点最多营业税,算上增值税、所得税,还有各种诸如城建税、教育税之类杂税,摊上个人所得税、强制社保,你 15 万甚至拿不到 60%,这还不包括你办公司要的一些诸如场地租赁、账目外包的开销。

    单你短期做一笔,既然允许签劳务,注册公司我觉得不划算。