无常是常

分享技术见解、学习心得和生活感悟

最新文章

消息中间件是什么?

1、了解消息中间件之前先了解一下什么是“同步”?

通常业务系统直接的调用如下图所示:

假设系统A收到了一个请求,可能是用户通过浏览器或者APP发起的,这时候系统A收到请求后马上去调用系统B,然后系统B再把返回结果返回给系统A,系统A才能返回给用户。如下图所示:

以上就是所谓的**“同步”**调用。这个同步的意思就是各个系统联动都是同步依次进行的,一个系统先动,然后立马带动另外一个系统一起动,最后大家依次干完活以后再返回结果。

2、如何依托消息中间件实现异步?

我们往系统A和系统B之间加入一个消息中间件,简称为“MQ”,也就是消息队列。

加入消息队列之后如何通信呢?

其实就是系统A执行完逻辑后给MQ中发送一条消息,然后就直接把结果返回给用户了。

(注:前提是系统A返回给用户的结果不依赖于系统B的返回结果。假设系统B为短信系统,系统A向MQ发送一条发送短信的消息指令,系统A并不关心短信是否立即发送,只要最终在有效的时间内发送成功就行了。)

如上图所示,系统B什么时候执行自己的任务呢?

这时候系统B根据自己的情况,可能是系统A投递消息到MQ之后的1秒内,也可能是1分钟之后,多长时间都有可能,不管多长时间,系统B肯定会从MQ里获取到一条属于自己的消息。

3、消息中间件到底有什么用?

假设系统A要调用系统B干一些事,然后系统A先执行一些操作,需要耗费20ms,接着系统B执行一些操作,需要200ms,所以总共需要220ms。如下图所示:

如果在系统A和系统中间加一个MQ呢?

系统A干完自己的事情耗时20ms,然后发一个消息到MQ耗时5ms,然后就直接返回结果给用户,总计耗时25ms。系统B从MQ中获取消息花费200ms和用户就没有关系了。所以用户只需等待25ms就收到结果了。如下图所示:

假设系统A调用系统B出现故障呢?因为系统A调用系统B肯定返回异常,此时系统A是不是也得返回异常给用户?而系统A是不是还要去处理这个异常?

这一切是因为系统A和系统B通过同步调用的模式耦合在一起,所以系统B一旦出现故障,很肯能影响系统A也有故障,而且系统A还得去关心系统B的故障,去处理对应的异常。

如果在系统A和系统B中间加一个消息中间件,系统A就不用关心系统B是否出现故障了,因为那是系统B自己的事,等系统B故障恢复以后,就继续执行它自己的事,此时就对系统A没任何影响了。

为什么会有这样的效果呢?正式因为通过引入MQ,两个系统实现了异步化调用,也就实现了解耦,所以相互之间并没有任何影响。

5、流量削峰

假设系统A是不操作数据库的,因此只要多部署几台机器,就可以抗下每秒1万的请求,比如部署个20台机器,就可以轻松抗下每秒上万请求。

然后系统B是要操作一台数据库服务器的,那台数据库的上限是接收每秒6000请求,那么系统B无论部署多少台机器都没用,因为他依赖的数据库最多只能接收每秒6000请求。

如下图所示:

假设现在有1万的QPS请求到了系统A,由于系统A部署了20台机器,所以可以抗住1万QPS。然后系统A会瞬间把1万QPS转发给系统B,假设系统B也抗住了1万QPS,但是系统B对数据库发起了1万QPS的请求,数据库一定会瞬间被压垮。

所以这时如果引入MQ,就可以解决这个问题了。MQ这个技术抗高并发的能力远远高于数据库,同样的机器配置下,如果数据库可以抗每秒6000请求,MQ至少可以抗每秒几万请求。

为什么呢?因为数据库复杂啊,他要能够支持你执行复杂的SQL语句,支持事务等复杂的机制,支持你对数据进行增删改查,听着简单,其实是很复杂的!所以一般数据库单服务器也就支撑每秒几千的请求。

所以只要你引入一个MQ,那么就可以让系统A把每秒1万请求都作为消息直接发送到MQ里,MQ可以轻松抗下来这每秒1万请求。

接着,系统B只要慢慢的从MQ里获取消息然后执行数据库读写操作即可,这个获取消息的速度是系统B自己可以控制的,所以系统B完全可以用一个比较低的速率获取消息然后写入数据库,保证对数据库的QPS不要超过他的极限值6000。

这个时候因为系统A发送消息到MQ很快,系统B从MQ消费消息很慢,所以MQ里自然会积压一些消息,不过不要紧,MQ一般都是基于磁盘来存储消息的,所以适当积压一些消息是可以的。当系统A的高峰过去,每秒可能就恢复到1000 QPS了,此时系统b还是以每秒6000QPS的速度获取消息写入数据库,那么自然MQ里积压的消息就会慢慢被消化掉了。

所以这就是MQ进行流量削峰的效果,系统A发送过来的每秒1万请求是一个流量洪峰,然后MQ直接给扛下来了,都存储自己本地磁盘,这个过程就是流量削峰的过程,瞬间把一个洪峰给削下来了,让系统B后续慢慢获取消息来处理。

6、总结

消息中间件的主要作用就是削峰解耦,提升系统的响应速度。

1、了解消息中间件之前先了解一下什么是“同步”?

通常业务系统直接的调用如下图所示:

假设系统A收到了一个请求,可能是用户通过浏览器或者APP发起的,这时候系统A收到请求后马上去调用系统B,然后系统B再把返回结果返回给系统A,系统A才能返回给用户。如下图所示:

以上就是所谓的**“同步”**调用。这个同步的意思就是各个系统联动都是同步依次进行的,一个系统先动,然后立马带动另外一个系统一起动,最后大家依次干完活以后再返回结果。

2、如何依托消息中间件实现异步?

我们往系统A和系统B之间加入一个消息中间件,简称为“MQ”,也就是消息队列。

加入消息队列之后如何通信呢?

其实就是系统A执行完逻辑后给MQ中发送一条消息,然后就直接把结果返回给用户了。

(注:前提是系统A返回给用户的结果不依赖于系统B的返回结果。假设系统B为短信系统,系统A向MQ发送一条发送短信的消息指令,系统A并不关心短信是否立即发送,只要最终在有效的时间内发送成功就行了。)

如上图所示,系统B什么时候执行自己的任务呢?

这时候系统B根据自己的情况,可能是系统A投递消息到MQ之后的1秒内,也可能是1分钟之后,多长时间都有可能,不管多长时间,系统B肯定会从MQ里获取到一条属于自己的消息。

3、消息中间件到底有什么用?

假设系统A要调用系统B干一些事,然后系统A先执行一些操作,需要耗费20ms,接着系统B执行一些操作,需要200ms,所以总共需要220ms。如下图所示:

如果在系统A和系统中间加一个MQ呢?

系统A干完自己的事情耗时20ms,然后发一个消息到MQ耗时5ms,然后就直接返回结果给用户,总计耗时25ms。系统B从MQ中获取消息花费200ms和用户就没有关系了。所以用户只需等待25ms就收到结果了。如下图所示:

假设系统A调用系统B出现故障呢?因为系统A调用系统B肯定返回异常,此时系统A是不是也得返回异常给用户?而系统A是不是还要去处理这个异常?

这一切是因为系统A和系统B通过同步调用的模式耦合在一起,所以系统B一旦出现故障,很肯能影响系统A也有故障,而且系统A还得去关心系统B的故障,去处理对应的异常。

如果在系统A和系统B中间加一个消息中间件,系统A就不用关心系统B是否出现故障了,因为那是系统B自己的事,等系统B故障恢复以后,就继续执行它自己的事,此时就对系统A没任何影响了。

为什么会有这样的效果呢?正式因为通过引入MQ,两个系统实现了异步化调用,也就实现了解耦,所以相互之间并没有任何影响。

5、流量削峰

假设系统A是不操作数据库的,因此只要多部署几台机器,就可以抗下每秒1万的请求,比如部署个20台机器,就可以轻松抗下每秒上万请求。

然后系统B是要操作一台数据库服务器的,那台数据库的上限是接收每秒6000请求,那么系统B无论部署多少台机器都没用,因为他依赖的数据库最多只能接收每秒6000请求。

如下图所示:

假设现在有1万的QPS请求到了系统A,由于系统A部署了20台机器,所以可以抗住1万QPS。然后系统A会瞬间把1万QPS转发给系统B,假设系统B也抗住了1万QPS,但是系统B对数据库发起了1万QPS的请求,数据库一定会瞬间被压垮。

所以这时如果引入MQ,就可以解决这个问题了。MQ这个技术抗高并发的能力远远高于数据库,同样的机器配置下,如果数据库可以抗每秒6000请求,MQ至少可以抗每秒几万请求。

为什么呢?因为数据库复杂啊,他要能够支持你执行复杂的SQL语句,支持事务等复杂的机制,支持你对数据进行增删改查,听着简单,其实是很复杂的!所以一般数据库单服务器也就支撑每秒几千的请求。

所以只要你引入一个MQ,那么就可以让系统A把每秒1万请求都作为消息直接发送到MQ里,MQ可以轻松抗下来这每秒1万请求。

接着,系统B只要慢慢的从MQ里获取消息然后执行数据库读写操作即可,这个获取消息的速度是系统B自己可以控制的,所以系统B完全可以用一个比较低的速率获取消息然后写入数据库,保证对数据库的QPS不要超过他的极限值6000。

