书写技术成长之路

Redis pipeline优化

Redis pipeline 可以一次包含多条要执行的命令,减少和redis-server之间的网络来回,减少了建立网络连接的时间。

但是如果在pipeline中一次发送大量的命令,也会导致阻塞,直到所有命令执行完成。所以开发者通常会关心在pipeline中一次执行多少条命令才好呢。

其实这个没有标准答案,它视你服务器的配置而定,但是你可以通过redis-benchmark来测试最佳的命令条数。

测试普通情况下执行效率

redis-benchmark -n 100000 -t set,get -q

测试使用pipeline情况下的执行效率

redis-benchmark -n 100000 -t set,get -P 600 -q

n后面的数字是总请求数,P后面的是每次pipeline执行的命令个数,默认是1, q参数表示只显示每秒执行的命令数,如果不加q的话会显示详细的压测信息。

通过不断调整P后面的参数便可测试出你redis服务器pipeline最优数量。

参考

redis-benchmark 官网介绍

Golang 字符串转换

// 转换为snake
package main

import (
    "fmt"
    "regexp"
    "strings"
)

var matchAllCap = regexp.MustCompile("([a-z0-9])([A-Z])")

func ToSnakeCase(str string) string {
    snake := matchAllCap.ReplaceAllString(str, "${1}_${2}")
    fmt.Println(snake)
    return strings.ToLower(snake)
}

func main() {
    fmt.Println(ToSnakeCase("JapanCanadaAustraliaHelloWorld"))
}

// 转换为驼峰
package main

import (
    "fmt"
    "strings"
)

func ToCamelCase(str string) string {
    temp := strings.Split(str, "-")
    for i, r := range temp {
        if i > 0 {
            temp[i] = strings.Title(r)
        }
    }

    return strings.Join(temp, "")
}

func main() {
    str := "the-stealth-warrior"
    fmt.Println(ToCamelCase(str))
}
// 转驼峰 优化版
package main

import (
    "fmt"
    "regexp"
    "strings"
)

var re = regexp.MustCompile("(_|-)([a-zA-Z]+)")

func ToCamelCase(str string) string {
    camel := re.ReplaceAllString(str, " $2")
    camel = strings.Title(camel)
    camel = strings.Replace(camel, " ", "", -1)

    return camel
}

func main() {
    str := "the_start_boy"
    fmt.Println(ToCamelCase(str))
}

Golang与MySQL的日期格式化问题

废了一番功夫,终于搞定了Golang与MySQL的日期格式化问题,实在是费劲啊,特此记录一下。

话不多说,上代码

package main

import (
    "database/sql"
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "time"

    _ "github.com/go-sql-driver/mysql"
    "github.com/gorilla/mux"
)

// Article - Our struct for all articles
type Article struct {
    Id        int    `json:"id"`
    Title     string `json:"title"`
    Content   string `json:"content"`
    CreatedAt string `json:"created_at"`
}

type Articles []Article

func homePage(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Welcome to the HomePage!")
    fmt.Println("Endpoint Hit: homepage")
}

// get articles list
func list(w http.ResponseWriter, r *http.Request) {
    db, err := sql.Open("mysql", "root:@tcp(127.0.0.1:3306)/golang")
    if err != nil {
        panic(err.Error())
    }
    defer db.Close()

    result, err := db.Query("SELECT id, title, content FROM articles")
    if err != nil {
        panic(err.Error())
    }

    article := Article{}
    articles := []Article{}

    for result.Next() {
        var id int
        var title, content string
        err = result.Scan(&id, &title, &content)
        if err != nil {
            panic(err.Error())
        }

        article.Id = id
        article.Title = title
        article.Content = content

        articles = append(articles, article)
    }

    json.NewEncoder(w).Encode(articles)
}

