一段不是很理解的sql

回复

有问必答小张的烦恼 发起了问题 • 2 人关注 • 0 个回复 • 192 次浏览 • 2018-02-10 09:38 • 来自相关话题

Go 内嵌静态文件工具 packr

文章分享xfstart07 发表了文章 • 0 个评论 • 427 次浏览 • 2018-02-09 18:38 • 来自相关话题

[Go] Go 内嵌静态文件工具 packr

介绍一个简单实用的 Go 内嵌静态文件工具 packr。

安装

$ go get -... 			查看全部
					

[Go] Go 内嵌静态文件工具 packr


介绍一个简单实用的 Go 内嵌静态文件工具 packr。


安装


$ go get -u github.com/gobuffalo/packr/...

使用


使用 packr 打包静态文件非常简单,通过创建一个 box.


// templates 是相对路径,例子是在同一个目录下
box := packr.NewBox("./templates")

// 以字符串的形式获取静态文件
html := box.String("index.html")
fmt.Println("String获取:", html)

html, err := box.MustString("index.html")
if err != nil {
log.Fatal(err)
}
fmt.Println("MustString获取", html)

// 以字节数组的形式获取静态文件
htmlByte := box.Bytes("index.html")
fmt.Println("Bytes: ", htmlByte)
// 对应的还有 MustBytes 方法

packr 在查找文件时的解析规则:



  • 在二进制文件中,在内存中查找文件

  • 开发时,在本地查找文件


在 HTTP 中使用


因为 box 实现了 http.FileSystem 接口,所有可以直接用来提供静态文件访问


package main

import (
"net/http"

"github.com/gobuffalo/packr"
)

func main() {
box := packr.NewBox("./templates")

http.Handle("/", http.FileServer(box))
http.ListenAndServe(":3000", nil)
}

使用 gorilla/mux 作为路由库,mux 也有提供静态文件访问的方式


r := mux.NewRouter()

box := packr.NewBox("./css")
r.PathPrefix("/css").Handler(http.StripPrefix("/css", http.FileServer(box)))

在渲染库(render)中使用


使用 unrolled/render 库来渲染资源,在初始化渲染选项中,将静态文件加入到 Asset 中。


var boxTemp = packr.NewBox("../templates")
var ren = render.New(render.Options{
Directory: "templates",
Asset: func(name string) ([]byte, error) {
// 返回指定路径名称的文件资源
return boxTemp.Bytes("index.html"), nil
},
AssetNames: func() []string {
// 静态文件的路径名称
return []string{"templates/index.html"}
},
Extensions: []string{".html"},
})

使用


ren.HTML(w, http.StatusOK, "index.html", "")

打包命令


使用命令建立二进制文件


packr build 包括了 go build


packr install 包括了 go install


还可以使用 go generate 命令来生成静态资源文件(.go),


package main

// 打包静态文件命令,生成 packr.go 文件
//go:generate packr

func main() {
Run()
}

然后运行命令


go generate && go build

资源


https://github.com/gobuffalo/packr


https://github.com/unrolled/render


https://github.com/gorilla/mux

golang有哪些“生僻”语法糖

Golangwhisper219 回复了问题 • 6 人关注 • 8 个回复 • 858 次浏览 • 2018-02-09 18:33 • 来自相关话题

golang80行代码钉钉群机器人订阅百度新闻

文章分享jjjjerk 发表了文章 • 0 个评论 • 190 次浏览 • 2018-02-09 17:54 • 来自相关话题

1. 资料


1.1.第三方包


1.2.接口


2. 初始化项目变量


package main

import (
"fmt"
"log"
"github.com/PuerkitoBio/goquery"
"github.com/go-redis/redis"
"net/http"
"bytes"
"github.com/astaxie/beego/toolbox"
)

var (
redisClient *redis.Client //redis 缓存
//钉钉群机器人webhook地址
dingdingURL = "https://oapi.dingtalk.com/robot/send?access_token=dingding_talk_group_bot_webhook_token"
//百度新闻搜索关键字URL
baiduNewsUrlWithSearchKeyword = "http://news.baidu.com/ns?cl=2&rn=20&tn=news&word=%E7%89%A9%E8%81%94%E7%BD%91"
)