这个时候因为系统A发送消息到MQ很快,系统B从MQ消费消息很慢,所以MQ里自然会积压一些消息,不过不要紧,MQ一般都是基于磁盘来存储消息的,所以适当积压一些消息是可以的。当系统A的高峰过去,每秒可能就恢复到1000 QPS了,此时系统b还是以每秒6000QPS的速度获取消息写入数据库,那么自然MQ里积压的消息就会慢慢被消化掉了。

所以这就是MQ进行流量削峰的效果,系统A发送过来的每秒1万请求是一个流量洪峰,然后MQ直接给扛下来了,都存储自己本地磁盘,这个过程就是流量削峰的过程,瞬间把一个洪峰给削下来了,让系统B后续慢慢获取消息来处理。

6、总结

消息中间件的主要作用就是削峰解耦,提升系统的响应速度。

链表

反转链表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package main

import (
"fmt"
)

func main() {
head := &ListNode{
1, &ListNode{
2, &ListNode{
3, &ListNode{
4, &ListNode{
5, nil}},
},
},
}
for list := reverseList(head); list != nil; list = list.Next {
fmt.Println(list.Val)
}
}

type ListNode struct {
Val int
Next *ListNode
}

func reverseList(head *ListNode) *ListNode {
var cur, prev *ListNode = head, nil
for cur != nil {
cur.Next, prev, cur = prev, cur, cur.Next
}
return prev
}

交换相邻两个节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package main

import "fmt"

func main() {
head := &ListNode{
1, &ListNode{
2, &ListNode{
3, &ListNode{
4, nil},
},
},
}
for list := swapPairs(head); list != nil; list = list.Next {
fmt.Println(list.Val)
}
}

type ListNode struct {
Val int
Next *ListNode
}

func swapPairs(head *ListNode) *ListNode {
if head == nil || head.Next == nil {
return head
}
newHead := head.Next
head.Next = swapPairs(newHead.Next)
newHead.Next = head
return newHead
}

判断链表是否有环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package main

import "fmt"

func main() {

cycle := &ListNode{
2, &ListNode{
0, &ListNode{
-4, nil}}}
cycle.Next.Next.Next = cycle
head := &ListNode{
3, cycle,
}

fmt.Println(hasCycle(head))
}

type ListNode struct {
Val int
Next *ListNode
}

func hasCycle(head *ListNode) bool {
if head == nil || head.Next == nil {
return false
}
slow, fast := head, head
for fast != nil && fast.Next != nil {
slow, fast = slow.Next, fast.Next.Next
if slow == fast {
return true
}
}
return false
}

反转链表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package main

import (
"fmt"
)

func main() {
head := &ListNode{
1, &ListNode{
2, &ListNode{
3, &ListNode{
4, &ListNode{
5, nil}},
},
},
}
for list := reverseList(head); list != nil; list = list.Next {
fmt.Println(list.Val)
}
}

type ListNode struct {
Val int
Next *ListNode
}

func reverseList(head *ListNode) *ListNode {
var cur, prev *ListNode = head, nil
for cur != nil {
cur.Next, prev, cur = prev, cur, cur.Next
}
return prev
}

交换相邻两个节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package main

import "fmt"

func main() {
head := &ListNode{
1, &ListNode{
2, &ListNode{
3, &ListNode{
4, nil},
},
},
}
for list := swapPairs(head); list != nil; list = list.Next {
fmt.Println(list.Val)
}
}

type ListNode struct {
Val int
Next *ListNode
}

func swapPairs(head *ListNode) *ListNode {
if head == nil || head.Next == nil {
return head
}
newHead := head.Next
head.Next = swapPairs(newHead.Next)
newHead.Next = head
return newHead
}

判断链表是否有环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package main

import "fmt"

func main() {

cycle := &ListNode{
2, &ListNode{
0, &ListNode{
-4, nil}}}
cycle.Next.Next.Next = cycle
head := &ListNode{
3, cycle,
}

fmt.Println(hasCycle(head))
}

type ListNode struct {
Val int
Next *ListNode
}

func hasCycle(head *ListNode) bool {
if head == nil || head.Next == nil {
return false
}
slow, fast := head, head
for fast != nil && fast.Next != nil {
slow, fast = slow.Next, fast.Next.Next
if slow == fast {
return true
}
}
return false
}

排序算法

排序算法

冒泡排序

双层循环,每次循环将最大的值放到数组的最后面,外层循环n次,内层循环n-i次完成排序,时间复杂度为O(n²)

1
2
3
4
5
6
7
8
9
func bubbleSort(arr []int) {
for i := 0; i < len(arr)-1; i++ {
for j := 0; j < len(arr)-i-1; j++ {
if arr[j] > arr[j+1] {
arr[j], arr[j+1] = arr[j+1], arr[j]
}
}
}
}

选择排序

双层循环,每次循环找出arr[n-i]中最小的数与当前数进行交换,时间复杂度为O(n²)

1
2
3
4
5
6
7
8
9
10
11
func selectSort(arr []int) {
for i := 0; i < len(arr)-1; i++ {
var min = i
for j := i + 1; j < len(arr); j++ {
if arr[min] > arr[j] {
min = j
}
}
arr[i], arr[min] = arr[min], arr[i]
}
}

插入排序

双层循环,类似打扑克牌调整牌的顺序,每次循环对当前数字顺序进行插入调整,时间复杂度为O(n²)

1
2
3
4
5
6
7
8
9
10
11
func insertionSort(arr []int) {
for i := range arr {
preIndex := i - 1
current := arr[i]
for preIndex >= 0 && arr[preIndex] > current {
arr[preIndex+1] = arr[preIndex]
preIndex -= 1
}
arr[preIndex+1] = current
}
}

归并排序

分治的思想,时间复杂度O(N*logN)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
func mergeSort(arr []int) []int {
if len(arr) < 2 {
return arr
}
i := len(arr) / 2
left := mergeSort(arr[:i])
right := mergeSort(arr[i:])
result := merge(left, right)
return result
}

func merge(left, right []int) []int {
result := make([]int, 0)
m, n := 0, 0
l, r := len(left), len(right)
for m < l && n < r {
if left[m] > right[n] {
result = append(result, right[n])
n++
} else {
result = append(result, left[m])
m++
}
}
result = append(result, right[n:]...)
result = append(result, left[m:]...)
return result
}

堆排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package heap

func sink(array []int, parentIndex int, length int) {
//保存父节点,用于最后的赋值
temp := array[parentIndex]
//左子节点
childIndex := 2*parentIndex + 1
//是否有左子节点
for childIndex < length {
//判断是否有右子节点,并且右子节点大于左子节点的值
if childIndex+1 < length && array[childIndex+1] > array[childIndex] {
childIndex++
}
//如果父节点大于任何一个子节点的值直接跳出
if temp >= array[childIndex] {
break
}
array[parentIndex] = array[childIndex]
parentIndex = childIndex
childIndex = 2*childIndex + 1
}
array[parentIndex] = temp
}

func heapSort(array []int) {
//构建大顶堆
for i := (len(array) - 2) / 2; i >= 0; i-- {
sink(array, i, len(array))
}
//将堆顶元素和最后一个元素交换,数组长度i--(相当于循环删除根顶部元素,然后sink 调整最大堆)
for i := len(array) - 1; i > 0; i-- {
array[i], array[0] = array[0], array[i]
sink(array, 0, i)
}
}

排序算法

冒泡排序

双层循环,每次循环将最大的值放到数组的最后面,外层循环n次,内层循环n-i次完成排序,时间复杂度为O(n²)

1
2
3
4
5
6
7
8
9
func bubbleSort(arr []int) {
for i := 0; i < len(arr)-1; i++ {
for j := 0; j < len(arr)-i-1; j++ {
if arr[j] > arr[j+1] {
arr[j], arr[j+1] = arr[j+1], arr[j]
}
}
}
}

选择排序

双层循环,每次循环找出arr[n-i]中最小的数与当前数进行交换,时间复杂度为O(n²)

1
2
3
4
5
6
7
8
9
10
11
func selectSort(arr []int) {
for i := 0; i < len(arr)-1; i++ {
var min = i
for j := i + 1; j < len(arr); j++ {
if arr[min] > arr[j] {
min = j
}
}
arr[i], arr[min] = arr[min], arr[i]
}
}

插入排序

双层循环,类似打扑克牌调整牌的顺序,每次循环对当前数字顺序进行插入调整,时间复杂度为O(n²)

1
2
3
4
5
6
7
8
9
10
11
func insertionSort(arr []int) {
for i := range arr {
preIndex := i - 1
current := arr[i]
for preIndex >= 0 && arr[preIndex] > current {
arr[preIndex+1] = arr[preIndex]
preIndex -= 1
}
arr[preIndex+1] = current
}
}

归并排序

分治的思想,时间复杂度O(N*logN)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
func mergeSort(arr []int) []int {
if len(arr) < 2 {
return arr
}
i := len(arr) / 2
left := mergeSort(arr[:i])
right := mergeSort(arr[i:])
result := merge(left, right)
return result
}

func merge(left, right []int) []int {
result := make([]int, 0)
m, n := 0, 0
l, r := len(left), len(right)
for m < l && n < r {
if left[m] > right[n] {
result = append(result, right[n])
n++
} else {
result = append(result, left[m])
m++
}
}
result = append(result, right[n:]...)
result = append(result, left[m:]...)
return result
}

堆排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package heap

func sink(array []int, parentIndex int, length int) {
//保存父节点,用于最后的赋值
temp := array[parentIndex]
//左子节点
childIndex := 2*parentIndex + 1
//是否有左子节点
for childIndex < length {
//判断是否有右子节点,并且右子节点大于左子节点的值
if childIndex+1 < length && array[childIndex+1] > array[childIndex] {
childIndex++
}
//如果父节点大于任何一个子节点的值直接跳出
if temp >= array[childIndex] {
break
}
array[parentIndex] = array[childIndex]
parentIndex = childIndex
childIndex = 2*childIndex + 1
}
array[parentIndex] = temp
}