// get one artcle
func show(w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)
    request_id := vars["id"]

    // 这里连接mysql的参数设置Ioc和parseTime很关键
    db, err := sql.Open("mysql", "root:@tcp(127.0.0.1:3306)/golang?charset=utf8&loc=Asia%2FShanghai&parseTime=true")
    if err != nil {
        panic(err.Error())
    }
    defer db.Close()

    var id int
    var title, content string
    var createdAt string
    article := Article{}
    err = db.QueryRow("select id, title, content, created_at from articles where id = ?", request_id).Scan(&id, &title, &content, &createdAt)
    if err != nil {
        if err == sql.ErrNoRows {
            errData := map[string]string{"errcode": "ER404", "errmsg": "not found"}
            json.NewEncoder(w).Encode(errData)
            return
        }
    }

    // 这里处理日期格式化问题
    t1, _ := time.Parse(time.RFC3339, createdAt)
    createdAt = t1.Format("2006-01-02 15:04:05")

    article.Id = id
    article.Title = title
    article.Content = content
    article.CreatedAt = createdAt
    json.NewEncoder(w).Encode(article)
}

func handleRequests() {
    myRouter := mux.NewRouter().StrictSlash(true)
    myRouter.HandleFunc("/", homePage)
    myRouter.HandleFunc("/articles", list)
    myRouter.HandleFunc("/articles/{id}", show)
    log.Fatal(http.ListenAndServe(":10000", myRouter))
}

func main() {
    handleRequests()
}

这里有几点需要注意的事项

  1. 连接MySQL的时候需要指定时区和解析时间的标识就是那个parseTime=true的设置
  2. 从MySQL取出来以后要按照时间layout去格式化,然后再最后你想要的格式再格式化一遍

以下两图揭示了关键的两点

屏幕快照 2018-12-22 23.23.07.png

屏幕快照 2018-12-22 23.25.57.png

屏幕快照 2018-12-22 23.25.51.png

参考

https://github.com/go-sql-driver/mysql#timetime-support

https://github.com/jinzhu/gorm/issues/1047

https://github.com/jinzhu/gorm/issues/18

https://programming.guide/go/format-parse-string-time-date-example.html

https://yourbasic.org/golang/format-parse-string-time-date-example/

Golang VSCode 开发环境配置插件安装失败的解决方案

最近开始学习golang开发,一直以来从事PHP开发,接触了golang后便被她深深的吸引了。

  1. gofmt带来了代码一致性,解决了困扰开发多年的代码风格不统一的问题,这是给人的最直观感受。

  2. 语法的简洁性,以及自定义类型的灵活性。语言本身只定义了基本数据类型结构,例如int, string, array, slice, map,其他可由struct来自行实现。

  3. goroutine带来的飞跃性好处,是其他语言所没有或没那么容易实现的。它可由以前串行执行的方法改为并行执行,想想都叫人愉快 ^_^, 充分利用了机器的多核性能。

  4. 编译型语言带来的好处不言而喻,以及跨平台特性。真正实现了Build Once, Run anywhere

  5. 方便的文档以及可分享的playground。

在使用vscode开发go程序的时候,需要安装相关的go插件来提升开发效率。不过总是安装失败,提示包没找到等错误。即使我用了shadowsocks也是一样,通过Google一番,有两种解决方案。

这是安装插件时候的报错信息

    go get -u -t -v github.com/golang/lint/golint