const (
newsFeed = "news_feed"//爬取到的百度新闻redis key
newsPost = "news_post"//已发送的百度新闻redis key
newsList = "iot_news" //储存了的百度新闻redis key
)
//实例化redis缓存
func init() {
redisClient = redis.NewClient(&redis.Options{
Addr: "127.0.0.1:6379",
Password: "ddfrfgtre4353252", // redis password
DB: 0, // redis 数据库ID
})
}

在机器人管理页面选择“自定义”机器人,输入机器人名字并选择要发送消息的群。如果需要的话,可以为机器人设置一个头像。点击“完成添加”。


点击“复制”按钮,即可获得这个机器人对应的Webhook地址,赋值给 dingdingURl


3 func newsBot


3.1 使用goquery和网页元素选择器语法提取有用信息

func newsBot() error {
// 获取html doc
doc, err := goquery.NewDocument(baiduNewsUrlWithSearchKeyword)
if err != nil {
return nil
}
//使用redis pipeline 减少redis连接数
pipe := redisClient.Pipeline()
// 使用selector xpath 语法获取有用信息
// 储存新闻到redis中 newsList
// 储存新闻ur到redis-set 建newfeed 为以后是用sdiff 找出没有发送的新闻

doc.Find("div.result").Each(func(i int, s *goquery.Selection) {
// For each item found, get the band and title
URL, _ := s.Find("h3 > a").Attr("href")
Source := s.Find("p.c-author").Text()
Title := s.Find("h3 > a").Text()
markdown := fmt.Sprintf("- [%s](%s) _%s_", Title, URL, Source)
pipe.HSet(newsList, URL, markdown)
pipe.SAdd(newsFeed, URL)
})
//执行redis pipeline
pipe.Exec()

3.2 排除以发送的新闻,拼接markdown字符串

        //使用redis sdiff找出没有发送的新闻url
unSendNewsUrls := redisClient.SDiff(newsFeed, newsPost).Val()
//新闻按dingding文档markdonw 规范拼接

content := ""
for _, url := range unSendNewsUrls {
md := redisClient.HGet(newsList, url).Val()
content = content + " \n " + md
//记录已发送新闻的url地址
pipe.SAdd(newsPost, url)
}
pipe.Exec()

3.3 调用钉钉群机器人接口

        //如果有未发送新闻 请求钉钉webhook
if content != "" {
formt := `
{
"msgtype": "markdown",
"markdown": {
"title":"IOT每日新闻",
"text": "%s"
}
}`
body := fmt.Sprintf(formt, content)
jsonValue := []byte(body)
//发送消息到钉钉群使用webhook
//钉钉文档 https://open-doc.dingtalk.com/ ... e%3D1
resp, err := http.Post(dingdingURL, "application/json", bytes.NewBuffer(jsonValue))
if (err != nil) {
return err
}
log.Println(resp)
}
return nil
}

func newsBot函数完成


4. 设置定时任务


func main() {
//销毁redisClient
defer redisClient.Close()

//创建定时任务
//每天 8点 13点 18点 自动执行爬虫和机器人
//
dingdingNewsBot := toolbox.NewTask("dingding-news-bot", "0 0 8,13,18 * * *", newsBot)
//dingdingNewsBot := toolbox.NewTask("dingding-news-bot", "0 40 */1 * * *", newsBot)
//err := dingdingNewsBot.Run()
//检测定时任务
// if err != nil {
// log.Fatal(err)
// }
//添加定时任务
toolbox.AddTask("dingding-news-bot", dingdingNewsBot)
//启动定时任务
toolbox.StartTask()
defer toolbox.StopTask()
select {}
}


spec 格式是参照



5 最终代码



go build main.go
nohup ./main &

最终效果
dingding-webhook-bot


7 最后


能否提供 RSS/ATOM 訂閱呢?

站点反馈yulibaozi 回复了问题 • 2 人关注 • 1 个回复 • 98 次浏览 • 2018-02-09 17:03 • 来自相关话题

应该如何组织代码结构以将工具代码和业务代码分离?