func heapSort(array []int) {
//构建大顶堆
for i := (len(array) - 2) / 2; i >= 0; i-- {
sink(array, i, len(array))
}
//将堆顶元素和最后一个元素交换,数组长度i--(相当于循环删除根顶部元素,然后sink 调整最大堆)
for i := len(array) - 1; i > 0; i-- {
array[i], array[0] = array[0], array[i]
sink(array, 0, i)
}
}

一份建议的Git Commit 模版

1
<类型>: (类型的值见下面描述) <主题> (最多50个字)

feat (新特性)
fix (bug修复)
docs (文档改动)
style (格式化, 缺失分号等; 不包括生产代码变动)
refactor (重构代码)
test (添加缺失的测试, 重构测试, 不包括生产代码变动)
chore (更新grunt任务等; 不包括生产代码变动)

  • 主题和内容以一个空行分隔
  • 主题限制为最大50个字
  • 主题行大写
  • 主题行结束不用标点
  • 主题行使用祈使名
  • 内容每行72个字
  • 内容用于解释为什么和是什么,而不是怎么做
  • 内容多行时以’-'分隔

Git常用命令

一、新建代码库

# 在当前目录新建一个Git代码库
$ git init

# 新建一个目录,将其初始化为Git代码库
$ git init [project-name]

# 下载一个项目和它的整个代码历史
$ git clone [url]

二、配置

Git的设置文件为.gitconfig,它可以在用户主目录下(全局配置),也可以在项目目录下(项目配置)

# 显示当前的Git配置
$ git config --list

# 编辑Git配置文件
$ git config -e [–global]

# 设置提交代码时的用户信息
$ git config [–global] user.name "[name]"
$ git config [–global] user.email "[email address]"

三、增加/删除文件

# 添加指定文件到暂存区
$ git add [file1] [file2] …

# 添加指定目录到暂存区,包括子目录
$ git add [dir]

# 添加当前目录的所有文件到暂存区
$ git add .

# 添加每个变化前,都会要求确认
# 对于同一个文件的多处变化,可以实现分次提交
$ git add -p

# 删除工作区文件,并且将这次删除放入暂存区
$ git rm [file1] [file2] …

# 停止追踪指定文件,但该文件会保留在工作区
$ git rm --cached [file]

# 改名文件,并且将这个改名放入暂存区
$ git mv [file-original] [file-renamed]

四、代码提交

# 提交暂存区到仓库区
$ git commit -m [message]

# 提交暂存区的指定文件到仓库区
$ git commit [file1] [file2] … -m [message]

# 提交工作区自上次commit之后的变化,直接到仓库区
$ git commit -a

# 提交时显示所有diff信息
$ git commit -v

# 使用一次新的commit,替代上一次提交
# 如果代码没有任何新变化,则用来改写上一次commit的提交信息
$ git commit --amend -m [message]

# 重做上一次commit,并包括指定文件的新变化
$ git commit --amend [file1] [file2] …

五、分支

# 列出所有本地分支
$ git branch

# 列出所有远程分支
$ git branch -r

# 列出所有本地分支和远程分支
$ git branch -a

# 新建一个分支,但依然停留在当前分支
$ git branch [branch-name]

# 新建一个分支,并切换到该分支
$ git checkout -b [branch]

# 新建一个分支,指向指定commit
$ git branch [branch] [commit]

# 新建一个分支,与指定的远程分支建立追踪关系
$ git branch --track [branch] [remote-branch]

# 切换到指定分支,并更新工作区
$ git checkout [branch-name]

# 切换到上一个分支
$ git checkout -

# 建立追踪关系,在现有分支与指定的远程分支之间
$ git branch --set-upstream [branch] [remote-branch]

# 合并指定分支到当前分支
$ git merge [branch]

# 选择一个commit,合并进当前分支
$ git cherry-pick [commit]

# 删除分支
$ git branch -d [branch-name]

# 删除远程分支
$ git push origin --delete [branch-name]
$ git branch -dr [remote/branch]

六、标签

# 列出所有tag
$ git tag

# 新建一个tag在当前commit
$ git tag [tag]

# 新建一个tag在指定commit
$ git tag [tag] [commit]

# 删除本地tag
$ git tag -d [tag]

# 删除远程tag
$ git push origin :refs/tags/[tagName]

# 查看tag信息
$ git show [tag]

# 提交指定tag
$ git push [remote] [tag]

# 提交所有tag
$ git push [remote] --tags

# 新建一个分支,指向某个tag
$ git checkout -b [branch] [tag]

七、查看信息

# 显示有变更的文件
$ git status

# 显示当前分支的版本历史
$ git log

# 显示commit历史,以及每次commit发生变更的文件
$ git log --stat

# 搜索提交历史,根据关键词
$ git log -S [keyword]

# 显示某个commit之后的所有变动,每个commit占据一行
$ git log [tag] HEAD --pretty=format:%s

# 显示某个commit之后的所有变动,其"提交说明"必须符合搜索条件
$ git log [tag] HEAD --grep feature

# 显示某个文件的版本历史,包括文件改名
$ git log --follow [file]
$ git whatchanged [file]

# 显示指定文件相关的每一次diff
$ git log -p [file]

# 显示过去5次提交
$ git log -5 --pretty --oneline

# 显示所有提交过的用户,按提交次数排序
$ git shortlog -sn

# 显示指定文件是什么人在什么时间修改过
$ git blame [file]

# 显示暂存区和工作区的代码差异
$ git diff

# 显示暂存区和上一个commit的差异
$ git diff --cached [file]

# 显示工作区与当前分支最新commit之间的差异
$ git diff HEAD

# 显示两次提交之间的差异
$ git diff [first-branch]…[second-branch]

# 显示今天你写了多少行代码
$ git diff --shortstat "@{0 day ago}"

# 显示某次提交的元数据和内容变化
$ git show [commit]

# 显示某次提交发生变化的文件
$ git show --name-only [commit]

# 显示某次提交时,某个文件的内容
$ git show [commit]:[filename]

# 显示当前分支的最近几次提交
$ git reflog

# 从本地master拉取代码更新当前分支:branch 一般为master
$ git rebase [branch]

八、远程同步

$ git remote update --更新远程仓储
# 下载远程仓库的所有变动
$ git fetch [remote]

# 显示所有远程仓库
$ git remote -v

# 显示某个远程仓库的信息
$ git remote show [remote]

# 增加一个新的远程仓库,并命名
$ git remote add [shortname] [url]

# 取回远程仓库的变化,并与本地分支合并
$ git pull [remote] [branch]

# 上传本地指定分支到远程仓库
$ git push [remote] [branch]

# 强行推送当前分支到远程仓库,即使有冲突
$ git push [remote] --force

# 推送所有分支到远程仓库
$ git push [remote] --all

九、撤销

# 恢复暂存区的指定文件到工作区
$ git checkout [file]

# 恢复某个commit的指定文件到暂存区和工作区
$ git checkout [commit] [file]

# 恢复暂存区的所有文件到工作区
$ git checkout .

# 重置暂存区的指定文件,与上一次commit保持一致,但工作区不变
$ git reset [file]

# 重置暂存区与工作区,与上一次commit保持一致
$ git reset --hard

# 重置当前分支的指针为指定commit,同时重置暂存区,但工作区不变
$ git reset [commit]

# 重置当前分支的HEAD为指定commit,同时重置暂存区和工作区,与指定commit一致
$ git reset --hard [commit]

# 重置当前HEAD为指定commit,但保持暂存区和工作区不变
$ git reset --keep [commit]

# 新建一个commit,用来撤销指定commit
# 后者的所有变化都将被前者抵消,并且应用到当前分支

$ git revert [commit]

# 暂时将未提交的变化移除,稍后再移入
$ git stash
$ git stash pop

十、其他

# 生成一个可供发布的压缩包
$ git archive

一、新建代码库

# 在当前目录新建一个Git代码库
$ git init

# 新建一个目录,将其初始化为Git代码库
$ git init [project-name]

# 下载一个项目和它的整个代码历史
$ git clone [url]

二、配置

Git的设置文件为.gitconfig,它可以在用户主目录下(全局配置),也可以在项目目录下(项目配置)

# 显示当前的Git配置
$ git config --list

# 编辑Git配置文件
$ git config -e [–global]

# 设置提交代码时的用户信息
$ git config [–global] user.name "[name]"
$ git config [–global] user.email "[email address]"

三、增加/删除文件

# 添加指定文件到暂存区
$ git add [file1] [file2] …

# 添加指定目录到暂存区,包括子目录
$ git add [dir]

# 添加当前目录的所有文件到暂存区
$ git add .

# 添加每个变化前,都会要求确认
# 对于同一个文件的多处变化,可以实现分次提交
$ git add -p

# 删除工作区文件,并且将这次删除放入暂存区
$ git rm [file1] [file2] …

# 停止追踪指定文件,但该文件会保留在工作区
$ git rm --cached [file]

# 改名文件,并且将这个改名放入暂存区
$ git mv [file-original] [file-renamed]

四、代码提交

# 提交暂存区到仓库区
$ git commit -m [message]

# 提交暂存区的指定文件到仓库区
$ git commit [file1] [file2] … -m [message]

# 提交工作区自上次commit之后的变化,直接到仓库区
$ git commit -a

