golang笔记

golang笔记,第1张

gin路由组
*main.go
...
router := gin.Default()

package_name.Router(router)
...

*router.go //总路由文件
package package_name

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

func Router(router *gin.Engine){
	ch01 := router.Group("/v1") //路由组

	valid.Router(ch01)  // valid是项目中的valid模块package名称
}

*router.go //模块路由
package valid

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

func Router(ch04 *gin.RouterGroup)  {
	ch04.GET("/gorm_test",CreateData) //CreateData当前项目的函数名
	ch04.GET("/log",LogInfo)
}
gorm整合gin框架
*mysql_conf.json
{
  "host":"localhost",
  "port":"3306",
  "user_name":"root",
  "password":"root",
  "database":"seckill_product",
  "logo_mode":true
}

*load_mysql_conf //加载mysql_conf.json
...
type MysqlConf struct {
	Host string `json:"host"`
	Port string `json:"port"`
	UserName string `json:"user_name"`
	Password string `json:"password"`
	DataBase string `json:"database"`
	LogoMode bool `json:"logo_mode"`
}

func LoadMysqlConf() *MysqlConf {
	mysql_conf := MysqlConf{}

	// 打开json文件
	file,err := os.Open("config/mysql_conf.json")
	if err != nil{
		panic(err)
	}
	defer file.Close()
	byte_data,error := ioutil.ReadAll(file)
	if error != nil{
		panic(error)
	}
	err1 := json.Unmarshal(byte_data,&mysql_conf)
	if err1 != nil{
		panic(err1)
	}

	return &mysql_conf
}
*mysql_coon.go
var DB *gorm.DB
var err error

func init() {
	mysql_conf := LoadMysqlConf()
	log_mode := mysql_conf.LogoMode
	//db,err := gorm.Open("mysql","root:Qazwsx123@tcp(localhost:3306)/gorm_project?charset=utf8&parseTime=True&loc=Local")
	data_source := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8&parseTime=True&loc=Local",
		mysql_conf.UserName,
		mysql_conf.Password,
		mysql_conf.Host,
		mysql_conf.Port,
		mysql_conf.DataBase,
		)

	DB,err = gorm.Open("mysql",data_source)
	if err != nil{
		panic(err)
	}
	DB.LogMode(log_mode)

	DB.DB().SetMaxOpenConns(100) // 最大连接数
	DB.DB().SetMaxIdleConns(50) // 最大空闲数

	DB.AutoMigrate(&models.FrontUser{})
}
//在main主函数中条用init方法
使用module和不使用的区别
使用环境变量中的GO111MODULE控制是否使用mod

1.开启mod:go env -w GO111MODULE=on,会将包下载到gopath下的pkg下的mod文件夹中

2.关闭mod:go env -w GO111MODULE=off,会将包下载到gopath下的src下

3.go env GO111MODULE=auto,只有当当前目录在GOPATH/src目录之外而且当前目录包含go.mod文件或者其子目录包含go.mod文件才会启用。

项目可以不用建在src下了,任何非中文路径下都可以,建议有个统一的代码路径
plugin到这个插件所在目录
go build
// ls 查看是否有protoc-gen-go可执行文件,如果有放到/bin目录下,确保随时可以使用
proto
protoc xxx.proto --go_out=. --micro_out=.
protoc-gen-micro   *.proto 文件添加 option go_package = ".;";
打通srv服务通信及升级grpc
*handler.go 
引入包 example "srv服务文件夹名/proto/example" //打通srv包
	  "github.com/micro/go-grpc" // grpc服务包

// 添加grpc服务
service := grpc.NewService()
// 升级:更改为service.Client()
example.NewExampleService(name:"srv服务名称", service.Client())

//注册处理器
front_user.RegisterFrontUserHandler(service.Server(), new(controller.FrontUser))

//web文件主要负责路由,向服务发送数据,做成一个网关
//grpc通信
service := grpc.NewService()
frontuserservice := __.NewFrontUserService("xmyuser.micro.srv.xmy_user_srv",service.Client())
rep,err := frontuserservice.FrontUserSendMail(context.TODO(),&__.FrontUserMailRequest{Email: email})
math/rand包
//设置种子生成随机数
rand.Seed(time.Now().UnixNano())

