golang基础:GoLang依赖管理go modules与web基础 | 青训营笔记

165 阅读4分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的第3篇笔记;

defer延迟调用

defer后面跟一个函数,意为当前函数执行完了再执行defer后面跟的这个函数,例如:

package main
import (
	"fmt"
)
func say(){
	fmt.Print("1\t")
}
func main(){
	defer say()
	fmt.Print("2\t")
	fmt.Print("3\t")
}

输出结果是:2 3 1

但是值得注意的是defer执行演示调用了函数,该函数里面传的参数并不受到defer后面操作的影响,例如:

func say(name string){
	fmt.Print(name)
}
func main(){
	name:="mike"
	defer say(name)
	name="张三"
}

输出结果是mike

还有就是defer的反序调用,如果函数里面有多个defer,那么最终执行顺序是先执行后面的defer函数,再执行前面的defer函数,如下:

func say(name string){
	fmt.Print(name)
}
func main(){
	defer say("name1\t")
	defer say("name2\t")
	defer say("name3\t")
}

输出结果是name3 name2 name1

go mod

go modules是最新版的依赖管理方案,在v1.11版本正式推出,有一个参数GO111MODULE表示go mod的开关:

  1. GO111MODULE=off关闭go mod,编译时从GOPATHvender查找依赖;
  2. GO111MODULE=on开启go mod,编译时只根据go.mod下载依赖;
  3. GO111MODULE=auth,当项目在$GOPATH$/src外面并且根目录有go.mod文件时自动开启模块支持。

示例: 首先在$GOPATH$外键一个项目,并在go文件中引入依赖:

import (log "github.com/sirupsen/logrus")

之后在此go文件处执行命令go mod init 文件名.go进行初始化,初始化之后在此文件旁边会生成一个go.mod文件;

image.png

之后执行命令go mod download:手动触发下载依赖包到本地cache(默认为$GOPATH/pkg/mod目录下)(go mod download下载根据的是go.mod里面的require 后面的内容进行下载,用于克隆别人的项目然后下载相应的依赖,因为一个新的项目go mod init初始化后生成的go.mod文件中没有require ,一个新项目需要先go get 相应依赖来进行下载)

go mod graph: 打印项目的模块依赖结构;

go mod tidy:添加需要的依赖,删除不需要的依赖;

并发编程

并发指的是多线程程序在一个核的cpu上运行,并行指的是并发指的是多线程程序在多个核的cpu上运行;

image.png

  1. 线程:可以理解为用户态的轻量级线程,栈是MB级别;
  2. 协程:是内核态的,一个线程可以跑多个协程,栈是kb级别;

golang调用子协程示例:

package main
import (
	"fmt"
	"time"
)
func say(age int){
	fmt.Println(age)
}
func main(){
	for i:=0;i<5;i++{
		go say(i)//go后面加一个函数表示使用一个子协程执行这个函数
	}
	time.Sleep(time.Second)	//Sleep阻塞当前go线程一段时间,以免还未打印出结果程序已结束
}

输出结果(并未按照顺序执行):

4

2

3

1

0

并发安全

这样的话使用go 函数就会引起一系列的并发安全问题,如下:

func say(num *int)(int){
	for i:=0;i<100;i++{
		*num++;
	}
	return *num
}
func main(){
	a:=0
	for i:=0;i<10;i++{
		go say(&a)
	}
	time.Sleep(time.Second)	
	defer fmt.Print(a)
}

多次执行,发现结果并不一定都是我们期待的1000:

image.png

我们可以通过加锁的方式解决这个问题:

var (
	lock sync.Mutex//创建锁对象
)
func say(num *int)(int){
	for i:=0;i<100;i++{
		lock.Lock()//加锁
		*num++;
		lock.Unlock()//释放锁
	}
	return *num
}
func main(){
	a:=0 
	for i:=0;i<10;i++{
		go say(&a)		
	}
	time.Sleep(time.Second)	
	defer fmt.Print(a)
}

go web

分层结构

image.png

第一个Gin服务

Gin是一个高性能开源的go web框架,我们首先导入Gin依赖:

  • go get github.com/gin-gonic/gin v1.3.0 然后写出的第一个运行程序:
package main
import(
	"github.com/gin-gonic/gin"
)
func main(){
	r :=gin.Default()// 创建一个默认的路由引擎
        //GET:请求方式;/ping:请求的路径
        //当客户端以GET方法请求/ping路径时,会执行后面的匿名函数
	r.GET("/ping",func(c *gin.Context){
                // c.JSON:返回JSON格式的数据
		c.JSON(200,gin.H{
			"message":"pong",
		})
	})
	r.Run()// 启动HTTP服务,默认在localhost:8080启动服务
}

之后执行命令go mod tidy将需要的依赖进行下载,不需要的依赖进行删除,浏览器访问http://localhost:8080/ping如下:

image.png

类似于Rest风格携带参数

package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

func main(){
	router:=gin.Default()
	//1.此处只能接受带name参数的/user访问,也就是说必须携带参数name
        //2.如果想要既可以响应“/user”也可以相应“/user/xxx”的话可以将“:name”改为“*name”,与上面不同的是:
            //此时如果是“/user/xxx”请求name参数就是"/xxx"而不是“xxx”
            //此时如果是“/user”请求name参数就是“/”
	router.GET("user/:name",func (c *gin.Context)  {//这里参数是name,类似于Rest风格
		//gin有自带的Context即gin.Context,其中包含了
		//web端发送来的httpRequest,url等众多参数
		var name string
		name=c.Param("name")//取出参数name
		c.String(http.StatusOK, "Hello %s", name)
                //返回一个string字符串,其中http.StatusOK相当于响应码200,这里也可以写200
		// c.JSON(200,gin.H{
		// 	"message":name,
		// })
	})	
	router.Run(":80")	
}

访问如下:

image.png