# 提交时显示所有diff信息
$ git commit -v

# 使用一次新的commit,替代上一次提交
# 如果代码没有任何新变化,则用来改写上一次commit的提交信息
$ git commit --amend -m [message]

# 重做上一次commit,并包括指定文件的新变化
$ git commit --amend [file1] [file2] …

五、分支

# 列出所有本地分支
$ git branch

# 列出所有远程分支
$ git branch -r

# 列出所有本地分支和远程分支
$ git branch -a

# 新建一个分支,但依然停留在当前分支
$ git branch [branch-name]

# 新建一个分支,并切换到该分支
$ git checkout -b [branch]

# 新建一个分支,指向指定commit
$ git branch [branch] [commit]

# 新建一个分支,与指定的远程分支建立追踪关系
$ git branch --track [branch] [remote-branch]

# 切换到指定分支,并更新工作区
$ git checkout [branch-name]

# 切换到上一个分支
$ git checkout -

# 建立追踪关系,在现有分支与指定的远程分支之间
$ git branch --set-upstream [branch] [remote-branch]

# 合并指定分支到当前分支
$ git merge [branch]

# 选择一个commit,合并进当前分支
$ git cherry-pick [commit]

# 删除分支
$ git branch -d [branch-name]

# 删除远程分支
$ git push origin --delete [branch-name]
$ git branch -dr [remote/branch]

六、标签

# 列出所有tag
$ git tag

# 新建一个tag在当前commit
$ git tag [tag]

# 新建一个tag在指定commit
$ git tag [tag] [commit]

# 删除本地tag
$ git tag -d [tag]

# 删除远程tag
$ git push origin :refs/tags/[tagName]

# 查看tag信息
$ git show [tag]

# 提交指定tag
$ git push [remote] [tag]

# 提交所有tag
$ git push [remote] --tags

# 新建一个分支,指向某个tag
$ git checkout -b [branch] [tag]

七、查看信息

# 显示有变更的文件
$ git status

# 显示当前分支的版本历史
$ git log

# 显示commit历史,以及每次commit发生变更的文件
$ git log --stat

# 搜索提交历史,根据关键词
$ git log -S [keyword]

# 显示某个commit之后的所有变动,每个commit占据一行
$ git log [tag] HEAD --pretty=format:%s

# 显示某个commit之后的所有变动,其"提交说明"必须符合搜索条件
$ git log [tag] HEAD --grep feature

# 显示某个文件的版本历史,包括文件改名
$ git log --follow [file]
$ git whatchanged [file]

# 显示指定文件相关的每一次diff
$ git log -p [file]

# 显示过去5次提交
$ git log -5 --pretty --oneline

# 显示所有提交过的用户,按提交次数排序
$ git shortlog -sn

# 显示指定文件是什么人在什么时间修改过
$ git blame [file]

# 显示暂存区和工作区的代码差异
$ git diff

# 显示暂存区和上一个commit的差异
$ git diff --cached [file]

# 显示工作区与当前分支最新commit之间的差异
$ git diff HEAD

# 显示两次提交之间的差异
$ git diff [first-branch]…[second-branch]

# 显示今天你写了多少行代码
$ git diff --shortstat "@{0 day ago}"

# 显示某次提交的元数据和内容变化
$ git show [commit]

# 显示某次提交发生变化的文件
$ git show --name-only [commit]

# 显示某次提交时,某个文件的内容
$ git show [commit]:[filename]

# 显示当前分支的最近几次提交
$ git reflog

# 从本地master拉取代码更新当前分支:branch 一般为master
$ git rebase [branch]

八、远程同步

$ git remote update --更新远程仓储
# 下载远程仓库的所有变动
$ git fetch [remote]

# 显示所有远程仓库
$ git remote -v

# 显示某个远程仓库的信息
$ git remote show [remote]

# 增加一个新的远程仓库,并命名
$ git remote add [shortname] [url]

# 取回远程仓库的变化,并与本地分支合并
$ git pull [remote] [branch]

# 上传本地指定分支到远程仓库
$ git push [remote] [branch]

# 强行推送当前分支到远程仓库,即使有冲突
$ git push [remote] --force

# 推送所有分支到远程仓库
$ git push [remote] --all

九、撤销

# 恢复暂存区的指定文件到工作区
$ git checkout [file]

# 恢复某个commit的指定文件到暂存区和工作区
$ git checkout [commit] [file]

# 恢复暂存区的所有文件到工作区
$ git checkout .

# 重置暂存区的指定文件,与上一次commit保持一致,但工作区不变
$ git reset [file]

# 重置暂存区与工作区,与上一次commit保持一致
$ git reset --hard

# 重置当前分支的指针为指定commit,同时重置暂存区,但工作区不变
$ git reset [commit]

# 重置当前分支的HEAD为指定commit,同时重置暂存区和工作区,与指定commit一致
$ git reset --hard [commit]

# 重置当前HEAD为指定commit,但保持暂存区和工作区不变
$ git reset --keep [commit]

# 新建一个commit,用来撤销指定commit
# 后者的所有变化都将被前者抵消,并且应用到当前分支

$ git revert [commit]

# 暂时将未提交的变化移除,稍后再移入
$ git stash
$ git stash pop

十、其他

# 生成一个可供发布的压缩包
$ git archive

Nginx基本配置

1. Nginx 常用操作整理

1.1 启动 Nginx

1
./sbin/nginx

1.2 停止 Nginx

1
2
3
./sbin/nginx -s stop
./sbin/nginx -s quit
-s 都是采用向 Nginx 发送信号的方式。

1.3 重载 Nginx

1
./sbin/nginx -s reload

1.4 指定配置文件

1
./sbin/nginx -c /etc/nginx/nginx.conf

1.5 配置语法检查

1
2
3
./sbin/nginx -t /etc/nginx/nginx.conf

常规用法: ./sbin/nginx -tc /etc/nginx/nginx.conf

2. 配置文件详解

Nginx配置文件的分块下,基本就分为以下几块:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
main # 全局设置
events { # Nginx工作模式
....
}
http { # http设置
....
upstream myproject { # 负载均衡服务器设置
.....
}
server { # 主机设置
....
location { # URL匹配
....
}
}
server {
....
location {
....
}
}
....
}

2.1 main模块

下面是一个main区域,它是一个全局的设置:

1
2
3
4
5
user nobody nobody;
worker_processes 2;
error_log /usr/local/var/log/nginx/error.log notice;
pid /usr/local/var/run/nginx/nginx.pid;
worker_rlimit_nofile 1024;

user 来指定Nginx Worker进程运行用户以及用户组,默认由nobody账号运行。

worker_processes 来指定了Nginx要开启的子进程数。每个Nginx进程平均耗费10M~12M内存。根据经验,一般指定1个进程就足够了,如果是多核CPU,建议指定和CPU的数量一样的进程数即可。我这里写2,那么就会开启2个子进程,总共3个进程。

error_log 来定义全局错误日志文件。日志输出级别有debug、info、notice、warn、error、crit可供选择,其中,debug输出日志最为最详细,而crit输出日志最少。

pid 来指定进程id的存储文件位置。

worker_rlimit_nofile 来指定一个nginx进程可以打开的最多文件描述符数目,这里是65535,需要使用命令“ulimit -n 65535”来设置。

2.2 events模块

events模块来用指定nginx的工作模式和工作模式及连接数上限,一般是这样:

1
2
3
4
events {
use kqueue; #mac平台
worker_connections 1024;
}

use 用来指定Nginx的工作模式。Nginx支持的工作模式有select、poll、kqueue、epoll、rtsig和/dev/poll。其中select和poll都是标准的工作模式,kqueue和epoll是高效的工作模式,不同的是epoll用在Linux平台上,而kqueue用在BSD系统中,因为Mac基于BSD,所以Mac也得用这个模式,对于Linux系统,epoll工作模式是首选。

worker_connections 用于定义Nginx每个进程的最大连接数,即接收前端的最大请求数,默认是1024。最大客户端连接数由worker_processes和worker_connections决定,即Max_clients = worker_processes * worker_connections,在作为反向代理时,Max_clients变为:Max_clients = worker_processes * worker_connections / 4。

进程的最大连接数受Linux系统进程的最大打开文件数限制,在执行操作系统命令“ulimit -n 65536”后worker_connections的设置才能生效。

2.3 http模块

http模块可以说是最核心的模块了,它负责HTTP服务器相关属性的配置,它里面的server和upstream子模块,至关重要,等到反向代理和负载均衡以及虚拟目录等会仔细说。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
http {
include mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /usr/local/var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 10;
#gzip on;
upstream myproject {
.....
}
server {
....
}
}
2.3.1 include

用来设定文件的mime类型,类型在配置文件目录下的mime.type文件定义,来告诉nginx来识别文件类型。

2.3.2 default_type

设定了默认的类型为二进制流,也就是当文件类型未定义时使用这种方式,例如在没有配置asp的locate 环境时,Nginx是不予解析的,此时,用浏览器访问asp文件就会出现下载窗口了。

2.3.3 log_format

用于设置日志的格式,和记录哪些参数,这里设置为main,刚好用于access_log来纪录这种类型。

2.3.4 access_log

用来纪录每次的访问日志的文件地址,后面的main是日志的格式样式,对应于log_format的main。

2.3.5 sendfile

用于开启高效文件传输模式。将tcp_nopush和tcp_nodelay两个指令设置为on用于防止网络阻塞。

2.3.6 keepalive_timeout

设置客户端连接保持活动的超时时间。在超过这个时间之后,服务器会关闭该连接。

6.4 server模块