有问必答EyesChan 回复了问题 • 3 人关注 • 2 个回复 • 1167 次浏览 • 2018-02-09 14:43 • 来自相关话题

[北京]滴滴招聘golang工程师-特征系统[长期]

招聘应聘Xargin 回复了问题 • 13 人关注 • 34 个回复 • 4927 次浏览 • 2018-02-09 12:12 • 来自相关话题

GoCN每日新闻(2018-02-09)

回复

每日新闻DennisMao 发起了问题 • 1 人关注 • 0 个回复 • 617 次浏览 • 2018-02-09 09:14 • 来自相关话题

golang restful 框架之 go-swagger

文章分享hatlonely 发表了文章 • 0 个评论 • 235 次浏览 • 2018-02-09 02:53 • 来自相关话题

restful 是这些年的高频词汇了,各大互联网公司也都纷纷推出了自己的 restful api,其实 restful 和 thrift,grpc 类似,就是一种协议,但是这种协议有点特殊的就是使用 http 接口,返回的对象一般是 json 格式,这样... 查看全部

restful 是这些年的高频词汇了,各大互联网公司也都纷纷推出了自己的 restful api,其实 restful 和 thrift,grpc 类似,就是一种协议,但是这种协议有点特殊的就是使用 http 接口,返回的对象一般是 json 格式,这样有个好处,就是可以供前端的 js 直接调用,使用非常方便,但 http 本身并不是一个高效的协议,后端的内部通信还是使用 grpc 或者 thrift 可以获得更高的性能


其实如果只是要用 http 返回 json 本身并不是一件很难的事情,不用任何框架,golang 本身也能很方便做到,但是当你有很多 api 的时候,这些 api 的维护和管理就会变得很复杂,你自己都无法记住这些 api 应该填什么参数,返回什么,当然你可以花很多时间去维护一份接口文档,这样不仅耗时而且很难保证文档的即时性,准确性以及一致性


swagger 有一整套规范来定义一个接口文件,类似于 thrift 和 proto 文件,定义了服务的请求内容和返回内容,同样也有工具可以生成各种不同语言的框架代码,在 golang 里面我们使用 go-swagger 这个工具,这个工具还提供了额外的功能,可以可视化显示这个接口,方便阅读


下面通过一个例子来简单介绍一下这个框架的使用,还是之前的点赞评论系统:https://github.com/hatlonely/microservices


go-swagger 使用方法


api 定义文件


首先需要写一个 api 定义文件,这里我只展示其中一个接口 countlike,请求中带有某篇文章,返回点赞的次数


paths:
/countlike:
get:
tags:
- like
summary: 有多少赞
description: ''
operationId: countLike
consumes:
- application/json
produces:
- application/json
parameters:
- name: title
in: query
description: 文章标题
required: true
type: string
responses:
'200':
description: 成功
schema:
$ref: '#/definitions/CountLikeModel'
'500':
description: 内部错误
schema:
$ref: '#/definitions/ErrorModel'
definitions:
CountLikeModel:
type: object
properties:
count:
type: integer
title:
type: string
example: golang json 性能分析
ErrorModel:
type: object
properties:
message:
type: string
example: error message
code:
type: integer
example: 400

这个是 yaml 语法,有点像去掉了括号的 json


这里完整地定义了请求方法、请求参数、正常返回接口、异常返回结果,有了这个文件只需要执行下面命令就能生成框架代码了


swagger generate server -f api/comment_like/comment_like.yaml

还可以下面这个命令可视化查看这个接口文件


swagger serve api/comment_like/comment_like.yaml

这个命令依赖 swagger 工具,可以通过下面命令获取


Mac


brew tap go-swagger/go-swagger
brew install go-swagger

Linux


go get -u github.com/go-swagger/go-swagger/cmd/swagger
export PATH=$GOPATH/bin:$PATH

执行完了之后,你发现多了几个文件夹,其中 cmd 目录里面包含 main 函数,是整个程序的入口,restapi 文件夹下面包含协议相关代码,其中 configure_xxx.go 是需要特别关注的,你需要在这个文件里面实现你具体的业务逻辑


现在你就其实已经可以运行程序了,go run cmd/comment-like-server/main.go,在浏览器里面访问一下你的 api,会返回一个错误信息,告诉你 api 还没有实现,下面就来实现一下吧