type Rand struct {
// 内含隐藏或非导出字段
}
// Rand实现的几个方法
func (r *Rand) Int() int =》 返回一个非负的伪随机int值。
func (r *Rand) Int31() int32 =》 返回一个int32类型的非负的31位伪随机数。
func (r *Rand) Intn(n int) int =》 返回一个取值范围在[0,n)的伪随机int值,如果n<=0会panic。
go-cache
go get github.com/patrickmn/go-cache

go-cache是一款类似于memached 的key/value 缓存软件。它比较适用于单机执行的应用程序。

c := cache.New(30*time.Second, 10*time.Second) // 第一个参数为过期时间,第二个参数为清理缓存中过期key

c.Set("Title", "Spring Festival", cache.DefaultExpiration) // 设置key/value

c.Get("Title") // 通过key获取值
time
func main() {
   //获取当前时间
   t := time.Now() //2018-07-11 15:07:51.8858085 +0800 CST m=+0.004000001
   fmt.Println(t)
 
   //获取当前时间戳
   fmt.Println(t.Unix()) //1531293019
 
   //获得当前的时间
   fmt.Println(t.Uninx().Format("2006-01-02 15:04:05"))  //2018-7-15 15:23:00
 
   //时间 to 时间戳
   loc, _ := time.LoadLocation("Asia/Shanghai")        //设置时区
   tt, _ := time.ParseInLocation("2006-01-02 15:04:05", "2018-07-11 15:07:51", loc) //2006-01-02 15:04:05是转换的格式如php的"Y-m-d H:i:s"
   fmt.Println(tt.Unix())                             //1531292871
 
   //时间戳 to 时间
   tm := time.Unix(1531293019, 0)
   fmt.Println(tm.Format("2006-01-02 15:04:05")) //2018-07-11 15:10:19
 
   //获取当前年月日,时分秒
   y := t.Year()                 //年
   m := t.Month()                //月
   d := t.Day()                  //日
   h := t.Hour()                 //小时
   i := t.Minute()               //分钟
   s := t.Second()               //秒
   fmt.Println(y, m, d, h, i, s) //2018 July 11 15 24 59
}
jwt认证
*utils/jwt_token.go

// MyClaims 自定义声明结构体并内嵌jwt.StandardClaims
// jwt包自带的jwt.StandardClaims只包含了官方字段
// 我们这里需要额外记录一个username字段,所以要自定义结构体
// 如果想要保存更多信息,都可以添加到这个结构体中
type MyClaims struct {
	Username string `json:"username"`
	jwt.StandardClaims
}

// 定义JWT的过期时间
const TokenExpireDuration = time.Hour * 2
//定义Secret
var MySecret = []byte("夏天夏天悄悄过去")

// GenToken 生成JWT
func GenToken(username string) (string, error) {
	// 创建一个我们自己的声明
	c := MyClaims{
		username, // 自定义字段
		jwt.StandardClaims{
			ExpiresAt: time.Now().Add(TokenExpireDuration).Unix(), // 过期时间
			Issuer:    "my-project",                               // 签发人
		},
	}
	// 使用指定的签名方法创建签名对象
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, c)
	// 使用指定的secret签名并获得完整的编码后的字符串token
	return token.SignedString(MySecret)
}

// 解析token
// 认证token
func ParseToken(tokenString string,secret_key []byte) (*UserToken, error) {
	// 解析token
	token,err := jwt.ParseWithClaims(tokenString,&UserToken{}, func(token *jwt.Token) (key interface{},err error) {
		return secret_key,nil
	})
	if err != nil {
		return nil,err
	}
	//token:
	//&{eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MzExOTI1NzcsImlzcyI6Im1pY3JvX2dpbl92dWUiLCJ1c2VybmFtZSI6InhteSJ9.2M_F7uidevIXfHHWRlRORFtyahPOypj_Q-5UJ-nqu_I 0xc0000047c8 map[alg:HS256 typ:JWT] 0xc0000b0070 2M_F7uidevIXfHHWRlRORFtyahPOypj_Q-5UJ-nqu_I true}
	claims,ok :=token.Claims.(*UserToken) //interface 转换成 string
	if ok && token.Valid{// 校验token
		return claims,nil
	}
	return nil,errors.New("Token不可用!")
}
str 转换 int
 strconv.ParseInt 是将字符串转换为数字的函数,功能灰常之强大,看的我口水直流.

 func ParseInt(s string, base int, bitSize int) (i int64, err error)
	参数1 数字的字符串形式
	参数2 数字字符串的进制 比如二进制 八进制 十进制 十六进制
	参数3 返回结果的bit大小 也就是int8 int16 int32 int64