server模块是http的子模块,它用来定一个虚拟主机,我们先讲最基本的配置,这些在后面再讲。我们看一下一个简单的server是如何做的?

1
2
3
4
5
6
7
8
9
10
11
server {
listen 8080;
server_name localhost 192.168.12.10 www.yangyi.com;
# 全局定义,如果都是这一个目录,这样定义最简单。
root /Users/yangyi/www;
index index.php index.html index.htm;
charset utf-8;
access_log usr/local/var/log/host.access.log main;
error_log usr/local/var/log/host.error.log error;
....
}

server 标志定义虚拟主机开始。

listen 用于指定虚拟主机的服务端口。

server_name 用来指定IP地址或者域名,多个域名之间用空格分开。

root 表示在这整个server虚拟主机内,全部的root web根目录。注意要和locate {}下面定义的区分开来。

index 全局定义访问的默认首页地址。注意要和locate {}下面定义的区分开来。

charset 用于设置网页的默认编码格式。

access_log 用来指定此虚拟主机的访问日志存放路径,最后的main用于指定访问日志的输出格式。

2.5 location模块

location模块是nginx中用的最多的,也是最重要的模块了,什么负载均衡啊、反向代理啊、虚拟域名啊都与它相关。

location根据它字面意思就知道是来定位的,定位URL,解析URL,所以,它也提供了强大的正则匹配功能,也支持条件判断匹配,用户可以通过location指令实现Nginx对动、静态网页进行过滤处理。像我们的php环境搭建就是用到了它。

2.5.1 我们先来看这个,设定默认首页和虚拟机目录
1
2
3
4
location / {
root /Users/yangyi/www;
index index.php index.html index.htm;
}

location / 表示匹配访问根目录。

root 指令用于指定访问根目录时,虚拟主机的web目录,这个目录可以是相对路径(相对路径是相对于nginx的安装目录)。也可以是绝对路径。

index 用于设定我们只输入域名后访问的默认首页地址,有个先后顺序:index.php index.html index.htm,如果没有开启目录浏览权限,又找不到这些默认首页,就会报403错误。

2.5.2 location 还有一种方式就是正则匹配

下面这个例子是运用正则匹配来链接php。我们之前搭建环境也是这样做:

1
2
3
4
5
6
location ~ \.php$ {
root /Users/yangyi/www;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include fastcgi.conf;
}

.php$ 熟悉正则的我们直到,这是匹配.php结尾的URL,用来解析php文件。里面的root也是一样,用来表示虚拟主机的根目录。
fastcgi_pass 链接的是php-fpm的地址。其他几个参数我们以后再说。

location 还有其他用法,等讲到实例的时候,再看吧。

2.6 upstream模块

upstream 模块负责负载均衡模块,通过一个简单的调度算法来实现客户端IP到后端服务器的负载均衡。先学习怎么用,具体的使用实例以后再说。

1
2
3
4
5
6
7
upstream deveye.cn{
ip_hash;
server 192.168.12.1:80;
server 192.168.12.2:80 down;
server 192.168.12.3:8080 max_fails=3 fail_timeout=20s;
server 192.168.12.4:8080;
}

在上面的例子中,通过upstream指令指定了一个负载均衡器的名称iyangyi.com。这个名称可以任意指定,在后面需要的地方直接调用即可。里面是ip_hash这是其中的一种负载均衡调度算法,下面会着重介绍。紧接着就是各种服务器了。用server关键字表识,后面接ip。
Nginx的负载均衡模块目前支持4种调度算法:

  1. weight 轮询(默认)。每个请求按时间顺序逐一分配到不同的后端服务器,如果后端某台服务器宕机,故障系统被自动剔除,使用户访问不受影响。weight,指定轮询权值,weight值越大,分配到的访问机率越高,主要用于后端每个服务器性能不均的情况下。

  2. ip_hash。每个请求按访问IP的hash结果分配,这样来自同一个IP的访客固定访问一个后端服务器,有效解决了动态网页存在的session共享问题。

  3. fair(第三方)。比上面两个更加智能的负载均衡算法。此种算法可以依据页面大小和加载时间长短智能地进行负载均衡,也就是根据后端服务器的响应时间来分配请求,响应时间短的优先分配。Nginx本身是不支持fair的,如果需要使用这种调度算法,必须下载Nginx的upstream_fair模块。

  4. url_hash(第三方)。按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,可以进一步提高后端缓存服务器的效率。Nginx本身是不支持url_hash的,如果需要使用这种调度算法,必须安装Nginx的hash软件包。

在HTTP Upstream模块中,可以通过server指令指定后端服务器的IP地址和端口,同时还可以设定每个后端服务器在负载均衡调度中的状态。常用的状态有:

  1. down,表示当前的server暂时不参与负载均衡。

  2. backup,预留的备份机器。当其他所有的非backup机器出现故障或者忙的时候,才会请求backup机器,因此这台机器的压力最轻。

  3. max_fails,允许请求失败的次数,默认为1。当超过最大次数时,返回proxy_next_upstream 模块定义的错误。

  4. fail_timeout,在经历了max_fails次失败后,暂停服务的时间。max_fails可以和fail_timeout一起使用。

注意:当负载调度算法为ip_hash时,后端服务器在负载均衡调度中的状态不能是weight和backup。

转载自 :https://www.jianshu.com/p/f04733896a48

1. Nginx 常用操作整理

1.1 启动 Nginx

1
./sbin/nginx

1.2 停止 Nginx

1
2
3
./sbin/nginx -s stop
./sbin/nginx -s quit
-s 都是采用向 Nginx 发送信号的方式。

1.3 重载 Nginx

1
./sbin/nginx -s reload

1.4 指定配置文件

1
./sbin/nginx -c /etc/nginx/nginx.conf

1.5 配置语法检查

1
2
3
./sbin/nginx -t /etc/nginx/nginx.conf

常规用法: ./sbin/nginx -tc /etc/nginx/nginx.conf

2. 配置文件详解

Nginx配置文件的分块下,基本就分为以下几块:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
main # 全局设置
events { # Nginx工作模式
....
}
http { # http设置
....
upstream myproject { # 负载均衡服务器设置
.....
}
server { # 主机设置
....
location { # URL匹配
....
}
}
server {
....
location {
....
}
}
....
}

2.1 main模块

下面是一个main区域,它是一个全局的设置:

1
2
3
4
5
user nobody nobody;
worker_processes 2;
error_log /usr/local/var/log/nginx/error.log notice;
pid /usr/local/var/run/nginx/nginx.pid;
worker_rlimit_nofile 1024;

user 来指定Nginx Worker进程运行用户以及用户组,默认由nobody账号运行。

worker_processes 来指定了Nginx要开启的子进程数。每个Nginx进程平均耗费10M~12M内存。根据经验,一般指定1个进程就足够了,如果是多核CPU,建议指定和CPU的数量一样的进程数即可。我这里写2,那么就会开启2个子进程,总共3个进程。

error_log 来定义全局错误日志文件。日志输出级别有debug、info、notice、warn、error、crit可供选择,其中,debug输出日志最为最详细,而crit输出日志最少。

pid 来指定进程id的存储文件位置。

worker_rlimit_nofile 来指定一个nginx进程可以打开的最多文件描述符数目,这里是65535,需要使用命令“ulimit -n 65535”来设置。

2.2 events模块

events模块来用指定nginx的工作模式和工作模式及连接数上限,一般是这样:

1
2
3
4
events {
use kqueue; #mac平台
worker_connections 1024;
}

use 用来指定Nginx的工作模式。Nginx支持的工作模式有select、poll、kqueue、epoll、rtsig和/dev/poll。其中select和poll都是标准的工作模式,kqueue和epoll是高效的工作模式,不同的是epoll用在Linux平台上,而kqueue用在BSD系统中,因为Mac基于BSD,所以Mac也得用这个模式,对于Linux系统,epoll工作模式是首选。

worker_connections 用于定义Nginx每个进程的最大连接数,即接收前端的最大请求数,默认是1024。最大客户端连接数由worker_processes和worker_connections决定,即Max_clients = worker_processes * worker_connections,在作为反向代理时,Max_clients变为:Max_clients = worker_processes * worker_connections / 4。

进程的最大连接数受Linux系统进程的最大打开文件数限制,在执行操作系统命令“ulimit -n 65536”后worker_connections的设置才能生效。

2.3 http模块

http模块可以说是最核心的模块了,它负责HTTP服务器相关属性的配置,它里面的server和upstream子模块,至关重要,等到反向代理和负载均衡以及虚拟目录等会仔细说。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
http {
include mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /usr/local/var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 10;
#gzip on;
upstream myproject {
.....
}
server {
....
}
}
2.3.1 include

用来设定文件的mime类型,类型在配置文件目录下的mime.type文件定义,来告诉nginx来识别文件类型。

2.3.2 default_type

设定了默认的类型为二进制流,也就是当文件类型未定义时使用这种方式,例如在没有配置asp的locate 环境时,Nginx是不予解析的,此时,用浏览器访问asp文件就会出现下载窗口了。

2.3.3 log_format

用于设置日志的格式,和记录哪些参数,这里设置为main,刚好用于access_log来纪录这种类型。

2.3.4 access_log

用来纪录每次的访问日志的文件地址,后面的main是日志的格式样式,对应于log_format的main。

2.3.5 sendfile

用于开启高效文件传输模式。将tcp_nopush和tcp_nodelay两个指令设置为on用于防止网络阻塞。

2.3.6 keepalive_timeout

设置客户端连接保持活动的超时时间。在超过这个时间之后,服务器会关闭该连接。

6.4 server模块

