golang

golang

有没有好的 sql 数据库 driver 推荐, 最好同时支持 mysql 跟 pg?

有问必答Xargin 回复了问题 • 3 人关注 • 2 个回复 • 143 次浏览 • 1 小时前 • 来自相关话题

(开源)基于vue, react, node.js, go开发的微商城(含微信小程序)

开源程序zhaoyun4122 回复了问题 • 7 人关注 • 5 个回复 • 1758 次浏览 • 6 天前 • 来自相关话题

分享一个带 TTL 以及失效回调的 LRU 库. https://github.com/Akagi201/kvcache

开源程序Akagi201 发表了文章 • 0 个评论 • 116 次浏览 • 2017-12-06 15:04 • 来自相关话题

https://github.com/Akagi201/kvcache

完美达到我的业务需要的效果. 大家有同样的需求可以测试测试.

...

https://github.com/Akagi201/kvcache


完美达到我的业务需要的效果. 大家有同样的需求可以测试测试.


有问题一定向我反馈. akagi201@gmail.com

tsdump-用于导出数据库表结构的工具(支持导出为text、markdown、csv、json)

开源程序voidint 发表了文章 • 0 个评论 • 126 次浏览 • 2017-12-03 20:32 • 来自相关话题

项目地址: https://github.com/voidint/tsdump