业务逻辑实现


api.LikeCountLikeHandler = like.CountLikeHandlerFunc(func(params like.CountLikeParams) middleware.Responder {
count, err := comment_like.CountLike(params.Title)
if err != nil {
return like.NewCountLikeInternalServerError().WithPayload(&models.ErrorModel{
Code: http.StatusInternalServerError,
Message: err.Error(),
})
}
return like.NewCountLikeOK().WithPayload(&models.CountLikeModel{
Count: count,
Title: params.Title,
})
})

你只需要在这些 handler 里面实现自己的业务逻辑即可,这里对协议的封装非常好,除了业务逻辑以及打包返回,没有多余的逻辑


再次运行,现在返回已经正常了


统一处理


如果你对请求有一些操作需要统一处理,比如输出统一的日志之类的,可以重写这个函数,也在 configure_xxx.go 这个文件中


func setupGlobalMiddleware(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
handler.ServeHTTP(w, r)
})
}

这里我统一设置了一下头部,解决跨域访问问题


参考链接




转载请注明出处
本文链接:http://hatlonely.github.io/2018/02/08/golang-restful-%E6%A1%86%E6%9E%B6%E4%B9%8B-go-swagger/


GoCN每日新闻(2018-02-08)

每日新闻ykit 回复了问题 • 3 人关注 • 1 个回复 • 619 次浏览 • 2018-02-08 21:59 • 来自相关话题

slice 初始化问题

有问必答simple 回复了问题 • 2 人关注 • 2 个回复 • 199 次浏览 • 2018-02-08 18:58 • 来自相关话题

golang orm 框架之 gorm

文章分享hatlonely 发表了文章 • 3 个评论 • 258 次浏览 • 2018-02-08 16:34 • 来自相关话题

最近在想给这个小站增加点赞和评论功能,第三方系统又有各种限制,就想自己弄个后端,实现类似的功能,对于个人来说,数据量不是很大,单机的 mysql 足够存下所有数据,mysql 作为底层存储是个不错的选择

之前在公司是直接用的 g... 查看全部

最近在想给这个小站增加点赞和评论功能,第三方系统又有各种限制,就想自己弄个后端,实现类似的功能,对于个人来说,数据量不是很大,单机的 mysql 足够存下所有数据,mysql 作为底层存储是个不错的选择


之前在公司是直接用的 github.com/go-sql-driver/mysql 访问数据库都是直接用写 sql,取出结果然后自己拼成对象,使用上面不是很方便,可读性也不好。想起之前研究 php laravel 框架的时候,直接把数据库层屏蔽了,用户看到的只有对象,使用非常方便,java 里面这种操作方式基本上已经成了标准做法,就去 github 上找了一下 golang 里面有没有类似的东西,果然已经有非常成熟的框架了,github.com/jinzhu/gorm 已经有 7k+ 的 star 了


ORM(Object Relation Mapping),对象关系映射,实际上就是对数据库的操作进行封装,对上层开发人员屏蔽数据操作的细节,开发人员看到的就是一个个对象,大大简化了开发工作,提高了生产效率


好了,下面我以这个点赞评论系统为例,介绍一下 gorm 的简单用法,以下使用的完整代码:https://github.com/hatlonely/microservices/blob/master/internal/comment_like/comment_like.go


gorm 用法介绍


库安装


go get -u github.com/jinzhu/gorm

数据库连接