server模块是http的子模块,它用来定一个虚拟主机,我们先讲最基本的配置,这些在后面再讲。我们看一下一个简单的server是如何做的?

1
2
3
4
5
6
7
8
9
10
11
server {
listen 8080;
server_name localhost 192.168.12.10 www.yangyi.com;
# 全局定义,如果都是这一个目录,这样定义最简单。
root /Users/yangyi/www;
index index.php index.html index.htm;
charset utf-8;
access_log usr/local/var/log/host.access.log main;
error_log usr/local/var/log/host.error.log error;
....
}

server 标志定义虚拟主机开始。

listen 用于指定虚拟主机的服务端口。

server_name 用来指定IP地址或者域名,多个域名之间用空格分开。

root 表示在这整个server虚拟主机内,全部的root web根目录。注意要和locate {}下面定义的区分开来。

index 全局定义访问的默认首页地址。注意要和locate {}下面定义的区分开来。

charset 用于设置网页的默认编码格式。

access_log 用来指定此虚拟主机的访问日志存放路径,最后的main用于指定访问日志的输出格式。

2.5 location模块

location模块是nginx中用的最多的,也是最重要的模块了,什么负载均衡啊、反向代理啊、虚拟域名啊都与它相关。

location根据它字面意思就知道是来定位的,定位URL,解析URL,所以,它也提供了强大的正则匹配功能,也支持条件判断匹配,用户可以通过location指令实现Nginx对动、静态网页进行过滤处理。像我们的php环境搭建就是用到了它。

2.5.1 我们先来看这个,设定默认首页和虚拟机目录
1
2
3
4
location / {
root /Users/yangyi/www;
index index.php index.html index.htm;
}

location / 表示匹配访问根目录。

root 指令用于指定访问根目录时,虚拟主机的web目录,这个目录可以是相对路径(相对路径是相对于nginx的安装目录)。也可以是绝对路径。

index 用于设定我们只输入域名后访问的默认首页地址,有个先后顺序:index.php index.html index.htm,如果没有开启目录浏览权限,又找不到这些默认首页,就会报403错误。

2.5.2 location 还有一种方式就是正则匹配

下面这个例子是运用正则匹配来链接php。我们之前搭建环境也是这样做:

1
2
3
4
5
6
location ~ \.php$ {
root /Users/yangyi/www;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include fastcgi.conf;
}

.php$ 熟悉正则的我们直到,这是匹配.php结尾的URL,用来解析php文件。里面的root也是一样,用来表示虚拟主机的根目录。
fastcgi_pass 链接的是php-fpm的地址。其他几个参数我们以后再说。

location 还有其他用法,等讲到实例的时候,再看吧。

2.6 upstream模块

upstream 模块负责负载均衡模块,通过一个简单的调度算法来实现客户端IP到后端服务器的负载均衡。先学习怎么用,具体的使用实例以后再说。

1
2
3
4
5
6
7
upstream deveye.cn{
ip_hash;
server 192.168.12.1:80;
server 192.168.12.2:80 down;
server 192.168.12.3:8080 max_fails=3 fail_timeout=20s;
server 192.168.12.4:8080;
}

在上面的例子中,通过upstream指令指定了一个负载均衡器的名称iyangyi.com。这个名称可以任意指定,在后面需要的地方直接调用即可。里面是ip_hash这是其中的一种负载均衡调度算法,下面会着重介绍。紧接着就是各种服务器了。用server关键字表识,后面接ip。
Nginx的负载均衡模块目前支持4种调度算法:

  1. weight 轮询(默认)。每个请求按时间顺序逐一分配到不同的后端服务器,如果后端某台服务器宕机,故障系统被自动剔除,使用户访问不受影响。weight,指定轮询权值,weight值越大,分配到的访问机率越高,主要用于后端每个服务器性能不均的情况下。

  2. ip_hash。每个请求按访问IP的hash结果分配,这样来自同一个IP的访客固定访问一个后端服务器,有效解决了动态网页存在的session共享问题。

  3. fair(第三方)。比上面两个更加智能的负载均衡算法。此种算法可以依据页面大小和加载时间长短智能地进行负载均衡,也就是根据后端服务器的响应时间来分配请求,响应时间短的优先分配。Nginx本身是不支持fair的,如果需要使用这种调度算法,必须下载Nginx的upstream_fair模块。

  4. url_hash(第三方)。按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,可以进一步提高后端缓存服务器的效率。Nginx本身是不支持url_hash的,如果需要使用这种调度算法,必须安装Nginx的hash软件包。

在HTTP Upstream模块中,可以通过server指令指定后端服务器的IP地址和端口,同时还可以设定每个后端服务器在负载均衡调度中的状态。常用的状态有:

  1. down,表示当前的server暂时不参与负载均衡。

  2. backup,预留的备份机器。当其他所有的非backup机器出现故障或者忙的时候,才会请求backup机器,因此这台机器的压力最轻。

  3. max_fails,允许请求失败的次数,默认为1。当超过最大次数时,返回proxy_next_upstream 模块定义的错误。

  4. fail_timeout,在经历了max_fails次失败后,暂停服务的时间。max_fails可以和fail_timeout一起使用。

注意:当负载调度算法为ip_hash时,后端服务器在负载均衡调度中的状态不能是weight和backup。

转载自 :https://www.jianshu.com/p/f04733896a48

Redis复制

Replication

Redis使用主从复制非常简单,它允许从实例成为主实例的副本,每当链接断开时,从节点将自动重新连接到主服务器上。并且无论主服务器发生什么情况,从节点都尝试完全复制所有数据。

Redis主从复制使用以下三种机制:

  • 当主从实例连接良好时,主节点向从节点发送命令流来进行更新,是为了复制由于Key过期或回收等其他操作对数据产生影响。
  • 当主从实例断开时,从节点重新连接后只会尝试获取链接断开期间错开的命令流。
  • 如果无法进行部分重新同步,则会要求完全重新同步。

默认情况下,Redis会使用异步复制,Redis从节点会异步地确认接收的数据。因此,主节点不会等待从节点处理完成。但是,如果需要知道从节点都处理了那些命令,也可以选择同步复制。

Redis 复制的几个特征:

  1. Redis是使用异步复制
  2. 一个主节点可以有多个从节点
  3. 从节点还可以有子节点,从Redis 4.0开始,所有子节点将从主节点接收完全相同的复制流。
  4. Redis主从复制是无阻塞的,意味着在进行复制或同步是仍然可以进行数据查询
  5. 复制既可以用于可伸缩性,也可以用于只读查询的多个副本(读写分离)
  6. 可以配置主节点不进行数据保存或只启用AOF,然后将数据保存到副本,但是注意:当服务器重启后,从节点服务器重新同步数据时,同时会清空从节点数据。

Redis 复制的原理:

每个Redis Master 节点都有一个replication ID:这是一个较大的伪随机字符串,用于标记当前数据集。每个Master 还有一个偏移量,该偏移量会针对复制流中要发送到副本的每个字节的增量而增加。
以便更新副本的状态。即使未连接任何副本,复制偏移也会增加。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Replication
role:master
connected_slaves:1
slave0:ip=39.105.157.176,port=6379,state=online,offset=61238,lag=0
# 主 Replication ID
master_replid:649255ffb2183786d00203aa51715169b06f4f47
# 副 Replication ID
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:61238
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:61238

当从节点连接主节点时,会使用PSYNC命令来发送当前旧的主节点的Replication ID和offset,这样主节点只会同步相差的数据量,如果主节点缓冲区的副本引用(Replication ID)不存在,则会进行全量同步。

全量同步的工作流程如下:

主节点开始后台生成RDB文件。同时,它开始缓冲从客户端收到的所有新写入命令。后台保存完成后,主数据库将数据库文件传输到从节点,从节点将其保存在磁盘上,然后将其加载到内存中。然后,主服务器将所有缓冲的命令发送到副本。

Replication ID 说明:

如果两个实例具有相同的Replication ID和offset,则它们具有完全相同的数据。但是为什么会有两个Replication ID(主和副)?

因为两个实例A和B具有相同的Replication ID,但一个实例的offset为1000,另一个实例的offset为1023,则意味着第一个实例缺少应用于数据集的某些命令。这也意味着,仅通过应用一些命令,A即可达到与B完全相同的状态。

Redis实例具有两个Replication ID的原因是由于副本被提升为主副本。故障转移后,升级后的副本仍需要记住其过去的Replication ID,因为该Replication ID是以前的主副本之一。这样,当其他副本将与新的主副本同步时,它们将尝试使用旧的主Replication ID执行部分重新同步。因为将副本提升为主副本时,它会将其辅助ID设置为其主ID,并记住发生此ID切换时的offset。稍后它将选择一个新的随机Replication ID,因为新的历史记录开始了。处理新的副本连接时,主机将其ID和offset与当前ID和辅助ID相匹配(为安全起见,直到给定的偏移量)。简而言之,这意味着在故障转移后,连接到新提升的主服务器的副本不必执行完全同步。

无盘复制

通常,完全重新同步需要在磁盘上创建RDB文件,然后从磁盘重新加载相同的RDB,以便为副本提供数据。
但是对于硬盘速度慢,但是在内网环境下,可以采用无盘复制,这样可以直接通过Socket将RDB发送给从节点,而无需使用硬盘作为中间存储。

配置

1
replicaof 192.168.1.1 6379

也可以使用REPLICAOF命令进行同步。
可以使用repl-diskless-sync配置参数启用无盘复制。在repl-diskless-sync-delay 参数控制第一个副本之后,为了等待更多副本到达而开始传输的延迟。

1
masterauth <password>

