写写代码,喝喝茶,搞搞 Go,一起吧,这是我的项目地址:https://github.com/eddycjy/blog
所有文章和最新进度,请关注:
如果有任何疑问或错误,欢迎在 issues 进行提问或给予修正意见
如果喜欢或对你有所帮助,欢迎 Star,对作者是一种鼓励和推进 😀
所有文章采用知识共享署名-非商业性使用-相同方式共享 3.0 **大陆许可协议进行许可
煎鱼的博客,有点忙,传送门:https://eddycjy.com
写写代码,喝喝茶,搞搞 Go,一起吧,这是我的项目地址:https://github.com/eddycjy/blog
所有文章和最新进度,请关注:
如果有任何疑问或错误,欢迎在 issues 进行提问或给予修正意见
如果喜欢或对你有所帮助,欢迎 Star,对作者是一种鼓励和推进 😀
所有文章采用知识共享署名-非商业性使用-相同方式共享 3.0 **大陆许可协议进行许可
增加和修改的数据都是通过params来传 这很不利于安全,而且URL长度每个游览器都有所限制。
标签里的标题、状态、创建者都应该放在POST或PUT的body下(其余API同理)
在models下你已经定义了tag的结构体,所以可以直接引用结构体里面的数据类型
下面是我修改过后的AddTags方法:
func AddTags(c *gin.Context) {
tag := new(models.Tag)
err := c.Bind(tag)
if err != nil {
c.JSON(500,gin.H{
"state": "初始化失败",
})
return
}
name := tag.Name
state := tag.State
createdBy := tag.CreatedBy
valid := validation.Validation{}
valid.Required(name, "name").Message("名称不能为空")
...........
...........
}
// 读取回复
command := make([]byte, 1024)
n, err := redisConn.Read(command)
if err != nil {
log.Fatalf("Conn Read err: %v", err)
}
那么这里是直接开辟了1024个字节,但是如果redis返回的结构超出了1024个字节,又该怎么处理呢。
第四个成员 d
类型为 int64
大小/对齐值为 8 字节
根据规则 1,其偏移量必须为 8 的整数倍。确定偏移量为 16,因此 9-16 位为 Padding。而当前数值从第 17 位开始写入,到第 24 位。如下:axxx|bbbb|cxxx|xxxx|dddd|dddd
为啥不是:axxx|bbbb|cxxx|dddd|dddd
在Gin实践 连载四 搭建Blog API's(三)中我发现
for _, err := range valid.Errors {
logging.Info(err.Key, err.Message)
}
使用的logging在编辑器中是没有相关引入包的而且引起的log包好像没用到
https://book.eddycjy.com/golang/gin/golang-docker.html
文中提到编写Dockerfile:
WORKDIR $GOPATH/src/github.com/EDDYCJY/go-gin-example
COPY . $GOPATH/src/github.com/EDDYCJY/go-gin-example
但是紧接着解释了该代码:
golang:latest 镜像为基础镜像,将工作目录设置为 $GOPATH/src/go-gin-example,
并将当前上下文目录的内容复制到 $GOPATH/src/go-gin-example 中
这里的解释和上面的代码路径不一样,请问应该如何理解呢?
如果使用Shutdown函数来优雅重启,怎么使得不丢失旧的访问请求呢?
$ go run main.go
models\models.go:27:14: cannot refer to unexported name setting.config
models\models.go:27:14: undefined: setting.config
models.go
`
package models
import (
"fmt"
"log"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"
"gin-blog/pkg/setting"
)
var db *gorm.DB
type Model struct {
ID int gorm:"primary_key" json:"id"
CreatedOn int json:"created_on"
ModifiedOn int json:"modified_on"
}
func init() {
var (
err error
dbType, dbName, user, password, host, tablePrefix string
)
ser, err := setting.config.GetSection("database")
if err != nil {
log.Fatalf("Fail get selection %v", err)
}
dbType = ser.Key("TYPE").String()
dbName = ser.Key("NAME").String()
user = ser.Key("USER").String()
password = ser.Key("PASSWORD").String()
host = ser.Key("HOST").String()
tablePrefix = ser.Key("TABLE_PREFIX").String()
db, err = gorm.Open(dbType, fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8&parseTime=True&loc=Local", user, password, host, dbName))
if err != nil {
log.Println(err)
}
gorm.DefaultTableNameHandler = func(db *gorm.DB, defaultTableName string) string {
return tablePrefix + defaultTableName
}
db.SingularTable(true)
db.DB().SetMaxIdleConns(10)
db.DB().SetMaxOpenConns(100)
}
func CloseDB() {
defer db.Close()
}
setting.go
package setting
import (
"time"
"log"
"github.com/go-ini/ini"
)
var (
config *ini.File
RunModel string
Httpport int
ReadTimeOut time.Duration
WriteTimeOut time.Duration
Pagesize int
Jwtsecret string
)
func init() {
var err error
config, err = ini.Load("conf/app.ini")
if err != nil {
log.Fatalf("Fail to read file: %v", err)
}
LoadBase()
LoadServer()
LoadApp()
}
func LoadBase() {
RunModel = config.Section("").Key("RUN_MODE").MustString("debug")
}
func LoadServer() {
ser, err := config.GetSection("server")
if err != nil {
log.Fatalf("Fail to get selection %v",err)
}
RunModel = config.Section("").Key("RUN_MODE").MustString("debug")
Httpport = ser.Key("HTTP_PORT").MustInt(8000)
ReadTimeOut = time.Duration(ser.Key("READ_TIMEOUT").MustInt(60)) * time.Second
WriteTimeOut = time.Duration(ser.Key("WRITE_TIMEOUT").MustInt(60)) * time.Second
}
func LoadApp() {
App, err := config.GetSection("app")
if err != nil {
log.Fatalf("Fail to get selection %v",err)
}
Pagesize = App.Key("PAGE_SIZE").MustInt(10)
Jwtsecret = App.Key("JWT_SECRET").MustString("!@)#)!@U#@!@!)")
}
`
thank you
在学习的Blog API's 的时候发现一个问题, 想咨询一下
问题描述:
根据第三课gin的前三章所述内容, 使用postman请求Get接口, IDE的输出控制台显示200, 但页面无法响应,
BUG出现频率:
每次请求都会出现, 偶尔刷新几次后能正常刷出一次结果, 再次刷新就又无法响应
尝试解决:
尝试更换接口: 无效
将router更换为Default后, 直接router.Run()解决问题
问题可能存在:
s := &http.Server{
Addr: fmt.Sprintf(":%d", settings.HTTPPort),
Handler: router,
ReadTimeout: settings.ReadTimeOut,
WriteTimeout: settings.WriteTimeOut,
MaxHeaderBytes: 1<<20,
}
_ = s.ListenAndServe()
这段代码, 但是我不知道出现在哪里,
https://github.com/EDDYCJY/blog/blob/master/golang/pkg/2019-05-26-Go-defer.md
···go
package main
import(
"fmt"
)
func main(){
fmt.Println(exchangeBack(0,1))
}
func exchangeBack(a,b int) (int,int) {
defer func(){
a,b = b,a
}()
return b, a
}
···
输出是 1,0
并非期望的 0,1
在response.go中第15行代码 "code": httpCode, 此处应该"code": errCode更合理吧!
另外还有个疑问:
为什么不把service中的方法直接封装到model中为结构方法,这样调用在v1/api中直接调用model的方法,这样不是更好吗? 我看到你在service里面从新定义了结构,和对应model类似,作用仅仅只是用来接收v1/api传递下来的参数, 那为什么不直接用model呢,这样感觉过于繁琐。
https://book.eddycjy.com/golang/gin/api-03.html
获取文章列表:GET("/articles")
获取指定文章:POST("/articles/:id")
新建文章:POST("/articles")
获取指定文章:POST("/articles/:id")
--> 获取指定文章:GET("/articles/:id")
gitbook里的trace部分图片链接挂了,可能需要更新一下
p = mallocgc(capmem, et, true)
if !writeBarrier.enabled {
memmove(p, old.array, lenmem)
} else {
for i := uintptr(0); i < lenmem; i += et.size {
typedmemmove(et, add(p, i), add(old.array, i))
}
}
在有写屏障时是遍历拷贝内存,没有时整体拷贝。整体拷贝当然效率更高。这里文中说反了
test:存放用来测试喝验证Go本身的所有相关文件
应为
test:存放用来测试和
验证Go本身的所有相关文件
可以写些envoy的实践
未更新地址:https://github.com/EDDYCJY/blog/blob/master/gin/jwt.md
文章显示的代码如下
func JWT() gin.HandlerFunc {
......
if token == "" {
code = e.INVALID_PARAMS
} else {
claims, err := util.ParseToken(token)
if err != nil {
code = e.ERROR_AUTH_CHECK_TOKEN_FAIL
} else if time.Now().Unix() > claims.ExpiresAt {
code = e.ERROR_AUTH_CHECK_TOKEN_TIMEOUT
}
}
......
}
go-gin-example已更新的代码为
func JWT() gin.HandlerFunc {
return func(c *gin.Context) {
.......
if token == "" {
code = e.INVALID_PARAMS
} else {
_, err := util.ParseToken(token)
if err != nil {
switch err.(*jwt.ValidationError).Errors {
case jwt.ValidationErrorExpired:
code = e.ERROR_AUTH_CHECK_TOKEN_TIMEOUT
default:
code = e.ERROR_AUTH_CHECK_TOKEN_FAIL
}
}
}
......
}
tcpAddr, err := net.ResolveTCPAddr("tcp4", "192.168.66.181:6379") //获取一个TCP地址信息,TCPAddr
if err != nil {
fmt.Print(err)
os.Exit(1)
}
conn, err := net.DialTCP("tcp", nil, tcpAddr) //创建一个TCP连接:TCPConn
defer conn.Close()
if err != nil {
fmt.Print(err)
os.Exit(1)
}
_, err = conn.Write([]byte("*2\r\n$4\r\nKEYS\r\n$7\r\n*score*\r\n")) //发送协议
if err != nil {
fmt.Print(err)
os.Exit(1)
}
for {
line := make([]byte, 5) // \n算2个
readed, err := conn.Read(line)
if err != nil || readed == 0 {
fmt.Print(err)
break
}
if err == io.EOF {
break
}
fmt.Print(string(line[:readed]))
//fmt.Print("@@")
}
model.go 39行:
db, err = gorm.Open(...
如果我按照教程将变量db的类型定义为*gorm.DB
的话会报错,原因是Open返回DB类型而不是*DB
func Open(dialect string, args ...interface{}) (DB, error)
,所以我将13行改为
var db gorm.DB
正常
至少在我的环境是这种情况,不知道是否和依赖库版本不同有关
func mkDir(filePath string) {
dir, _ := os.Getwd()
err := os.MkdirAll(dir + "/" + getLogFilePath(), os.ModePerm)
if err != nil {
panic(err)
}
}
filePath没用
getLogFilePath() -----> filePath
func mkDir() {
dir, _ := os.Getwd()
err := os.MkdirAll(dir + "/" + getLogFilePath(), os.ModePerm)
if err != nil {
panic(err)
}
}
Describe the bug
In chapter 3.8, parse comment error exists:
2019/11/27 15:12:10 Generate swagger docs....
2019/11/27 15:12:10 Generate general API Info, search dir:./
2019/11/27 15:12:10 ParseComment error in file routers/api/v1/tag.go :can not find schema type: "v1.json"
Your swag version
e.g. 1.6.3
Your go version
go version go1.13.3 darwin/amd64
dial tcp 127.0.0.1:3306: connect: connection refused
不是在docker下面连接的,是在mac上连接mysql出的错
使用sql的驱动包可以连接,但是使用gorm无法连接,想问一下这是什么错误
您好!
我切换了postgres数据库,PostIco客户端远程连接正常,app.ini,models.go也确定修改正确……
但运行后,gorm.Open报 dial tcp [::1]:5432: connect: connection refused
google没找到解决方案,请问知道怎样解决吗?
谢谢!
最后一个错误示例解释有问题,参考
gorm怎么根据业务选择不同的db呢? @eddycjy
优雅的重启服务这块第二次kill pid之后报错
启动之后kill第一次的pid 自动重启成功,再次kill新的pid会报错,错误如下,这个是怎么回事,怎么会找不到了呢main: no such file or directory?
Restart: Failed to launch, error: fork/exec /var/folders/27/l15hxsp56vnbhdm3mzv7534r0000gp/T/go-build220521703/b001/exe/main: no such file or directory
作者你好,关于swagger,我在写post请求的时候,想定义入参为数组类型。
type X struct {
IDs []int64
}
大概想这么写,在@param里面类型写的body,但是得不到正确的输出,请问有什么解决方案吗
在第2篇和第3篇中的curl 127.0.0.1:8000总是不能正确运行
[root@vagrant-dev ~]# curl 127.0.0.1:8000/test
curl: (7) Failed connect to 127.0.0.1:8000; Connection refused
哪里配置不正确吗?app.ini
如下:
#debug or release
RUN_MODE = debug
[app]
PAGE_SIZE = 10
JWT_SECRET = 23347$040412
[server]
HTTP_PORT = 8000
READ_TIMEOUT = 60
WRITE_TIMEOUT = 60
[database]
TYPE = mysql
USER = root
PASSWORD = Hello1234!world
#127.0.0.1:3306
HOST = 192.168.33.3:3306
NAME = gin_blog
TABLE_PREFIX = blog_
编译能通过,和博客中的编译结果一样
我用的go v1.11
想问一下是否只是起始点是随机的呢?对于Go map {1, 2, 3}是不是不会输出1, 3, 2?
在Gin实践-连载十二-优化配置结构及实现图片上传中 if exist := CheckExist(src); exist == false {
应该改成 if exist := CheckExist(src); exist == true {
在 《3.8 为它加上Swagger》中缺失了针对 router 创建和 index.html 文件相关的说明,因此,按照教程访问时,只能得到 404 ;
煎鱼大佬,您好
看到7.1 slice那一节
https://github.com/EDDYCJY/blog/blob/master/slice/slice.md#lencap-%E4%B8%8D%E5%90%8C
在这个地方,您说 “cap 为所引用的数组元素总个数”
我认为这样表达会造成误解
我认为cap应该从当前指向array的索引开始计算
比如说
arr := [10]int{}
sli := arr[1:] // cap应该从当前指向arr的索引开始计算, 这里也就是10-1=9
printSlice(sli)
这段代码,的执行结果为:
[0 0 0 0 0 0 0 0 0], len=9, cap=9
所以 cap 并不是 “所引用的数组元素总个数”
这个测试在本地跟着教程走的时候需要修改下原来的数据库表,增加delete_on字段测试才能成功
2018-02-16-Gin实践-连载六-编写一个简单的文件日志.md
Line:246
log.Println(...)
改为log.Info(...)
应该是改成logging.Info(...)
@eddycjy
打开models目录下v1版本的tag.go,修改文件(增加2个方法):
此处没有V1目录,应去掉
https://github.com/EDDYCJY/blog/blob/master/golang/gin/2018-02-16-Gin%E5%AE%9E%E8%B7%B5-%E8%BF%9E%E8%BD%BD%E4%B8%89-%E6%90%AD%E5%BB%BABlogAPIs-02.md
logging.Info(err.Key, err.Message)这里是不是有问题,log里面没有这个。
看了你完整项目里面,是自己弄了个logging包文件。
文档更新下
@eddycjy
hello,你好,最近在看一些你写的文章,感觉写的很好很有帮助,先感谢一下你😁
我在看
1.6 来,控制一下 goroutine 的并发数量
这节的:方案一:简单 Semaphore 时,感觉你写的
func main() {
userCount := 10
for i := 0; i < userCount; i++ {
go Read(i)
}
sema.Wait()
}
func Read(i int) {
defer sema.Done()
sema.Add(1)
fmt.Printf("go func: %d, time: %d\n", i, time.Now().Unix())
time.Sleep(time.Second)
}
其中 sema.Add(1)
和 sema.Wait()
放在了两个goroutine中,这种写法会在一个场景:
Wait和Add并发执行下存在问题,源码WaitGroup
的Add
方法中76-78行和87-89行会panic
if w != 0 && delta > 0 && v == int32(delta) {
panic("sync: WaitGroup misuse: Add called concurrently with Wait")
}
所以将Add和Wait 放在不同的goroutine中调用值不可靠的,我的理解提醒一下😁
在作者 3.9 将Golang应用部署到Docker 中通过Dockerfile 构建镜像。可能会有同学遇到跟我一样的错误,
pkg/util/pagination.go:5:2: cannot find package "github.com/Unknwon/com" in any of:
/usr/local/go/src/github.com/Unknwon/com (from $GOROOT)
/go/src/github.com/Unknwon/com (from $GOPATH)
这个原因很简单,我们使用 docker 构建的项目基于网络拉取的 golang 镜像的 $GOROOT 和
$GOPATH中并没有我们实际本机中$GOPATH里面下载的第三方库。并且Dockerfile仅仅能
识别出当前文件根目录下的文件和文件夹。这个 dep 的作用主要就是将自己依赖 $GOPATH
的第三方库 copy 到自己项目的根目录新建文件夹 'vendor' 下。仅仅供自己使用,如此一来
构建镜像的时候所有依赖的完整代码都有了就可以构建通过了
需要执行下
go get -u github.com/golang/dep/cmd/dep
dep init -gopath -v
关于dep的知识作者单独提了一篇文章讲述2.1 Go依赖管理工具dep
talkgo/night#199 (comment)
大佬可以有空的时候看看吗,不知道这个是为什么,有空可以解答解答,感谢了。
这周刚入门 go ,看到了你的文章,写的不错,没有发声的地方,这里给作者点个赞。
api/article.go文件里
if models.ExistsArticleById(id) {
data = models.getArticle(id)
主要是这2句话。
IDE能够正确读取到原代码。
也已经将当前代码放在了$GOPATH/src下,因为models.ExistsArticleById
能够正确执行。
但是models.getArticle(id)
却出现了cannot refer to unexported name models.getArticle
错误。
getArticle
、getArticleTotal
、getArticales
都出现了cannot refer to unexported name的错误。但是其他/models/article.go
里的函数,比如ExistsIeoById()
的调用就完全没有问题。
完全摸不着头脑啊……
--------问题解决,解决方法补充-------
go,模块中要导出的函数,必须首字母大写。(参考:https://thakordhaval28.wordpress.com/2012/06/26/go-language-tutorial-5exported-names/)
所以getArticle
这些应修改为GetArticle
。
我的问题,写的时候思路没有切换过来。
连载中swagger的配置一切都OK后,可能有新手更我犯一样的错误
1、http://xxxx:xxxx/swagger/index.html 访问如果出现“404 page not found”。需要在routers.go中加下路由配置,这个连载文章中没有提到
2、路由配置重启后,刷新能访问,但是界面却出现 ”Failed to load spec.“ 是因为没有将swag init初始化生成的文件夹进行导包。所以加载失败 import 中加入下 _ "you_project_name/docs"
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.