import (
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"


var db *gorm.DB

func init() {
var err error
db, err = gorm.Open("mysql", "<user>:<password>/<database>?charset=utf8&parseTime=True&loc=Local")
if err != nil {
panic(err)
}
}

连接比较简单,直接调用 gorm.Open 传入数据库地址即可


github.com/jinzhu/gorm/dialects/mysql 是 golang 的 mysql 驱动,实际上就是 github.com/go-sql-driver/mysql 作者这里为了好记,重新弄了个名字


这里我用的 mysql,实际上支持基本上所有主流的关系数据库,连接方式上略有不同


db.DB().SetMaxIdleConns(10)
db.DB().SetMaxOpenConns(100)

还可以使用 db.DB() 对象设置连接池信息


表定义


先来定义一个点赞表,这里面一条记录表示某个用户在某个时刻对某篇文章点了一个赞,用 ip + ua 来标识用户,title 标识文章标题


type Like struct {
ID int `gorm:"primary_key"`
Ip string `gorm:"type:varchar(20);not null;index:ip_idx"`
Ua string `gorm:"type:varchar(256);not null;"`
Title string `gorm:"type:varchar(128);not null;index:title_idx"`
Hash uint64 `gorm:"unique_index:hash_idx;"`
CreatedAt time.Time
}

gorm 用 tag 的方式来标识 mysql 里面的约束


创建索引只需要直接指定列即可,这里创建了两个索引,ip_idxtitle_idx;如果需要多列组合索引,直接让索引的名字相同即可;如果需要创建唯一索引,指定为 unique_index 即可


支持时间类型,直接使用 time.Time 即可


创建表


if !db.HasTable(&Like{}) {
if err := db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8").CreateTable(&Like{}).Error; err != nil {
panic(err)
}
}

直接通过 db.CreateTable 就可以创建表了,非常方便,还可以通过 db.Set 设置一些额外的表属性


插入


like := &Like{
Ip: ip,
Ua: ua,
Title: title,
Hash: murmur3.Sum64([]byte(strings.Join([]string{ip, ua, title}, "-"))) >> 1,
CreatedAt: time.Now(),
}

if err := db.Create(like).Error; err != nil {
return err
}

先构造已给对象,直接调用 db.Create() 就可以插入一条记录了


删除


if err := db.Where(&Like{Hash: hash}).Delete(Like{}).Error; err != nil {
return err
}

先用 db.Where() 构造查询条件,再调用 db.Delete() 就可以删除


查询


var count int
err := db.Model(&Like{}).Where(&Like{Ip: ip, Ua: ua, Title: title}).Count(&count).Error
if err != nil {
return false, err
}

先用 db.Model() 选择一个表,再用 db.Where() 构造查询条件,后面可以使用 db.Count() 计算数量,如果要获取对象,可以使用 db.Find(&Likes) 或者只需要查一条记录 db.First(&Like)


修改


db.Model(&user).Update("name", "hello")
db.Model(&user).Updates(User{Name: "hello", Age: 18})
db.Model(&user).Updates(User{Name: "", Age: 0, Actived: false}) // nothing update

我这个系统里面没有更新需求,这几个例子来自于官网,第一个是更新单条记录;第二个是更新整条记录,注意只有非空字段才会更新;第三个例子是不会更新的,在系统设计的时候要尽量避免这些空值有特殊的含义,如果一定要更新,可以使用第一种方式,设置单个值


错误处理


其实你已经看到了,这里基本上所有的函数都是链式的,全部都返回 db 对象,任何时候调用 db.Error 就能获取到错误信息,非常方便


事务


func CreateAnimals(db *gorm.DB) err {
tx := db.Begin()
if err := tx.Create(&Animal{Name: "Giraffe"}).Error; err != nil {
tx.Rollback()
return err
}
if err := tx.Create(&Animal{Name: "Lion"}).Error; err != nil {
tx.Rollback()
return err
}
tx.Commit()
return nil
}

事务的处理也很简单,用 db.Begin() 声明开启事务,结束的时候调用 tx.Commit(),异常的时候调用 tx.Rollback()


其他


还可以使用如下方式设置日志输出级别以及改变日志输出地方


db.LogMode(true)
db.SetLogger(gorm.Logger{revel.TRACE})
db.SetLogger(log.New(os.Stdout, "\r\n", 0))

也支持普通的 sql,但是建议尽量不要使用


参考链接




转载请注明出处
本文链接:http://hatlonely.github.io/2018/02/08/golang-orm-%E6%A1%86%E6%9E%B6%E4%B9%8B-gorm/


【北京】【云联万维】-招聘后端研发工程师/技术专家

招聘应聘yunion 发表了文章 • 0 个评论 • 157 次浏览 • 2018-02-08 16:12 • 来自相关话题