特性

  • 支持将数据库(当前仅支持查看全部

项目地址: https://github.com/voidint/tsdump


特性



  • 支持将数据库(当前仅支持MySQL)及其表结构的元数据以textmarkdownjsoncsv形式输出。


安装


$ go get -u github.com/voidint/tsdump

基本使用




  • 全局选项


    GLOBAL OPTIONS:
    -H value, --host value Connect to host. (default: "127.0.0.1")
    -P value, --port value Port number to use for connection. (default: 3306)
    -u value, --user value User for login if not current user. (default: "voidint")
    -p value, --password value Password to use when connecting to server.
    -d value, --db value Database name.
    -V value, --viewer value Output viewer. Optional values: txt|csv|json|md (default: "txt")
    -o value, --output value Write to a file, instead of STDOUT.
    -D, --debug Enable debug mode.
    --help, -h show help
    --version, -v print the version



  • 使用root用户创建一个名为mydb的数据库实例,以及一张student的表。


    CREATE DATABASE IF NOT EXISTS `mydb` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;

    USE `mydb`;

    CREATE TABLE `student` (
    `sno` char(8) NOT NULL COMMENT '学号',
    `sname` varchar(255) NOT NULL COMMENT '姓名',
    `gender` char(2) DEFAULT NULL COMMENT '性别',
    `native` char(20) DEFAULT NULL COMMENT '籍贯',
    `birthday` datetime DEFAULT NULL COMMENT '出生日期',
    `dno` char(6) DEFAULT NULL COMMENT '所在院系',
    `spno` char(8) DEFAULT NULL COMMENT '专业代码',
    `classno` char(4) DEFAULT NULL COMMENT '班级号',
    `entime` date DEFAULT NULL COMMENT '入校时间',
    `home` varchar(40) DEFAULT NULL COMMENT '家庭住址',
    `tell` varchar(40) DEFAULT NULL COMMENT '联系电话',
    PRIMARY KEY (`sno`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='学生信息表';



  • 将数据库及其表结构数据以表格形式输出到console


    $ tsdump -H 127.0.0.1 -P 3307 -u root -p "mypassword" --db mydb
    |----------|---------------|--------------------|
    | DATABASE | CHARACTER SET | COLLATION |
    |----------|---------------|--------------------|
    | mydb | utf8mb4 | utf8mb4_general_ci |
    |----------|---------------|--------------------|

    TABLE: student 学生信息表
    |----------|----------|--------------|---------------|--------------------|----------|
    | COLUMN | NULLABLE | DATA TYPE | CHARACTER SET | COLLATION | COMMENT |
    |----------|----------|--------------|---------------|--------------------|----------|
    | sno | NO | char(8) | utf8mb4 | utf8mb4_general_ci | 学号 |
    | sname | NO | varchar(255) | utf8mb4 | utf8mb4_general_ci | 姓名 |
    | gender | YES | char(2) | utf8mb4 | utf8mb4_general_ci | 性别 |
    | native | YES | char(20) | utf8mb4 | utf8mb4_general_ci | 籍贯 |
    | birthday | YES | datetime | | | 出生日期 |
    | dno | YES | char(6) | utf8mb4 | utf8mb4_general_ci | 所在院系 |
    | spno | YES | char(8) | utf8mb4 | utf8mb4_general_ci | 专业代码 |
    | classno | YES | char(4) | utf8mb4 | utf8mb4_general_ci | 班级号 |
    | entime | YES | date | | | 入校时间 |
    | home | YES | varchar(40) | utf8mb4 | utf8mb4_general_ci | 家庭住址 |
    | tell | YES | varchar(40) | utf8mb4 | utf8mb4_general_ci | 联系电话 |
    |----------|----------|--------------|---------------|--------------------|----------|



  • 将数据库及其表结构数据输出到markdown文件


    $ tsdump -H 127.0.0.1 -P 3307 -u root -p "mypassword" --db mydb -V md > ./mydb.md

    output:


    student


    学生信息表







































































































    COLUMN NULLABLE DATA TYPE CHARACTER SET COLLATION COMMENT
    sno NO char(8) utf8mb4 utf8mb4_general_ci 学号
    sname NO varchar(255) utf8mb4 utf8mb4_general_ci 姓名
    gender YES char(2) utf8mb4 utf8mb4_general_ci 性别
    native YES char(20) utf8mb4 utf8mb4_general_ci 籍贯
    birthday YES datetime 出生日期
    dno YES char(6) utf8mb4 utf8mb4_general_ci 所在院系
    spno YES char(8) utf8mb4 utf8mb4_general_ci 专业代码
    classno YES char(4) utf8mb4 utf8mb4_general_ci 班级号
    entime YES date 入校时间
    home YES varchar(40) utf8mb4 utf8mb4_general_ci 家庭住址
    tell YES varchar(40) utf8mb4 utf8mb4_general_ci 联系电话



  • 将数据库及其表结构数据输出到csv文件


    $ tsdump -H 127.0.0.1 -P 3307 -u root -p "mypassword" --db mydb -V csv -o ./mydb.csv


  • 将数据库及其表结构数据输出到JSON文件
    $ tsdump -H 127.0.0.1 -P 3307 -u root -p "mypassword" --db mydb -V json -o ./mydb.json

搭建轻量级的 Docker 容器云管理平台

开源程序bobliu0909 发表了文章 • 0 个评论 • 232 次浏览 • 2017-12-02 11:09 • 来自相关话题

什么是 Humpback?

Humpback 可以帮助企业快速搭建轻量级的 Docker 容器云管理平台,若将你的 Docker 主机接入到 Humpback 平台中,就能够为你带来更快捷稳定的容器操作体验。

查看全部

什么是 Humpback?


Humpback 可以帮助企业快速搭建轻量级的 Docker 容器云管理平台,若将你的 Docker 主机接入到 Humpback 平台中,就能够为你带来更快捷稳定的容器操作体验。


humpback架构


Humpback 功能特点


-Web操作,简单易用

-权限分组隔离

-容器升级与克隆

-容器监控

-容器日志

-集群容器调度

-集群弹性伸缩

-私有仓库


Humpback 模式介绍


-Single Mode 单一模式,对单组主机实现容器管理,提供容器创建,容器操作,容器重命名,容器升级与克隆,容器监控,容器日志输出等功能。

-Cluster Mode 容器集群模式,实现按实例数批量创建容器,容器调度,批量操作容器,升级和迁移等功能。


平台采用分组方式(Group)来管理多主机,多组之间权限操作隔离,同时也可以将一台主机加入到多个分组中交叉管理。


系统登录


Single Mode

SingleMode


Cluster Mode

ClusterMode


Container Monitor

ContainerMonitor


Container Logs

Container Logs


Container Detail

ContainerDetail


ContainerDetail


项目地址:https://humpback.github.io/humpback

授权协议:Apache

开发语言:TypeScript、Golang

操作系统:垮平台

golang兼职讲师 imooc 20K-45K

回复

招聘应聘gigiyuan 发起了问题 • 1 人关注 • 0 个回复 • 249 次浏览 • 2017-12-01 14:06 • 来自相关话题

大家说说看都用啥写Go

技术讨论MarkYang 回复了问题 • 68 人关注 • 84 个回复 • 5696 次浏览 • 2017-11-30 16:33 • 来自相关话题

很期待Golang能有自己的客户端UI库

有问必答jitongxi 回复了问题 • 4 人关注 • 4 个回复 • 745 次浏览 • 2017-11-24 14:41 • 来自相关话题

想用golang写个分布式的监控,大神给点建议

有问必答my3157 回复了问题 • 19 人关注 • 14 个回复 • 4128 次浏览 • 2017-11-24 10:49 • 来自相关话题

GOLANG探测HTTP连接断开

技术讨论winlin 发表了文章 • 1 个评论 • 318 次浏览 • 2017-11-22 12:22 • 来自相关话题

考虑基于HTTP的RPC,或者HTTP服务器主动通知客户端的机制,就是HTTP Long-Polling,意思... 查看全部

考虑基于HTTP的RPC,或者HTTP服务器主动通知客户端的机制,就是HTTP Long-Polling,意思就是客户端发起一个长连接,服务器阻塞忍住不响应直到:



  1. 超时,比如5秒后,我们给客户端响应一个keepalive,意思是现在还没有啥事,请继续polling。

  2. 拿到结果,这个可能是任何时候,比如300毫秒、1100毫秒、2300毫秒拿到一个事件,响应给客户端,实现了有事件异步通知。


这样客户端和服务器之间RPC的效率就非常高,只有在有事件时才会通知。但是,实际上还有一种情况需要处理:



  1. 当客户端断开连接,比如客户端设置了3秒钟TCP请求超时,或者因为客户端Crash时OS回收了FD等等,这个时候服务器应该要终止polling事务,停止获取事件。因为如果这个时候获取了事件,那么如何处理这个事件?只能丢弃,如果客户端再次发起请求,就拿不到这个事件了。


问题就来了,如何在HTTP Handler中探测客户端断开?例如:


var incoming chan []byte
http.HandleFunc("/polling", func(w http.ResponseWriter, r *http.Request) {
select {
case b := <- incoming:
w.Write(b)
case <-time.After(5 * time.Second):
w.Write("keepalive")
// how to detect TCP disconnect event?
}
})

可能有以下方式:



  1. 读取r.Body,如果发现断开应该会有错误。

  2. 有朋友用reflect或hijack取到底层的TCPConn,然后Peek。

  3. w转换成http.CloseNotifier,在TCP连接关闭时拿到事件。


r.Body Read


这种方式是不靠谱的,假设没有Body内容,直接读取检测是否有error:


nn,err := io.Copy(ioutil.Discard, r.Body)

实际上返回的是nn=0err=nil,也就是没有Body,没有错误。因为这个读取的含义是指Request结束。


如果读取完Body后再读呢?收到的是io.EOF,在没有发送Response之前,Request已经结束了,所以就是io.EOF,并不能检测到底层TCP断开。


Peek TcpConn


使用reflect获取底层的TCPConn对象,是知道w http.ResponseWriter实际上是http.response


// A response represents the server side of an HTTP response.
type response struct {
conn *conn

它有个Field就是conn,再转成TCPConn就可以Peek。


这样做的风险就是,不同的GOLANG版本,可能会对底层实现进行变更,在升级时会有风险。


Reflect方式始终不是最好的。


另外,还有一种方式,就是用http hijack方式,这种方式虽然是http库提供的接口,但是很多地方注释都说hijack需要特殊处理,因此也不是最好的方式。参考When to use hijack


Close Notifier


在GO1.1提供了http.CloseNotifier接口,参考Close Notifier,但是也注意会有一些问题,参考net/http: CloseNotifier fails to fire when underlying connection is gone。用法如下:


var incoming chan []byte
http.HandleFunc("/polling", func(w http.ResponseWriter, r *http.Request) {
select {
case <- w.(http.CloseNotifier).CloseNotify():
fmt.Println("connection closed")
}
})

实际上,超时机制始终是需要的,加上之前的逻辑,考虑context.Context取消事件,http-long polling的完整实现应该是:


func polling(ctx context.Context, incoming chan []byte) {
http.HandleFunc("/polling", func(w http.ResponseWriter, r *http.Request) {
select {
case <- ctx.Done():
fmt.Println("system quit")
case b := <- incoming:
w.Write(b)
case <-time.After(5 * time.Second):
w.Write("keepalive")
case <- w.(http.CloseNotifier).CloseNotify():
fmt.Println("connection closed")
}
})
}

Gopher 深圳 meetup

线下活动mai_yang 发表了文章 • 1 个评论 • 257 次浏览 • 2017-11-16 09:13 • 来自相关话题

Gopher 深圳 meetup 开始报名啦,由小恩爱、PingCAP、随手记、史蒂夫软件的Go语言专家给大家带来 Go 语言实战经验和技巧分享,还有丰厚的奖品等你来拿,名额有限哦。

活动议程

13:00-13:30 签... 查看全部

Gopher 深圳 meetup 开始报名啦,由小恩爱、PingCAP、随手记、史蒂夫软件的Go语言专家给大家带来 Go 语言实战经验和技巧分享,还有丰厚的奖品等你来拿,名额有限哦。


活动议程


13:00-13:30 签到,微信墙交流


13:30-14:00 开场


14:00-14:55 《Go TCP Socket编程之teleport框架是怎样炼成的?》


14:55-15:50 《Go 开发游戏踩过的那些坑》


15:50-16:10 茶歇


16:10-17:05 《随手记 Go 实践:高并发积分系统改造》


17:05-18:00 《Go in TiDB》


18:00-18:10 抽奖交流,合影留念


时间地点


2017/12/16 13:30-18:20


深圳市南山区科发路1号富利臻大厦3F


分享话题




  • 《Go TCP Socket编程之teleport框架是怎样炼成的?》


    李亚川,Pholcus、Faygo、Teleport等Go语言开源项目作者。自2014年起全面转向Go语言的服务端开发领域。现就职于小恩爱,负责服务器底层架构与基础服务的研发工作。Github:https://github.com/henrylee2cn


    内容介绍:通过回顾开源项目teleport(通用、高效、灵活的TCP Socket框架)的开发历程,分享Go Socket的开发实战经验。




  • 《Go 开发游戏踩过的那些坑》


    庾俊:现任史蒂夫软件技术经理,原美国 Kabam 公司亚特兰蒂斯之龙族崛起游戏后端主程,该游戏三年收入超过一亿美金。


    内容介绍:带着大家一步一步的回顾我在创业三年以来,用 Go 开发游戏服务器过程中艰苦历程,并分享给你可能你从未踩过或者听过的一些坑,以及一些个人经验。




  • 《随手记 Go 实践:高并发积分系统改造》


    曾龙:随手记高级开发工程师


    内容介绍:随手记积分系统基于Go语言进行了改造,以支撑高并发、高性能的业务需求。本次分享主要介绍在改造过程中的实战经验、踩过的坑以及改造前后的对比提升,当然还有提升的经验。




  • 《Go in TiDB》


    姚维: PingCAP 资深数据库技术专家,华南区GM, 开源数据库中间件 360 Atlas 作者. mysql-sniffer作者.


    内容介绍:介绍 TiDB 如何用Go实现一个分布式的数据库SQL引擎, 以及在 TiDB 中利用的一些黑科技。




报名请前往:https://www.bagevent.com/event/979909

[基础问题] 数组赋值

有问必答koala 回复了问题 • 3 人关注 • 2 个回复 • 216 次浏览 • 2017-11-15 16:03 • 来自相关话题

go分布式日志跟踪怎么实现

有问必答Wilence 回复了问题 • 4 人关注 • 3 个回复 • 649 次浏览 • 2017-11-14 17:02 • 来自相关话题

go的大文件内容比较

有问必答littledriver 回复了问题 • 4 人关注 • 4 个回复 • 459 次浏览 • 2017-11-10 08:12 • 来自相关话题

记录一个拷贝文件到GlusterFS卡住的解决过程

文章分享houyy668 回复了问题 • 3 人关注 • 2 个回复 • 471 次浏览 • 2017-11-03 16:32 • 来自相关话题

条新动态, 点击查看
astaxie

astaxie 回答了问题 • 2016-10-10 18:35 • 26 个回复 不感兴趣

大家推荐哪种golang包管理方式?

赞同来自:

我们目前项目中使用的是godep,但是我最近尝试迁移到glide里面来,两个的功能都差不多,但是glide更强大一点,而且是Go1.5 vendor目录支持之后出来的,所以我还是比较推荐用这个。

这里列出来一些目前支持vendor的工具

* [manul... 显示全部 »
我们目前项目中使用的是godep,但是我最近尝试迁移到glide里面来,两个的功能都差不多,但是glide更强大一点,而且是Go1.5 vendor目录支持之后出来的,所以我还是比较推荐用这个。

这里列出来一些目前支持vendor的工具

* [manul](https://github.com/kovetskiy/manul) - Vendor packages using git submodules.
* [Godep](https://github.com/tools/godep)
* [Govendor](https://github.com/kardianos/govendor)
* [godm](https://github.com/hectorj/godm)
* [vexp](https://github.com/kr/vexp)
* [gv](https://github.com/forestgiant/gv)
* [gvt](https://github.com/FiloSottile/gvt) - Recursively retrieve and vendor packages.
* [govend](https://github.com/govend/govend)
* [Glide](https://github.com/Masterminds/glide) - Manage packages like composer, npm, bundler, or other languages.
* [Vendetta](https://github.com/dpw/vendetta)
* [trash](https://github.com/rancher/trash)
* [gsv](https://github.com/toxeus/gsv)
* [gom](https://github.com/mattn/gom)
astaxie

astaxie 回答了问题 • 2016-10-11 21:41 • 9 个回复 不感兴趣

golang 的channel是否适合做消息队列?

赞同来自:

我猜你看到的文章的担心是万一程序挂了怎么办,在缓冲channel里面的数据就可能丢失了,如果这个是可以忍受的话其实是非常适合做消息队列的
我猜你看到的文章的担心是万一程序挂了怎么办,在缓冲channel里面的数据就可能丢失了,如果这个是可以忍受的话其实是非常适合做消息队列的
name5566

name5566 回答了问题 • 2016-10-12 11:36 • 12 个回复 不感兴趣

golang有没有好的开源游戏框架

赞同来自:

> 使用 Leaf 已知的上线项目:
> * 2014 年,某手游(棋牌)项目上线
> * 2016 年,某 H5 手游项目上线
> * 2016 年,某卡牌手游项目上线
> 正在研发项目 N 个,已知情况 N >= 4

... 显示全部 »
> 使用 Leaf 已知的上线项目:
> * 2014 年,某手游(棋牌)项目上线
> * 2016 年,某 H5 手游项目上线
> * 2016 年,某卡牌手游项目上线
> 正在研发项目 N 个,已知情况 N >= 4

来自:https://github.com/name5566/leaf/wiki
leoliu

leoliu 回答了问题 • 2016-10-12 13:44 • 26 个回复 不感兴趣

求一些golang的教程,书籍也可以

赞同来自:

《The Golang Programming Language》
《Golang 学习笔记》
《The Golang Programming Language》
《Golang 学习笔记》
yougg

yougg 回答了问题 • 2016-10-14 10:01 • 84 个回复 不感兴趣

大家说说看都用啥写Go

赞同来自:

# IDEA大法好
# 天灭vscode 退软保平安
# 人在做,天在看 中文乱码留祸患
# 界面卡顿天地灭 赶紧卸载保平安
# 诚心诚念IDEA好 JetBrains大法平安保
# 众生皆为IDEA来 现世险恶忘前缘
# 开源为你说真相 教你脱险莫拒绝
# ... 显示全部 »
# IDEA大法好
# 天灭vscode 退软保平安
# 人在做,天在看 中文乱码留祸患
# 界面卡顿天地灭 赶紧卸载保平安
# 诚心诚念IDEA好 JetBrains大法平安保
# 众生皆为IDEA来 现世险恶忘前缘
# 开源为你说真相 教你脱险莫拒绝
# 早日不做软粉,早日获得新生
# 上网搜索“九评纳德拉”
# 有 真 相
sryan

sryan 回答了问题 • 2016-10-13 11:44 • 9 个回复 不感兴趣

golang 如何动态创建struct

赞同来自:

静态语言貌似不能直接实现
可以自己实现个map[string]func(string)interface{}
将要动态生成的结构体的函数注册上去
通过string来调用相应的函数来获取对应的结构体
静态语言貌似不能直接实现
可以自己实现个map[string]func(string)interface{}
将要动态生成的结构体的函数注册上去
通过string来调用相应的函数来获取对应的结构体
astaxie

astaxie 回答了问题 • 2016-10-13 22:04 • 14 个回复 不感兴趣

想用golang写个分布式的监控,大神给点建议

赞同来自:

这个问题很有意思,很多场景设计都会来考虑拉和推两种方案,我分别对拉和推两种的优缺点对比以下,你自己权衡一下,欢迎大家继续补充

## 拉的方案(不写agent)
优点:
- 不需要agent,不需要再部署新的程序

缺点:
- 网络中断的情况下,就无法监控机器... 显示全部 »
这个问题很有意思,很多场景设计都会来考虑拉和推两种方案,我分别对拉和推两种的优缺点对比以下,你自己权衡一下,欢迎大家继续补充

## 拉的方案(不写agent)
优点:
- 不需要agent,不需要再部署新的程序

缺点:
- 网络中断的情况下,就无法监控机器的信息

## 推的方案(agent)
优点:
- 本地运行,在和中控机失去网络连接的时候还是可以继续保存监控数据

缺点:
- 需要部署agent,如果机器多得话将来升级也是比较麻烦

拉取和推送其实大家可以考虑,微博的follow逻辑,直播流里面也有同样的问题,很多场景都会遇到

至于说第二种方案走什么协议,这种程序我建议走tcp协议,HTTP的话相对重了一点。
jinzhu

jinzhu 回答了问题 • 2016-10-19 15:38 • 14 个回复 不感兴趣

2016 年 10 月,当前好用的 ORM 是哪个?

赞同来自:

作为作者,推荐 GORM ;)
作为作者,推荐 GORM ;)
sheepbao

sheepbao 回答了问题 • 2016-10-30 20:16 • 15 个回复 不感兴趣

字符串连接哪一种方式最高效

赞同来自:

```go
package main

import (
"bytes"
"fmt"
"strings"
"time"
)

var way map[int]string
... 显示全部 »
```go
package main

import (
"bytes"
"fmt"
"strings"
"time"
)

var way map[int]string

func benchmarkStringFunction(n int, index int) (d time.Duration) {
v := "ni shuo wo shi bu shi tai wu liao le a?"
var s string
var buf bytes.Buffer

t0 := time.Now()
for i := 0; i < n; i++ {
switch index {
case 0: // fmt.Sprintf
s = fmt.Sprintf("%s[%s]", s, v)
case 1: // string +
s = s + "[" + v + "]"
case 2: // strings.Join
s = strings.Join([]string{s, "[", v, "]"}, "")
case 3: // stable bytes.Buffer
buf.WriteString("[")
buf.WriteString(v)
buf.WriteString("]")
}

}
d = time.Since(t0)
if index == 3 {
s = buf.String()
}
fmt.Printf("string len: %d\t", len(s))
fmt.Printf("time of [%s]=\t %v\n", way[index], d)
return d
}

func main() {
way = make(map[int]string, 5)
way[0] = "fmt.Sprintf"
way[1] = "+"
way[2] = "strings.Join"
way[3] = "bytes.Buffer"

k := 4
d := [5]time.Duration{}
for i := 0; i < k; i++ {
d[i] = benchmarkStringFunction(10000, i)
}
}

```
结果:
```
string len: 410000 time of [fmt.Sprintf]= 426.001476ms
string len: 410000 time of [+]= 307.044147ms
string len: 410000 time of [strings.Join]= 738.44362ms
string len: 410000 time of [bytes.Buffer]= 742.248µs
```
* strings.Join 最慢
* fmt.Sprintf 和 string + 差不多
* bytes.Buffer又比上者快约500倍
CodyGuo

CodyGuo 回答了问题 • 2016-10-30 12:57 • 4 个回复 不感兴趣

Go里面如何写多行的字符串吗?

赞同来自:

```go
`line 1
line 2
line 3`
```
```go
`line 1
line 2
line 3`
```
philosophia14

philosophia14 回答了问题 • 2016-11-05 00:30 • 3 个回复 不感兴趣

新版本Go将会对database/sql进行大量改进

赞同来自:

但愿不要多改..
但愿不要多改..
zdreamx

zdreamx 回答了问题 • 2016-11-08 11:27 • 26 个回复 不感兴趣

beego1.8版本功能征集

赞同来自:

orm有没有考虑分表分区分库的优化或支持
orm有没有考虑分表分区分库的优化或支持
println 是把结果输出到 standard error

fmt.Println 是把结果输出到 standard output
println 是把结果输出到 standard error

fmt.Println 是把结果输出到 standard output
傅小黑

傅小黑 回答了问题 • 2017-06-06 17:18 • 5 个回复 不感兴趣

Go指针复制问题

赞同来自:

```go
for k, r := range *rr {
fmt.Printf("%dth r, id: %d, cpu: %f, mem: %f\n", k, r.ID, r.CPU, r.MEM)
rs = append(rs, ... 显示全部 »
```go
for k, r := range *rr {
fmt.Printf("%dth r, id: %d, cpu: %f, mem: %f\n", k, r.ID, r.CPU, r.MEM)
rs = append(rs, &r)
}
```

这里的 r 一直是同一个地址的值的,for 循环的每次是覆盖旧的 r,你要用 *rr[k]
voidint

voidint 回答了问题 • 2017-07-25 09:20 • 7 个回复 不感兴趣

有必要设置多个gopath吗?

赞同来自:

我会设置起码2个`GOPATH`。因为`go get`会把代码拉倒第一个`GOPATH`的缘故,我会把第一个`GOPATH`用于存放第三方库,之后的`GOPATH`才是自己的项目,这样会更清晰。
我会设置起码2个`GOPATH`。因为`go get`会把代码拉倒第一个`GOPATH`的缘故,我会把第一个`GOPATH`用于存放第三方库,之后的`GOPATH`才是自己的项目,这样会更清晰。

大家是如何处理 golang web 应用静态资源的?

技术讨论astaxie 回复了问题 • 5 人关注 • 1 个回复 • 1627 次浏览 • 2016-10-14 13:08 • 来自相关话题

Python 程序员的 Golang 学习指南(II): 开发环境搭建

文章分享Cloudinsight 发表了文章 • 0 个评论 • 1852 次浏览 • 2016-10-12 15:44 • 来自相关话题

Authors: startover

Authors: startover





上一篇文章我们已经对 Golang 有了初步的了解,这篇主要介绍如何在 Ubuntu 14.04 上搭建 Golang 开发环境。


安装 Golang


这里就按照官方文档进行安装即可,如下:



  • 下载并解压安装包到指定目录


$ wget https://storage.googleapis.com ... ar.gz
$ tar -C /usr/local -xzf go1.6.3.linux-amd64.tar.gz


  • 设置 PATH


$ echo "export PATH=$PATH:/usr/local/go/bin" >> ~/.bashrc
$ source ~/.bashrc


  • 验证安装


$ go version
go version go1.6.3 linux/amd64

环境变量设置


$ echo "export GOROOT=/usr/local/go" >> ~/.bashrc
$ echo "export GOPATH=$HOME/go" >> ~/.bashrc
$ source ~/.bashrc

其中,GOROOT 为 Golang 的安装目录,只有当 Golang 安装到除 /usr/local 之外的路径时需要设置,反之则不用设置,GOPATH 是 Golang 的开发目录,详细可参考官方文档


开发工具


工欲善其事,必先利其器,作为一名伪 VIMer,这里主要介绍下如何在 Vim 下配置 Golang 开发环境。


由于之前一直使用 k-vim 作为 Python 开发环境,而 k-vim 已经集成了当前使用最为广泛的用于搭建 Golang 开发环境的 vim 插件 vim-go,只是默认没有开启,需要我们手动进行相关设置。


k-vim 中开启 Golang 语言的支持,非常简单,如下:



  • 修改 ~/.vimrc.bundles(开启 golang 支持,并修改 vim-go 的默认配置,增加快捷键配置等)。


let g:bundle_groups=['python', 'javascript', 'markdown', 'html', 'css', 'tmux', 'beta', 'json', 'golang']

" vimgo {{{
let g:go_highlight_functions = 1
let g:go_highlight_methods = 1
let g:go_highlight_structs = 1
let g:go_highlight_operators = 1
let g:go_highlight_build_constraints = 1

let g:go_fmt_fail_silently = 1
let g:go_fmt_command = "goimports"
let g:syntastic_go_checkers = ['golint', 'govet', 'errcheck']

" vim-go custom mappings
au FileType go nmap <Leader>s <Plug>(go-implements)
au FileType go nmap <Leader>i <Plug>(go-info)
au FileType go nmap <Leader>gd <Plug>(go-doc)
au FileType go nmap <Leader>gv <Plug>(go-doc-vertical)
au FileType go nmap <leader>r <Plug>(go-run)
au FileType go nmap <leader>b <Plug>(go-build)
au FileType go nmap <leader>t <Plug>(go-test)
au FileType go nmap <leader>c <Plug>(go-coverage)
au FileType go nmap <Leader>ds <Plug>(go-def-split)
au FileType go nmap <Leader>dv <Plug>(go-def-vertical)
au FileType go nmap <Leader>dt <Plug>(go-def-tab)
au FileType go nmap <Leader>e <Plug>(go-rename)
au FileType go nnoremap <leader>gr :GoRun %<CR>
" }}}



  • 在 Vim 内执行 :PlugInstall,安装 vim-go




  • 在 Vim 内执行 :GoInstallBinaries,下载并安装 vim-go 依赖的二进制工具,goimportsgolint 等。



  • 安装 gotags,使 tagbar 配置生效。


$ go get -u github.com/jstemmer/gotags

我们来看一下最终效果:


Image of Golang Environment in Vim


编写第一个程序


进入工作目录,新建文件 hello.go,如下:


$ cd $GOPATH
$ vim hello.go
package main

import "fmt"

func main() {
fmt.Println("Hello, World!")
}

运行程序:


$ go run hello.go
Hello, World!



本文章为 Cloudinsight 技术团队工程师原创,更多技术文章可访问 Cloudinsight 技术博客Cloudinsight 为可视化系统监控工具,涵盖 Windows、Linux 操作系统,用 Golang 开发的 Cloudinsight Agent 正式开源了,欢迎 fork,Github:https://github.com/cloudinsight/cloudinsight-agent


golang-for-pythonistas 系列持续更新中,欢迎关注~

Python 程序员的 Golang 学习指南(I): Go 之初体验

文章分享Cloudinsight 发表了文章 • 3 个评论 • 1848 次浏览 • 2016-10-12 15:27 • 来自相关话题

Authors: startover

Authors: startover





Go 语言简介


Go,又称 golang,是 Google 开发的一种静态强类型,编译型,并发型,并具有垃圾回收功能的编程语言。


Go 语言于2009年11月正式宣布推出,自2012年发布1.0,最新稳定版1.7。目前,Go的相关工具和生态已逐渐趋于完善,也不乏重量级项目,如 Docker, Kubernetes, Etcd, InfluxDB 等。


Go 语言能解决什么样的问题


同绝大多数通用型编程语言相比,Go 语言更多的是为了解决我们在构建大型服务器软件过程中所遇到的软件工程方面的问题而设计的。乍看上去,这么讲可能会让人感觉 Go 非常无趣且工业化,但实际上,在设计过程中就着重于清晰和简洁,以及较高的可组合性,最后得到的反而会是一门使用起来效率高而且很有趣的编程语言,很多程序员都会发现,它有极强的表达力而且功能非常强大。


总结为以下几点:



  • 清晰的依赖关系

  • 清晰的语法

  • 清晰的语义

  • 偏向组合而不是继承

  • 提供简单的编程模型(垃圾回收、并发)

  • 强大的内置工具(gofmt、godoc、gofix等)


建议有兴趣的同学看看 Go在谷歌:以软件工程为目的的语言设计


Go 语言相对 Python 有哪些优势


这里引用一段知乎上某大牛的回答,如下:




  • 部署简单。Go 编译生成的是一个静态可执行文件,除了 glibc 外没有其他外部依赖。这让部署变得异常方便:目标机器上只需要一个基础的系统和必要的管理、监控工具,完全不需要操心应用所需的各种包、库的依赖关系,大大减轻了维护的负担。这和 Python 有着巨大的区别。由于历史的原因,Python 的部署工具生态相当混乱【比如 setuptools, distutils, pip, buildout 的不同适用场合以及兼容性问题】。官方 PyPI 源又经常出问题,需要搭建私有镜像,而维护这个镜像又要花费不少时间和精力。




  • 并发性好。Goroutine 和 channel 使得编写高并发的服务端软件变得相当容易,很多情况下完全不需要考虑锁机制以及由此带来的各种问题。单个 Go 应用也能有效的利用多个 CPU 核,并行执行的性能好。这和 Python 也是天壤之比。多线程和多进程的服务端程序编写起来并不简单,而且由于全局锁 GIL 的原因,多线程的 Python 程序并不能有效利用多核,只能用多进程的方式部署;如果用标准库里的 multiprocessing 包又会对监控和管理造成不少的挑战【我们用的 supervisor 管理进程,对 fork 支持不好】。部署 Python 应用的时候通常是每个 CPU 核部署一个应用,这会造成不少资源的浪费,比如假设某个 Python 应用启动后需要占用 100MB 内存,而服务器有 32 个 CPU 核,那么留一个核给系统、运行 31 个应用副本就要浪费 3GB 的内存资源。




  • 良好的语言设计。从学术的角度讲 Go 语言其实非常平庸,不支持许多高级的语言特性;但从工程的角度讲,Go 的设计是非常优秀的:规范足够简单灵活,有其他语言基础的程序员都能迅速上手。更重要的是 Go 自带完善的工具链,大大提高了团队协作的一致性。比如 gofmt 自动排版 Go 代码,很大程度上杜绝了不同人写的代码排版风格不一致的问题。把编辑器配置成在编辑存档的时候自动运行 gofmt,这样在编写代码的时候可以随意摆放位置,存档的时候自动变成正确排版的代码。此外还有 gofix, govet 等非常有用的工具。



  • 执行性能好。虽然不如 C 和 Java,但通常比原生 Python 应用还是高一个数量级的,适合编写一些瓶颈业务。内存占用也非常省。


从个人对 Golang 的初步使用来说,体验还是相当不错的,但是也有下面几点需要注意:




  • 驼峰式命名风格(依据首字母大小写来决定其是否能被其他包引用),但我更喜欢 Python 的小写字母加下划线命名风格。




  • 没有好用的包管理器,Golang 官方也没有推荐最佳的包管理方案,目前公认的比较好用的有 Godeps, Govendor 及 Glide,而 Python 的包管理器 pip 已形成自己的一套标准。




  • 多行字符串的变量声明需要用反引号(`),Python 里是三个双引号("""),参考http://stackoverflow.com/questions/7933460/how-do-you-write-multiline-strings-in-go




  • Golang 中的类型匹配是很严格的,不同的类型之间通常需要手动转换,所以在字符串拼接时往往需要对整型进行显式转换,如 fmt.Println("num: " + strconv.Itoa(1))



  • Golang 语言语法里的语法糖并不多,如在 Python 中很流行的 map, reduce, range 等,在 Golang 里都没有得到支持。


另外,推荐阅读 Golang 新手开发者要注意的陷阱和常见错误


学习资料推荐


建议先把 Go 的官方文档过一遍,主要有以下几项:



官方文档看完后,基本也算入门了,这时候可以看看 Go 的示例代码,或者去 Project Euler 刷刷题。


当然也可以去知乎看看大牛们都是如何学习的,链接 https://www.zhihu.com/question/23486344


总结


虽然 Go 有很多被诟病的地方,比如 GC 和对错误的处理方式,但没有任何语言是完美的,从实用角度来讲,Go 有着不输于 Python 的开发效率,完善的第三方工具,以及强大的社区支持,这些就足够了。


相关链接:

https://golang.org/doc/

https://talks.golang.org/2012/splash.article

https://www.zhihu.com/question/21409296

https://www.zhihu.com/question/23486344

http://stackoverflow.com/questions/7933460/how-do-you-write-multiline-strings-in-go

http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-golang/

http://www.oschina.net/translate/go-at-google-language-design-in-the-service-of-software-engineering




本文章为 Cloudinsight 技术团队工程师原创,更多技术文章可访问 Cloudinsight 技术博客Cloudinsight 为可视化系统监控工具,涵盖 Windows、Linux 操作系统,用 Golang 开发的 Cloudinsight Agent 正式开源了,欢迎 fork,Github:https://github.com/cloudinsight/cloudinsight-agent


golang-for-pythonistas 系列持续更新中,欢迎关注~

求一些golang的教程,书籍也可以

有问必答huhuyou2 回复了问题 • 46 人关注 • 26 个回复 • 4836 次浏览 • 2017-10-26 09:19 • 来自相关话题

golang有没有好的开源游戏框架

技术讨论cye 回复了问题 • 24 人关注 • 12 个回复 • 8812 次浏览 • 2017-08-16 17:23 • 来自相关话题

为什么Go里面大多数的接口返回的是int类型

有问必答negronihe 回复了问题 • 4 人关注 • 3 个回复 • 2124 次浏览 • 2016-10-16 13:06 • 来自相关话题

大家推荐哪种golang包管理方式?

有问必答topgrd 回复了问题 • 32 人关注 • 26 个回复 • 8553 次浏览 • 2017-08-04 17:12 • 来自相关话题

有没有好的 sql 数据库 driver 推荐, 最好同时支持 mysql 跟 pg?

回复

有问必答Xargin 回复了问题 • 3 人关注 • 2 个回复 • 143 次浏览 • 1 小时前 • 来自相关话题

(开源)基于vue, react, node.js, go开发的微商城(含微信小程序)

回复

开源程序zhaoyun4122 回复了问题 • 7 人关注 • 5 个回复 • 1758 次浏览 • 6 天前 • 来自相关话题

golang兼职讲师 imooc 20K-45K

回复

招聘应聘gigiyuan 发起了问题 • 1 人关注 • 0 个回复 • 249 次浏览 • 2017-12-01 14:06 • 来自相关话题

大家说说看都用啥写Go

回复

技术讨论MarkYang 回复了问题 • 68 人关注 • 84 个回复 • 5696 次浏览 • 2017-11-30 16:33 • 来自相关话题

很期待Golang能有自己的客户端UI库

回复

有问必答jitongxi 回复了问题 • 4 人关注 • 4 个回复 • 745 次浏览 • 2017-11-24 14:41 • 来自相关话题

想用golang写个分布式的监控,大神给点建议

回复

有问必答my3157 回复了问题 • 19 人关注 • 14 个回复 • 4128 次浏览 • 2017-11-24 10:49 • 来自相关话题

[基础问题] 数组赋值

回复

有问必答koala 回复了问题 • 3 人关注 • 2 个回复 • 216 次浏览 • 2017-11-15 16:03 • 来自相关话题

go分布式日志跟踪怎么实现

回复

有问必答Wilence 回复了问题 • 4 人关注 • 3 个回复 • 649 次浏览 • 2017-11-14 17:02 • 来自相关话题

go的大文件内容比较

回复

有问必答littledriver 回复了问题 • 4 人关注 • 4 个回复 • 459 次浏览 • 2017-11-10 08:12 • 来自相关话题

记录一个拷贝文件到GlusterFS卡住的解决过程

回复

文章分享houyy668 回复了问题 • 3 人关注 • 2 个回复 • 471 次浏览 • 2017-11-03 16:32 • 来自相关话题

使用beego orm: dial tcp <ip>:3306: connect: cannot assign requested address

回复

有问必答qingfeng 发起了问题 • 1 人关注 • 0 个回复 • 283 次浏览 • 2017-11-01 21:01 • 来自相关话题

【广州】Go后端:高并发多线程/性能分析/解决问题,如果你❤️ 更多的挑战,更大的发挥

回复

招聘应聘张伯雨 回复了问题 • 4 人关注 • 3 个回复 • 687 次浏览 • 2017-11-01 17:32 • 来自相关话题

有没有工具可以检查代码中忘记手动关闭资源的地方?

回复

有问必答astaxie 回复了问题 • 2 人关注 • 1 个回复 • 445 次浏览 • 2017-10-28 22:11 • 来自相关话题

golang变量作用域

回复

有问必答sunnynewlife 回复了问题 • 6 人关注 • 3 个回复 • 1287 次浏览 • 2017-10-26 14:59 • 来自相关话题

求一些golang的教程,书籍也可以

回复

有问必答huhuyou2 回复了问题 • 46 人关注 • 26 个回复 • 4836 次浏览 • 2017-10-26 09:19 • 来自相关话题

分享一个带 TTL 以及失效回调的 LRU 库. https://github.com/Akagi201/kvcache

开源程序Akagi201 发表了文章 • 0 个评论 • 116 次浏览 • 2017-12-06 15:04 • 来自相关话题

https://github.com/Akagi201/kvcache

完美达到我的业务需要的效果. 大家有同样的需求可以测试测试.

...

https://github.com/Akagi201/kvcache


完美达到我的业务需要的效果. 大家有同样的需求可以测试测试.


有问题一定向我反馈. akagi201@gmail.com

tsdump-用于导出数据库表结构的工具(支持导出为text、markdown、csv、json)

开源程序voidint 发表了文章 • 0 个评论 • 126 次浏览 • 2017-12-03 20:32 • 来自相关话题

项目地址: https://github.com/voidint/tsdump

特性

  • 支持将数据库(当前仅支持查看全部

项目地址: https://github.com/voidint/tsdump


特性



  • 支持将数据库(当前仅支持MySQL)及其表结构的元数据以textmarkdownjsoncsv形式输出。


安装


$ go get -u github.com/voidint/tsdump

基本使用




  • 全局选项


    GLOBAL OPTIONS:
    -H value, --host value Connect to host. (default: "127.0.0.1")
    -P value, --port value Port number to use for connection. (default: 3306)
    -u value, --user value User for login if not current user. (default: "voidint")
    -p value, --password value Password to use when connecting to server.
    -d value, --db value Database name.
    -V value, --viewer value Output viewer. Optional values: txt|csv|json|md (default: "txt")
    -o value, --output value Write to a file, instead of STDOUT.
    -D, --debug Enable debug mode.
    --help, -h show help
    --version, -v print the version



  • 使用root用户创建一个名为mydb的数据库实例,以及一张student的表。


    CREATE DATABASE IF NOT EXISTS `mydb` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;

    USE `mydb`;

    CREATE TABLE `student` (
    `sno` char(8) NOT NULL COMMENT '学号',
    `sname` varchar(255) NOT NULL COMMENT '姓名',
    `gender` char(2) DEFAULT NULL COMMENT '性别',
    `native` char(20) DEFAULT NULL COMMENT '籍贯',
    `birthday` datetime DEFAULT NULL COMMENT '出生日期',
    `dno` char(6) DEFAULT NULL COMMENT '所在院系',
    `spno` char(8) DEFAULT NULL COMMENT '专业代码',
    `classno` char(4) DEFAULT NULL COMMENT '班级号',
    `entime` date DEFAULT NULL COMMENT '入校时间',
    `home` varchar(40) DEFAULT NULL COMMENT '家庭住址',
    `tell` varchar(40) DEFAULT NULL COMMENT '联系电话',
    PRIMARY KEY (`sno`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='学生信息表';



  • 将数据库及其表结构数据以表格形式输出到console


    $ tsdump -H 127.0.0.1 -P 3307 -u root -p "mypassword" --db mydb
    |----------|---------------|--------------------|
    | DATABASE | CHARACTER SET | COLLATION |
    |----------|---------------|--------------------|
    | mydb | utf8mb4 | utf8mb4_general_ci |
    |----------|---------------|--------------------|

    TABLE: student 学生信息表
    |----------|----------|--------------|---------------|--------------------|----------|
    | COLUMN | NULLABLE | DATA TYPE | CHARACTER SET | COLLATION | COMMENT |
    |----------|----------|--------------|---------------|--------------------|----------|
    | sno | NO | char(8) | utf8mb4 | utf8mb4_general_ci | 学号 |
    | sname | NO | varchar(255) | utf8mb4 | utf8mb4_general_ci | 姓名 |
    | gender | YES | char(2) | utf8mb4 | utf8mb4_general_ci | 性别 |
    | native | YES | char(20) | utf8mb4 | utf8mb4_general_ci | 籍贯 |
    | birthday | YES | datetime | | | 出生日期 |
    | dno | YES | char(6) | utf8mb4 | utf8mb4_general_ci | 所在院系 |
    | spno | YES | char(8) | utf8mb4 | utf8mb4_general_ci | 专业代码 |
    | classno | YES | char(4) | utf8mb4 | utf8mb4_general_ci | 班级号 |
    | entime | YES | date | | | 入校时间 |
    | home | YES | varchar(40) | utf8mb4 | utf8mb4_general_ci | 家庭住址 |
    | tell | YES | varchar(40) | utf8mb4 | utf8mb4_general_ci | 联系电话 |
    |----------|----------|--------------|---------------|--------------------|----------|



  • 将数据库及其表结构数据输出到markdown文件


    $ tsdump -H 127.0.0.1 -P 3307 -u root -p "mypassword" --db mydb -V md > ./mydb.md

    output:


    student


    学生信息表







































































































    COLUMN NULLABLE DATA TYPE CHARACTER SET COLLATION COMMENT
    sno NO char(8) utf8mb4 utf8mb4_general_ci 学号
    sname NO varchar(255) utf8mb4 utf8mb4_general_ci 姓名
    gender YES char(2) utf8mb4 utf8mb4_general_ci 性别
    native YES char(20) utf8mb4 utf8mb4_general_ci 籍贯
    birthday YES datetime 出生日期
    dno YES char(6) utf8mb4 utf8mb4_general_ci 所在院系
    spno YES char(8) utf8mb4 utf8mb4_general_ci 专业代码
    classno YES char(4) utf8mb4 utf8mb4_general_ci 班级号
    entime YES date 入校时间
    home YES varchar(40) utf8mb4 utf8mb4_general_ci 家庭住址
    tell YES varchar(40) utf8mb4 utf8mb4_general_ci 联系电话



  • 将数据库及其表结构数据输出到csv文件


    $ tsdump -H 127.0.0.1 -P 3307 -u root -p "mypassword" --db mydb -V csv -o ./mydb.csv


  • 将数据库及其表结构数据输出到JSON文件
    $ tsdump -H 127.0.0.1 -P 3307 -u root -p "mypassword" --db mydb -V json -o ./mydb.json

搭建轻量级的 Docker 容器云管理平台

开源程序bobliu0909 发表了文章 • 0 个评论 • 232 次浏览 • 2017-12-02 11:09 • 来自相关话题

什么是 Humpback?

Humpback 可以帮助企业快速搭建轻量级的 Docker 容器云管理平台,若将你的 Docker 主机接入到 Humpback 平台中,就能够为你带来更快捷稳定的容器操作体验。

查看全部

什么是 Humpback?


Humpback 可以帮助企业快速搭建轻量级的 Docker 容器云管理平台,若将你的 Docker 主机接入到 Humpback 平台中,就能够为你带来更快捷稳定的容器操作体验。


humpback架构


Humpback 功能特点


-Web操作,简单易用

-权限分组隔离

-容器升级与克隆

-容器监控

-容器日志

-集群容器调度

-集群弹性伸缩

-私有仓库


Humpback 模式介绍


-Single Mode 单一模式,对单组主机实现容器管理,提供容器创建,容器操作,容器重命名,容器升级与克隆,容器监控,容器日志输出等功能。

-Cluster Mode 容器集群模式,实现按实例数批量创建容器,容器调度,批量操作容器,升级和迁移等功能。


平台采用分组方式(Group)来管理多主机,多组之间权限操作隔离,同时也可以将一台主机加入到多个分组中交叉管理。


系统登录


Single Mode

SingleMode


Cluster Mode

ClusterMode


Container Monitor

ContainerMonitor


Container Logs

Container Logs


Container Detail

ContainerDetail


ContainerDetail


项目地址:https://humpback.github.io/humpback

授权协议:Apache

开发语言:TypeScript、Golang

操作系统:垮平台

GOLANG探测HTTP连接断开

技术讨论winlin 发表了文章 • 1 个评论 • 318 次浏览 • 2017-11-22 12:22 • 来自相关话题

考虑基于HTTP的RPC,或者HTTP服务器主动通知客户端的机制,就是HTTP Long-Polling,意思... 查看全部

考虑基于HTTP的RPC,或者HTTP服务器主动通知客户端的机制,就是HTTP Long-Polling,意思就是客户端发起一个长连接,服务器阻塞忍住不响应直到:



  1. 超时,比如5秒后,我们给客户端响应一个keepalive,意思是现在还没有啥事,请继续polling。

  2. 拿到结果,这个可能是任何时候,比如300毫秒、1100毫秒、2300毫秒拿到一个事件,响应给客户端,实现了有事件异步通知。


这样客户端和服务器之间RPC的效率就非常高,只有在有事件时才会通知。但是,实际上还有一种情况需要处理:



  1. 当客户端断开连接,比如客户端设置了3秒钟TCP请求超时,或者因为客户端Crash时OS回收了FD等等,这个时候服务器应该要终止polling事务,停止获取事件。因为如果这个时候获取了事件,那么如何处理这个事件?只能丢弃,如果客户端再次发起请求,就拿不到这个事件了。


问题就来了,如何在HTTP Handler中探测客户端断开?例如:


var incoming chan []byte
http.HandleFunc("/polling", func(w http.ResponseWriter, r *http.Request) {
select {
case b := <- incoming:
w.Write(b)
case <-time.After(5 * time.Second):
w.Write("keepalive")
// how to detect TCP disconnect event?
}
})

可能有以下方式:



  1. 读取r.Body,如果发现断开应该会有错误。

  2. 有朋友用reflect或hijack取到底层的TCPConn,然后Peek。

  3. w转换成http.CloseNotifier,在TCP连接关闭时拿到事件。


r.Body Read


这种方式是不靠谱的,假设没有Body内容,直接读取检测是否有error:


nn,err := io.Copy(ioutil.Discard, r.Body)

实际上返回的是nn=0err=nil,也就是没有Body,没有错误。因为这个读取的含义是指Request结束。


如果读取完Body后再读呢?收到的是io.EOF,在没有发送Response之前,Request已经结束了,所以就是io.EOF,并不能检测到底层TCP断开。


Peek TcpConn


使用reflect获取底层的TCPConn对象,是知道w http.ResponseWriter实际上是http.response


// A response represents the server side of an HTTP response.
type response struct {
conn *conn

它有个Field就是conn,再转成TCPConn就可以Peek。


这样做的风险就是,不同的GOLANG版本,可能会对底层实现进行变更,在升级时会有风险。


Reflect方式始终不是最好的。


另外,还有一种方式,就是用http hijack方式,这种方式虽然是http库提供的接口,但是很多地方注释都说hijack需要特殊处理,因此也不是最好的方式。参考When to use hijack


Close Notifier


在GO1.1提供了http.CloseNotifier接口,参考Close Notifier,但是也注意会有一些问题,参考net/http: CloseNotifier fails to fire when underlying connection is gone。用法如下:


var incoming chan []byte
http.HandleFunc("/polling", func(w http.ResponseWriter, r *http.Request) {
select {
case <- w.(http.CloseNotifier).CloseNotify():
fmt.Println("connection closed")
}
})

实际上,超时机制始终是需要的,加上之前的逻辑,考虑context.Context取消事件,http-long polling的完整实现应该是:


func polling(ctx context.Context, incoming chan []byte) {
http.HandleFunc("/polling", func(w http.ResponseWriter, r *http.Request) {
select {
case <- ctx.Done():
fmt.Println("system quit")
case b := <- incoming:
w.Write(b)
case <-time.After(5 * time.Second):
w.Write("keepalive")
case <- w.(http.CloseNotifier).CloseNotify():
fmt.Println("connection closed")
}
})
}

Gopher 深圳 meetup

线下活动mai_yang 发表了文章 • 1 个评论 • 257 次浏览 • 2017-11-16 09:13 • 来自相关话题

Gopher 深圳 meetup 开始报名啦,由小恩爱、PingCAP、随手记、史蒂夫软件的Go语言专家给大家带来 Go 语言实战经验和技巧分享,还有丰厚的奖品等你来拿,名额有限哦。

活动议程

13:00-13:30 签... 查看全部

Gopher 深圳 meetup 开始报名啦,由小恩爱、PingCAP、随手记、史蒂夫软件的Go语言专家给大家带来 Go 语言实战经验和技巧分享,还有丰厚的奖品等你来拿,名额有限哦。


活动议程


13:00-13:30 签到,微信墙交流


13:30-14:00 开场


14:00-14:55 《Go TCP Socket编程之teleport框架是怎样炼成的?》


14:55-15:50 《Go 开发游戏踩过的那些坑》


15:50-16:10 茶歇


16:10-17:05 《随手记 Go 实践:高并发积分系统改造》


17:05-18:00 《Go in TiDB》


18:00-18:10 抽奖交流,合影留念


时间地点


2017/12/16 13:30-18:20


深圳市南山区科发路1号富利臻大厦3F


分享话题




  • 《Go TCP Socket编程之teleport框架是怎样炼成的?》


    李亚川,Pholcus、Faygo、Teleport等Go语言开源项目作者。自2014年起全面转向Go语言的服务端开发领域。现就职于小恩爱,负责服务器底层架构与基础服务的研发工作。Github:https://github.com/henrylee2cn


    内容介绍:通过回顾开源项目teleport(通用、高效、灵活的TCP Socket框架)的开发历程,分享Go Socket的开发实战经验。




  • 《Go 开发游戏踩过的那些坑》


    庾俊:现任史蒂夫软件技术经理,原美国 Kabam 公司亚特兰蒂斯之龙族崛起游戏后端主程,该游戏三年收入超过一亿美金。


    内容介绍:带着大家一步一步的回顾我在创业三年以来,用 Go 开发游戏服务器过程中艰苦历程,并分享给你可能你从未踩过或者听过的一些坑,以及一些个人经验。




  • 《随手记 Go 实践:高并发积分系统改造》


    曾龙:随手记高级开发工程师


    内容介绍:随手记积分系统基于Go语言进行了改造,以支撑高并发、高性能的业务需求。本次分享主要介绍在改造过程中的实战经验、踩过的坑以及改造前后的对比提升,当然还有提升的经验。




  • 《Go in TiDB》


    姚维: PingCAP 资深数据库技术专家,华南区GM, 开源数据库中间件 360 Atlas 作者. mysql-sniffer作者.


    内容介绍:介绍 TiDB 如何用Go实现一个分布式的数据库SQL引擎, 以及在 TiDB 中利用的一些黑科技。




报名请前往:https://www.bagevent.com/event/979909

GoReporter第三版

开源程序fiisio 发表了文章 • 0 个评论 • 334 次浏览 • 2017-10-18 15:08 • 来自相关话题

GoReporter第三版重构了展示页面,分类更清晰,展示模型更多。可以作为白盒测试,CodeReview助手或者最佳实践评估工具。

欢迎大家使用和提出改进建议或者帮助完善功能! 查看全部

GoReporter第三版重构了展示页面,分类更清晰,展示模型更多。可以作为白盒测试,CodeReview助手或者最佳实践评估工具。


欢迎大家使用和提出改进建议或者帮助完善功能!
https://github.com/360EntSecGroup-Skylar/goreporter

链表以及golang介入式链表的实现

文章分享sheepbao 发表了文章 • 4 个评论 • 483 次浏览 • 2017-10-14 21:47 • 来自相关话题

链表以及golang介入式链表的实现

今天看tcp/ip协议栈的代码时看到一个双向链表,链表吗?听过它的顶顶大名,知道它是由节点构成的,每个节点还有个指针指向下一个节点,但是从来没自己实现过一个,没有实践就不能深刻理解,遂有此文。查看全部

链表以及golang介入式链表的实现


今天看tcp/ip协议栈的代码时看到一个双向链表,链表吗?听过它的顶顶大名,知道它是由节点构成的,每个节点还有个指针指向下一个节点,但是从来没自己实现过一个,没有实践就不能深刻理解,遂有此文。

以下所有观点都是个人愚见,有不同建议或补充的的欢迎emial我aboutme


何为链表?


链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer)。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而顺序表相应的时间复杂度分别是O(logn)和O(1)。

简单的说链表是一个具有逻辑顺序的线性表,每一个节点里存到下一个节点的指针。


图示


单链表


list1


双向链表


list2


链表有啥用?


因为链表插入很快,而且具有动态性,想添加几个元素就添加几个(内存空间足够),不像数组一样那么死板,正因为链表的灵活性,所有链表的用处是大大的有啊。

链表最适合用于频繁更新变化的数据,比如一个需要异步执行并且不可丢失的命令序列、一个需要进行实时加载与卸载的驱动,无序且数量未知,这个时候就需要链表结构来协助完成数据的管理。如果不需要过度关注数据的顺序,还可以用链表方便快捷地在任意一个地方插入或删除一个元素,并且不会影响到其它的元素。

又或者我在今天看tcp/ip源码中,链表用来构造队列,作为数据段的队列。我想链表用于队列应该是最多的。如果你看过linux内核源码,应该会发现linux内核中多处使用链表这种结构。


go标准库的双向链表


golang的标准库中实现了一个双向链表,该链表可以存储任何数据,先看看使用标准库链表的例子:


package list_test

import (
"container/list"
"fmt"
"testing"
)

func TestList(t *testing.T) {
// Create a new list and put some numbers in it.
l := list.New()
e4 := l.PushBack(4)
e1 := l.PushFront(1)
l.InsertBefore(3, e4)
l.InsertAfter(2, e1)

// Iterate through list and print its contents.
for e := l.Front(); e != nil; e = e.Next() {
fmt.Println(e.Value)
}
}
// output
// 1
// 2
// 3
// 4

该链表实现了链表所有的功能,链表的增删查改。


实现该链表的数据结构


// List represents a doubly linked list.
// The zero value for List is an empty list ready to use.
type List struct {
root Element // sentinel list element, only &root, root.prev, and root.next are used
len int // current list length excluding (this) sentinel element
}

// Element is an element of a linked list.
type Element struct {
// Next and previous pointers in the doubly-linked list of elements.
// To simplify the implementation, internally a list l is implemented
// as a ring, such that &l.root is both the next element of the last
// list element (l.Back()) and the previous element of the first list
// element (l.Front()).
next, prev *Element

// The list to which this element belongs.
list *List

// The value stored with this element.
Value interface{}
}

可以看到Element结构体看到了链表的结构,next,prev分别指向下一个和前一个元素的指针。Value就是链表中的数据段,可以理解为上图中的object。


介入式链表(intrusive list)


前面的链表都是普通链表,记得<<c语言程序设计>>上讲的链表也是一样,就是链表的节点指针和数据段是放在同一个struct,每实现一个不同的struct就得重新实现一遍链表的功能,这对于“懒惰”的程序员来说是不可忍受的,所以就出来了介入式链表,将数据段和链表的功能区别开来。最经典的例子莫过于linux内核的list_head,详情请看链接klist or Linked List in Linux Kernel,linux中是用c实现的,我想用go实现一个介入式链表。


实现代码


package list

type Intrusive interface {
Next() Intrusive
Prev() Intrusive
AddNext(Intrusive)
AddPrev(Intrusive)
}

// List provides the implementation of intrusive linked lists
type List struct {
prev Intrusive
next Intrusive
}

func (l *List) Next() Intrusive {
return l.next
}

func (l *List) Prev() Intrusive {
return l.prev
}

func (l *List) AddNext(i Intrusive) {
l.next = i
}

func (l *List) AddPrev(i Intrusive) {
l.prev = i
}

func (l *List) Reset() {
l.prev = nil
l.next = nil
}

func (l *List) Empty() bool {
return l.prev == nil
}

// Front returns the first element of list l or nil.
func (l *List) Front() Intrusive {
return l.prev
}

// Back returns the last element of list l or nil.
func (l *List) Back() Intrusive {
return l.next
}

// PushFront inserts the element e at the front of list l.
func (l *List) PushFront(e Intrusive) {
e.AddPrev(nil)
e.AddNext(l.prev)

if l.prev != nil {
l.prev.AddPrev(e)
} else {
l.next = e
}
l.prev = e
}

// PushBack inserts the element e at the back of list l.
func (l *List) PushBack(e Intrusive) {
e.AddNext(nil)
e.AddPrev(l.next)

if l.next != nil {
l.next.AddNext(e)
} else {
l.prev = e
}
l.next = e
}

// InsertAfter inserts e after b.
func (l *List) InsertAfter(e, b Intrusive) {
a := b.Next()
e.AddNext(a)
e.AddPrev(b)
b.AddNext(e)

if a != nil {
a.AddPrev(e)
} else {
l.next = e
}
}

// InsertBefore inserts e before a.
func (l *List) InsertBefore(e, a Intrusive) {
b := a.Prev()
e.AddNext(a)
e.AddPrev(b)
a.AddPrev(e)

if b != nil {
b.AddNext(e)
} else {
l.prev = e
}
}

// Remove removes e from l.
func (l *List) Remove(e Intrusive) {
prev := e.Prev()
next := e.Next()

if prev != nil {
prev.AddNext(next)
} else {
l.prev = next
}

if next != nil {
next.AddPrev(prev)
} else {
l.next = prev
}
}

我们这里用List表示实现了Intrusive接口,也实现了链表的基本功能,所以任何实现了Intrusive接口的对象都是可以作为链表的节点,利用这个介入式链表就很简单了,只要在现有的struct嵌入List这个结构体即可,在举个例子:


package list

import (
"container/list"
"fmt"
"testing"
)

func TestIntrusiveList(t *testing.T) {
type E struct {
List
data int
}
// Create a new list and put some numbers in it.
l := List{}
e4 := &E{data: 4}
e1 := &E{data: 1}
l.PushBack(e4)
l.PushFront(e1)
l.InsertBefore(&E{data: 3}, e4)
l.InsertAfter(&E{data: 2}, e1)

for e := l.Front(); e != nil; e = e.Next() {
fmt.Printf("e: %#v\n", e)
fmt.Printf("data: %#v\n", e.(*E).data)
}
}

E里嵌入List即可作为链表的节点,是不是很方便,其实当我写完介入式链表的栗子后,发现其实标准库的链表更方便,哈哈。。因为golang有interface{}


参考


https://blog.goquxiao.com/posts/2013/07/06/intrusive-list/

http://blog.nlogn.cn/linked-list-in-linux-kernel/

GOLANG实现的HTTP转HTTPS的代理

技术讨论winlin 发表了文章 • 0 个评论 • 418 次浏览 • 2017-10-13 12:17 • 来自相关话题

有时候需要将后端的HTTP服务,转成HTTPS,可以用一个代理。

Reamark: 如果是GOLANG的后端服务,可以直接用库go-oryx-lib/https

查看全部

有时候需要将后端的HTTP服务,转成HTTPS,可以用一个代理。



Reamark: 如果是GOLANG的后端服务,可以直接用库go-oryx-lib/https



这个代理支持自签名的证书,也支持letsencrypt的证书。



Remark: Letsencrypt只支持少量域名的情况,比如自己的网站,它会有请求次数限制,另外CA是letsencrypt的,商业用户不适合用。



我们有个HTTP API, SRS Version:


{
"code": 0,
"server": 12504,
"data": {
"major": 2,
"minor": 0,
"revision": 243,
"version": "2.0.243"
}
}

下面演示实现HTTPS的代理。


Self-sign Certificate


自签名证书可以用在测试中,先生成私钥server.key和证书server.crt


openssl genrsa -out server.key 2048 &&
openssl req -new -x509 -key server.key -out server.crt -days 365


Remark: 生成证书时会有很多提问,直接回车就好了。还可以参考openssl的文档,直接在命令行设置这些参数。



生成私钥和证书后,下载HTTPS代理:


go get github.com/ossrs/go-oryx/httpx-static


Remark: GOLANG的设置请参考GO环境配置


Note: 详细参数可以直接运行httpx-static程序不带参数,会显示help。



启动服务,代理到SRS Version:


sudo $GOPATH/bin/httpx-static -http 80 -https 443 \
-proxy http://ossrs.net:1985/api/v1/versions \
-ssc server.crt -ssk server.key

访问本机HTTP和HTTPS就可以:



  1. HTTP: http://localhost/api/v1/versions

  2. HTTPS: https://localhost/api/v1/versions



Remark: 浏览器访问自签名证书时,可能会提示不安全,选择高级然后继续浏览就可以了。



LetsEncrypt Certificate


可以使用letsencrypt签名的证书,在浏览器中会显示合法的绿色,不会提示有错误。参考:ossrs.net


ossrs.net也是使用httpx-static,参数如下:


sudo $GOPATH/bin/httpx-static -http 80 -https 443 \
-lets=true -domains ossrs.net


Remark: 注意在局域网的机器无法使用,因为ACME会有反向验证,也就是你的服务器得能在公网访问到。



Advance Proxy


如果需要代理所有的API怎么办呢?直接指定父目录就好,如果指定/则代理所有的请求。例如:


下面的命令,代理所有的/api请求:


sudo $GOPATH/bin/httpx-static -http 80 -https 443 \
-proxy http://ossrs.net:1985/api \
-ssc server.crt -ssk server.key

下面的命令,代理所有的请求,相当于做了镜像:


sudo $GOPATH/bin/httpx-static -http 80 -https 443 \
-proxy http://ossrs.net/ \
-ssc server.crt -ssk server.key

其他的参数请参考httpx-static的参数。

golang string和[]byte的对比

文章分享sheepbao 发表了文章 • 2 个评论 • 594 次浏览 • 2017-09-30 16:42 • 来自相关话题

golang string和[]byte的对比

为啥string和[]byte类型转换需要一定的代价?
为啥内置函数copy会有一种特殊情况copy(dst []byte, src string) int查看全部

golang string和[]byte的对比


为啥string和[]byte类型转换需要一定的代价?

为啥内置函数copy会有一种特殊情况copy(dst []byte, src string) int?

string和[]byte,底层都是数组,但为什么[]byte比string灵活,拼接性能也更高(动态字符串拼接性能对比)?

今天看了源码探究了一下。

以下所有观点都是个人愚见,有不同建议或补充的的欢迎emial我aboutme


何为string?


什么是字符串?标准库builtin的解释:


type string

string is the set of all strings of 8-bit bytes, conventionally but not necessarily representing UTF-8-encoded text. A string may be empty, but not nil. Values of string type are immutable.

简单的来说字符串是一系列8位字节的集合,通常但不一定代表UTF-8编码的文本。字符串可以为空,但不能为nil。而且字符串的值是不能改变的。

不同的语言字符串有不同的实现,在go的源码中src/runtime/string.go,string的定义如下:


type stringStruct struct {
str unsafe.Pointer
len int
}

可以看到str其实是个指针,指向某个数组的首地址,另一个字段是len长度。那到这个数组是什么呢?
在实例化这个stringStruct的时候:


func gostringnocopy(str *byte) string {
ss := stringStruct{str: unsafe.Pointer(str), len: findnull(str)}
s := *(*string)(unsafe.Pointer(&ss))
return s
}

哈哈,其实就是byte数组,而且要注意string其实就是个struct。


何为[]byte?


首先在go里面,byte是uint8的别名。而slice结构在go的源码中src/runtime/slice.go定义:


type slice struct {
array unsafe.Pointer
len int
cap int
}

array是数组的指针,len表示长度,cap表示容量。除了cap,其他看起来和string的结构很像。

但其实他们差别真的很大。


区别


字符串的值是不能改变


在前面说到了字符串的值是不能改变的,这句话其实不完整,应该说字符串的值不能被更改,但可以被替换。 还是以string的结构体来解释吧,所有的string在底层都是这样的一个结构体stringStruct{str: str_point, len: str_len},string结构体的str指针指向的是一个字符常量的地址,
这个地址里面的内容是不可以被改变的,因为它是只读的,但是这个指针可以指向不同的地址,我们来对比一下string、[]byte类型重新赋值的区别:


s := "A1" // 分配存储"A1"的内存空间,s结构体里的str指针指向这快内存
s = "A2" // 重新给"A2"的分配内存空间,s结构体里的str指针指向这快内存

其实[]byte和string的差别是更改变量的时候array的内容可以被更改。


s := []byte{1} // 分配存储1数组的内存空间,s结构体的array指针指向这个数组。
s = []byte{2} // 将array的内容改为2

因为string的指针指向的内容是不可以更改的,所以每更改一次字符串,就得重新分配一次内存,之前分配空间的还得由gc回收,这是导致string操作低效的根本原因。


string和[]byte的相互转换


将string转为[]byte,语法[]byte(string)源码如下:


func stringtoslicebyte(buf *tmpBuf, s string) []byte {
var b []byte
if buf != nil && len(s) <= len(buf) {
*buf = tmpBuf{}
b = buf[:len(s)]
} else {
b = rawbyteslice(len(s))
}
copy(b, s)
return b
}

func rawstring(size int) (s string, b []byte) {
p := mallocgc(uintptr(size), nil, false)

stringStructOf(&s).str = p
stringStructOf(&s).len = size

*(*slice)(unsafe.Pointer(&b)) = slice{p, size, size}

return
}

可以看到b是新分配的,然后再将s复制给b,至于为啥copy函数可以直接把string复制给[]byte,那是因为go源码单独实现了一个slicestringcopy函数来实现,具体可以看src/runtime/slice.go


将[]byte转为string,语法string([]byte)源码如下:


func slicebytetostring(buf *tmpBuf, b []byte) string {
l := len(b)
if l == 0 {
// Turns out to be a relatively common case.
// Consider that you want to parse out data between parens in "foo()bar",
// you find the indices and convert the subslice to string.
return ""
}
if raceenabled && l > 0 {
racereadrangepc(unsafe.Pointer(&b[0]),
uintptr(l),
getcallerpc(unsafe.Pointer(&buf)),
funcPC(slicebytetostring))
}
if msanenabled && l > 0 {
msanread(unsafe.Pointer(&b[0]), uintptr(l))
}
s, c := rawstringtmp(buf, l)
copy(c, b)
return s
}

func rawstringtmp(buf *tmpBuf, l int) (s string, b []byte) {
if buf != nil && l <= len(buf) {
b = buf[:l]
s = slicebytetostringtmp(b)
} else {
s, b = rawstring(l)
}
return
}

依然可以看到s是新分配的,然后再将b复制给s。

正因为string和[]byte相互转换都会有新的内存分配,才导致其代价不小,但读者千万不要误会,对于现在的机器来说这些代价其实不值一提。
但如果想要频繁string和[]byte相互转换(仅假设),又不会有新的内存分配,能有办法吗?答案是有的。


package string_slicebyte_test

import (
"log"
"reflect"
"testing"
"unsafe"
)

func stringtoslicebyte(s string) []byte {
sh := (*reflect.StringHeader)(unsafe.Pointer(&s))
bh := reflect.SliceHeader{
Data: sh.Data,
Len: sh.Len,
Cap: sh.Len,
}
return *(*[]byte)(unsafe.Pointer(&bh))
}

func slicebytetostring(b []byte) string {
bh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
sh := reflect.StringHeader{
Data: bh.Data,
Len: bh.Len,
}
return *(*string)(unsafe.Pointer(&sh))
}

func TestStringSliceByte(t *testing.T) {
s1 := "abc"
b1 := []byte("def")
copy(b1, s1)
log.Println(s1, b1)

s := "hello"
b2 := stringtoslicebyte(s)
log.Println(b2)
// b2[0] = byte(99) unexpected fault address

b3 := []byte("test")
s3 := slicebytetostring(b3)
log.Println(s3)
}

答案虽然有,但强烈推荐不要使用这种方法来转换类型,因为如果通过stringtoslicebyte将string转为[]byte的时候,共用的时同一块内存,原先的string内存区域是只读的,一但更改将会导致整个进程down掉,而且这个错误是runtime没法恢复的。


如何取舍?


既然string就是一系列字节,而[]byte也可以表达一系列字节,那么实际运用中应当如何取舍?



  • string可以直接比较,而[]byte不可以,所以[]byte不可以当map的key值。

  • 因为无法修改string中的某个字符,需要粒度小到操作一个字符时,用[]byte。

  • string值不可为nil,所以如果你想要通过返回nil表达额外的含义,就用[]byte。

  • []byte切片这么灵活,想要用切片的特性就用[]byte。

  • 需要大量字符串处理的时候用[]byte,性能好很多。


最后脱离场景谈性能都是耍流氓,需要根据实际场景来抉择。

golang版本的curl请求库

开源程序mikemintang 发表了文章 • 0 个评论 • 514 次浏览 • 2017-09-15 07:55 • 来自相关话题

Github地址


https://github.com/mikemintang/go-curl


安装


go get github.com/mikemintang/go-curl

使用


package main

import (
"fmt"
"github.com/mikemintang/go-curl"
)

func main() {

url := "http://php.dev/api.php"

headers := map[string]string{
"User-Agent": "Sublime",
"Authorization": "Bearer access_token",
"Content-Type": "application/json",
}

cookies := map[string]string{
"userId": "12",
"loginTime": "15045682199",
}

queries := map[string]string{
"page": "2",
"act": "update",
}

postData := map[string]interface{}{
"name": "mike",
"age": 24,
"interests": []string{"basketball", "reading", "coding"},
"isAdmin": true,
}

// 链式操作
req := curl.NewRequest()
resp, err := req.
SetUrl(url).
SetHeaders(headers).
SetCookies(cookies).
SetQueries(queries).
SetPostData(postData).
Post()

if err != nil {
fmt.Println(err)
} else {
if resp.IsOk() {
fmt.Println(resp.Body)
} else {
fmt.Println(resp.Raw)
}
}

}

接收请求的api.php


<?php  

//echo json_encode($_GET); // 获取url地址中的查询参数
//echo json_encode(getallheaders()); // 获取请求头
//echo json_encode($_COOKIE); // 获取cookies
echo file_get_contents("php://input"); // 获取post提交的数据

function getallheaders() {
$headers = [];
foreach ($_SERVER as $name => $value) {
if (substr($name, 0, 5) == 'HTTP_') {
$headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
}
}
return $headers;
}

可导出的成员变量和方法



TodoList



  • [x] 以链式操作的方式发起请求

  • [ ] 以函数回调的方式发起请求

  • [ ] 以类Jquery Ajax的方式发起请求

  • [x] 发起GET/POST请求

  • [ ] 发起PUT/PATCH/DELETE/OPTIONS操作

  • [x] 以application/x-www-form-urlencoded形式提交post数据

  • [x] 以application/json形式提交post数据

  • [ ] 以multipart/form-data形式提交post数据

  • [ ] proxy代理设置

Go 语言在命令行以表格的形式输出结构体切片

开源程序modood 发表了文章 • 3 个评论 • 415 次浏览 • 2017-08-29 15:32 • 来自相关话题

最近写的小工具,可以在命令行以表格的形式输出结构体切片

  • 没有第三方依赖
  • 支持中文汉字
  • 表格每列自动对齐
  • 支持自动适应列宽
  • 结构体的字段支持所有数据类型(字符... 查看全部

最近写的小工具,可以在命令行以表格的形式输出结构体切片



  • 没有第三方依赖

  • 支持中文汉字

  • 表格每列自动对齐

  • 支持自动适应列宽

  • 结构体的字段支持所有数据类型(字符串,切片,映射等)


例如可以很方便清晰地将数据库查询结果列表(结构体切片)在命令行以表格的形式输出。


项目 Github 主页:https://github.com/modood/table


对你有用的话,给个 star 支持一下吧~


package main

import (
"fmt"

"github.com/modood/table"
)

type House struct {
Name string
Sigil string
Motto string
}

func main() {
s := []House{
{"Stark", "direwolf", "Winter is coming"},
{"Targaryen", "dragon", "Fire and Blood"},
{"Lannister", "lion", "Hear Me Roar"},
}

table.Output(s)
}

输出结果:


┌───────────┬──────────┬──────────────────┐
│ Name │ Sigil │ Motto │
├───────────┼──────────┼──────────────────┤
│ Stark │ direwolf │ Winter is coming │
│ Targaryen │ dragon │ Fire and Blood │
│ Lannister │ lion │ Hear Me Roar │
└───────────┴──────────┴──────────────────┘

【上海】换手率-来自小米腾讯联合投资的互联网金融短线炒股APP-跪求Golang

招聘应聘catty 发表了文章 • 2 个评论 • 550 次浏览 • 2017-08-21 15:02 • 来自相关话题

关于我们:https://www.huanshoulv.com/

硅谷式氛围,简单可依赖。我们是一个充满激情和梦想的团队,请无情地向我砸简历。

查看全部

关于我们:https://www.huanshoulv.com/


硅谷式氛围,简单可依赖。我们是一个充满激情和梦想的团队,请无情地向我砸简历。




岗位职责:



  1. 上交所、深交所股票数据处理;

  2. 高性能API接口开发;




任职要求:



  1. 熟悉Linux环境;

  2. 熟悉Go语言;

  3. 2年以上工作经验;

  4. 有高并发开发经验;

  5. 熟悉MongDB、Redis;

  6. 沟通能力良好,有较强的抗压能力;




优先条件:



  1. 熟悉MQTT协议优先;

  2. 熟悉git,有github优先 请在简历中写明github地址;

  3. 熟悉c/c++/nodejs/python优先;

  4. 股票用户优先;




岗位待遇:20k-40k


简历投递邮箱:catty@huanshoulv.com 简历标题:姓名+岗位+来源

阳历和阴历相互转化的工具类 golang版本

文章分享nosix 发表了文章 • 1 个评论 • 402 次浏览 • 2017-08-15 15:47 • 来自相关话题

github 地址 https://github.com/nosixtools/solarlunar

  1. 实现了阳历和阴历... 查看全部

github 地址 https://github.com/nosixtools/solarlunar



  1. 实现了阳历和阴历的相互转化,支持1900年到2049年。

  2. 支持节假日的计算


分享给你们


转化例子


package main 

import (
"github.com/nosixtools/solarlunar"
"fmt"
)

func main() {
solarDate := "1990-05-06"
fmt.Println(solarlunar.SolarToChineseLuanr(solarDate))
fmt.Println(solarlunar.SolarToSimpleLuanr(solarDate))

lunarDate := "1990-04-12"
fmt.Println(solarlunar.LunarToSolar(lunarDate, false))
}

节假日计算例子


package main

import (
"fmt"
"github.com/nosixtools/solarlunar/festival"
)

func main() {
festival := festival.NewFestival("./festival.json")
fmt.Println(festival.GetFestivals("2017-08-28"))
fmt.Println(festival.GetFestivals("2017-05-01"))
fmt.Println(festival.GetFestivals("2017-04-05"))
fmt.Println(festival.GetFestivals("2017-10-01"))
fmt.Println(festival.GetFestivals("2018-02-15"))
fmt.Println(festival.GetFestivals("2018-02-16"))
}

用喜欢和舒服的方式在Golang中使用锁、使用channel自定义锁

文章分享pathbox 发表了文章 • 4 个评论 • 624 次浏览 • 2017-08-12 18:52 • 来自相关话题

众所周知,我们能使用Golang轻松编写并发程序。Golang利用goroutine,让我们编写并发程序变得容易。并发程序中重要的问题之一就是如何正确的处理“竞争资源”或“共享资源”。Golang为我们提供了锁的机制。这篇文章,就简单介绍Golang中锁... 查看全部

众所周知,我们能使用Golang轻松编写并发程序。Golang利用goroutine,让我们编写并发程序变得容易。并发程序中重要的问题之一就是如何正确的处理“竞争资源”或“共享资源”。Golang为我们提供了锁的机制。这篇文章,就简单介绍Golang中锁的使用方法。并且进行错误的使用方法和正确的使用方法的代码示例对比。文章的所以代码示例在:https://github.com/pathbox/learning-go/tree/master/src/lock


原文链接


我们看第一个栗子:


package main

import (
"fmt"
"sync"
)

type Counter struct {
Value int
}

var wg sync.WaitGroup
var mutex sync.Mutex // 声明了一个全局锁
func main() {

wg.Add(1000)
counter := &Counter{Value: 0}

for i := 0; i < 1000; i++ {
go Count(counter, mutex)
}
wg.Wait()
fmt.Println("Count Value: ", counter.Value)
}

func Count(counter *Counter, mutex sync.Mutex) {
mutex.Lock()
defer mutex.Unlock()
counter.Value++
wg.Done()
}

/*
输出结果:
Count Value: 982
*/

这里声明了一个全局锁 sync.Mutex,然后将这个全局锁以参数的方式代入到方法中,这样并没有真正起到加锁的作用。


正确的方式是:


package main

import (
"fmt"
"sync"
)

type Counter struct {
Value int
}

var wg sync.WaitGroup
var mutex sync.Mutex // 声明了一个全局锁
func main() {

wg.Add(1000)
counter := &Counter{Value: 0}

for i := 0; i < 1000; i++ {
go Count(counter)
}
wg.Wait()
fmt.Println("Count Value: ", counter.Value)
}

func Count(counter *Counter) {
mutex.Lock()
defer mutex.Unlock()
counter.Value++
wg.Done()
}

/*
输出结果:
Count Value: 1000
*/

声明了一个全局锁后,其作用范围是全局。直接使用,而不是将其作为参数传递到方法中。


下一个栗子


package main

import (
"fmt"
"sync"
)

type Counter struct {
Value int
}

var wg sync.WaitGroup

func main() {
var mutex sync.Mutex // 声明了一个非全局锁
wg.Add(1000)
counter := &Counter{Value: 0}

for i := 0; i < 1000; i++ {
go Count(counter, mutex)
}
wg.Wait()
fmt.Println("Count Value: ", counter.Value)
}

func Count(counter *Counter, mutex sync.Mutex) {
mutex.Lock()
defer mutex.Unlock()
counter.Value++
wg.Done()
}

/*
输出结果:
Count Value: 954
*/

上面栗子中,声明的不是全局锁。然后将这个锁作为参数传入到Count()方法中,这样并没有真正起到加锁的作用。


正确的方式:


package main

import (
"fmt"
"sync"
)

type Counter struct {
Value int
}

var wg sync.WaitGroup

func main() {
mutex := &sync.Mutex{} // 定义了一个锁 mutex,赋值给mutex
wg.Add(1000)
counter := &Counter{Value: 0}

for i := 0; i < 1000; i++ {
go Count(counter, mutex)
}
wg.Wait()
fmt.Println("Count Value: ", counter.Value)
}

func Count(counter *Counter, mutex *sync.Mutex) {
mutex.Lock()
defer mutex.Unlock()
counter.Value++
wg.Done()
}

/*
输出结果:
Count Value: 1000
*/

这次通过 mutex := &sync.Mutex{},定义了mutex,然后作为参数传递到方法中,正确实现了加锁功能。


简单的说,在全局声明全局锁,之后这个全局锁就能在代码中的作用域范围内都能使用了。但是,也许你需要的不是全局锁。这和锁的粒度有关。
所以,你可以声明一个锁,在其作用域范围内使用,并且这个作用域范围是有并发执行的,别将锁当成参数传递。如果,需要将锁当成参数传递,那么你传的不是一个锁的声明,而是这个锁的指针。


下面,我们讨论一种更好的使用方式。通过阅读过很多”牛人“写的Go的程序或源码库,在锁的使用中。常常将锁放入对应的 struct 中定义,我觉得这是一种不错的方法。


package main

import (
"fmt"
"sync"
)

type Counter struct {
Value int
sync.Mutex
}

var wg sync.WaitGroup

func main() {

wg.Add(1000)
counter := &Counter{Value: 0}

for i := 0; i < 1000; i++ {
go Count(counter)
}
wg.Wait()
fmt.Println("Count Value: ", counter.Value)
}

func Count(counter *Counter) {
counter.Lock()
defer counter.Unlock()
counter.Value++
wg.Done()
}

/*
输出结果:
Count Value: 1000
*/

这样,我们声明的不是全局锁,并且这个需要加锁的竞争资源也正是 struct Counter 本身的Value属性,反映了这个锁的粒度。我觉得这是一种很舒服的使用方式(暂不知道这种方式会带来什么负面影响,如果有踩过坑的朋友,欢迎聊一聊这个坑),当然,如果你需要全局锁,那么请定义全局锁。


还可以有更多的使用方式:


// 1.
type Counter struct {
Value int
Mutex sync.Mutex
}

counter := &Counter{Value: 0}
counter.Mutex.Lock()
defer counter.Mutex.Unlock()

//2.
type Counter struct {
Value int
Mutex *sync.Mutex
}

counter := &Counter{Value: 0, Mutex: &sync.Mutex{}}
counter.Mutex.Lock()
defer counter.Mutex.Unlock()

Choose the way you like~


接下来,我们自己尝试创建一个互斥锁。


简单的说,简单的互斥锁锁的原理是:一个线程(进程)拿到了这个互斥锁,在这个时刻,只有这个线程(进程)能够进行互斥锁锁的范围中的"共享资源"的操作,主要是写操作。我们这里不讨论读锁的实现。锁的种类很多,有不同的实现场景和功能。这里我们讨论的是最简单的互斥锁。


我们能够利用Golang 的channel所具有特性,创建一个简单的互斥锁。


/locker/locker.go


package locker

// Mutext struct
type Mutex struct {
lock chan struct{}
}

// 创建一个互斥锁
func NewMutex() *Mutex {
return &Mutex{lock: make(chan struct{}, 1)}
}

// 锁操作
func (m *Mutex) Lock() {
m.lock <- struct{}{}
}

// 解锁操作
func (m *Mutex) Unlock() {
<-m.lock
}

main.go


package main

import (
"./locker"
"fmt"
"time"
)

type record struct {
lock *locker.Mutex
lock_count int
no_lock_count int
}

func newRecord() *record {
return &record{
lock: locker.NewMutex(),
lock_count: 0,
no_lock_count: 0,
}
}

func main() {
r := newRecord()

for i := 0; i < 1000; i++ {
go CountWithoutLock(r)
go CountWithLock(r)
}
time.Sleep(2 * time.Second)
fmt.Println("Record no_lock_count: ", r.no_lock_count)
fmt.Println("Record lock_count: ", r.lock_count)
}

func CountWithLock(r *record) {
r.lock.Lock()
defer r.lock.Unlock()
r.lock_count++
}

func CountWithoutLock(r *record) {
r.no_lock_count++
}

/* 输出结果
Record no_lock_count: 995
Record lock_count: 1000
*/

locker 就是通过使用channel的读操作和写操作会互相阻塞等待的这个同步性质。
可以简单的理解为,channel中传递的就是互斥锁。一个线程(进程)申请了一个互斥锁(struct{}{}),将这个互斥锁存放在channel中,
其他线程(进程)就没法申请互斥锁放入channel,而处于阻塞状态,等待channel恢复空闲空间。该线程(进程)进行操作”共享资源“,然后释放这个互斥锁(从channel中取走),channel这时候恢复了空闲的空间,其他线程(进程)
就能申请互斥锁并且放入channel。这样,在某一时刻,只会有一个线程(进程)拥有互斥锁,在操作"共享资源"。

GOLANG中time.After释放的问题

技术讨论winlin 发表了文章 • 7 个评论 • 1401 次浏览 • 2017-07-29 11:58 • 来自相关话题

在谢大群里看到有同学在讨论time.After泄漏的问题,就算时间到了也不会释放,瞬间就惊呆了,忍不住做了试验,结果发现应该没有这么的恐怖的,是有泄漏的风险不过不算是泄漏,先看API的说明:

查看全部
					

在谢大群里看到有同学在讨论time.After泄漏的问题,就算时间到了也不会释放,瞬间就惊呆了,忍不住做了试验,结果发现应该没有这么的恐怖的,是有泄漏的风险不过不算是泄漏,先看API的说明:


// After waits for the duration to elapse and then sends the current time
// on the returned channel.
// It is equivalent to NewTimer(d).C.
// The underlying Timer is not recovered by the garbage collector
// until the timer fires. If efficiency is a concern, use NewTimer
// instead and call Timer.Stop if the timer is no longer needed.
func After(d Duration) <-chan Time {
return NewTimer(d).C
}

提到了一句The underlying Timer is not recovered by the garbage collector,这句挺吓人不会被GC回收,不过后面还有条件until the timer fires,说明fire后是会被回收的,所谓fire就是到时间了,写个例子证明下压压惊:


package main

import "time"

func main() {
for {
<- time.After(10 * time.Nanosecond)
}
}

显示内存稳定在5.3MB,CPU为161%,肯定被GC回收了的。当然如果放在goroutine也是没有问题的,一样会回收:


package main

import "time"

func main() {
for i := 0; i < 100; i++ {
go func(){
for {
<- time.After(10 * time.Nanosecond)
}
}()
}
time.Sleep(1 * time.Hour)
}

只是资源消耗会多一点,CPU为422%,内存占用6.4MB。因此:



Remark: time.After(d)在d时间之后就会fire,然后被GC回收,不会造成资源泄漏的。



那么API所说的If efficieny is a concern, user NewTimer instead and call Timer.Stop是什么意思呢?这是因为一般time.After会在select中使用,如果另外的分支跑得更快,那么timer是不会立马释放的(到期后才会释放),比如这种:


select {
case time.After(3*time.Second):
return errTimeout
case packet := packetChannel:
// process packet.
}

如果packet非常多,那么总是会走到下面的分支,上面的timer不会立刻释放而是在3秒后才能释放,和下面代码一样:


package main

import "time"

func main() {
for {
select {
case <-time.After(3 * time.Second):
default:
}
}
}

这个时候,就相当于会堆积了3秒的timer没有释放而已,会不断的新建和释放timer,内存会稳定在2.8GB,这个当然就不是最好的了,可以主动释放:


package main

import "time"

func main() {
for {
t := time.NewTimer(3*time.Second)

select {
case <- t.C:
default:
t.Stop()
}
}
}

这样就不会占用2.8GB内存了,只有5MB左右。因此,总结下这个After的说明:



  1. GC肯定会回收time.After的,就在d之后就回收。一般情况下让系统自己回收就好了。

  2. 如果有效率问题,应该使用Timer在不需要时主动Stop。大部分时候都不用考虑这个问题的。


交作业。