github.com/golang/lint (download)
Fetching https://golang.org/x/tools/go/gcexportdata?go-get=1
https fetch failed: Get https://golang.org/x/tools/go/gcexportdata?go-get=1: dial tcp 172.217.10.241:443: connectex: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.
package golang.org/x/tools/go/gcexportdata: unrecognized import path "golang.org/x/tools/go/gcexportdata" (https fetch: Get https://golang.org/x/tools/go/gcexportdata?go-get=1: dial tcp 172.217.10.241:443: connectex: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.)

第一种就是使用其他软件将shadowsocks代理转换为http代理。

第二种就是在github上手动下载相关的包,然后编译安装。

我用了第一种,因为比较懒,不想以后安装其他插件的时候也要一个个手动。

下面说下解决步骤:

  1. brew install polipo 安装polipo, 一种将其他代理转换为http代理的程序

  2. 编辑polipo的配置文件 /etc/polipo/config

    socksParentProxy = "127.0.0.1:1080"
    socksProxyType = socks5
    proxyAddress = "::0"
    proxyPort  = 8123
    
  3. 启动polipo服务 polipo -c /etc/polipo/config

  4. 设置终端临时代理, 可能需要在vscode的终端设置.

     export http_proxy="http://127.0.0.1:8123"
     export https_proxy="http://127.0.0.1:8123"
    
  5. 测试HTTP代理 curl --proxy http://127.0.0.1:8123 https://www.google.com, 如果成功输出内容就可以开始安装VSCode插件了

以上只是我的解决方案,下面是一些下该方案和其他方案的链接

第二种是需要手动下载包的, 可做参考 vscode-problem-2018-10-03.png

第三种,终端直接设置第三方代理工具,goproxy.io提供了很好的解决方案,具体请参考https://goproxy.io/zh/

参考

  1. https://github.com/golang/lint/issues/288#issuecomment-377672640

  2. http://blog.leanote.com/post/xuhuan@live.cn/Socks代理转换成HTTP代理

  3. https://maiyang.me/post/2018-09-14-tips-vscode/

  4. https://blog.csdn.net/hejunqing14/article/details/52670341

  5. https://cloud.tencent.com/developer/article/1026320

GO渲染HTML

server.go

package main

import (
    "net/http"
    "log"
    "html/template"
)

func sayHello(w http.ResponseWriter, r *http.Request) {
    t, _ := template.ParseFiles("templates/hello.html")
    t.Execute(w, "this is template data")
}

func main() {
    http.HandleFunc("/hello", sayHello)
    err := http.ListenAndServe(":9090", nil)
    if err != nil {
        log.Fatal("ListenAndServe: ", err)
    }
}

hello.html

<h1>Hello, Gopher!</h1>

<h3>{{.}}</h3>

参考

https://www.jianshu.com/p/05671bab2357

RHEL7安装protobuf

  1. sudo yum install autoconf automake libtool unzip gcc-c++ git -y
  2. git clone https://github.com/google/protobuf.git
  3. cd protobuf
  4. ./autogen.sh
  5. ./configure
  6. make
  7. make install
  8. 测试是否安装成功 protoc --version

在RHEL7上安装Docker CE

服务器版本: Red Hat Enterprise Linux Server release 7.5

在安装Docker CE的时候提示 WARNING: rhel is now only supported by Docker EE Check https://store.docker.com for information on Docker EE

解决办法如下:

  1. Install yum-utils. 执行 yum install -y yum-utils
  2. Install epel-release. 执行 wget http://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpmrpm -ivh epel-release-latest-7.noarch.rpm
  3. Add Docker CE to yum repos. yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
  4. 安装container-selinux,因为docker-ce依赖于container-selinux, yum install -y http://mirror.centos.org/centos/7/extras/x86_64/Packages/container-selinux-2.33-1.git86f33cd.el7.noarch.rpm, 如果不行尝试执行 yum -y --enablerepo=rhui-REGION-rhel-server-extras install container-selinux
  5. 安装Docker CE yum install -y docker-ce
  6. 查看是否安装成功 docker --version
  7. 重启Docker服务并加入开机启动 systemctl restart dockersystemctl enable docker
  8. 安装nginx容器测试下 docker run --name webserver -d -p 9090:80 nginx

安装完成后,需要把当前用户添加到用户组才能不以root身份运行 sudo usermod -aG docker ec2-user

参考地址

https://getstart.blog/2018/03/24/docker-ce-installation-on-red-hat-7/

https://www.itzgeek.com/how-tos/linux/centos-how-tos/installing-docker-on-centos-7-rhel-7-fedora-21.html

https://nickjanetakis.com/blog/docker-tip-39-installing-docker-ce-on-redhat-rhel-7x

https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-centos-7

container-selinux问题的解决方案, 参考nderzhak的回答

脚本安装Docker-CE

Docker系列教程