value:= *(*int32)(unsafe.Pointer(&value_int64)) // int64 转 int32
update 解决更新不能为空 *** 作
goods := map[string]interface{}{}
gorm表设计
// 自定义表名:
func (模型) TableName() string{
    return "新的表名"
}

// gorm标签属性值

-: 忽略,不映射这个字段 `gorm:"-"`
primary_key:主键 `gorm:"primary_key"`
AUTO_INCREMENT:自增 `gorm:"AUTO_INCREMENT"`
not null:不为空,默认为空 `gorm:"not null"`
index:索引, `gorm:"index"` 创建索引并命名: `gorm:"index:idx_name_code"` 优化查询,相当于图书的目录
unique_index:唯一索引 `gorm:"unique_index"`
unique:唯一 `gorm:"unique"`
column:指定列名 `gorm:"column:user_name"`
size:字符串长度,默认为255 `gorm:"size:64"`
type:设置sql类型 `gorm:"type:varchar(100)"` // 不推荐直接改类型
default `default:'galeone'` 默认值

多个属性值之间用分号分隔(英文的;):`gorm:"size:64;not null"`

// 一对多

属于:关系和外键在同一方,有关系的那一方属于另外一个模型
包含:关系和外键不在同一方,有关系的那一方包含另外一个有外键的模型  `gorm:"ForeignKey:PId;AssociationForeignKey:Id"`  // 关联关系
current pagesize 分页
currentpage := in.CurrentPage
pagesize := in.Pagesize

/*
	current offset limit
	1       0        2       2 * (1 - 1)
	2       2        2		 2 * (2 - 1)
	3       4        2		 2 * (3 - 1)

	offset = limit * (current - 1)
*/
offs := pagesize * (currentpage -1)
RabbitMQ
交换机
RabbitMQ包含四种不同的交换机类型:

Direct exchange:直连交换机,转发消息到routigKey指定的队列
	直连交换机是一种带路由功能的交互机,一个队列通过routing_key与一个交换机绑定,当消息被发送的时候,需要指定一个routing_key,这个消息被送达交换机的时候,就会被交换机送到指定的队列里面去。同样一个routing_key也是支持应用到多个队列中的,当一个交换机绑定多个队列时,消息就会被送到对应的队列去处理。
	适用场景:有优先级的任务,根据任务的优先级把消息发送到对应的队列,这样可以指派更多的资源去处理高优先级的队列。
Fanout exchange:扇形交换机,转发消息到所有绑定队列(速度最快)
    扇形交换机是最基本的交换机类型,它做的事情很简单--广播信息。Fanout交换机会把接收到的消息全部转发到绑定的队列上。因为广播不需要“思考”,所以Fanout交换机是四种交换机中速度最快的。
    适用场景:需要随时增加减少业务处理的队列,例如注册、下单等功能需要增加送积分功能,只需要增加一个绑定到交换机的队列去处理新业务,无需修改旧的业务逻辑,从而达到业务解耦,非常容易扩展。
Topic exchange:主题交换机,按规则转发消息(最灵活)
	直连交换机的routing_key方案非常简单,如果我们希望一条消息发送给多个队列,那么这个交换机需要绑定上非常多的routing_key,假设每个交换机上都绑定一堆的routing_key连接到各个队列上。那么消息队列的管理就会异常的困难。
	所以RabbitMQ提供了一种主题交换机,发送到主题交换机上的消息需要携带指定规则的routing_key,主题交换机会根据这个规则将数据发送到对应的(多个)队列上。
	主题交换机的routing_key需要有一定的规则,交换机和队列的binding_key需要采用*.#.*.....的格式,每个部分用.分开,其中:
	*表示一个单词
	#表示任意数量(零个或多个)单词。
	假设有一条消息的routing_key为fast.rabbit.white,那么带有这样binding_key的几个队列都会接收这条消息:
	fast..
	..white
	fast.#
	……
	适用场景:消息需要基于多重条件进行路由到达对应队列,例如:日志系统,不仅可以根据日志的级别而且能根据日志的来源进行订阅。
