Go实战Gin+Vue+微服务打造秒杀商城第五课 gin+vue实战
gin+vue实战
后端:
- 用户管理
- 用户列表
- 登录/登出
- 商品管理
- 商品的增上改查
- 活动管理
- 商品关联
- 成功率
- redis队列,不成功的回到队列继续,成功的从队列删除
结束难点:
- 代码和部署完全隔离
- 怎么避免雪崩
- 根据后端承载能力,进行限流和过载保护
- 使用redis承载海量QPS
- mysql性能瓶颈
- 可扩展行(机器的扩展等)
- 长连接
秒杀业务逻辑:
接入层功能:
实战技术选型
一、实战名称:gin+vue3+微服务打造秒杀商城
二、技术选型
1.前端:vue3,antdv,vue-router,axios
2.后端
- gin框架
- gorm
- 微服务:集群部署
- web
- srv
gin+vue+微服务打造秒杀商城
一、流程
二、技术难点
- 代码和部署完全隔离(微服务)
- 怎么避免雪崩(微服务)
- 根据后端承载能力,进行限流和过载保护(限流熔断)
- 使用redis承载海量QPS(redis队列)
- mysql性能瓶颈(sql优化,拆库,拆表)
- 可扩展性(机器的扩展等)
- 长连接(先不管)
三、项目分类
1.前端:zhiliao_vue_gin
2.micro-web:zhiliao_web
3.用户管理服务:zhiliao_user_srv
4.商品和活动管理服务:zhiliao_product_srv
5.秒杀服务:zhiliao_seckill_srv
表设计
一、管理员表
表名:sys_admin
字段名称 | 类型 | 长度 | 说明 |
---|---|---|---|
Id | int | 11 | 主键 |
UserName | varchar | 64 | 用户名 |
Password | varchar | 64 | 密码,md5加密 |
Desc | varchar | 255 | 用户描述 |
Status | int | 2 | 用户状态 |
CreateTime | datetime | 0 | 用户创建时间 |
二、用户表(注册)
表名:sys_user
字段名称 | 类型 | 长度 | 说明 |
---|---|---|---|
Id | int | 11 | 主键 |
varchar | 64 | 邮箱地址 | |
Password | varchar | 64 | 密码,md5加密 |
Desc | varchar | 255 | 用户描述 |
Status | int | 2 | 用户状态 |
CreateTime | datetime | 0 | 用户创建时间 |
三、商品表
表名:sys_product
字段名称 | 类型 | 长度 | 说明 |
---|---|---|---|
Id | int | 11 | 主键 |
Name | varchar | 64 | 商品名称 |
Price | decimal | 11,2 | 价格,保留两位小数 |
Num | int | 11 | 商品数量 |
Unit | varchar | 32 | 商品单位 |
Pic | varchar | 255 | 商品图片 |
Desc | varchar | 255 | 商品描述 |
CreateTime | datetime | 0 | 用户创建时间 |
四、活动表
表名:sys_product_seckill
字段名称 | 类型 | 长度 | 说明 |
---|---|---|---|
Id | int | 11 | 主键 |
Name | varchar | 64 | 活动名称 |
Price | decimal | 11,2 | 活动价格,保留两位小数,小于等于商品价格 |
Num | int | 11 | 参与秒杀的数量,小于等于商品数量 |
PId | int | 11 | 商品外键 |
StartTime | datetime | 0 | 秒杀开始时间 |
EndTime | datetime | 0 | 秒杀结束时间 |
CreateTime | datetime | 0 | 活动创建时间 |
五、订单表
表名:sys_orders
字段名称 | 类型 | 长度 | 说明 |
---|---|---|---|
Id | int | 11 | 主键 |
OrderNum | varchar | 64 | 订单编号 |
Uid | int | 11 | 用户外键,关联sys_user |
SId | int | 11 | 活动的外键,关联sys_product_seckill |
PayStatus | int | 2 | 支付状态 |
CreateTime | datetime | 0 | 订单创建时间 |
数据校验
一、vue中使用rules校验数据
1.使用
1.form中绑定rules<a-form :model="form" :rules="rules">2.在要校验的item上设置prop属性,这里的prop是第三中的key(高版本的antdv用name代替了prop)<a-form-item label="邮箱地址" name="mail"> // 这里和下面的mail必须一致,不然获取不到值<a-input v-model:value="form.mail" placeholder="请输入正确的邮箱地址">注意:prop对应的不单单是rules规则里面的验证项,同时应该对应着我们form-item下的v-model的值 3.在data中定义rulesrules:{ // 这里的rules就是前面绑定的rulesemail:[{required: true,message: "必填",trigger: "blur"}]}
2.使用自定义验证器
1.定义验证器:新版的antdv返回Promiselet validateEMail = async(rule, value) => {const reg = /^([a-zA-Z0-9]+[-_.]?)+@[a-zA-Z0-9]+\.[a-z]+$/;if (value == "" || value == undefined || value == null) {// callback(new Error("请输入邮箱"));return Promise.reject((new Error("请输入邮箱")));} else {if (!reg.test(value)) {return Promise.reject((new Error("请输入正确的邮箱")));} else {return Promise.resolve();}}};如果报错:Warning: callback is deprecated. Please return a promise instead则使用下面的2.使用自定义验证器{required: true,validator: validateEMail,trigger: "blur"}正则中的符号含义:+ :匹配前面的子表达式一次或多次。要匹配 + 字符,请使用 \+。?:匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。要匹配 ? 字符,请使用 \?。
3.表单提交的时候校验
1.form中加ref属性<a-form :model="form" :rules="rules" ref="form">
2.提交按钮传参,参数和前面的ref中的值一致<a-button type="primary" @click="onSubmit('form')">提交</a-button>3.函数中校验sendMail(ruleForm) {alert(this.form.mail);this.$refs[ruleForm].validate().then(()=>{alert("校验通过");}).catch(() => {alert("校验不通过");})
常用校验正则:https://www.cnblogs.com/lieone/p/11856330.html
二、golang中验证邮箱
func VerifyEmailFormat(email string) bool {pattern := `\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*` //匹配电子邮箱reg := regexp.MustCompile(pattern)return reg.MatchString(email)}VerifyEmailFormat("12345@126.com") // true
发送邮件
一、生成随机数
import ("fmt""math/rand""strings""time"
)
func GenEmailCode(width int) string {numeric := [10]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}r := len(numeric)rand.Seed(time.Now().UnixNano())var sb strings.Builderfor i := 0; i < width; i++ {fmt.Fprintf(&sb, "%d", numeric[ rand.Intn(r) ])}return sb.String()
}
二、发送邮件
// 使用beego下utils下的NewEMail
func SendEmail(to_email, msg string) {username := "2713058923@qq.com" // 发送者的邮箱地址password := "xxx" // 授权密码host := "smtp.qq.com" // 邮件协议port := "587" // 端口号emailConfig := fmt.Sprintf(`{"username":"%s","password":"%s","host":"%s","port":%s}`, username, password,host,port)fmt.Println("emailConfig", emailConfig)emailConn := utils.NewEMail(emailConfig) // beego下的emailConn.From = strings.TrimSpace(from_email)emailConn.To = []string{strings.TrimSpace(to_email)}emailConn.Subject = "知了传课注册验证码"//注意这里我们发送给用户的是激活请求地址emailConn.Text = msgerr := emailConn.Send()fmt.Println(err)}使用:
// 生成六位随机数
session_email_code := utils.GenEmailCode(6)
// 消息内容
email_msg := fmt.Sprintf("您的注册验证码为:%s",session_email_code)
// 发送邮件
utils.EmailSend(mail, email_msg)// 缓存邮件和随机数
"github.com/patrickmn/go-cache"//初始化
c := cache.New(30*time.Second, 10*time.Second)
//使用
c.Set("Title", "Spring Festival", cache.DefaultExpiration)
jwt-token认证
一、什么是JWT?
JSON Web Token:是一种跨域认证解决方案,它规定了一种Token实现方式,多用于前后端分离等场景
二、为什么需要JWT?
1.前后端不分离的验证逻辑
- 前端提交数据
- 后端校验存session,通过后保存session,生成session_id标识
- 服务端返回响应时将上一步的session_id写入用户浏览器的Cookie
- 前端每次请求都会自动携带包含session_id的Cookie
- 服务端通过请求中的session_id就能找到之前保存的该用户那份session数据
2.使用jwt认证
服务端完成登录校验后,会生成一个令牌(就是token)再发回给用户,用户后续请求只需要带上这个Token,服务端解密之后就能获取该用户的相关信息了。
三、使用JWT
使用jwt-go完成生成JWT和解析JWT的功能
go get github.com/dgrijalva/jwt-go
1.生成jwt
定义结构体,这个就是要返回给前端的
type FrontUserToken struct { // jwt的匿名字段 jwt.StandardClaims// 要返回给前端的用户信息Username string `json:"user_name"`UserId int `json:"user_id"`}
定义JWT过期时间
// 过期时间1小时,n小时的话 * n
const TokenExpireDuration = time.Hour
- 定义加密的盐,生成token和解析的时候都需要用到,使用同一个
var TokenSecret = []byte("gin_vue_token")
- 生成token
type UserToken struct {jwt.StandardClaims// 自定义的用户信息UserName string `json:"user_name"`}// 前端用户token过期时间
var FrontUserExpireDuration = time.Hour
var FrontUserSecretKey = []byte("front_user_token")// 管理端用户token过期时间
var AdminUserExpireDuration = time.Hour * 2
var AdminUserSecretKey = []byte("admin_user_token")// 生成token
func GenToken(UserName string,expireDuration time.Duration,secret_key []byte) (string,error){user := UserToken{jwt.StandardClaims{// 现在 + 加上传的过期时间ExpiresAt:time.Now().Add(expireDuration).Unix(),Issuer:"micro_gin_vue",},UserName,}token := jwt.NewWithClaims(jwt.SigningMethodHS256,user)return token.SignedString(secret_key)}
2.解析jwt
// 认证token
func AuthToken(tokenString string,secretKey []byte) (*UserToken, error){// 解析tokentoken,err := jwt.ParseWithClaims(tokenString,&UserToken{}, func(token *jwt.Token) (key interface{}, err error) {return secretKey,nil})if err != nil {return nil,err}clasims,is_ok := token.Claims.(*UserToken)// 验证tokenif is_ok && token.Valid { // 正常的return clasims,nil}return nil,errors.New("token valid err")}
3.中间件,在所有需要验证的路由中加上该解析,如果没有token则不让访问
func JwtTokenValid(ctx *gin.Context) {auth_header := ctx.Request.Header.Get("Authorization")if auth_header == "" {ctx.JSON(http.StatusOK,gin.H{"code":401,"msg":"请携带token",})ctx.Abort()return}auths := strings.Split(auth_header," ")bearer := auths[0]token := auths[1]if len(token) == 0 || len(bearer) == 0 {ctx.JSON(http.StatusOK,gin.H{"code":401,"msg":"请携带正确格式的token",})ctx.Abort()return}user, err := utils.AuthToken(token,utils.AdminUserSecretKey)if err != nil {ctx.JSON(http.StatusOK,gin.H{"code":401,"msg":"无效的token",})ctx.Abort()return}ctx.Set("user_name",user.UserName)ctx.Next()}
key:Authorization
value:token值
前端状态管理
一、自定义状态管理
const front_token_key = "front_token"
const front_user_key = "front_user_name"const admin_token_key = "admin_token"
const admin_user_name_key = "admin_user_name"class Auth {// 构造函数浏览器刷新的情况下从localStorage加载constructor(){this.token = localStorage.getItem(front_token_key)this.username = localStorage.getItem(front_user_key)this.admin_token = localStorage.getItem(admin_token_key)this.admin_user_name = localStorage.getItem(admin_user_name_key)}// 用户端存储信息setFrontAuth(token,username){this.token = tokenthis.username = username// 用户端的管理端需要区分开localStorage.setItem(front_token_key,token)localStorage.setItem(front_user_key,username)}// 管理端存储信息setAdminAuth(admin_token,admin_user_name){this.admin_token = admin_tokenthis.admin_user_name = admin_user_namelocalStorage.setItem(admin_token_key,admin_token)localStorage.setItem(admin_user_name_key,admin_user_name)}// 用户端清空缓存信息delFrontAuth(){this.token = null,this.username = null}// 管理端清空缓存信息delAdminAuth(){this.admin_token = nullthis.admin_user_name = null}// 也可以定义导航守卫is_authed(){if(this.token && this.username){return true}else{return false}}
}// 导出,单例
const auth = new Auth()
export default auth在main.js中定义全局变量router.js中定义导航守卫// 定义导航守卫router.beforeEach((to, from, next) => {if (to.path === '/login' || to.path === '/register') {next('/');} else {let token = localStorage.getItem('token');if (token === 'null' || token === '') {next('/login');} else {next();}}});
二、使用vuex第三方库,适用于中大型项目
vue3.0不支持vuex,参考:https://www.codingsky.com/doc/2020/7/31/1024.html
1.vue2中使用vuex示例代码
npm install vuex --save
vuex/vuex.js中:import Vuex from 'vuex';const store = new Vuex.Store({state: {// 没有获取到设置为nulltoken:localStorage.getItem('token')?localStorage.getItem('token') : nulluser:localStorage.getItem('user')?localStorage.getItem('user') : null},mutations: {setAuth(state,token,user){state.token = token;localStorage.setItem('token', user.Authorization);},delAuth(state){state.token = null;state.user = null;localStorage.removeItem("token")localStorage.removeItem("user")}}export default storemain.js中use下import vuex from './vuex/vuex.js'app.use(vuex);设置全局变量:app.config.globalProperties.$vuex = vuexrouter.js中:// 定义导航守卫router.beforeEach((to, from, next) => {if (to.path === '/login') {next();} else {let token = localStorage.getItem('token');if (token === 'null' || token === '') {next('/login');} else {next();}}});
state:数据仓库,主要存储共享的数据,不是存储的过程,是存储的结果,
getters:获取数据
mutactions:存数据
actions:对数据先进行处理,再存储到仓库,也可以不用进行处理
*执行流程:*后端返回数据,使用actions先进行数据处理(也可以不处理),然后通过
mutation 把处理后的数据放入数据仓库state中,想使用数据就通过
getters从数据仓库state中取。
antdv分页
结合table使用分页
一、在table标签上加属性
<a-table :pagination="users_pagenation">
二、在data中指定users_pagenation
front_users:[],
columns,
position:top,
users_pagenation:{current:1,pageSize:5,pageSizeOptions:['10','20','30'], // 可选每页显示几条showSizeChanger:true,total:11
}
三、绑定change事件
<a-table @change="chanPage">
四、定义chanPage事件
chanPage(pagination){this.users_pagenation.current = pagination.currentthis.users_pagenation.pageSize = pagination.pageSize
},
jmeter压测工具
一、介绍
1.免费的
2.使用简单
3.能满足大多数压测要求
二、环境准备及软件安装
1.jdk环境搭建
- 环境配置:
- Java_Home:jdk安装路径
- %Java_Home%\bin;%Java_Home%\jre\bin;
- 验证:
- java -version
2.软件安装
- 下载地址:http://jmeter.apache.org/download_jmeter.cgi,注意jdk版本对应上
- 解压即可,不需要安装
3.启动:双击解压文件夹bin目录下的jmeter.bat,启动之后会有两个窗口,一个cmd窗口,一个JMeter的 GUI,不要使用GUI运行压力测试,GUI仅用于压力测试的创建和调试;执行压力测试请不要使用GUI。使用下面的命令来执行测试:
jmeter -n -t [jmx file] -l [results file] -e -o [Path to web report folder]
4.通过 【Options】->【Choose Language】变更为简体中文
5.修改字体大小
选项—>外观—>windows
- 编辑bin目录下的jmeter.properties文件,修改jsyntaxtextarea.font.size的值,并将注释取消
- 在jmeter.bat文件中添加如下代码
set JVM_ARGS=%JVM_ARGS% -Dswing.plaf.metal.controlFont=Dialog-20
set JVM_ARGS=%JVM_ARGS% -Dswing.plaf.metal.systemFont=Dialog-20
set JVM_ARGS=%JVM_ARGS% -Dswing.plaf.metal.userFont=SansSerif-20
set JVM_ARGS=%JVM_ARGS% -Dswing.plaf.metal.smallFont=SansSerif-20
三、创建测试
Jmeter-http接口测试添加步骤:
1.创建线程组
在左侧的"TestPlan"上右键 【添加】–>【Threads(Users)】–>【线程组】,设置线程数和循环次数。只设置这两个即可,比如1000的线程数,1次循环
2.配置元件
在我们刚刚创建的线程组上右键 【添加】–>【配置元件】–>【HTTP请求默认值】。只需要配置协议、地址和端口这三项即可,这样后面所有的请求都是基于现在的这个进行的,比如http://127.0.0.1:8080,后面的的请求只需要使用path即可
3.http请求
在“线程组”右键 【添加-】->【samlper:取样器】–>【HTTP 请求】设置我们需要测试的API的请求路径和数据。我这里是用的json
4.添加请求头
线程组上右键 【添加】–>【配置元件】–>【HTTP信息头管理器】
5.添加断言
线程组上右键 【添加】–>【断言】–>【响应断言】,根据响应的数据来判断请求是否正常。比如只根据状态码判断是否正常。
要测试的响应字段:响应代码
模式匹配规则:Equales
要测试的模式:200
错误提示信息:“出错啦!”
6.添加察看结果树
线程组上右键 【添加】–>【监听器】–>【察看结果树】。点击工具栏上的运行按钮就可以看到结果了
7.添加Summary Report
线程组上右键 【添加】–>【监听器】–>【Summary Report:汇总报告】。点击工具栏上的运行按钮就可以看到结果了
以上的测试计划已构建完整,点击左上角的报错按钮保存下
8.执行测试计划
cmd中执行:进入jmeter的bin目录,执行下面的命令
jmeter -n -t [jmx file] -l [results file] -e -o [Path to web report folder]
- jmx file:测试计划文件路径
- results file:测试结果文件路径
- Path to web report folder:web报告保存路径
e.g.:jmeter -n -t [testplan/RedisLock.jmx] -l [testplan/result/result.txt] -e -o [testplan/webreport]
秒杀接口压测
一、需要的添加及使用的jmeter技术
1.需要满足的条件:
- 第一步使用邮箱地址、密码登录获取到返回的token
- 第二步携带token请求秒杀接口
需要邮箱地址、密码及返回的token
2.jmeter测试需要用到的技术
- jmeter操作数据库,读取邮箱地址和密码
- jmeter关联,使用第一步返回的token作为参数执行第二步,jmeter关联就可以保存这个token信息
二、jmeter操作数据库
1.下载mysql-connector-java-5.1.7-bin.jar,地址:https://dev.mysql.com/downloads/connector/j/,
- 选择Platform Independent
- 选择ZIP文件进行下载
2.解压,把里面的jar包放到jmeter的lib目录下
3.配置连接信息
- 线程组右键添加“配置原件”–“JDBC Connection Configuration”
- 线程组右键添加“samlper:取样器” – “JDBC Request”
- 在TestPlan页面,点击浏览 ,将目录或jar添加到类路径 Add directory or jar to classpath。此处选择我们刚刚放在lib下的jar即可
- JDBC Connection Configuration页面配置连接信息
- 数据库:mysql
- DriverName–>com.mysql.jdbc.Driver
- URL–>jdbc:mysql://host:port/{dbname}?allowMultiQueries=true&serverTimezone=UTC
- 用户名、密码
4.使用
在JDBC Request 页面
三、jmeter关联
1.添加关联
- 在某个请求上右键添加”后置处理器“ – ”json提取器“
- 设置
- 响应字段:主体
- 引用名称:token
- 正则表达式:$.key1.key2
2.获取关联数据
- parameters中获取:${token}
四、压测指标
- 压测前要明确压测功能和压测指标,一般需要确定的几个问题:
- 固定接口参数进行压测还是进行接口参数随机化压测?
- 要求支持多少并发数?
- TPS(每秒钟处理事务数)目标多少?响应时间要达到多少?
jdbc request:设置结果集存储的变量:userBeanShell 后置处理器:var email = vars.getObject("user").get(0).get("email");
vars.put("email",email.toString());
测试问题记录:
- 并发查询怎么确保每个线程一个用户?使用计数器,不勾选“与每用户独立的跟踪计数器”选项
select email from front_user WHERE email not in (SELECT uemail from orders) limit ${user_offset},1
- 相隔时间很小的时候,可能一个用户会下单两次都成功
五、需要明确的问题
有错误率同开发确认,确定是否允许错误的发生或者错误率允许在多大的范围内;
5.1、Throughput吞吐量每秒请求的数大于并发数,则可以慢慢的往上面增加;
若在压测的机器性能很好的情况下,出现吞吐量小于并发数,说明并发数不能再增加了,可以慢慢的往下减,找到最佳的并发数;压测结束,登陆相应的web服务器查看CPU等性能指标,进行数据的分析;
5.2、最大的tps:不断的增加并发数,加到tps达到一定值开始出现下降,那么那个值就是最大的tps。
5.3、最大的并发数:最大的并发数和最大的tps是不同的概率,一般不断增加并发数,达到一个值后,服务器出现请求超时,则可认为该值为最大的并发数。
**5.4、**压测过程出现性能瓶颈,若压力机任务管理器查看到的cpu、网络和cpu都正常,未达到90%以上,则可以说明服务器有问题,压力机没有问题。
5.5、影响性能考虑点包括:数据库、应用程序、中间件(tomact、Nginx)、网络和操作系统等方面。
秒杀优化
一、前端优化
可以使用静态化的方式,把常用的资源加载到缓存中,不要经常访问后端,减轻一部分服务器压力
二、后端优化
1.秒杀功能独立,不受其他接口的影响,这里我们使用的微服务,秒杀是单独的服务
2.秒杀数量限制(限流):每隔一段时间发放一个有效的抢购
3.消息队列:减小数据库压力,生产者生产任务存放在队列中,消费者从队列中取任务消费,可以有效降低数据库压力
消息队列的几种模式:
简单模式:生产者生产任务放到队列中,消费者从队列中取任务消费
工作模式:生产者生产任务放到队列中,多个消费者从队列中取任务消费
订阅模式:
路由模式:
话题模式:
RPC模式:
rabbitmq
一、环境安装
1.windows安装:https://www.cnblogs.com/JustinLau/p/11738511.html
2.linux安装
安装:sudo apt-get install rabbitmq-server
查看状态:service rabbitmq-server status / systemctl status rabbitmq-server
开放端口:
- 15672:管理后台
- 5672:连接服务的端口
3.web可视化配置:
- sudo rabbitmq-plugins enable rabbitmq_management
- service rabbitmq-server restart # 重启服务
- 访问:
- 本地:localhost:15672 用户名和密码都是guest,只适用于本地访问
- 其他机器访问: ip:15672 必须得创建用户并授权
4.配置用户及授权
查看用户:sudo rabbitmqctl list_users
添加用户:sudo rabbitmqctl add_user admin(用户名) admin(密码)
授权:sudo rabbitmqctl set_user_tags admin(用户名) administrator(角色)
- administrator(超级管理员)
- monitoring(监控者):可以查看rabbitmq节点的相关信息
- policymaker(策略制定者):无法查看节点的相关信息
- management(普通管理者):无法看到节点信息,也无法对策略进行管理
- none(其他):无法登陆管理控制台,通常就是普通的生产者和消费者
5.rabbitmqctl服务的使用
查看当前用户列表:sudo rabbitmqctl list_users
添加用户:sudo rabbitmqctl add_user admin(用户名) admin(密码)
删除用户:sudo rabbitmqctl delete_user admin(用户名)
修改用户密码:sudo rabbitmqctl change_password admin(用户名) admin1(新密码)
设置vhost:
- sudo rabbitmqctl add_vhost /myvhost
- sudo rabbitmqctl set_permissions -p /myvhost myuser “." ".” “.*”
vhost说明:virtual host相当于一个单独的rabbitmq服务器,每个virtual是独立的,不可互通的,相当于mysql中的数据库,都是独立的,可以单独设置权限,Virtual Name一般以/开头
6.几种交换机
- direct:直连,通过routingKey和exchange决定的那个唯一的queue可以接收消息
- routing_key和exchange对应起来
- fanout:发布/订阅,所有bind到此exchange的queue都可以接收消息
- routing_key可以省略
- topic:和direct类似,但是在匹配规则上进行了扩展,支持通配符的方式
- headers:通过headers 来决定把消息发给哪些queue
二、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()// 声明队列
queue,err_q := ch.QueueDeclare("mysql_queue",false,false,false,false,nil)
fmt.Println(err_q)// 生产任务:生产者
ch.Publish("",queue.Name,false,false,amqp.Publishing{ContentType:"text/plain",Body:[]byte("hello world"),
})// 消费者
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.流程
- 生产者创建channel发送消息到交换机
- 交换机根据bind队列将消息发送到队列
- 队列存储消息并负责分发消息到消费者
- 消费者使用channel获取消息并消费,也可以选择拒收消息,消息重新打回队列,在规定时间后重试
4.确保服务器重启不会清空队列
// 1.创建队列设置持久化:durable表示是否持久化
queue,err_q := ch.QueueDeclare("my_queue",true,false,false,false,nil)// 2.生产者设置持久化,DeliveryMode// 3.消费者持久化:如果生产者这边设置了持久化,那么消费者同样也需要设置成持久化。
amqp.Publishing{ContentType:"text/plain",Body:[]byte("hello world"),DeliveryMode:amqp.Persistent,}
5.消息确认机制
// 消费者消费的时候会出现两种情况,消费完成和消费失败,消费失败的要再次回到队列,重新分配// 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) // 拒绝接收并且重新放回队列}
6.使用交换机
// 创建两个队列
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)
7.限流:确保消费者每次只能消费一个任务,消费完成后再分配任务,ack后再继续接收任务
第一种方式:
//设置每次从消息队列获取任务的数量
err = ch.Qos(1, //预取任务数量,这里可以理解为线程的数量0, //预取大小false, //全局设置
)if err != nil {//无法设置Qosreturn err
}
8.notify确认
生产者的任务是否成功入列
// 在publish之前
ret := <- ch.NotifyReturn(make(chan amqp.Return))
if (string(ret.Body) != ""){// 获得body重新发,注意这里得需要异步执行,不然会卡死:ret.Body
}
参数说明:
创建队列:
创建交换机:
生产者:
exchange:交换机名称
key:routingKey
mandatory:如果生产者生产的任务没有正常进入队列中,设置为true会返还给生产者,设置为false会直接丢弃
immediate:
msg:发送的消息,amqp.Publishing类型的数据
消费者:
queue:队列名称
consumer:消费者名称
autoAck:自动确认
exclusive:
noLocal:
noWait:
args:参数
redis的使用
下载第三方库:github.com/garyburd/redigo/redis
一、连接redis
conn,err := redis.Dial("tcp","10.1.210.69:6379")defer conn.Close()
二、设置过期时间
设置key过期时间
conn.Do("expire", "name", 10) //10秒过期
二、设置key、value
conn.Do("SET", "name", "hallen")
三、获取key对应的值
redis.String(conn.Do("GET", "name"))
git版本控制
gitlab
一、介绍
二、安装服务
1.安装依赖包
sudo apt-get update
sudo apt-get install -y curl openssh-server ca-certificates
2.邮件配置
sudo apt-get install -y postfix
选择Internet site
3.添加镜像
curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.deb.sh | sudo bash
4.安装gitlab
sudo apt-get install gitlab-ce /gitlab-ee如果报错:Unable to fetch some archives, maybe run apt-get update or try with --fix-missing?
执行:sudo apt-get updatesudo apt-get upgrade
出现如下说明安装成功了
5.修改访问路径及端口号,端口号必须开放,否则访问不了
sudo vim /etc/gitlab/gitlab.rbexternal_url 修改为:http://ip:端口号
unicorn['port'] = 8080修改为自己的备用端口号,不能和上面的重复
6.更新配置
sudo gitlab-ctl reconfigure
7.重启服务
sudo gitlab-ctl restart
8.打开 sshd 和 postfix 服务
service sshd start
service postfix start
9.查看状态
sudo gitlab-ctl status
访问:http://ip:端口号
第一次访问会默认以root管理员用户登陆,需要输入两遍密码
10.本地设置git的用户名密码
git config --global user.name "hallen"
git config --global user.email "1277405413@qq.com"
11.访问502错误
- 内存不够,至少需要2G内存
- 端口被占用:netstat -ntpl
- gitlab占用内存太多,导致服务器崩溃。尤其是使用阿里云服务器最容易出现502
- swap
查看日志:sudo gitlab-ctl tail -f unicorn
初始化项目
一、仓库已有项目,本地怎么拉取
git clone 地址
二、本地已有项目,怎么提交到远程
初始化仓库:git init
// 手动的为你的远程仓库的地址在本地起一个别名:
git remote add origin 仓库地址
// 从远程分支拉取master分支并与本地master分支合并
git pull origin master:master
//提交本地分支到远程分支
git push -u origin master
// 提交本地代码:
git add -A
git commit -m ''
git push --set-upstream origin master
gin项目部署
windows部署
window上部署:bee pack -be GOOS=windows
- 进入到项目目录,执行:bee pack -be GOOS=windows
- 如果发生错误:
- SET CGO_ENABLED=0
- SET GOOS=windows
- SET GOARCH=amd64
- bee pack -be GOOS=windows
- 将打包好的项目包拷贝到要存放的路径下,解压
- 安装nssm服务管理工具: 支持Windows 7, Windows 8 and Windows 10
- 管理员身份打开cmd,进入到nssm软件存放exe文件的目录
- nssm install servicename就是你要添加的服务的名称
- 然后在弹出的框中选择第二步解压文件夹中的exe文件
- 开始中搜服务,找到 ,启动即可
- 修改服务的指向路径:
- 1.进入服务,查看路径,【开始】=>【运行】=>【services.msc】
- 2.进入注册表,修改服务路径【开始】=>【运行】=>【regedit】,打开HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\【服务名称】,找到要修改的服务名称,然后修改【ImagePath】中数据值即可
- 如果发生错误:
vue项目部署
略
linux上部署
一、独立部署
1.进入项目目录使用bee工具打包
bee pack -be GOOS=linux
2.将打包文件放在linux中
安装:sudo apt-get install lrzsz
3.修改权限
chmod 777 -R /home/go
4.进入到该目录
nohup 命令启动:nohup ./项目名称 &
指定日志路径:nohup ./项目名称 >run.log 2>&1 &
二、supervisor部署
1.安装supervisor
sudo apt-get install supervisor
2.创建配置文件
1.在/etc/supervisor/conf.d目录下新建文件:supervisord.conf
2.配置内容如下:[program:zhiliao_web]# 项目文件夹directory = /home/go/src/zhiliao_web# 项目可执行文件位置command = /home/go/src/zhiliao_web/zhiliao_webautostart = truestartsecs = 5user = rootredirect_stderr = true# 输出日志文件的位置stdout_logfile = /home/go/src/zhiliao_web/supervisor.logport=127.0.0.1:9000#登录web用的用户名和密码username=userpassword=admin
3.supervisor命令
- 启动supervisor服务:supervisord -c /etc/supervisor/supervisord.conf
- 如果提示有程序已经在运行,先把服务停了:systemctl stop supervisor.service
- 重启supervisord:supervisorctl reload
- 进入supervisor客户端:supervisorctlstart program_namestop xxxrestart xxx
三、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: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
4.linux文件监听限额
cd /proc/sys/fs/inotify/
临时限额:
sudo sysctl fs.inotify.max_user_watches = 524288
sudo sysctl -p
永久限额
echo fs.inotify.max_user_watches = 524288 | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
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
3.supervisor命令- 启动supervisor服务:supervisord -c /etc/supervisor/supervisord.conf
- 如果提示有程序已经在运行,先把服务停了:systemctl stop supervisor.service
- 重启supervisord:supervisorctl reload
- 进入supervisor客户端:supervisorctlstart program_namestop xxxrestart xxx## 三、vue项目部署1.下载node文件uname -a查看系统位数(x86_64表示64位系统, i686 i386表示32位系统)[https://nodejs.org/en/download/ ](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: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
4.linux文件监听限额
cd /proc/sys/fs/inotify/临时限额:
sudo sysctl fs.inotify.max_user_watches = 524288
sudo sysctl -p
永久限额
echo fs.inotify.max_user_watches = 524288 | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
Go实战Gin+Vue+微服务打造秒杀商城第五课 gin+vue实战相关推荐
- SpringCloud+Vue微服务教程与实战(1)--全新的开始
点此查看全部文字教程.视频教程.源代码 本文目录 1. 背景 2. 微服务架构的优缺点 3. 实现技术 4. 开发环境 5. 小结 1. 背景 之前已经讲过SSM完整的教程,我个人觉得是相当不错的,附 ...
- 【项目实战】Java从单体到微服务打造房产销售平台(一) - 整体概述
Java从单体到微服务打造房产销售平台 可掌握技能:从0-1业务设计 + SpringCloud(微服务改造) https://coding.imooc.com/class/174.html
- Docker实战-部署GPE微服务的监控体系(二)
前言 上篇文章:我们介绍了GPE体系中,grafana的部署和安装(<Docker实战-部署GPE微服务的监控体系>),今天这个文章,我们继续介绍GPE体系中,Prometheus和Exp ...
- Docker实战-部署GPE微服务的监控体系
Docker实战-部署GPE微服务的监控体系 前言 微服务体系架构里,有很多的解决方案都是使用GPE作为微服务体系的监控体系, 如下图所示: 我们这里经常提到的GPE,包括Grafana,Promet ...
- 《深入理解 Spring Cloud 与微服务构建》第十五章 微服务监控 Spring Boot Admin
<深入理解 Spring Cloud 与微服务构建>第十五章 微服务监控 Spring Boot Admin 文章目录 <深入理解 Spring Cloud 与微服务构建>第十 ...
- 微服务时代之2017年五军之战:Net PHP谁先死
微服务时代之2017年五军之战:Net PHP谁先死 1.引言 其实我一直是个懒人,开博也有好几年了,但是一直懒得写文章,主要怕打字麻烦, 手机都是用讯飞语音输入的, 可惜博客里面很多专业性的词语,用 ...
- 基于springboot+dubbo微服务开发的商城系统
基于springboot+dubbo微服务开发的商城系统 模仿天猫商城 更多资源,访问搬砖联盟-每天搬一点,收货多一点.
- 微服务技术方案:Spring Cloud 从入门到实战
随着互联网技术的发展与不断创新,以及用户流量的不断增大,越来越多的企业项目面临大数据.高并发等问题,随之而来的就是通过分布式模型组建架构,微服务思想就集中体现了应用价值,2020 年的你还没有掌握微服 ...
- go语言微服务项目,高级篇--03go-mirco框架-gin框架-mvc-REST-Session
go-Micro 框架 创建 micro 服务 命令:micro new --type srv test66 框架默认自带服务发现:mdns. 使用consul服务发现: 1. 初始consul服务发 ...
最新文章
- 自动驾驶制图中的深度学习
- SilverLight简介和优点
- php转换图片为.bin文件
- mybatis 连接池_应用框架之Mybatis数据源和连接池
- Delphi 与 DirectX 之 DelphiX(80): TDIB.BlendPixel();
- [Angularjs]视图和路由(一)
- simple_html_dom.php 使用 乱码处理作者:gaoming13
- mongoose在子文档的array里update或insert
- vue - 使用vue实现自定义多选与单选的答题功能
- python 用itchat会封吗_在python中使用itchat发送微信消息
- 不用MindManager打开mmap文件
- 85 缓存, 验证码 序列化
- c语言prime函数怎么用_用一个自动关机小程序小试牛刀,玩转C语言System函数,边学边玩...
- After trying to increase PLL frequency, system shows the error: “Device may be operating in low-powe
- 医学图像彩色化相关--20201208论文笔记Colorization of CT images to improve tissue contrast for tumor segmentation
- 飞机计算机系统叫什么,飞机电脑和个人电脑有什么不同?
- 仿热血江湖游戏NpcClass_自动攻击事件 刀反伤
- 在Vue中Promise.all的使用
- 工具分享:elasticsearch-7.3.1请自行下载(Linux、windows_64)(附下载链接)
- 防晒隔离产品基础知识大全