设置主节点的密码,也可以使用redis-cli输入config set masterauth <password>进行设置。

1
2
3
4
# 在当前至少有N个副本连接到主服务器时才接受写查询
min-replicas-to-write <number of replicas>
# 在有N个副本延迟少于M秒时,则接受写入
min-replicas-max-lag <number of seconds>

Replication

Redis使用主从复制非常简单,它允许从实例成为主实例的副本,每当链接断开时,从节点将自动重新连接到主服务器上。并且无论主服务器发生什么情况,从节点都尝试完全复制所有数据。

Redis主从复制使用以下三种机制:

  • 当主从实例连接良好时,主节点向从节点发送命令流来进行更新,是为了复制由于Key过期或回收等其他操作对数据产生影响。
  • 当主从实例断开时,从节点重新连接后只会尝试获取链接断开期间错开的命令流。
  • 如果无法进行部分重新同步,则会要求完全重新同步。

默认情况下,Redis会使用异步复制,Redis从节点会异步地确认接收的数据。因此,主节点不会等待从节点处理完成。但是,如果需要知道从节点都处理了那些命令,也可以选择同步复制。

Redis 复制的几个特征:

  1. Redis是使用异步复制
  2. 一个主节点可以有多个从节点
  3. 从节点还可以有子节点,从Redis 4.0开始,所有子节点将从主节点接收完全相同的复制流。
  4. Redis主从复制是无阻塞的,意味着在进行复制或同步是仍然可以进行数据查询
  5. 复制既可以用于可伸缩性,也可以用于只读查询的多个副本(读写分离)
  6. 可以配置主节点不进行数据保存或只启用AOF,然后将数据保存到副本,但是注意:当服务器重启后,从节点服务器重新同步数据时,同时会清空从节点数据。

Redis 复制的原理:

每个Redis Master 节点都有一个replication ID:这是一个较大的伪随机字符串,用于标记当前数据集。每个Master 还有一个偏移量,该偏移量会针对复制流中要发送到副本的每个字节的增量而增加。
以便更新副本的状态。即使未连接任何副本,复制偏移也会增加。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Replication
role:master
connected_slaves:1
slave0:ip=39.105.157.176,port=6379,state=online,offset=61238,lag=0
# 主 Replication ID
master_replid:649255ffb2183786d00203aa51715169b06f4f47
# 副 Replication ID
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:61238
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:61238

当从节点连接主节点时,会使用PSYNC命令来发送当前旧的主节点的Replication ID和offset,这样主节点只会同步相差的数据量,如果主节点缓冲区的副本引用(Replication ID)不存在,则会进行全量同步。

全量同步的工作流程如下:

主节点开始后台生成RDB文件。同时,它开始缓冲从客户端收到的所有新写入命令。后台保存完成后,主数据库将数据库文件传输到从节点,从节点将其保存在磁盘上,然后将其加载到内存中。然后,主服务器将所有缓冲的命令发送到副本。

Replication ID 说明:

如果两个实例具有相同的Replication ID和offset,则它们具有完全相同的数据。但是为什么会有两个Replication ID(主和副)?

因为两个实例A和B具有相同的Replication ID,但一个实例的offset为1000,另一个实例的offset为1023,则意味着第一个实例缺少应用于数据集的某些命令。这也意味着,仅通过应用一些命令,A即可达到与B完全相同的状态。

Redis实例具有两个Replication ID的原因是由于副本被提升为主副本。故障转移后,升级后的副本仍需要记住其过去的Replication ID,因为该Replication ID是以前的主副本之一。这样,当其他副本将与新的主副本同步时,它们将尝试使用旧的主Replication ID执行部分重新同步。因为将副本提升为主副本时,它会将其辅助ID设置为其主ID,并记住发生此ID切换时的offset。稍后它将选择一个新的随机Replication ID,因为新的历史记录开始了。处理新的副本连接时,主机将其ID和offset与当前ID和辅助ID相匹配(为安全起见,直到给定的偏移量)。简而言之,这意味着在故障转移后,连接到新提升的主服务器的副本不必执行完全同步。

无盘复制

通常,完全重新同步需要在磁盘上创建RDB文件,然后从磁盘重新加载相同的RDB,以便为副本提供数据。
但是对于硬盘速度慢,但是在内网环境下,可以采用无盘复制,这样可以直接通过Socket将RDB发送给从节点,而无需使用硬盘作为中间存储。

配置

1
replicaof 192.168.1.1 6379

也可以使用REPLICAOF命令进行同步。
可以使用repl-diskless-sync配置参数启用无盘复制。在repl-diskless-sync-delay 参数控制第一个副本之后,为了等待更多副本到达而开始传输的延迟。

1
masterauth <password>

设置主节点的密码,也可以使用redis-cli输入config set masterauth <password>进行设置。

1
2
3
4
# 在当前至少有N个副本连接到主服务器时才接受写查询
min-replicas-to-write <number of replicas>
# 在有N个副本延迟少于M秒时,则接受写入
min-replicas-max-lag <number of seconds>

Redis持久化

Redis的持久化机制-RDB

1.什么是RDB

The RDB persistence performs point-in-time snapshots of your dataset at specified intervals.(RDB持久化是以指定的时间间隔执行数据集的时间点快照。)
简单来说,RDB是每隔一段时间,会把内存中的数据写入磁盘的临时文件作为快照,恢复时把快照文件读取到内存中。如果机器宕机,那么内存中的数据就会丢失,使用RDB机制后,重启后数据会恢复。

2.备份与恢复

内存备份 --> 磁盘临时文件
临时文件 --> 恢复到内存

3.RDB优劣势

  • 优势

    1. 每隔一段时间全量备份
    2. 容灾简单,适合进行远程冷备份
    3. 子进程备份的时候,主进程不会有任何IO操作(不会有写入或修改),保证备份数据的完整性
    4. 相对AOF来说,当有更大的文件时候可以快速重启恢复
  • 劣势

    1. 发生故障时,可能会丢失最后一次备份数据
    2. 子进程所占用的内存会和父进程一模一样,会造成CPU负担
    3. 由于定时全量备份是重量级操作,所以对于实时备份,就无法处理了

4.RDB的配置

  1. 保存位置可以自定义配置:
1
dir /var/redis/6379/dump.rdb
  1. 保存机制
1
2
3
4
5
6
7
8
#   save <seconds> <changes>
# 900秒(15分钟)后,如果至少有 1 个Key发生改变
# 300秒(5分钟)后,如果至少有 10 个Key发生改变
# 60秒(1分钟)后,如果至少有 10000 个Key发生改变

save 900 1
save 300 10
save 60 10000

Redis的持久化机制-AOF

1.什么是AOF

The AOF persistence logs every write operation received by the server, that will be played again at server startup, reconstructing the original dataset. Commands are logged using the same format as the Redis protocol itself, in an append-only fashion. Redis is able to rewrite the log in the background when it gets too big.

AOF持久化会记录服务器接收到的每个写入操作,这些操作将在服务器启动时再次执行,以重新构建原始的数据。命令记录的格式与Redis协议本身的格式相同,采用追加方式。当日志文件过大时,Redis可以在后台进行rewrite

2.备份与恢复

日志备份 --> 命令追加方式
日志文件 --> 根据命令日志文件重新构建

3.AOF优劣势

  • 优势
    1. 使用AOF数据持久化更加完整
    2. 可以使用不同的fsync策略,使用默认策略fsync时,每秒的写入性能仍然很好(fsync是使用后台线程执行的,并且在没有进行fsync的情况下,主线程将尽力执行写入操作)但是会损失一秒的写入时间
    3. AOF是使用日志追加的方式,如果断电或其他原因导致日志只写了一半,可以使用redis-check-aof工具进行修复
    4. Redis太大时,Redis可以在后台自动重写AOF。 Redis继续追加到旧文件时,会生成一个新的文件,其中包含创建当前数据集所需的最少操作集,一旦准备好第二个文件,Redis会切换这两个文件并开始追加到新的那一个
    5. 即使使用FLUSHALL命令清空了所有数据,也可以通过修改AOF文件进行数据恢复
  • 劣势
    1. AOF相比RDB占用空间更大
    2. 使用fsync策略后,相比RDB性能差一些
    3. 当服务器宕机后,使用AOF恢复的数据可能不完整

4.AOF的配置

  1. 开启AOF
1
appendonly yes
  1. 配置fsync策略
1
2
3
4
5
6
# If unsure, use "everysec".
# 如果不确定,推荐使用 "everysec"

# appendfsync always
appendfsync everysec
# appendfsync no
  1. AOF rewrite配置
1
2
3
4
# 设置一个百分比
auto-aof-rewrite-percentage 100
# 设置AOF 的最小大小
auto-aof-rewrite-min-size 64mb
  1. Redis 启动加载配置
1
2
# 优先加载AOF 文件
aof-use-rdb-preamble yes

两种持久化方式改如何选择

  • 如果对数据可靠性要求很高,则应同时使用两种持久性方式。
  • 如果在灾难情况下仍然可以承受几分钟的数据丢失,则可以仅使用RDB。
  • 不推荐单独使用AOF,因为AOF在数据恢复时,有时候会出现Bugs。

Redis的持久化机制-RDB

1.什么是RDB

The RDB persistence performs point-in-time snapshots of your dataset at specified intervals.(RDB持久化是以指定的时间间隔执行数据集的时间点快照。)
简单来说,RDB是每隔一段时间,会把内存中的数据写入磁盘的临时文件作为快照,恢复时把快照文件读取到内存中。如果机器宕机,那么内存中的数据就会丢失,使用RDB机制后,重启后数据会恢复。