Headers exchange:首部交换机 (未接触)

go使用rabbitmq

1.下载第三方库:go get github.com/streadway/amqp

2.连接

// 连接rabbitmq
conn,_ := amqp.Dial("amqp://用户名:密码@IP:端口号")   // 端口号:5672
defer conn.close

// 打开通道
ch, err := conn.Channel()
defer ch.Close()

// 创建队列
// 创建队列设置持久化:durable表示是否持久化
queue,err_q := ch.QueueDeclare("mysql_queue",false,false,false,false,nil)
fmt.Println(err_q)

// 生产任务:生产者
// 生产者设置持久化,DeliveryMode
ch.Publish("",queue.Name,false,false,amqp.Publishing{
    ContentType:"text/plain",
    Body:[]byte("hello world"),
    DeliveryMode:amqp.Persistent,
})

// 消费者
msgs,err_c := ch.Consume("mysql_queue","my_consumer",false,false,false,false,nil)
fmt.Println(err_c)

for msg := range msgs{  // chan类型
    // DeliveryTag:唯一标识
    fmt.Println(msg.DeliveryTag,string(msg.Body))
}

3.消息确认机制

// 消费者消费的时候会出现两种情况,消费完成和消费失败,消费失败的要再次回到队列,重新分配
// 1.在消费的时候autoAck设置未false,表示不自动确认,当消息消费失败会再次放到队列
// 2.消费成功后手动确认,这样就会从队列中删减掉改任务,不会重复执行
deliveries,err_c := ch.Consume("my_queue","my_consumer",false,false,false,false,nil)
fmt.Println(err_c)

for delivery := range deliveries{
    delivery.Ack(true)
    // delivery.Ack(false)  // 失败重新放入队列中,注意这里需要加延迟时间,不然接收到的消息还是这个失败的,
        // 可以加重试次数,使用缓存计数的方式
    // delivery.Ack(true)  // 拒绝接收并且重新放回队列

}

4.使用交换机

// 创建两个队列
queue1,err_q1 := ch.QueueDeclare("first_queue",true,false,false,false,nil)
queue2,err_q2 := ch.QueueDeclare("second_queue",true,false,false,false,nil)

// 创建两个交换机
err1 := ch.ExchangeDeclarePassive("frist_exchange","direct",true,false,false,false,nil)
err2 := ch.ExchangeDeclarePassive("second_exchange","direct",true,false,false,false,nil)

// queue和交换机绑定
err3 := ch.QueueBind(queue.Name,"frist_routingKey","frist_exchange",false,nil)
err4 := ch.QueueBind(queue.Name,"second_routingKey","second_exchange",false,nil)
// 第一个参数是队列名称,第二个参数是routingKey,第三个参数是交换机名称

// 生产者:第一个参数是交换机名称,第二个参数routingKey
err_p := ch.Publish("frist_exchange","frist_routingKey",false,false,amqp.Publishing{
        ContentType:"text/plain",
        Body:[]byte("hello world"),
        DeliveryMode:amqp.Persistent,
})

// 消费者:使用queue name
deliveries,err_c := ch.Consume("my_queue","my_consumer",false,false,false,false,nil)

5.限流:确保消费者每次只能消费一个任务,消费完成后再分配任务,ack后再继续接收任务
//消费者流控  防止数据库爆库
//消息的消费需要配合Qos
r.channel.Qos(
  //每次队列只消费一个消息 这个消息处理不完服务器不会发送第二个消息过来
  //当前消费者一次能接受的最大消息数量
  1,
  //服务器传递的最大容量
  0,
  //如果为true 对channel可用 false则只对当前队列可用
  false,)
字符串类型相互转换
字符串和各种int类型之间的相互转换方式:

