如何部署一个Web应用到云服务器
Table of Contents
常用信息 #
dev安装依赖 #
npm i --save-dev
数据库账户 #
user:backend
passwd:123456
localhost:3306
部署服务器上的数据库账户 #
user:root
passwd:as251421
6.本地初始化仓库,进入代码目录
changing #
material-ui/core
material-ui/icons
以后可以放到后台运行:
nohup ./main &
然后不管是否是当前终端,都可以如下方式查看:
ps -aux|grep main
如果要杀掉进程,可以kill -9:
kill -9 进程号
1、显示数据库列表。
mysql -u backend -p
update users set pg_id=2 where name="Johnson";
show databases;
2、显示库中的数据表:
use dbdesign;
show tables;
3、显示数据表的结构:
describe 表名;
4、建库:
create database 库名;
5、建表:
use 库名;
create table 表名 (字段设定列表);
6、删库和删表:
drop database 库名;
drop table 表名;
git:初始化仓库 #
echo “# java_code” » README.md//添加一个README.md git init //将本目录创建为git可以管理的仓库 git add README.md //添加到本地仓库 git commit -m “first commit” ////把文件提交到仓库 git remote add origin https://github.com/****/java_code.git //关联远程仓库 git push -u origin master //第一次需要-u 后来不需要 ,
git:更新代码 #
第一步:查看当前的git仓库状态,可以使用git status git status 第二步:更新全部 git add * 第三步:接着输入git commit -m “更新说明 git commit - m “更新说明” 第四步:先git pull,拉取当前分支最新代码 git pull 第五步:push到远程master分支上 git push origin master
.gitignore的使用 #
# 此为注释 – 将被 Git 忽略
2
3 *.a # 忽略所有 .a 结尾的文件
4 !lib.a # 但 lib.a 除外
5 /TODO # 仅仅忽略项目根目录下的 TODO 文件,不包括 subdir/TODO
6 build/ # 忽略 build/ 目录下的所有文件
7 doc/*.txt # 会忽略 doc/notes.txt 但不包括 doc/server/arch.txt
入门配置 #
如何建一个数据库 #
sudo mysql -uroot -p //以Linux root权限、root用户名使用密码登陆数据库。
create user 'linton'@'localhost' IDENTIFIED BY "123456"; //创建localhost上的linton用户(注意,mysql的用户是“用户名”+“其地址”。)
CREATE DATABASE myTest; //创建数据库myTest
GRANT ALL PRIVILEGES ON myTest.* TO linton@"localhost"; //授予权限(其后面不应该加identified by,因为新版本的mysql中禁止使用grant来创建新用户。只能是先执行create user,再为其授予权限,一步一步来。)(一定要这么授权,否则自己都没法访问自己机器上的数据库。)(当鉴权时,如果你的linux用户名是linton,那么你的数据库的“@”前的用户名也是linton。即它们是联动的。)
配置react环境 #
1.安装node+npm
2.安装VS Code
3.选择工作区文件夹——右键点击在终端中打开
4.按照Using React in Visual Studio Code的文档进行操作
npm install -g create-react-app
create-react-app my-app
运行create-react-app my-app命令时如果速度很慢或者卡住不动可采用变更资源来源的方式解决
将来源换成淘宝源 npm config set registry https://registry.npm.taobao.org
配置go环境 #
sudo apt-get install golang
sudo gedit /etc/profile
或~/.bashrc
然后添加如下三行到文件末尾:
export GOPATH=/home/GO_PATH //用于go的工作目录
export GOROOT=/usr/lib/go-1.13 //安装目录
PATH=$GOROOT/bin:$PATH
然后检查是否成功,查看go的相关环境信息如下:
go env
弹出:
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/linton/.cache/go-build"
GOENV="/home/linton/.config/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/linton/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/lib/go-1.13"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/lib/go-1.13/pkg/tool/linux_amd64"
GCCGO="gccgo"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build458845879=/tmp/go-build -gno-record-gcc-switches"
通过打印 Go 版本号,验证安装过程。
go version
输出应该像下面这样:
go version go1.14.2 linux/amd64
想要测试 Go 安装过程,我们将会创建一个工作区,并且构建一个简单的程序,用来打印经典的"Hello World"信息。
01.默认情况下,GOPATH
变量,指定为工作区的位置,设置为$HOME/go
。想要创建工作区目录,输入:
mkdir ~/go
02.在工作区内,创建一个新的目录src/hello
:
mkdir -p ~/go/src/hello
在那个目录下,创建一个新文件,名称为hello.go
:
package main
import "fmt"
func main() {
fmt.Printf("Hello, World\n")
}
想要学习更多关于 Go 工作区的目录,浏览 Go 文档页面。
03.浏览到~/go/src/hello
目录,并且运行go build
构建程序:
cd ~/go/src/hello
go build
上面的这个命令将会构建一个名为hello
的可执行文件。
04.你可以通过简单执行下面的命令,运行这个可执行文件:
./hello
输出应该像下面这样:
Hello, World
配置gin #
先设置代理:
go env -w GOPROXY=https://goproxy.io,direct
无法科学上网,如何配置Gin #
那我们该如何解决问题呢?毕竟还要制造 bug 的嘛~
手动下载 #
我们常见的 golang.org/x/...
包,一般在 GitHub 上都有官方的镜像仓库对应。比如 golang.org/x/text
对应 github.com/golang/text
。所以,我们可以手动下载或 clone 对应的 GitHub 仓库到指定的目录下。
mkdir $GOPATH/src/golang.org/x cd $GOPATH/src/golang.org/x git clone git@github.com:golang/text.git rm -rf text/.git |
|
---|---|
当如果需要指定版本的时候,该方法就无解了,因为 GitHub 上的镜像仓库多数都没有 tag。并且,手动嘛,程序员怎么能干呢,尤其是依赖的依赖,太多了。
设置代理 #
如果你有代理,那么可以设置对应的环境变量:
export http_proxy=http://proxyAddress:port export https_proxy=http://proxyAddress:port |
|
---|---|
或者,直接用 all_proxy
:
export all_proxy=http://proxyAddress:port |
|
---|---|
go mod replace #
从 Go 1.11 版本开始,新增支持了 go modules
用于解决包依赖管理问题。该工具提供了 replace
,就是为了解决包的别名问题,也能替我们解决 golang.org/x
无法下载的的问题。
go module
被集成到原生的 go mod
命令中,但是如果你的代码库在 $GOPATH
中,module
功能是默认不会开启的,想要开启也非常简单,通过一个环境变量即可开启 export GO111MODULE=on
。
以下为参考示例:
module example.com/hello require ( golang.org/x/text v0.3.0 ) replace ( golang.org/x/text => github.com/golang/text v0.3.0 ) |
|
---|---|
类似的还有 glide、gopm 等这类第三方包管理工具,都不同方式的解决方案提供给我们。
GOPROXY 环境变量 #
终于到了本文的终极大杀器 —— GOPROXY。
我们知道从 Go 1.11
版本开始,官方支持了 go module
包依赖管理工具。
其实还新增了 GOPROXY
环境变量。如果设置了该变量,下载源代码时将会通过这个环境变量设置的代理地址,而不再是以前的直接从代码库下载。这无疑对我等无法科学上网的开发良民来说是最大的福音。
更可喜的是,goproxy.io 这个开源项目帮我们实现好了我们想要的。该项目允许开发者一键构建自己的 GOPROXY
代理服务。同时,也提供了公用的代理服务 https://goproxy.io
,我们只需设置该环境变量即可正常下载被墙的源码包了:
1 |
export GOPROXY=https://goproxy.io |
---|---|
不过,需要依赖于 go module
功能。可通过 export GO111MODULE=on
开启 MODULE。
如果项目不在 GOPATH
中,则无法使用 go get ...
,但可以使用 go mod ...
相关命令。
也可以通过置空这个环境变量来关闭,export GOPROXY=
。
对于 Windows 用户,可以在 PowerShell
中设置:
1 |
$env:GOPROXY = "https://goproxy.io" |
---|---|
最后,我们当然推荐使用 GOPROXY
这个环境变量的解决方式,前提是 Go version >= 1.11。
最后的最后,七牛也出了个国内代理 goproxy.cn 方便国内用户更快的访问不能访问的包,真是良心。
数据库设计 #
关系设计 #
GroupPermission #
是用于联系权限组PermGroup和权限Permssion的关系集合。
该关系模式没有主码,全靠PermGroup的外码依赖PGID和Permission的外码依赖PermID的组合来区分。
权限 #
ID | 含义 |
---|---|
1xx | dashboard.* |
2xx | function.* |
安全与风控 #
接口设计 #
方法 | 接口 | 调用参数 | 返回参数 |
---|---|---|---|
POST | login | 手机号、SHA256加密的密码 | ok或passwd |
POST | register | 手机号、SHA256加密的密码、Email、昵称 | ok或phone |
GET | logout | 无 | ok或error |
GET | checkLogged | 用户token(cookie中) | ok或notlogged |
登录 #
采用axios+formik+yup的前端库,完成对用户密码和安全问题的检测。
采用客户端+服务端SHA256双加密
数据输入合法性由客户端的formik+yup负责检查。同时,调用库进行SHA256加密。
后端负责比对数据库中的SHA256与本次登录传入的SHA256是否相等。
axios负责异步发送HTTP POST请求,并检查SPAM。
遇到的问题 #
如果登录就立即跳转,则不要把http反馈写在set cookie前面。因为很可能还没有设置cookie就已经跳转了。
一般情况下,一定要保证把http反馈写在所在http请求应答函数的最后。
基于cookie的token验证 #
context.SetCookie("user_cookie", string(u.Id), 1000, "/", "localhost", false, true)
这里来介绍一下这些参数,第一个参数为 cookie 名;第二个参数为 cookie 值;第三个参数为 cookie 有效时长,当 cookie 存在的时间超过设定时间时,cookie 就会失效,它就不再是我们有效的 cookie;第四个参数为 cookie 所在的目录;第五个为所在域,表示我们的 cookie 作用范围;第六个表示是否只能通过 https 访问;第七个表示 cookie 是否可以通过 js代码进行操作。
权限组 #
权限组id #
组 | id |
---|---|
业主用户 | 0 |
超级管理员 | 1 |
水务员 | 2 |
前端开发 #
覆盖主题样式的两种方法 #
直接创造自定义样式了的组件: #
const YellowSwitch = styled(Switch)(({ theme }) => ({
'& .Mui-checked': {
color: yellow[700],
'&:hover': {
backgroundColor: alpha(yellow[700], theme.palette.action.hoverOpacity),
},
},
'& .Mui-checked+ .MuiSwitch-track': {
backgroundColor: yellow[700],
},
}));
或可以利用themeProvider包围在待改写的组件外,临时更改其样式: #
const themeWXQ = createTheme({
components: {
// Name of the component
MuiChip: {
//需要重写样式的MUI组件名称
defaultProps: {
// The props to change the default for.即:使用组件时传入的参数的值。
},
styleOverrides: {
//styleOverrides是对css类选择器“ .MuiChip-colorSecondary”的重写。如果要重写其他类选择器
//,则可再建立一个新的这样的结构,名称换成那个类的名。
colorSecondary:{
color:yellow[700],
borderColor:yellow[700],
},
outlinedSecondary:{
color:yellow[700],
borderColor:yellow[700],
}
},
},
MuiButton: {
//需要重写样式的MUI组件名称
defaultProps: {
// The props to change the default for.即:使用组件时传入的参数的值。
},
styleOverrides: {
//styleOverrides是对css类选择器“ .MuiChip-colorSecondary”的重写。如果要重写其他类选择器
//,则可再建立一个新的这样的结构,名称换成那个类的名。
outlinedSecondary:{
color:yellow[700],
borderColor:yellow[700],
'&:hover': {
color:yellow[700],
borderColor:yellow[700],
},
}
},
},
},
});
Material UI #
menuitems #
在 menuitems
的对应选项卡组内增加项目即可。
views #
其中包括了每页的单独渲染。即页面中除了左侧菜单栏外的渲染任务。
你可以在view中增加自己希望的功能。
routes #
规定了各view应当以什么路径进行请求。
layout #
规定了网页的主要框架的样式。
React grid探针法对排版进行debug #
不管是什么元素,都可以在其内部紧紧嵌套一层grid container,然后设置 backgroundColor="#ffab91"
,这样就可以看出了。
教训:不要使用gridspacing,因为这样会让卡片的内容往右边偏移。
React Grid和Stack总结 #
- Stack一般倾向于自动分配空间。例如maxwidth=100的stack,如果第一个元素比较小,那么剩下的空间全是第二个元素的。如何解决此问题?把第一个元素的大小设置为非常大,这样就算第二个元素很小,它也不至于浪费空间了,因为都自动分给第一个了。注意,就算把第一个元素设置为999999px,也不会溢出。这是stack的号处。
- grid在需要相对定位的时候就抓瞎了。因为它是平均分配空间,所以在浏览器窗口过大的时候,间距过大;在浏览器过小的时候,甚至出现元素重叠的问题。
- stack是基于相对定位的空间分配,只需要调整spacing,这个spacing就固定了,适用于精致的相对定位。
- 很多元素的定位如果直接套在stack里会当场崩溃。这种情况下,如果先把元素套grid,再放到stack里,会修复这个问题。
- stack一般可以采用spacing来调整间距,而且只是调整元素之间的。stack一般采用flex-start为默认。stack可以指定divider(在其属性中指定即可),并自动插入。
- 如果要微调间距,可以使用sx的pl、pr等。并且支持响应式。
- 微调任何的元素相对于其所占空间的对齐方式,都可以使用grid container再套grid,通过调整justifycontent和alignitem的方式进行水平轴和垂直轴的对齐。
React 跨域、代理 #
react操作 #
安装http-proxy-middleware。
在src同级目录创建setupProxy.js如图所示
const { createProxyMiddleware } = require(‘http-proxy-middleware’); //注意写法,这是1.0以后的版本,最好按抄
module.exports = function (app) { app.use(createProxyMiddleware('/api', { target: ‘http://ip:8000/’, pathRewrite: { ‘^/api’: ‘’, }, changeOrigin: true, secure: false, // 是否验证证书 ws: true, // 启用websocket } )); }; 以上表达的意思是,把/api 代理到 http://ip:8000/ 到这个网址上,重写一定要,如果不写
pathRewrite: { ‘^/api’: ‘’, }, 5
nginx操作 #
可以如下方式操作:即把/api/再次进行反代。
server {
server_name nesto.cupof.beer;
root /home/lighthouse/nesto/build;
index index.html index.htm;
location / {
try_files $uri /index.html =404;
}
location /api/ {
proxy_pass http://127.0.0.1:8081/
}
}
React DOM渲染 #
封装 #
可以以const变量的方式呈现新的组件,也可以封装到函数中,也可以封装到ES6的类:React Component中。
在函数或类中,标签属性被转化为props对象(在类中是this.props)。它们可以像函数一样被传入。在访问时,使用props.xxx或this.props.xxx。
React JSX #
JSX表达式 #
用花括号框起来。例如 {1+1}
。
注释 #
注释也需要写到花括号中。
数组 #
可以把一个由HTML元素构成的数组,用花括号包起来,然后放到REACT DOM中渲染。
var arr = [
<h1>菜鸟教程</h1>,
<h2>学的不仅是技术,更是梦想!</h2>,
];
ReactDOM.render(
<div>{arr}</div>,
document.getElementById('example')
);
React 组件 #
可利用函数定义组件,利用props传入参数。组件可以嵌套形成更大的组件。
function Name(props) {
return <h1>网站名称:{props.name}</h1>;
}
function Url(props) {
return <h1>网站地址:{props.url}</h1>;
}
function Nickname(props) {
return <h1>网站小名:{props.nickname}</h1>;
}
function App() {
return (
<div>
<Name name="菜鸟教程" />
<Url url="http://www.runoob.com" />
<Nickname nickname="Runoob" />
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById('example')
);
React State #
自顶向下数据流 #
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello React!</title>
<script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
<script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
<script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
</head>
<body>
<div id="example"></div>
<script type="text/babel">
function FormattedDate(props) {
return <h2>现在是 {props.date.toLocaleTimeString()}.</h2>;
}
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<FormattedDate date={this.state.date} />
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('example')
);
</script>
</body>
</html>
- 这里使用ES6类来封装了一个组件。该类由构造函数、渲染函数render、用户函数、以及React Hook函数构成。其中,componentDidMount这个钩子是该元素被渲染到DOM中时(mount,被挂载),被调用;componentWillUnmount这个钩子是元素被停止渲染(unmont,停止挂载)时被调用。tick是浏览器函数,每过一秒被调用一次。
- 注意,构造函数中的this.state后面跟着一个map,其中规定了这个组件拥有哪些状态。在tick中,使用setstate,后面跟着一个map,表示更新状态。在
<FormattedDate date={this.state.date} />
中,使用了{this.state.date}
来访问这个状态的值。使用花括号是因为这是一个JSX表达式。
React Events #
阻止默认行为 #
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('链接被点击');
}
return (
<a href="#" onClick={handleClick}>
点我
</a>
);
}
ES6 箭头函数 #
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((numbers) =>
<li>{numbers}</li>
);
ReactDOM.render(
<ul>{listItems}</ul>,
document.getElementById('example')
);
箭头函数起到了简化的效果,是一种匿名函数。例如上述listitems中,就在定义这个常变量时,表达了预处理为表项的语义。注意,如果箭头函数的函数体不需要换行,就不需要写花括号;如果只有一处return,就不需要写return。
ES6 扩展运算符(…) #
输入:console.log(...[1, 2, 3])
输出: 1 2 3
可见,就是把数组转化为逗号分隔的参数序列。
...(props.success && {
bgcolor: yellow[600],
color:'#ffffff',
'&:hover': {
bgcolor: yellow[700],
color:'#ffffff',
},
}),
React 组件生命周期 #
通过实现组件接口来规定组件在各生命周期阶段的行为 #
- 组件类中,render方法是唯一必须给出实现的。
后端开发 #
上手Go语言 #
Go语言 Export #
如果要从一个包中调用另一个包的函数,则在确保go module开启的情况下,gomod存在的情况下,可以通过从被调用的包中export该函数解决。
如何export?即把函数名称的首字母改成大写即可。这样,在别的包中import其所在包,并直接利用“xxx.xxx”调用,就能使用了。
注意,只有跨包调用的时候才需要这么做。在同一个包里面,是可以随意互相调用的。且嵌套包的情况下,内包可以调外包的,外包不能直接调用里包的(需要上述export)。
Go语言的函数 #
函数可以返回tuple。如果需要返回tuple,那么函数类型需要用括号括起来,否则不需要写括号。
Go是强类型语言 #
Go只是省略了语法中的类型。实际上在goland中,你把鼠标移动上去,是能看出形如C一样的定义的。
Go里的赋值语句 #
一定要用“:=”。
Go的指针 #
和C一样,需要用星号来访问其中的数值。但是赋值数值给指针的时候不需要使用and来取地址,除非要弄二维数组。
Go的printf #
基本用法和C一样。
数据库操作 #
Go短连接MySQL #
sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/dbname")
MySQL中的数据库 #
- 数据库就相当于一整个DBS。其中可以存放很多种关系模式。访问时,以"dbs名称.关系名"的格式使用。
gorm操作数据库 #
非常简便,甚至不用写SQL语句。
ORM库提供了对象到对象的操作。一个关系的全部属性实际上就是对应对象的全部属性。
// 自动迁移(automatically creates the table.)
db.AutoMigrate(&UserInfo{})
u1 := UserInfo{3, "枯藤", "男", "篮球"}
u2 := UserInfo{2, "topgoer.com", "女", "足球"}
// 创建记录
db.Create(&u1)
db.Create(&u2)
// 查询
var u = new(UserInfo)
db.First(u)
fmt.Printf("%#v\n", u)
var uu UserInfo
db.Find(&uu, "hobby=?", "足球")
fmt.Printf("%#v\n", uu)
// 更新
db.Model(&uu).Update("hobby", "双色球")
fmt.Printf("%#v\n", uu)
// 删除
db.Delete(&u)
1.1.1. 库安装 #
go get -u github.com/jinzhu/gorm
1.1.2. 数据库连接 #
package main
import (
"fmt"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"
)
// UserInfo 用户信息
type UserInfo struct {
ID uint
Name string
Gender string
Hobby string
}
func main() {
db, err := gorm.Open("mysql", "root:root@(127.0.0.1:3306)/db1?charset=utf8mb4&parseTime=True&loc=Local")
if err!= nil{
panic(err)
}
defer db.Close()
// 自动迁移
db.AutoMigrate(&UserInfo{})
u1 := UserInfo{1, "枯藤", "男", "篮球"}
u2 := UserInfo{2, "topgoer.com", "女", "足球"}
// 创建记录
db.Create(&u1)
db.Create(&u2)
// 查询
var u = new(UserInfo)
db.First(u)
fmt.Printf("%#v\n", u)
var uu UserInfo
db.Find(&uu, "hobby=?", "足球")
fmt.Printf("%#v\n", uu)
// 更新
db.Model(&u).Update("hobby", "双色球")
// 删除
db.Delete(&u)
}
连接比较简单,直接调用 gorm.Open 传入数据库地址即可 github.com/jinzhu/gorm/dialects/mysql 是 golang 的 mysql 驱动,实际上就是 github.com/go-sql-driver/mysql 作者这里为了好记,重新弄了个名字 这里我用的 mysql,实际上支持基本上所有主流的关系数据库,连接方式上略有不同
db.DB().SetMaxIdleConns(10)
db.DB().SetMaxOpenConns(100)
还可以使用 db.DB() 对象设置连接池信息
1.1.3. 表定义 #
先来定义一个点赞表,这里面一条记录表示某个用户在某个时刻对某篇文章点了一个赞,用 ip + ua 来标识用户,title 标识文章标题
type Like struct {
ID int `gorm:"primary_key"`
Ip string `gorm:"type:varchar(20);not null;index:ip_idx"`
Ua string `gorm:"type:varchar(256);not null;"`
Title string `gorm:"type:varchar(128);not null;index:title_idx"`
Hash uint64 `gorm:"unique_index:hash_idx;"`
CreatedAt time.Time
}
gorm 用 tag 的方式来标识 mysql 里面的约束
创建索引只需要直接指定列即可,这里创建了两个索引,ip_idx 和 title_idx;如果需要多列组合索引,直接让索引的名字相同即可;如果需要创建唯一索引,指定为 unique_index 即可
支持时间类型,直接使用 time.Time 即可
1.1.4. 创建表 #
if !db.HasTable(&Like{}) {
if err := db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8").CreateTable(&Like{}).Error; err != nil {
panic(err)
}
}
直接通过 db.CreateTable 就可以创建表了,非常方便,还可以通过 db.Set 设置一些额外的表属性
1.1.5. 插入 #
like := &Like{
Ip: ip,
Ua: ua,
Title: title,
Hash: murmur3.Sum64([]byte(strings.Join([]string{ip, ua, title}, "-"))) >> 1,
CreatedAt: time.Now(),
}
if err := db.Create(like).Error; err != nil {
return err
}
先构造已给对象,直接调用 db.Create() 就可以插入一条记录了
1.1.6. 删除 #
if err := db.Where(&Like{Hash: hash}).Delete(Like{}).Error; err != nil {
return err
}
先用 db.Where() 构造查询条件,再调用 db.Delete() 就可以删除
1.1.7. 查询 #
var count int
err := db.Model(&Like{}).Where(&Like{Ip: ip, Ua: ua, Title: title}).Count(&count).Error
if err != nil {
return false, err
}
先用 db.Model() 选择一个表,再用 db.Where() 构造查询条件,后面可以使用 db.Count() 计算数量,如果要获取对象,可以使用 db.Find(&Likes) 或者只需要查一条记录 db.First(&Like)
注意,如果未找到,那么对象的所有值都为空。(但是不会产生nullpointer异常,因为GoLang比较高级。)
1.1.8. 修改 #
db.Model(&user).Update("name", "hello")
db.Model(&user).Updates(User{Name: "hello", Age: 18})
db.Model(&user).Updates(User{Name: "", Age: 0, Actived: false}) // nothing update
我这个系统里面没有更新需求,这几个例子来自于官网,第一个是更新单条记录;第二个是更新整条记录,注意只有非空字段才会更新;第三个例子是不会更新的,在系统设计的时候要尽量避免这些空值有特殊的含义,如果一定要更新,可以使用第一种方式,设置单个值
1.1.9. 错误处理 #
其实你已经看到了,这里基本上所有的函数都是链式的,全部都返回 db 对象,任何时候调用 db.Error 就能获取到错误信息,非常方便
1.1.10. 事务 #
func CreateAnimals(db *gorm.DB) err {
tx := db.Begin()
if err := tx.Create(&Animal{Name: "Giraffe"}).Error; err != nil {
tx.Rollback()
return err
}
if err := tx.Create(&Animal{Name: "Lion"}).Error; err != nil {
tx.Rollback()
return err
}
tx.Commit()
return nil
}
事务的处理也很简单,用 db.Begin() 声明开启事务,结束的时候调用 tx.Commit(),异常的时候调用 tx.Rollback()
1.1.11. 其他 #
还可以使用如下方式设置日志输出级别以及改变日志输出地方
db.LogMode(true)
db.SetLogger(gorm.Logger{revel.TRACE})
db.SetLogger(log.New(os.Stdout, "\r\n", 0))
也支持普通的 sql,但是建议尽量不要使用
Gin框架与接口实现 #
router.run() #
注意,调用run后,go程序会进入阻塞,等待访问请求。所以run一定要在完成了初始化后,最后才能调用。
全局变量 #
Go中不存在全局变量的概念。main函数中的变量就可以认为是全局变量。其他函数如果要当成全局变量来对其进行修改或赋值,可以采用指针。
项目部署 #
Linux 自动化初探 #
实现git自动部署、shell自动化后的部署方式 #
前端先npm run build,把build复制到本地仓库,push一下,前端就部署完成。
后端直接push,然后到服务器上运行一下nestoKillBack.sh ,把老的后端杀掉,然后再运行nestoRunBack.sh ,编译并开启新后端。
shell语法注意 #
定义变量
pid=`ps aux|grep dbdback|grep -v "grep"|awk '{print $2}'`
注意,变量和等于号之间不能有空格,否则识别成指令。
使用变量
echo ${pid}
注意,要加dollar。
如何解决sh脚本不能动态变更工作目录的问题 #
因为sh是在子进程中运行,其继承了父进程的路径。
解决方案:
把./script.sh运行方法,改成sh ./script即可。
如果还嫌麻烦,可以建立一个script.sh,其中写入“sh ./work.sh一行代码”(work里面是带有cd的),然后去执行./scrpit.sh,也能达到一样的效果。
根本在于,要直接用shell去运行,而不是直接”./"。
如何利用git自动部署代码 #
linux的三类权限 #
- owner:属主 u
- group:属组 g
- other:其他 o
注意,在多用户的情况下,如果要正常操作,一般要修改other。
搭建git仓库 #
为了安全,新建git用户,并设置密码
useradd git
passwd git
可以在git用户的家中建立一个.git目录,作为仓库文件夹:
cd ~
mkdir nesto.git
进入它,初始化为空仓库:
cd nesto.git
git init --bare .
允许被push:
git config receive.denyCurrentBranch ignore
测试git仓库 #
先把本地仓库添加该remote:
git remote add dep git@111.111.111.111:/home/git/nestob.git
111.111.111.111是服务器ip。
dep是该远程配置的名称,可以随意指定。
该操作的范围只是更改本仓库。
然后就可以push了:
git push -u dep master
利用git hook来实现自动部署 #
git hook就像react hook一样,会在特定的事件发生时执行一定的操作。这里是sh脚本。
进入hooks存放目录:
cd /home/git/nestob.git/hooks
创建post-receive钩子,使收到push后可以自动拉取代码到服务器的项目目录:
vi post-receive
写入:
#!/bin/bash
git --work-tree=/home/website/wwwroot checkout -f
然后记得授予可执行权限:
chmod +x post-receive
这样做的效果就是,当接收到push,就会自动把代码拉取到/home/website/wwwroot目录下。相当于完成了自动部署。
对于go语言一类的需要在目标机器上编译再运行的,还可以在此进行自动编译。
需要注意,一定要保证git用户拥有对/home/website/wwwroot的写权限!!!
nginx配置 #
文件目录 #
nginx文件安装完成之后的文件位置:
- /usr/sbin/nginx:主程序
- /etc/nginx:存放配置文件
- /usr/share/nginx:存放静态文件
- /var/log/nginx:存放日志
nginx使用 #
systemctl start nginx.service //开启
systemctl enable nginx.service //开机自启动
nginx -t # 查看nginx状态
nginx -s reload # 重新载入配置文件
nginx -s reopen # 重启 Nginx
#上述可能在做了大的操作后不管用,可以使用
#service nginx restart
#进行系统级重启,如果还是不行就重启服务器。
nginx -s stop # 停止 Nginx
1.打开 /etc/nginx/conf.d/文件夹,创建配置文件xxx.conf,内容如下:
server {
server_name nesto.cupof.beer;
root /home/lighthouse/nesto/build;
index index.html index.htm;
location / {
try_files $uri /index.html =404;
}
}
3.配置完成后重新启动nginx
service nginx restart
在本地npm build,在build文件夹内全选并打包tar.gz,使用sz上传到nginx中配置的项目目录下,原地解压即可。
Go项目部署 #
如何部署 #
远程服务器先安装go环境。
把本地整个gopath内的所有文件(例如/home/linton/go下的)打包tar.gz,上传到服务器的gopath下,解压。之所以这么做,是为了方便依赖的配置。这样就把所有需要的依赖都打包好了。
注意还可以打开服务器上的gomodule和服务器上的gomodule代理:
go env -w GOPROXY=https://goproxy.io,direct
go env -w export GO111MODULE=on
然后,在项目目录的src下,编译:
go build main.go
运行一下测试看看好不好用:
./main
以后可以放到后台运行:
nohup ./main &
然后不管是否是当前终端,都可以如下方式查看:
ps -aux|grep main
如果要杀掉进程,可以kill -9:
kill -9 进程号
react部署 #
创建生产环境打包 #
npm run build