
阅读本文需要一定的java开发经验以及一点c/c++的基础
_标记忽略const c_name1, c_name2 = value1, value2. 关键字: iota, 索引自增进行初始化常量package main
import "fmt"
const (
	i = 1 << iota
	j = 3 << iota
    // 等同于
    // k = 3 << iota
	k
    // l = 3 << iota
	l
)
func main() {
	fmt.Println("i=", i)
	fmt.Println("j=", j)
	fmt.Println("k=", k)
	fmt.Println("l=", l)
}4. 支持指针
5. 支持结构体
6. 切片/slice: 可以看作动态数组, 且go提供一些内置方法
7. channel: 通道, 例: ch := make(chan int), 声明一个int的通道, channel是引用类型, 只能使用make初始化. 通道可用于两个 goroutine 之间通过传递一个指定类型的值来同步运行和通讯. 通道类似一个先入先出队列. channel有发送/接收/关闭三种动作
ch = make(chan int, 2) 带缓冲区通道, 异步通道ch = make(chan int) 无缓冲区通道, 又称为同步通道, 必须要同步接受ch = make(<-chan int)v, ok := <- ch中的ok判断是否读取到了值? : 形式的条件判断var x interface{}
// 获取x的真实类型
switch i := x.(type) {
case nil:
    fmt.Printf(" x 的类型 :%T", i)
    fallthrough
case int:
    fmt.Printf("x 是 int 型")
case float64:
    fmt.Printf("x 是 float64 型")
case func(int) float64:
    fmt.Printf("x 是 func(int) 型")
case bool, string:
    fmt.Printf("x 是 bool 或 string 型")
default:
    fmt.Printf("未知型")
}3. x.(type)在if中的形式
type test struct {
}
func main() {
	var x interface{}
    // 判断x是否为指定类型
	if _, ok := x.(test); ok {
		
	}
}4. switch不再需要break, 执行了一个分支后自动退出, fallthrough该关键字会强制执行后面的 case 语句,fallthrough 不会判断下一条 case 的表达式结果是否为 true. 类型匹配中不能使用fallthrough
5. select是一种go可以处理多个通道之间的机制, 每个 case 必须是一个通信操作,要么是发送要么是接收. 当多个case可以执行时, 随机选取一个case执行, 当没有case可执行也没有default时, 发生阻塞. 即select可以同时监控多个通道的情况,只处理未阻塞的case. 对于没有case的select{}会一直等待, 可用于阻塞main函数
package main
import (
	"fmt"
	"time"
)
func main() {
    // int类型通道 channel
	chan1 := make(chan int)
	chan2 := make(chan int)
	// 开启goroutine
	go func() {
		for {
            // 向通道输入 1
			chan1 <- 1
			time.Sleep(time.Second * 3)
		}
	}()
	go func() {
		for {
			chan2 <- 2
			time.Sleep(time.Second * 3)
		}
	}()
	time.Sleep(time.Second)
	for {
		select {
        // 从通道取值, 用a接收
		case a := <-chan1:
			fmt.Printf("a = <-chan1 %d \n", a)
			time.Sleep(time.Second)
		case b := <-chan2:
			fmt.Printf("b = <-chan2 %d \n", b)
			time.Sleep(time.Second)
        // time.After返回是通道类型的值 所以可以用作case的表达式
        // time.After是go的time包提供的一个定时器的一个函数
        // 它返回一个channel,并在指定时间间隔后,向channel发送一条数据
        // time.After(time.Microsecond * 500)就是500ms后向这个channel发送一个数据.
		case <-time.After(time.Microsecond * 500):
			fmt.Println("timeout")
			time.Sleep(time.Second)
		}
	}
}6. 支持goto关键字
7. range 关键字用于 for 循环中迭代数组(array)、切片(slice)、通道(channel)或集合(map)的元素
a, b = b, a进行a/b值的交换func (b* math) ParseToken(a*int) error, 即为math结构体添加了一个方法type name interface{}, 每个类型都实现了空接口. 在函数的参数以及返回, 都可以用空接口做定义, 表示该函数接收或返回任意类型defer, 两个函数panic, recoverpackage main
import (
	"fmt"
	"runtime"
)
type panicContext struct {
	function string
}
func ProtectRun(entry func()) {
	defer fmt.Println("exit1")
	defer fmt.Println("exit2")
	// 延迟处理的函数
	defer func() {
		// 发生宕机时,获取panic传递的上下文并打印
		err := recover()
		switch err.(type) {
		// 运行时错误
		case runtime.Error:
			fmt.Println("runtime error:", err)
		// 非运行时错误
		default:
			fmt.Println("error:", err)
		}
	}()
	// 若entry发生异常, 则defer中的函数执行 再然后 输出 exit2 exit1
	entry()
}
func test() {
	defer fmt.Println("test退出前执行")
	fmt.Println("test")
}
func main() {
	ProtectRun(func() {
		fmt.Println("手动宕机前")
		// 手动触发 使用panic传递上下文
		panic(&panicContext{
			"手动触发panic",
		})
	})
	ProtectRun(func() {
		fmt.Println("赋值宕机前")
		var a *int
		// npe
		*a = 1
		fmt.Println("赋值宕机后")
	})
	// main 函数退出前执行这两个defer
	defer fmt.Println("无异常延迟运行1")
	defer fmt.Println("无异常延迟运行2")
	fmt.Println("-----------------------------")
	test()
}运行输出
手动宕机前
error: &{手动触发panic}
exit2
exit1
赋值宕机前
runtime error: runtime error: invalid memory address or nil pointer dereference
exit2
exit1
-----------------------------
test
test退出前执行
无异常延迟运行2
无异常延迟运行1go开启协程fmt.Printf("%s\n", string(48))package main
import "fmt"
// 定义一个结构体 类似class
type math struct {
    add int
}
// 为结构体添加方法 方法首字母大写 即 public方法 小写为private方法 此时的b相当于this指针
func (b *math) ParseToken(a *int) error {
    fmt.Printf("ss: %d %d\n", *a, b.add)
    *a++
    return nil
}
// 全局函数
func test() error {
	fmt.Printf("test: %d\n", 1)
	return nil
}
func main() {
    // 初始化结构体, := 类型推导
	m := math{add: 2}
	a := 1
    // 等同于
    // 1. var err = m.ParseToken(&a)
    // 2. err := m.ParseToken(&a)
	var err error = m.ParseToken(&a)
    // 错误处理
	if err != nil {
		return
	}
	err = test()
	if err != nil {
		return
	}
	fmt.Printf("ss: %d\n", a)
}package main
import (
    "fmt"
    "math"
    "sync"
    "time"
)
// isPrime 判断质数
func isPrime(n int) bool {
    i := 0
    if n <= 3 {
        return n > 1
    }
    for i = 2; float64(i) <= math.Sqrt(float64(n)); {
        if n%i == 0 {
            return false
        }
        i++
    }
    return true
}
func cal() {
    // 等待组
    var wg sync.WaitGroup
    arr := [count]int{}
    // 启动10个协程 即等待数量为10
    wg.Add(count)
    for i := 0; i < count; i++ {
        i := i
        go func() {
            for j := i; j < max; j += count {
                if isPrime(j) {
                    arr[i]++
                }
            }
            // 当前协程完成 计数减一
            wg.Done()
        }()
    }
    // 等待结果
    wg.Wait()
    // 统计结果
    sum := 0
    for i := range arr {
        sum += arr[i]
    }
    fmt.Println(sum)
}
// 常量 可以用来定义数组长度
const (
    count = 10
    max   = 200000000
)
func main() {
    now := time.Now()
    cal()
    // 统计耗时
    tc := time.Since(now)
    // 两亿数字耗时1m26.6016808s 共11078937个质数
    fmt.Printf("time cost = %v\n", tc)
}package main
import (
	"fmt"
	"math"
	"sync"
	"time"
)
func isPrime(n int) bool {
	i := 0
	if n <= 3 {
		return n > 1
	}
	for i = 2; float64(i) <= math.Sqrt(float64(n)); {
		if n%i == 0 {
			return false
		}
		i++
	}
	return true
}
func cal() {
	// 声明一个通道 缓冲区大小为1000
	ch := make(chan int, count*100)
	// 等待组 等待所有协程退出再结束
	var wg sync.WaitGroup
	wg.Add(count + 1)
	// 开启生产者协程
	go func() {
		for i := 1; i < max; i++ {
			ch <- i
		}
		close(ch)
		wg.Done()
	}()
	arr := [count]int{}
	for i := 0; i < count; i++ {
		// 避免匿名函数引用外部变量出现非预期的值
		i := i
		// 消费者协程
		go func() {
			for v := range ch {
				if isPrime(v) && v != 0 {
					arr[i]++
				}
			}
			wg.Done()
		}()
	}
	// 等待所有协程结束
	wg.Wait()
	// 统计结果
	sum := 0
	for i := range arr {
		sum += arr[i]
	}
	fmt.Println(sum)
}
// 常量 可以用来定义数组长度
const (
	count = 10
	max   = 200000000
)
func main() {
	now := time.Now()
	cal()
	// 统计耗时
	tc := time.Since(now)
	// 两亿数字耗时54.0657135s 共11078937个质数
	fmt.Printf("time cost = %v\n", tc)
}以这个项目的后端工程为例: https://github.com/flipped-aurora/gin-vue-admin
查看go.mod内的依赖
require (
// 模板引擎
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751
// 对象存储相关
github.com/aliyun/aliyun-oss-go-sdk v2.1.6+incompatible
// 对象存储相关
github.com/aws/aws-sdk-go v1.42.27
// 权限框架
github.com/casbin/casbin/v2 v2.51.0
// 使用数据库配置权限
github.com/casbin/gorm-adapter/v3 v3.7.3
// websocket
github.com/flipped-aurora/ws v1.0.2
// 监听配置文件修改 viper进行重新加载
github.com/fsnotify/fsnotify v1.4.9
// 不停机也可以重启服务
github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6
// web框架
github.com/gin-gonic/gin v1.7.0
// redis支持
github.com/go-redis/redis/v8 v8.11.4
// mysql驱动
github.com/go-sql-driver/mysql v1.6.0
// jwt
github.com/golang-jwt/jwt/v4 v4.3.0
// 用于终端显示颜色
github.com/gookit/color v1.3.1
// 对象存储相关
github.com/huaweicloud/huaweicloud-sdk-go-obs v3.21.8+incompatible
// 发邮件
github.com/jordan-wright/email v0.0.0-20200824153738-3f5bafa1cd84
// 日志文件归档
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible
// base64图像字符串的验证码
github.com/mojocn/base64Captcha v1.3.1
// 递归复制目录
github.com/otiai10/copy v1.7.0
// 更友好的错误处理
github.com/pkg/errors v0.9.1
// 对象存储相关
github.com/qiniu/api.v7/v7 v7.4.1
// 定时任务
github.com/robfig/cron/v3 v3.0.1
// 生成uuid
github.com/satori/go.uuid v1.2.0
// 提供一些机器信息 如内存 cpu核心之类
github.com/shirou/gopsutil/v3 v3.22.5
// 一些工具
github.com/songzhibin97/gkit v1.2.7
// 配置文件支持
github.com/spf13/viper v1.7.0
// 断言
github.com/stretchr/testify v1.7.1
// api文档
github.com/swaggo/gin-swagger v1.3.0
// api文档
github.com/swaggo/swag v1.7.0
// 对象存储相关
github.com/tencentyun/cos-go-sdk-v5 v0.7.19
// https支持
github.com/unrolled/secure v1.0.7
// 日志
go.uber.org/zap v1.16.0
// 加密支持
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4
// 并发支持
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4
// 文本处理支持 如unicode之类
golang.org/x/text v0.3.7
// mysql驱动
gorm.io/driver/mysql v1.3.3
// postgres驱动
gorm.io/driver/postgres v1.3.4
// orm
gorm.io/gorm v1.23.4
// websocket
nhooyr.io/websocket v1.8.6
)server/core/server.go line:28初始化总路由package system
import (
	v1 "github.com/flipped-aurora/gin-vue-admin/server/api/v1"
	"github.com/gin-gonic/gin"
)
type BaseRouter struct{}
func (s *BaseRouter) InitBaseRouter(Router *gin.RouterGroup) (R gin.IRoutes) {
	baseRouter := Router.Group("base")
	baseApi := v1.ApiGroupApp.SystemApiGroup.BaseApi
	{
		baseRouter.POST("login", baseApi.Login)
		baseRouter.POST("captcha", baseApi.Captcha)
	}
	return baseRouter
}3. 比如登录接口url是/base/login
4. server/initialize/router.go line: 52, 使用jwt和casbin对接口进行鉴权
5. 最终请求到达对应方法进行处理
根据id获取用户信息的接口, 并加上权限校验
api/v1/system/sys_user中实现方法func (b *BaseApi) GetUserById(c *gin.Context) {
	var id systemReq.GetUserInfoById
	if err := c.ShouldBind(&id); err != nil {
		global.GVA_LOG.Error("获取失败!", zap.Error(err))
		response.FailWithMessage("获取失败", c)
		return
	}
	data, err := userService.GetUserInfoById(1)
	if err != nil {
		global.GVA_LOG.Error("获取失败!", zap.Error(err))
		response.FailWithMessage("获取失败", c)
		return
	}
	response.OkWithData(data, c)
}2. service实现如下
func (userService *UserService) GetUserInfoById(id int) (data interface{}, err error) {
	db := global.GVA_DB.Model(&system.SysUser{})
	user := new(system.SysUser)
	err = db.Where("id = ?", id).First(&user).Error
	return user, err
}3. 在router/system/sys_user:InitUserRouter中, 添加一行userRouterWithoutRecord.GET("getUserInfoById", baseApi.GetUserById)
4. 数据库添加一行权限
INSERT INTO `gva`.`casbin_rule`(`ptype`, `v0`, `v1`, `v2`, `v3`, `v4`, `v5`, `v6`, `v7`) VALUES ('p', '888', '/user/getUserInfoById', 'GET', '', '', '', '', '');5. 根据路由映射, 找到/base/login对应的处理代码, 删除掉验证码校验的逻辑
6. 调用登录接口获取token
POST /base/login
{
    "username": "admin",
    "password": "123456"
}7. 使用该token调用/user/getUserInfoById?id=1就可以看到响应信息了
GET /user/getUserInfoById?id=1
headers: 
{
    "x-token": "xxxxxxxxxxxxxxxxxxx"
}原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。