string转成int:
int, err := strconv.Atoi(string)
string转成int64:
int64, err := strconv.ParseInt(string, 10, 64)
int转成string:
string := strconv.Itoa(int)
int64转成string:
string := strconv.FormatInt(int64,10)
微服务
protoc --go_out=. --micro_out=. **.proto
go部署
npm install node-sass --unsafe-perm --save-dev //Linux安装node-sass失败解决方法
访问项目外网网址出现 Invalid Host header 情况,在项目的根目录下,应该有一个 vue.config.js 文件,如果没有直接新建一个,然后在其中加入配置
	module.exports = {
	    devServer: {
	        disableHostCheck: true
	    }
	}

/etc/supervisor/conf.d
supervisord -c supervisor.conf                            # 通过配置文件启动supervisor
supervisorctl -c supervisor.conf status                   # 察看supervisor的状态
supervisorctl -c supervisor.conf reload                   # 重新载入 配置文件
supervisorctl -c supervisor.conf start [all]|[appname]    # 启动指定/所有 supervisor管理的程序进程
supervisorctl -c supervisor.conf stop [all]|[appname]     # 关闭指定/所有 supervisor管理的程序进程

# 从新配置supervisor
unlink /var/run/supervisor.sock
unlink /tmp/supervisor.sock 

//部署api接口服务

	1.进入项目目录使用bee工具打包
	bee pack -be GOOS=linux

	2.将打包文件放在linux中
	安装:sudo apt-get install lrzsz

	rz 从本地拉文件
	将文件放到linux服务器并解压
	解压:tar -xvf   node-v6.10.0-linux-x64.tar.xz  

	搭建consoul环境
	https://www.consul.io/downloads.html //下载指定系统版本
	./consul agent -dev -client 0.0.0.0 & //加 & 后台关闭仍运行

	安装supervisor
	sudo apt-get install supervisor
	1.在/etc/supervisor/conf.d目录下新建文件:supervisord.conf
	2.配置内容如下:
	    [program:zhiliao_web]
	    # 项目文件夹
	    directory = /home/go/src/zhiliao_web
	    # 项目可执行文件位置
	    command = /home/go/src/zhiliao_web/zhiliao_web
	    autostart = true
	    startsecs = 5
	    user = root
	    redirect_stderr = true
	    # 输出日志文件的位置
	    stdout_logfile = /home/go/src/zhiliao_web/supervisor.log

	    port=127.0.0.1:9000
	    #登录web用的用户名和密码
	    username=user
	    password=admin

//vue项目部署
	//运行环境搭建
	1.下载node文件
		uname -a查看系统位数(x86_64表示64位系统, i686 i386表示32位系统)
		https://nodejs.org/en/download/ 下载对应位数的编译好的文件
		将文件放到linux服务器并解压
		解压:tar -xvf   node-v6.10.0-linux-x64.tar.xz  
		重命名(软连接要用):mv node-v6.10.0-linux-x64  nodejs
	2.建立软连接
		npm prefix -g /查看node安装目录 替换下面
		npm:ln -s /home/hallen4/go/node/node-v14.15.1/bin/npm /usr/local/bin/
		node:ln -s /home/hallen4/go/node/node-v14.15.1/bin/node /usr/local/bin/
	3.验证
		node -v
	//vue项目服务器运行
	1.打包
		npm run build 
	2.安装pm2(Process Manager 2 )是具有内置负载均衡器的Node.js应用程序的生产运行时和进程管理器。 它允许您永久保持应用程序活跃,无需停机即可重新加载它们,并促进常见的Devops任务。
		npm i pm2 -g
	3.安装serve
		npm install -g serve
	4.建立软连接
		ln -sf /usr/node/bin/pm2 /usr/local/bin/
		ln -sf /usr/node/bin/serve /usr/local/bin/
	5.在dist文件夹项目新建一个serve.sh文件,运行命令
		touch serve.sh
		然后ls查看文件是否创建成功,创建成功后用vim打开,运行命令
		vim serve.sh
		打开后在文件中写入
		serve -l 8080
	6.运行命令
		开启服务,运行命令。online代表在运行
		pm2 start serve.sh
		停掉服务,运行命令
		pm2 stop serve.sh

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/995130.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-21
下一篇 2022-05-21

发表评论

登录后才能评论

评论列表(0条)

保存