赞
踩
后端:
结束难点:
秒杀业务逻辑:
接入层功能:
1.前端:vue3,antdv,vue-router,axios
2.后端
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 | 订单创建时间 |
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中定义rules
rules:{ // 这里的rules就是前面绑定的rules
email:[{required: true,message: "必填",trigger: "blur"}]
}
2.使用自定义验证器
1.定义验证器:新版的antdv返回Promise let 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
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.Builder for 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 = msg err := 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)
JSON Web Token:是一种跨域认证解决方案,它规定了一种Token实现方式,多用于前后端分离等场景
1.前后端不分离的验证逻辑
2.使用jwt认证
服务端完成登录校验后,会生成一个令牌(就是token)再发回给用户,用户后续请求只需要带上这个Token,服务端解密之后就能获取该用户的相关信息了。
使用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
var TokenSecret = []byte("gin_vue_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){ // 解析token token,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) // 验证token if 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 = token this.username = username // 用户端的管理端需要区分开 localStorage.setItem(front_token_key,token) localStorage.setItem(front_user_key,username) } // 管理端存储信息 setAdminAuth(admin_token,admin_user_name){ this.admin_token = admin_token this.admin_user_name = admin_user_name localStorage.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 = null this.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(); } } });
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: { // 没有获取到设置为null token:localStorage.getItem('token')?localStorage.getItem('token') : null user: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 store main.js中use下 import vuex from './vuex/vuex.js' app.use(vuex); 设置全局变量: app.config.globalProperties.$vuex = vuex router.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中取。
结合table使用分页
<a-table :pagination="users_pagenation">
front_users:[],
columns,
position:top,
users_pagenation:{
current:1,
pageSize:5,
pageSizeOptions:['10','20','30'], // 可选每页显示几条
showSizeChanger:true,
total:11
}
<a-table @change="chanPage">
chanPage(pagination){
this.users_pagenation.current = pagination.current
this.users_pagenation.pageSize = pagination.pageSize
},
1.免费的
2.使用简单
3.能满足大多数压测要求
1.jdk环境搭建
2.软件安装
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
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]
e.g.:jmeter -n -t [testplan/RedisLock.jmx] -l [testplan/result/result.txt] -e -o [testplan/webreport]
1.需要满足的条件:
需要邮箱地址、密码及返回的token
2.jmeter测试需要用到的技术
1.下载mysql-connector-java-5.1.7-bin.jar,地址:https://dev.mysql.com/downloads/connector/j/,
2.解压,把里面的jar包放到jmeter的lib目录下
3.配置连接信息
4.使用
在JDBC Request 页面
1.添加关联
2.获取关联数据
jdbc request:设置结果集存储的变量:user
BeanShell 后置处理器:
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模式:
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
开放端口:
3.web可视化配置:
4.配置用户及授权
查看用户:sudo rabbitmqctl list_users
添加用户:sudo rabbitmqctl add_user admin(用户名) admin(密码)
授权:sudo rabbitmqctl set_user_tags admin(用户名) administrator(角色)
5.rabbitmqctl服务的使用
查看当前用户列表:sudo rabbitmqctl list_users
添加用户:sudo rabbitmqctl add_user admin(用户名) admin(密码)
删除用户:sudo rabbitmqctl delete_user admin(用户名)
修改用户密码:sudo rabbitmqctl change_password admin(用户名) admin1(新密码)
设置vhost:
vhost说明:virtual host相当于一个单独的rabbitmq服务器,每个virtual是独立的,不可互通的,相当于mysql中的数据库,都是独立的,可以单独设置权限,Virtual Name一般以/开头
6.几种交换机
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.流程
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 {
//无法设置Qos
return 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:参数
下载第三方库:github.com/garyburd/redigo/redis
conn,err := redis.Dial("tcp","10.1.210.69:6379")
defer conn.Close()
设置key过期时间
conn.Do("expire", "name", 10) //10秒过期
conn.Do("SET", "name", "hallen")
redis.String(conn.Do("GET", "name"))
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 update
sudo apt-get upgrade
出现如下说明安装成功了
5.修改访问路径及端口号,端口号必须开放,否则访问不了
sudo vim /etc/gitlab/gitlab.rb
external_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错误
查看日志: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
window上部署:bee pack -be GOOS=windows
略
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 &
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_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命令
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
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。