├── qrcode.jpg ├── base ├── pointer.md └── strings.md ├── README.md ├── container ├── map.md └── array-and-slice.md └── projects └── microservices └── docker-nginx-01.md /qrcode.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mari0w/php2go-book/HEAD/qrcode.jpg -------------------------------------------------------------------------------- /base/pointer.md: -------------------------------------------------------------------------------- 1 | # 指针 2 | 3 | ## 认识指针 4 | 5 | 指针对于PHPer来说就比较难理解了,因为出于PHP的简单易用性,完全屏蔽了对指针的操作,唯一能与指针沾上边的恐怕只有`引用`了,也就是常在变量前用的`&`符号。 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 📚PHPer玩转Golang 2 | 3 | ## 介绍 4 | 5 | 本书适用于PHP从业者,主要通过与PHP比较的方式阐述Go语言,意在降低读者理解成本,以达到快速高效转型的目的。 6 | 7 | ## 目录 8 | 9 | * **基础篇** 10 | * 初识Golang 11 | * 背景介绍 12 | * 语言优势 13 | * 安装配置 14 | * 基础语法 15 | - 变量 16 | - 常量 17 | - [字符串](base/strings.md) 18 | - 数值 19 | - 指针 20 | - 类型别名 21 | * 数据存储容器 22 | - [数组](container/array-and-slice.md) 23 | - [切片](container/array-and-slice.md) 24 | - [映射](container/map.md) 25 | - 列表 26 | * 流程控制 27 | - 条件判断 28 | - 循环 29 | - 分支选择 30 | - 跳转 31 | * 函数 32 | * 结构体 33 | * 接口 34 | * 反射 35 | * 协程 36 | * 通道 37 | * 错误处理 38 | * **工作篇** 39 | - Docker 40 | - 编辑器 41 | - 依赖管理 42 | - 辅助工具 43 | - 常用包介绍 44 | - 源码发布 45 | * 测试 46 | - 单元测试 47 | - 压力测试 48 | - 覆盖测试 49 | - 性能测试 50 | * **进阶篇** 51 | * 交互篇 52 | * 社区篇 53 | * 教程篇 54 | - 书籍 55 | - 视频 56 | - 博客 57 | - 公众号 58 | - 大学课程 59 | * **项目篇** 60 | - 自动化运维 61 | - 爬虫 62 | - 区块链 63 | - 云计算 64 | - 游戏 65 | - 人工智能 66 | - 存储引擎 67 | - [微服务](projects/microservices) 68 | - [Nginx](projects/microservices) 69 | - [Nginx反向代理负载均衡容器化部署](projects/microservices/docker-nginx-01.md) 70 | - 安全 71 | 72 | ## 公众号 73 | 74 | - 名称:平也 75 | - ID:icoder_club 76 | 77 | ![](qrcode.jpg) 78 | -------------------------------------------------------------------------------- /container/map.md: -------------------------------------------------------------------------------- 1 | ## 映射的定义 2 | 3 | 初识映射会很懵,因为在PHP中没有映射类型的定义。其实没那么复杂,任何复杂的类型在PHP中都可以用数组表示,映射也不例外。 4 | ```php 5 | $array['name'] = '平也'; 6 | $array['sex'] = '1'; 7 | $array['age'] = '10'; 8 | 9 | //output 10 | Array 11 | ( 12 | [name] => 平也 13 | [sex] => 1 14 | [age] => 10 15 | ) 16 | ``` 17 | 18 | 映射其实就是有key有value的数组,在Go中的赋值也很类似,但需要提前声明该映射类型的键与值的类型,确保所有的键和值的赋值类型统一,否则会报错。 19 | ```go 20 | array := make(map[string]string) 21 | array["name"] = "平也" 22 | array["sex"] = "1" 23 | array["age"] = "10" 24 | fmt.Print(array) //output map[age:10 name:平也 sex:1] 25 | ``` 26 | 27 | 在PHP中还有一种初始化数组的方法,就是将所有要存储的键与值赋值给变量。 28 | ```php 29 | $array = [ 30 | 'name' => '平也', 31 | 'sex' => '1', 32 | 'age' => '10' 33 | ]; 34 | ``` 35 | 36 | 在Go中也有类似的初始化方法,但切记统一键与值的数据类型。 37 | ```go 38 | array := map[string]string{ 39 | "name": "平也", 40 | "sex": "1", 41 | "age": "10", 42 | } 43 | ``` 44 | 45 | ## 映射的遍历 46 | 47 | 在PHP中其实就是遍历数组的操作,foreach即可。 48 | ```php 49 | $array = [ 50 | 'name' => '平也', 51 | 'sex' => '1', 52 | 'age' => '10' 53 | ]; 54 | 55 | foreach ($array as $key => $value) { 56 | print_r($array); 57 | } 58 | 59 | //output 60 | Array 61 | ( 62 | [name] => 平也 63 | [sex] => 1 64 | [age] => 10 65 | ) 66 | Array 67 | ( 68 | [name] => 平也 69 | [sex] => 1 70 | [age] => 10 71 | ) 72 | Array 73 | ( 74 | [name] => 平也 75 | [sex] => 1 76 | [age] => 10 77 | ) 78 | ``` 79 | 80 | 在Go中也可以像遍历数组那样遍历map,依然使用range关键字。 81 | ```go 82 | array := map[string]string{ 83 | "name": "平也", 84 | "sex": "1", 85 | "age": "10", 86 | } 87 | for v, k := range array { 88 | fmt.Print(k, v) 89 | } 90 | ``` 91 | 92 | 上篇文章讲到遍历时可以通过下划线来忽略键或值,如果只遍历键,下划线也可以省略。 93 | ```go 94 | array := map[string]string{ 95 | "name": "平也", 96 | "sex": "1", 97 | "age": "10", 98 | } 99 | for k := range array { 100 | fmt.Print(k) 101 | } 102 | //output sexagename 103 | ``` 104 | 105 | ## 映射的取值 106 | 107 | PHP中可以直接通过读数组的key来取值。 108 | ```php 109 | $array = ['name' => 'pingye']; 110 | echo $array['name']; //output pingye 111 | ``` 112 | 113 | 在Go中的操作是一样的,与PHP不同的是,如果取了不存在的key,Go中默认输出空值,在PHP中就会产生warning警告。 114 | ```go 115 | array := map[string]string{ 116 | "name": "pingye", 117 | "sex": "1", 118 | "age": "10", 119 | } 120 | fmt.Print(array["name"]) //pingye 121 | ``` 122 | 123 | ## 映射元素的删除 124 | 125 | 在PHP中的unset可以删除任何你想删除的数组元素,非常好用。 126 | ```php 127 | $array = [ 128 | 'name' => '平也', 129 | 'sex' => '1', 130 | 'age' => '10' 131 | ]; 132 | unset($array['name']); 133 | print_r($array); 134 | 135 | //output 136 | Array 137 | ( 138 | [sex] => 1 139 | [age] => 10 140 | ) 141 | ``` 142 | 143 | 在Go中通过delete函数来删除map中的元素。 144 | ```go 145 | array := map[string]string{ 146 | "name": "pingye", 147 | "sex": "1", 148 | "age": "10", 149 | } 150 | delete(array, "name") 151 | fmt.Print(array) //output map[age:10 sex:1] 152 | ``` 153 | 154 | ## 清空map元素 155 | 156 | 在PHP中好像从来没有注意过是否把数组清空,很抱歉,我能想到的清空数组方法就是把空数组赋值给它。 157 | ```php 158 | $array = [ 159 | 'name' => '平也', 160 | 'sex' => '1', 161 | 'age' => '10' 162 | ]; 163 | $array = []; 164 | print_r($array); 165 | //output 166 | Array 167 | ( 168 | ) 169 | ``` 170 | 171 | 然而,在Go中也没有提供清空map的函数,重新make一个map就行了,原来的map会被Go的垃圾回收机制清除掉,甚至比写一个清空的函数效率还高。 -------------------------------------------------------------------------------- /container/array-and-slice.md: -------------------------------------------------------------------------------- 1 | ## 数组的定义 2 | 用过PHP的同学应该很清楚,无论多么复杂的数据格式都可以用数组来表达,什么类型的数据都可以往里塞,它是工作必备的一部分,使用很简单,易用程度简直变态。 3 | ```php 4 | $array = [1, 'name', ['sex' => '男']]; 5 | ``` 6 | 7 | 在Go语言中就有所不同了,数组是一段固定长度的连续内存区域,与C语言概念完全一致,在声明数组时,要先确定数组的长度,而且数组中的元素只能使用一种类型,心痛,我们再也不能随心所欲的存储数据了。 8 | ```go 9 | array := [3]int{1, 2, 3} 10 | ``` 11 | 12 | 以上是数组在Go语言中的定义方法,`[3]`代表数组长度为3,`int`是数组中元素的类型,花括号中的是数组元素,切记元素个数与类型一定要与前面声明的相符,否则会报错。除了这种方式,PHP中还有一种动态赋值的方法,代码如下。 13 | ```php 14 | $array = []; 15 | $array[0] = 'name'; 16 | $array[1] = 1; 17 | print_r($array); 18 | 19 | //output 20 | Array 21 | ( 22 | [0] => name 23 | [1] => 1 24 | ) 25 | ``` 26 | 27 | Go语言也能做到,不过别想那么多,要存储不同数据类型的元素就别想了(其实也不是没有办法,后续文章讲解)。可以通过var声明一个数组,数组的长度和类型提前定义好,接下来就可以根据数组下标来赋值了,可以不必为每个下标赋值,但是切记下标不能越界,否则会报错。 28 | ```go 29 | var array [3]string 30 | array[0] = "name" 31 | array[1] = "sex" 32 | fmt.Print(array) //output [name sex ] 33 | ``` 34 | 35 | 每次声明数组时都要数一下元素个数写在中括号里也太鸡肋了,这点Go不可能想不到,果然,可以用`...`方法来省略掉中括号的数值,这样编译器会自动确定数组中的元素。 36 | ```go 37 | array := [...]string{"a", "b", "c", "d"} 38 | fmt.Print(array) //output [a b c d] 39 | ``` 40 | 41 | ## 数组的遍历 42 | 43 | 数组已经定义了,来看一下怎么遍历,在PHP中通过`foreach`进行遍历。 44 | ```php 45 | $array = ["a", "b", "c", "d"]; 46 | foreach($array as $k => $v) { 47 | echo $v; 48 | } 49 | //output abcd 50 | ``` 51 | 52 | Go的写法很类似,通过`range`进行遍历,只是语法看起来有点怪怪的。 53 | ```go 54 | array := [...]string{"a", "b", "c", "d"} 55 | for k, v := range array { 56 | fmt.Print(k, v) 57 | } 58 | //output 0a1b2c3d 59 | ``` 60 | 61 | 在Go中没有使用的变量在编译时会报错,那遍历时的k不见得会用到,那怎么解决变量k未被使用的问题呢?其实可以使用Go内置的符号`_`来替换掉变量k,它的意思是把当前赋的值丢弃掉。 62 | ```go 63 | array := [...]string{"a", "b", "c", "d"} 64 | for _, v := range array { 65 | fmt.Print(v) 66 | } 67 | //output abcd 68 | ``` 69 | 70 | ### 数组的截取 71 | 72 | PHP中对数组截取通过`array_slice`函数,以下代码是从数组的第二个元素开始,截取两个元素。 73 | ```php 74 | $array = [1, 2, 3, 4, 5]; 75 | print_r(array_slice($array, 1, 2)); 76 | 77 | //output 78 | Array 79 | ( 80 | [0] => 2 81 | [1] => 3 82 | ) 83 | ``` 84 | 85 | 在Go中更简单一些,可以直接取出第二到第四个元素之间的数据,值得注意的是,取出来的已经不是数组了,叫做切片,记住了,从数组切出来的部分就叫切片,数组与切片最简单的区别就是:数组需要明确指定大小,切片不需要,数组是值传递,切片是地址传递。不过目前还是把它当数组好了。 86 | ```go 87 | array := [...]int{1, 2, 3, 4, 5} 88 | fmt.Print(array[1:3]) 89 | ``` 90 | 91 | ### 数组的追加 92 | 93 | 在PHP中为数组添加一个新元素使用`array_push`函数。 94 | ```php 95 | $array = [1, 2, 3, 4, 5]; 96 | array_push($array, 6); 97 | print_r($array); 98 | 99 | //output 100 | Array 101 | ( 102 | [0] => 1 103 | [1] => 2 104 | [2] => 3 105 | [3] => 4 106 | [4] => 5 107 | [5] => 6 108 | ) 109 | ``` 110 | 111 | 在Go中使用`append`函数,注意这里实际上是对切片的添加而不是数组。 112 | ```go 113 | array := []int{1, 2, 3, 4, 5} 114 | array = append(array, 6) 115 | fmt.Print(array) //output [1 2 3 4 5 6] 116 | ``` 117 | 118 | ### 数组元素的删除 119 | 120 | 在PHP中删除数组元素非常简单,直接`unset`即可,以下代码是删除第二个元素。 121 | ```php 122 | $array = [1, 2, 3, 4, 5]; 123 | unset($array[1]); 124 | print_r($array); 125 | 126 | //output 127 | Array 128 | ( 129 | [0] => 1 130 | [2] => 3 131 | [3] => 4 132 | [4] => 5 133 | ) 134 | ``` 135 | 136 | Go中比较特别,它并没有提供原生的删除方法,只能利用切片的特性,我们定义删除的元素索引为1,然后将1前面的元素与1后面的元素拼接起来,就过滤掉了第1个元素,感觉这种实现方式有点投机,不知道为什么不提供函数来操作。 137 | ```go 138 | index := 1 139 | array := []int{1, 2, 3, 4, 5} 140 | array = append(array[:index], array[index+1:]...) 141 | fmt.Print(array) //output [1 3 4 5] 142 | ``` -------------------------------------------------------------------------------- /base/strings.md: -------------------------------------------------------------------------------- 1 | ## 字符串的赋值 2 | 在PHP中,字符串的赋值虽然只有一行,其实包含了两步,一是声明变量,二是赋值给变量,同一个变量可以任意重新赋值。 3 | ```php 4 | $str = 'Hello World!'; 5 | $str = 'hia'; 6 | ``` 7 | 8 | Go语言实现上述两步也可以用一行语句解决,就是通过标识`var`赋值时同时声明变量,切记等号右侧的字符串不能用单引号,对变量的后续赋值也不能再重新声明,否则会报错。除此之外,定义的变量不使用也会报错,从这点来看,Go还是比PHP严格很多的,规避了很多在开发阶段产生的性能问题。 9 | ```go 10 | var str = "Hello World!" 11 | str = "hia" 12 | ``` 13 | 14 | 关于声明,Go提供了一种简化方式,不需要在行首写var,只需将等号左侧加上一个冒号就好了,切记这只是替代了声明语句,它并不会像PHP那样用一个赋值符号来统一所有的赋值操作。 15 | 16 | ```go 17 | str := "Hello World!" 18 | str = "hia" 19 | ``` 20 | 21 | ## 字符串的输出 22 | PHP中的输出非常简单,一个echo就搞定了。 23 | ```php 24 | 27 | ``` 28 | 29 | 而Go不一样的是,调用它的输出函数前需要先引入包`fmt`,这个包提供了非常全面的输入输出函数,如果只是输出普通字符串,那么和PHP对标的函数就是`Print`了,从这点来看,Go更有一种万物皆对象的感觉。 30 | ```go 31 | import "fmt" 32 | 33 | func main() { 34 | fmt.Print("Hello world!") 35 | } 36 | ``` 37 | 38 | 在PHP中还有一个格式化输出函数`sprintf`,可以用占位符替换字符串。 39 | ```php 40 | echo sprintf('name:%s', '平也'); //name:平也 41 | ``` 42 | 在Go中也有同名同功能的字符串格式化函数。 43 | ```go 44 | fmt.Print(fmt.Sprintf("name:%s", "平也")) 45 | ``` 46 | 官方提供的默认占位符有以下几种,感兴趣的同学可以自行了解。 47 | ``` 48 | bool: %t 49 | int, int8 etc.: %d 50 | uint, uint8 etc.: %d, %#x if printed with %#v 51 | float32, complex64, etc: %g 52 | string: %s 53 | chan: %p 54 | pointer: %p 55 | ``` 56 | 57 | ## 字符串的相关操作 58 | ### 字符串长度 59 | 60 | 在PHP中通过`strlen`计算字符串长度。 61 | ```php 62 | echo strlen('平也'); //output: 6 63 | ``` 64 | 在Go中也有类似函数`len`。 65 | ```go 66 | fmt.Print(len("平也")) //output: 6 67 | ``` 68 | 因为统计的是ASCII字符个数或字节长度,所以两个汉字被认定为长度6,如果要统计汉字的数量,可以使用如下方法,但要先引入`unicode/utf8`包。 69 | ```go 70 | import ( 71 | "fmt" 72 | "unicode/utf8" 73 | ) 74 | 75 | func main() { 76 | fmt.Print(utf8.RuneCountInString("平也")) //output: 2 77 | } 78 | ``` 79 | 80 | ### 字符串截取 81 | 82 | PHP有一个`substr`函数用来截取任意一段字符串。 83 | ```php 84 | echo substr('hello,world', 0, 3); //output: hel 85 | ``` 86 | Go中的写法有些特别,它是将字符串当做数组,截取其中的某段字符,比较麻烦的是,在PHP中可以将第二个参数设置为负数进行反向取值,但是Go无法做到。 87 | ```go 88 | str := "hello,world" 89 | fmt.Print(str[0:3]) //output: hel 90 | ``` 91 | 92 | ### 字符串搜索 93 | 94 | PHP中使用`strpos`查询某个字符串出现的位置。 95 | ```php 96 | echo strpos('hello,world', 'l'); //output: 2 97 | ``` 98 | Go中需要先引入`strings`包,再调用`Index`函数来实现。 99 | ```go 100 | fmt.Print(strings.Index("hello,world", "l")) //output: 2 101 | ``` 102 | 103 | ### 字符串替换 104 | 105 | PHP中替换字符串使用`str_replace`内置函数。 106 | ```php 107 | echo str_replace('world', 'girl', 'hello,world'); //output: hello,girl 108 | ``` 109 | Go中依然需要使用`strings`包中的函数`Replace`,不同的是,第四个参数是必填的,它代表替换的次数,可以为0,代表不替换,但没什么意义。还有就是字符串在PHP中放在第三个参数,在Go中是第一个参数。 110 | ```go 111 | fmt.Print(strings.Replace("hello,world", "world", "girl", 1)) //output: hello,girl 112 | ``` 113 | 114 | ### 字符串连接 115 | 116 | 在PHP中最经典的就是用点来连接字符串。 117 | ```php 118 | echo 'hello' . ',' . 'world'; //output: hello,world 119 | ``` 120 | 在Go中用加号来连接字符串。 121 | ```go 122 | fmt.Print("hello" + "," + "world") //output: hello,world 123 | ``` 124 | 除此之外,还可以使用`strings`包中的`Join`函数连接,这种写法非常类似与PHP中的数组拼接字符串函数`implode`。 125 | ```go 126 | str := []string{"hello", "world"} 127 | fmt.Print(strings.Join(str, ",")) //output: hello,world 128 | ``` 129 | 130 | ### 字符串编码 131 | 132 | PHP中使用内置函数`base64_encode`来进行编码。 133 | ```php 134 | echo base64_encode('hello, world'); //output: aGVsbG8sIHdvcmxk 135 | ``` 136 | 在Go中要先引入`encoding/base64`包,并定义一个切片,再通过`StdEncoding.EncodeToString`函数对切片编码,比PHP要复杂一些。 137 | ```go 138 | import ( 139 | "encoding/base64" 140 | "fmt" 141 | ) 142 | 143 | func main() { 144 | str := []byte("hello, world") 145 | fmt.Print(base64.StdEncoding.EncodeToString(str)) 146 | } 147 | ``` -------------------------------------------------------------------------------- /projects/microservices/docker-nginx-01.md: -------------------------------------------------------------------------------- 1 | # Nginx反向代理负载均衡的容器化部署 2 | 3 | 首先,在`home`目录创建`microservices`目录,开启第一篇章。 4 | 5 | ```bash 6 | cd ~ && mkdir microservices && cd microservices 7 | ``` 8 | 9 | 创建`nginx`目录,在目录下分别创建三个节点目录:`nginx01`、`nginx02`、`nginx03`,目的是使`nginx01`作为反向代理服务器,将请求均衡转发到`nginx02`、`nginx03`。 10 | 11 | ```bash 12 | mkdir -p ./nginx/nginx01 ./nginx/nginx02 ./nginx/nginx03 13 | ``` 14 | 15 | 展示效果如下所示。 16 | 17 | ``` 18 | nginx 19 | ├── nginx01 20 | └── nginx02 21 | └── nginx03 22 | ``` 23 | 24 | 将nginx镜像中的配置文件拷贝到各子目录中,以便做挂载,方法是创建一个临时容器,将配置文件拷贝至宿主机目录,再删除临时容器。 25 | 26 | ```bash 27 | docker run --name tmpnginx -d nginx:latest 28 | docker cp tmpnginx:/etc/nginx/nginx.conf ~/microservices/nginx/nginx01 29 | docker cp tmpnginx:/etc/nginx/nginx.conf ~/microservices/nginx/nginx02 30 | docker cp tmpnginx:/etc/nginx/nginx.conf ~/microservices/nginx/nginx03 31 | docker cp tmpnginx:/etc/nginx/conf.d ~/microservices/nginx/nginx01 32 | docker cp tmpnginx:/etc/nginx/conf.d ~/microservices/nginx/nginx02 33 | docker cp tmpnginx:/etc/nginx/conf.d ~/microservices/nginx/nginx03 34 | docker rm -f tmpnginx 35 | ``` 36 | 37 | 此时nginx目录如下所示。 38 | 39 | ``` 40 | nginx 41 | ├── nginx01 42 | │   ├── conf.d 43 | │   │   └── default.conf 44 | │   └── nginx.conf 45 | ├── nginx02 46 | │   ├── conf.d 47 | │   │   └── default.conf 48 | │   └── nginx.conf 49 | └── nginx03 50 | ├── conf.d 51 | │   └── default.conf 52 | └── nginx.conf 53 | ``` 54 | 55 | 在根目录创建文件`docker-compose.yml`,创建三个web服务,配置文件分别映射到容器中的对应文件。 56 | 57 | ```yml 58 | version: '3' 59 | 60 | services: 61 | web01: #服务名称 62 | image: nginx:latest #镜像 63 | container_name: web01 #容器名称 64 | ports: #映射端口号,前者宿主机端口,后者容器端口 65 | - 8080:80 66 | volumes: #映射的目录或文件,前者宿主机目录,后者容器目录 67 | - ./nginx/nginx01/nginx.conf:/etc/nginx/nginx.conf #配置文件 68 | - ./nginx/nginx01/conf.d:/etc/nginx/conf.d #扩展配置目录 69 | - ./nginx/html:/usr/share/nginx/html #html存放目录 70 | 71 | web02: 72 | image: nginx:latest 73 | container_name: web02 74 | volumes: 75 | - ./nginx/nginx02/nginx.conf:/etc/nginx/nginx.conf 76 | - ./nginx/nginx02/conf.d:/etc/nginx/conf.d 77 | - ./nginx/html:/usr/share/nginx/html 78 | 79 | web03: 80 | image: nginx:latest 81 | container_name: web03 82 | volumes: 83 | - ./nginx/nginx03/nginx.conf:/etc/nginx/nginx.conf 84 | - ./nginx/nginx03/conf.d:/etc/nginx/conf.d 85 | - ./nginx/html:/usr/share/nginx/html 86 | ``` 87 | 88 | 打开`nginx/nginx01/conf.d/default.conf`,在文章顶部加入`upstream`配置,web02与web03是`docker-compose.yml`中定义的容器名称`container_name`。 89 | 90 | ```bash 91 | upstream backend { 92 | server web02:80; 93 | server web03:80; 94 | } 95 | ``` 96 | 97 | 在`location /`中加入`proxy_pass`以便将请求转发给`backend`。 98 | 99 | ```bash 100 | location / { 101 | root /usr/share/nginx/html; 102 | index index.html index.htm; 103 | proxy_pass http://backend; #追加该行 104 | } 105 | ``` 106 | 107 | 配置完成后,执行以下命令将容器跑起来。 108 | 109 | ```bash 110 | cd ~/microservices 111 | docker-compose up 112 | ``` 113 | 114 | 提示以下内容即成功。 115 | 116 | ```bash 117 | Recreating microservices_web01_1 ... done 118 | Recreating microservices_web02_1 ... done 119 | Recreating microservices_web03_1 ... done 120 | Attaching to web02, web01, web03 121 | ``` 122 | 123 | 此时`microservices`目录结构如下,nginx目录下多出了一个`html`文件夹,可以在`html`目录下创建一个`index.html`,输入`Hello world!`,重新跑一下。 124 | 125 | ```bash 126 | microservices 127 | ├── docker-compose.yml 128 | └── nginx 129 | ├── html 130 | │   └── index.html 131 | ├── nginx01 132 | │   ├── conf.d 133 | │   │   └── default.conf 134 | │   └── nginx.conf 135 | ├── nginx02 136 | │   ├── conf.d 137 | │   │   └── default.conf 138 | │   └── nginx.conf 139 | └── nginx03 140 | ├── conf.d 141 | │   └── default.conf 142 | └── nginx.conf 143 | ``` 144 | 145 | 现在做个测试,在浏览器中访问`localhost:8080`,观察终端打印的日志。 146 | 147 | ```bash 148 | web01 | 172.24.0.1 - - [26/Jun/2019:01:48:28 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36" "-" 149 | web02 | 172.24.0.2 - - [26/Jun/2019:01:48:28 +0000] "GET / HTTP/1.0" 304 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36" "-" 150 | ``` 151 | 152 | 上述内容表示本次请求通过web01转发到了web02。 153 | 154 | ```bash 155 | web01 | 172.24.0.1 - - [26/Jun/2019:04:42:36 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36" "-" 156 | web03 | 172.24.0.2 - - [26/Jun/2019:04:42:36 +0000] "GET / HTTP/1.0" 304 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36" "-" 157 | ``` 158 | 159 | 再次刷新,可以看到请求通过web01转发到了web03,到目前为止,基本的负载均衡部署就已经完成了,上述的web01是将请求均衡转发到web02、web03的,这种方法叫轮询法,下篇文章介绍几种其他的负载算法。 160 | 161 | --- 162 | 欢迎入伙Github开源书籍 163 | - [《PHPer玩转Golang》](https://github.com/EnochZg/php2go-book) 164 | - [《徒手搭建微服务》](https://github.com/EnochZg/building-microservices-book) 165 | --------------------------------------------------------------------------------