2.备份与恢复

内存备份 --> 磁盘临时文件
临时文件 --> 恢复到内存

3.RDB优劣势

  • 优势

    1. 每隔一段时间全量备份
    2. 容灾简单,适合进行远程冷备份
    3. 子进程备份的时候,主进程不会有任何IO操作(不会有写入或修改),保证备份数据的完整性
    4. 相对AOF来说,当有更大的文件时候可以快速重启恢复
  • 劣势

    1. 发生故障时,可能会丢失最后一次备份数据
    2. 子进程所占用的内存会和父进程一模一样,会造成CPU负担
    3. 由于定时全量备份是重量级操作,所以对于实时备份,就无法处理了

4.RDB的配置

  1. 保存位置可以自定义配置:
1
dir /var/redis/6379/dump.rdb
  1. 保存机制
1
2
3
4
5
6
7
8
#   save <seconds> <changes>
# 900秒(15分钟)后,如果至少有 1 个Key发生改变
# 300秒(5分钟)后,如果至少有 10 个Key发生改变
# 60秒(1分钟)后,如果至少有 10000 个Key发生改变

save 900 1
save 300 10
save 60 10000

Redis的持久化机制-AOF

1.什么是AOF

The AOF persistence logs every write operation received by the server, that will be played again at server startup, reconstructing the original dataset. Commands are logged using the same format as the Redis protocol itself, in an append-only fashion. Redis is able to rewrite the log in the background when it gets too big.

AOF持久化会记录服务器接收到的每个写入操作,这些操作将在服务器启动时再次执行,以重新构建原始的数据。命令记录的格式与Redis协议本身的格式相同,采用追加方式。当日志文件过大时,Redis可以在后台进行rewrite

2.备份与恢复

日志备份 --> 命令追加方式
日志文件 --> 根据命令日志文件重新构建

3.AOF优劣势

  • 优势
    1. 使用AOF数据持久化更加完整
    2. 可以使用不同的fsync策略,使用默认策略fsync时,每秒的写入性能仍然很好(fsync是使用后台线程执行的,并且在没有进行fsync的情况下,主线程将尽力执行写入操作)但是会损失一秒的写入时间
    3. AOF是使用日志追加的方式,如果断电或其他原因导致日志只写了一半,可以使用redis-check-aof工具进行修复
    4. Redis太大时,Redis可以在后台自动重写AOF。 Redis继续追加到旧文件时,会生成一个新的文件,其中包含创建当前数据集所需的最少操作集,一旦准备好第二个文件,Redis会切换这两个文件并开始追加到新的那一个
    5. 即使使用FLUSHALL命令清空了所有数据,也可以通过修改AOF文件进行数据恢复
  • 劣势
    1. AOF相比RDB占用空间更大
    2. 使用fsync策略后,相比RDB性能差一些
    3. 当服务器宕机后,使用AOF恢复的数据可能不完整

4.AOF的配置

  1. 开启AOF
1
appendonly yes
  1. 配置fsync策略
1
2
3
4
5
6
# If unsure, use "everysec".
# 如果不确定,推荐使用 "everysec"

# appendfsync always
appendfsync everysec
# appendfsync no
  1. AOF rewrite配置
1
2
3
4
# 设置一个百分比
auto-aof-rewrite-percentage 100
# 设置AOF 的最小大小
auto-aof-rewrite-min-size 64mb
  1. Redis 启动加载配置
1
2
# 优先加载AOF 文件
aof-use-rdb-preamble yes

两种持久化方式改如何选择

  • 如果对数据可靠性要求很高,则应同时使用两种持久性方式。
  • 如果在灾难情况下仍然可以承受几分钟的数据丢失,则可以仅使用RDB。
  • 不推荐单独使用AOF,因为AOF在数据恢复时,有时候会出现Bugs。

Redis安装与配置

下载

  1. Redis官网下载redis 最新稳定版本
  2. 解压到 /usr/local/ 目录下

安装

1
make && make test && make install

make时报如下错误:

1
2
3
4
5
zmalloc.h:50:31: error: jemalloc/jemalloc.h: No such file or directory
zmalloc.h:55:2: error: #error "Newer version of jemalloc required"
make[1]: *** [adlist.o] Error 1
make[1]: Leaving directory `/data0/src/redis-2.6.2/src'
make: *** [all] Error 2

原因是jemalloc重载了Linux下的ANSI C的malloc和free函数。
解决办法:

1
make MALLOC=libc

redis 生产环境启动方案

  1. redis utils 目录下,有个redis_init_script脚本
  2. 将redis_init_script脚本拷贝到linux的/etc/init.d 目录中,将redis_init_script重命名为redis_6379, 6379是我们希望redis 的实例端口号
  3. 创建两个目录 /etc/redis (存放redis 配置的目录), /var/redis/6379 (存放redis的持久文件)
  4. 修改redis.conf配置文件(默认在根目录下),拷贝到/etc/redis目录下,修改名称为6379.conf
  5. 修改redis.conf中的部分配置为生产环境
参数 说明
daemonize yes 让redis以daemon进程运行
pidfile /var/run/redis_6379.pid 设置redis的pid文件位置
port 6379 设置redis的监听端口号
dir /var/redis/6379 设置持久化文件的存储位置
  1. 启动redis:
1
2
3
cd /etc/init.d 
chmod 777 redis_6379
./redis_6379 start
  1. 确认redis 进程是否启动: ps -ef | grep redis
  2. 让redis跟随系统启动自动启动,在redis_6379 脚本中,最上面加入两行注释
1
2
3
# chkconfig:    2345 90 10
# description: Redis is a persistent key-value database
chkconfig redis_6379 on

主从架构(主节点和从节点最好保证一致的版本)

  1. 在 slave node 上配置:
1
slaveof 192.168.1.1 6379
  1. 强制读写分离

基于主从复制架构,实现读写分离
redis slave node 只读,默认开启,slave-read-only
开启了只读的redis slave node,会拒绝所有的写操作,这样可以强制搭建成读写分离架构

  1. 集成安全认证
    master 上启用安全认证策略,requirepass
    master 连接口令 masterauth

  2. 读写分离架构的测试
    先启动主节点,再启动从节点,在搭建生产环境的时候,不要忘记修改一个配置:

1
bind 127.0.0.1

每个redis.conf 中的bind 127.0.0.1 -> bind自己的ip,并且防火墙打开6379端口

redis-cli的使用

命令 说明
redis-cli shutdown 连接本机的6379端口停止redis进程
redis-cli -h 127.0.0.1 -p 6379 指定要连接的ip和端口号
redis-cli ping ping redis的端口,看是否正常
redis-cli 进入交互式命令行
set k1 v1 设置一个key:value
get k1 获取key的值

下载

  1. Redis官网下载redis 最新稳定版本
  2. 解压到 /usr/local/ 目录下

安装

1
make && make test && make install

make时报如下错误:

1
2
3
4
5
zmalloc.h:50:31: error: jemalloc/jemalloc.h: No such file or directory
zmalloc.h:55:2: error: #error "Newer version of jemalloc required"
make[1]: *** [adlist.o] Error 1
make[1]: Leaving directory `/data0/src/redis-2.6.2/src'
make: *** [all] Error 2

原因是jemalloc重载了Linux下的ANSI C的malloc和free函数。
解决办法:

1
make MALLOC=libc

redis 生产环境启动方案

  1. redis utils 目录下,有个redis_init_script脚本
  2. 将redis_init_script脚本拷贝到linux的/etc/init.d 目录中,将redis_init_script重命名为redis_6379, 6379是我们希望redis 的实例端口号
  3. 创建两个目录 /etc/redis (存放redis 配置的目录), /var/redis/6379 (存放redis的持久文件)
  4. 修改redis.conf配置文件(默认在根目录下),拷贝到/etc/redis目录下,修改名称为6379.conf
  5. 修改redis.conf中的部分配置为生产环境
参数 说明
daemonize yes 让redis以daemon进程运行
pidfile /var/run/redis_6379.pid 设置redis的pid文件位置
port 6379 设置redis的监听端口号
dir /var/redis/6379 设置持久化文件的存储位置
  1. 启动redis:
1
2
3
cd /etc/init.d 
chmod 777 redis_6379
./redis_6379 start
  1. 确认redis 进程是否启动: ps -ef | grep redis
  2. 让redis跟随系统启动自动启动,在redis_6379 脚本中,最上面加入两行注释
1
2
3
# chkconfig:    2345 90 10
# description: Redis is a persistent key-value database
chkconfig redis_6379 on

主从架构(主节点和从节点最好保证一致的版本)

  1. 在 slave node 上配置:
1
slaveof 192.168.1.1 6379
  1. 强制读写分离

基于主从复制架构,实现读写分离
redis slave node 只读,默认开启,slave-read-only
开启了只读的redis slave node,会拒绝所有的写操作,这样可以强制搭建成读写分离架构

  1. 集成安全认证
    master 上启用安全认证策略,requirepass
    master 连接口令 masterauth

  2. 读写分离架构的测试
    先启动主节点,再启动从节点,在搭建生产环境的时候,不要忘记修改一个配置:

1
bind 127.0.0.1

每个redis.conf 中的bind 127.0.0.1 -> bind自己的ip,并且防火墙打开6379端口

redis-cli的使用

命令 说明
redis-cli shutdown 连接本机的6379端口停止redis进程
redis-cli -h 127.0.0.1 -p 6379 指定要连接的ip和端口号
redis-cli ping ping redis的端口,看是否正常
redis-cli 进入交互式命令行
set k1 v1 设置一个key:value
get k1 获取key的值