北京云联万维技术有限公司(www.yunionyun.com)招聘

 云联万维Yunion是云计算软件和服务提供商,专注于混合云和多云管理软件开发,自主研发的YunionCMP产品能够提供跨各种私有云和公有云的资源管理、监控... 			查看全部
					

北京云联万维技术有限公司(www.yunionyun.com)招聘


 云联万维Yunion是云计算软件和服务提供商,专注于混合云和多云管理软件开发,自主研发的YunionCMP产品能够提供跨各种私有云和公有云的资源管理、监控、应用部署、计费等服务。核心团队来自美团网早期员工,成立之初即获得徐小平老师的真格基金的天使轮投资及多个大客户订单。加入我们,团队靠谱/学习成长/有激情/有梦想/有期权/各种福利一应俱全。
欢迎有梦想,有能力的你加入Yunion大家庭,一起实现梦想!
简历投递:liwenzhen@yunionyun.com,成功推荐者,即获价值1000元的京东卡一张!

【高级前端工程师(Vue.js)】


待遇: 月薪30k-40k(有期权)
工作年限:3年以上
招聘人数:2人


岗位职责:



  1. 负责多云管理平台的HTML5 web app产品开发

  2. 根据产品原型图,通过css/javascript/html和后端API配合实现web交互页面

  3. 改进和完善前端架构,持续提高开发效率和质量


任职要求:



  1. 熟练掌握html, css和javascript

  2. 掌握http/https/http2/html5等技术原理

  3. 精通vue.js技术栈

  4. 有3年以上相关项目开发经验

  5. 勤奋好学,有自驱力


【后端研发工程师/技术专家】


待遇: 月薪30k-50k(有期权)
工作年限:3年以上
招聘人数:4人


岗位职责:



  1. 负责多云管理平台的后端开发,为前端提供API服务

  2. 改进和完善后端开发架构和工具链,持续提高开发效率和质量

  3. 开发对接OpenStack/VMWare vCenter/阿里云/AWS/Azure等云平台


任职要求:



  1. 计算机相关专业本科及以上,有两年相关产品研发经验;

  2. 极强的学习能力和动力,对未知技术和领域能快速掌握并实践;

  3. 熟练掌握操作系统原理,Internet网络原理;

  4. 熟练掌握至少一门编程语言,具备根据协议文档开发原型的能力;

  5. 熟练掌握RESTful API规范,并有丰富开发经验;

  6. 熟练掌握多线程编程,IPC编程;

  7. 具备任一以下知识背景者优先考虑:
    a) 对KVM/VMWare ESXi/cgroup/namespace等有深入研究和使用经验;
    b) 有云计算管理平台系统的开发经验,熟悉相关代码,并有部署、使用经验;
    c) 有OpenStack/VMWare vCenter等云平台二次开发经验;
    d) 有阿里云/AWS/Azure等公有云API开发经验;
    e) 有Linux内核内存、IO、网络、cgroup等系统优化和改进经验;

  8. 在开源社群活跃并有积极贡献者优先。


【多云管理平台(CMP)产品专家/总监】


待遇:月薪30k-40k(有期权)
工作年限:5年以上
招聘人数:1人


岗位职责:


1.负责产品团队的搭建



  1. 负责多云管理平台产品的需求分析,功能设计,产品形态定义

  2. 与技术团队紧密配合,高效推进产品的开发迭代

  3. 对产品的功能验证和验收,发现产品存在的缺陷和问题,不断跟踪和验证问题的改善

  4. 产品文档、用户手册,安装部署流程等产品交付流程和交付物的设计

  5. 不断探索产品的创新点


岗位要求:


1.5年以上B端产品经理从业经验,熟悉云计算,devops,运维等技术,且有浓厚兴趣



  1. 有热情、善于沟通,责任心强,有团队合作精神和一定的抗压能力

goroutine传值和用外部变量有区别吗?

有问必答simple 回复了问题 • 7 人关注 • 5 个回复 • 226 次浏览 • 2018-02-08 15:45 • 来自相关话题

如何优雅的关闭 nsq?

有问必答wangxingge 回复了问题 • 4 人关注 • 4 个回复 • 353 次浏览 • 2018-02-08 15:35 • 来自相关话题