├── .github └── workflows │ └── ci-cd.yml ├── LICENSE ├── README.md ├── lesson00 ├── images │ ├── 1.png │ ├── image-20231007213757592.png │ ├── image-20231007213835122.png │ ├── image-20231007213951785.png │ ├── image-20231007214308703.png │ ├── image-20231007214410280.png │ ├── image-20231007214554512.png │ ├── image-20231007215945173.png │ ├── image-20231008104402198.png │ ├── image-20231008105637726.png │ ├── image-20231008110110760.png │ └── image-20231008123718393.png └── 从 0 开始学 Go.md ├── lesson01 └── 蓝山工作室——Golang第一节课.md ├── lesson02 ├── 02.md └── images │ ├── img1.png │ ├── img2.png │ ├── img3.png │ └── img4.png ├── lesson03 └── 第三周课件.md ├── lesson04 ├── img │ ├── img01.png │ ├── img02.png │ ├── img03.png │ └── img04.png └── 后端 Go 第四次课.md ├── lesson05 ├── images │ ├── 1.png │ ├── 10.png │ ├── 11.png │ ├── 12.png │ ├── 13.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ ├── 7.png │ ├── 8.png │ └── 9.png └── 并发.md ├── lesson06 ├── README.md ├── clain │ └── main.go ├── m1 │ └── main.go └── m2 │ ├── api │ ├── middleware │ │ ├── cors.go │ │ └── jwt.go │ ├── router.go │ └── user.go │ ├── dao │ └── user.go │ ├── main.go │ ├── model │ └── user.go │ └── utils │ └── response.go ├── lesson07 └── lesson7.md ├── lesson08 ├── README.md └── images │ ├── 1.png │ ├── 2.png │ └── 3.png ├── lesson09 ├── README.md ├── example1 │ ├── go.mod │ ├── go.sum │ └── main.go └── example2 │ ├── baidu │ ├── a2.png │ ├── carm-baidu-black.png │ ├── carm-baidu-blue.png │ ├── carm-baidu-blue.psd │ ├── index.html │ └── tx.png │ ├── go.mod │ ├── go.sum │ └── main.go ├── lesson10 ├── Dockerfile ├── docker-compose.yaml ├── docker.md ├── go.mod ├── go.sum └── main.go ├── lesson11 ├── README.md └── images │ ├── GOLint.png │ ├── RESTful.png │ ├── bilibili1.PNG │ ├── bilibili2.png │ ├── bilibili3.png │ ├── blue_cat_worm.jpeg │ ├── demon.png │ ├── flow_chart.PNG │ ├── real_instance.png │ ├── structure1.png │ ├── structure2.png │ ├── structure3.png │ ├── structure_ability.png │ ├── structure_example.png │ ├── system_relations.png │ ├── system_user.png │ ├── tree.png │ ├── viper.PNG │ └── zap.PNG ├── lesson12 ├── images │ ├── 1.png │ ├── 10.png │ ├── 11.png │ ├── 12.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ ├── 7.png │ ├── 8.webp │ └── 9.webp ├── main.go └── 消息队列.md ├── lesson13 └── lesson13.md ├── lesson14 └── lesson14.md ├── lesson15 ├── README.md └── images │ ├── 1280X1280 (1).PNG │ ├── 1280X1280.PNG │ ├── 398cdbe8-1006-48b3-bba6-b73768c02aca.png │ ├── 4e760b81-7bfb-437a-83f7-c054ea6b414c.webp │ ├── 7f37cdb4-faa1-4341-9daa-9f24646f4443.png │ ├── 930904ca-0525-41af-b5e5-314fd377dfce.png │ ├── af08e7a2-c965-4084-a417-ada9ee07d899.webp │ ├── caec8684-c117-4521-a6bd-a124a795e819.webp │ ├── d0a32a7c-8b86-4083-a156-4570db6f3018.png │ ├── dfbb50b0-6a41-4045-9e47-6a77ffc2d5b3.png │ └── java_error_in_idea.png ├── lesson16 └── README.md ├── lesson17 ├── img │ ├── agent.png │ ├── component.png │ ├── gateway.png │ ├── jaeger.png │ ├── nocollector.png │ ├── otel_strcut.png │ ├── pipeline.png │ ├── prometheus.png │ ├── trace.png │ ├── trace1.png │ ├── trace2.png │ ├── trace3.png │ ├── trace4.png │ ├── trace5.png │ └── trace_collector.png └── 后端 Go 第十七次课.md └── lesson18 ├── images ├── cap.png ├── leader_turn.png ├── snowflake.png └── tcc.png └── 分布式基础.md /.github/workflows/ci-cd.yml: -------------------------------------------------------------------------------- 1 | name: Docker Build and Push a Simple # 工作流程的名称 2 | 3 | on: 4 | workflow_dispatch: # 通过 workflow_dispatch 手动触发工作流程 5 | inputs: 6 | parameter_name: 7 | description: 'go' 8 | required: true 9 | 10 | jobs: 11 | build-and-push-hello: # 构建和推送 hello 的作业 12 | runs-on: ubuntu-latest # 在 Ubuntu 最新版本上运行 13 | 14 | steps: 15 | - name: Checkout the repository # 检出存储库 16 | uses: actions/checkout@v2 17 | 18 | - name: Login to Docker Hub # 登录到 Docker Hub 19 | uses: docker/login-action@v1 20 | with: 21 | username: ${{ secrets.DOCKER_USERNAME }} # 使用存储库的 Docker 用户名 22 | password: ${{ secrets.DOCKERHUB_TOKEN }} # 使用存储库的 Docker 访问令牌 23 | 24 | - name: Set up QEMU # 设置 QEMU 25 | uses: docker/setup-qemu-action@v1 26 | 27 | - name: Set up Docker Buildx # 设置 Docker Buildx 28 | uses: docker/setup-buildx-action@v1 29 | 30 | - name: Create and push chat-rpc Docker image # 创建并推送 chat-rpc Docker 镜像 31 | uses: docker/build-push-action@v2 32 | with: 33 | context: . # Docker 上下文路径 34 | file: ./lesson10/docker-compose.yaml # Dockerfile 文件路径 35 | push: true # 推送镜像到 Docker Hub 36 | tags: liuxian123/hello:latest # 镜像的标签 37 | platforms: linux/amd64,linux/arm64 # 构建多个架构的镜像 38 | 39 | - name: executing remote ssh commands using password # 使用密码执行远程 SSH 命令 40 | uses: appleboy/ssh-action@v0.1.10 41 | with: 42 | host: ${{ secrets.HOST }} # SSH 主机 43 | username: ${{ secrets.USERNAME }} # SSH 用户名 44 | password: ${{ secrets.PASSWORD }} # SSH 密码 45 | port: ${{ secrets.PORT }} # SSH 端口 46 | script: | 47 | cd /home/project/docker_test # 执行远程服务器上的命令,此处进入特定目录 48 | docker-compose stop hello 49 | docker-compose rm -f hello 50 | docker image rm liuxian123/hello:latest 51 | docker-compose up -d hello 52 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Courseware-Backend-Go-2023 2 | 3 | 蓝山工作室 && 勤奋蜂工作室 Go 方向 2023-2024年课件 4 | 5 | 第一学期:2023.10.21(第7周)- 2024.1.7(第18周)(暂定) 6 | 7 | | 节数 | 课程 | 说明 | 讲师 | 8 | | :--: | :------------------------: | :----------------------------------------------------------: |:---:| 9 | | 0 | 通识课 | 提问的智慧,学会用 chatgpt,typora,安装 jetbrains 以及 goland,等等 | 万子渝 | 10 | | 1 | go 基础语法(1) | 变量,常量,基本数据类型,运算符,流程控制,函数基础,fmt包 | 邓卓 | 11 | | 2 | go 基础语法(2) | 数组,切片,map,函数进阶,git | 刘力延 | 12 | | 3 | struct、指针、interface | 包,struct、指针、interface | 王丹力 | 13 | | 4 | 常见的库与初识开源 | strconv,time,os,context,(template),(flag) / 了解 github 开源 | 梁伟健 | 14 | | 5 | goroutine、channel、select | go 语言并发编程 | 赵锡军 | 15 | | 6 | web 开发 | net/http 库,gin,cookie,session,jwt | 廖淑娴 | 16 | | 7 | mysql | 基本概念,sqlx,gorm | 王俊杰 | 17 | | 8 | redis,mongodb | 基本概念,缓存策略,go 操作 redis 和 mongodb | 单培民 | 18 | | 9 | linux | linux 常见命令,部署项目等 | 杨璟轩 | 19 | | 10 | docker | docker概念,docker部署项目,编写dockerfile | 刘显 | 20 | | 11 | 单体项目实战 | zap,viper,gin/hertz 框架实战 | 万子渝 | 21 | 22 | 23 | 24 | 第二学期:第一周 —— 第九周 25 | 26 | | 节数 | 课程 | 说明 | 讲师 | 27 | | ---- | ---------------- | --------------------------------------------------------- | ------ | 28 | | 1 | 消息队列 | 消息队列的应用,常见消息队列解决方案 | 王丹力 | 29 | | 2 | 微服务(1) | 微服务架构,服务拆分原则,服务间通信,服务间发现机制 | 王俊杰 | 30 | | 3 | 微服务(2) | Go-zero 快速搭建微服务 demo | 刘显 | 31 | | 4 | 微服务(3) | Kitex 快速构建微服务 demo | 万子渝 | 32 | | 5 | API网关 | 负载均衡,限流、熔断、降级,Nginx、Traefik、Kong | 刘力延 | 33 | | 6 | 可观测性 | Log、Traces、Metrics,OTEL | 梁伟建 | 34 | | 7 | 分布式数据一致性 | 分布式 ID,分布式锁,分布式事务,一致性共识算法,配置中心 | 赵锡军 | 35 | | 8 | k8s | k8s 架构,常见 k8s 资源 | 邓卓 | 36 | | 9 | 杂项 | CICD,八股,杂项 | 万子渝 | 37 | 38 | -------------------------------------------------------------------------------- /lesson00/images/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson00/images/1.png -------------------------------------------------------------------------------- /lesson00/images/image-20231007213757592.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson00/images/image-20231007213757592.png -------------------------------------------------------------------------------- /lesson00/images/image-20231007213835122.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson00/images/image-20231007213835122.png -------------------------------------------------------------------------------- /lesson00/images/image-20231007213951785.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson00/images/image-20231007213951785.png -------------------------------------------------------------------------------- /lesson00/images/image-20231007214308703.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson00/images/image-20231007214308703.png -------------------------------------------------------------------------------- /lesson00/images/image-20231007214410280.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson00/images/image-20231007214410280.png -------------------------------------------------------------------------------- /lesson00/images/image-20231007214554512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson00/images/image-20231007214554512.png -------------------------------------------------------------------------------- /lesson00/images/image-20231007215945173.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson00/images/image-20231007215945173.png -------------------------------------------------------------------------------- /lesson00/images/image-20231008104402198.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson00/images/image-20231008104402198.png -------------------------------------------------------------------------------- /lesson00/images/image-20231008105637726.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson00/images/image-20231008105637726.png -------------------------------------------------------------------------------- /lesson00/images/image-20231008110110760.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson00/images/image-20231008110110760.png -------------------------------------------------------------------------------- /lesson00/images/image-20231008123718393.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson00/images/image-20231008123718393.png -------------------------------------------------------------------------------- /lesson00/从 0 开始学 Go.md: -------------------------------------------------------------------------------- 1 | # 从 0 开始学 Go 2 | 3 | 接下来我会教大家如何成为一个入门级别的 Go 软件工程师,这个教程里面不会包含如何敲代码,但是包括了一些软件工程师的基本素养(很多都是代代相传的 4 | 5 | 因为是面向 0 基础写的教程,所以很多地方比如说安装软件我会写的很细,以后的课程中可能就不会有这么细了 6 | 7 | 不论你是否有基础相信都能有所收获 8 | 9 | ## 前言 10 | 11 | 如果你想问我想学好互联网开发,当一个成功的码农最重要的是什么 12 | 13 | 我觉得首先很重要的是**探索精神** 14 | 15 | 1. 好奇心:探索精神源于好奇心,即对世界和知识的渴望。好奇心驱使人们主动寻求新的经验、知识和理解,不满足于现有的答案和常规的思维模式。它激发人们对问题的提出、现象的解释和现实的改变。 16 | 2. 探索未知:探索精神鼓励人们主动面对未知领域和挑战。它推动人们跨越舒适区,积极探索新的领域、新的观点和新的经验。探索者愿意接受风险和不确定性,勇于面对失败和困难,以获取新的知识和发现。 17 | 3. 创新思维:探索精神促使人们采用创新的思维方式来解决问题和应对挑战。它鼓励人们打破传统的思维模式,尝试新的方法和观点,挖掘新的可能性。创新思维涉及跳出传统边界、建立连接和寻找新的解决方案。 18 | 4. 持续学习:探索精神与持续学习密切相关。探索者认识到知识和经验的不断积累对于个人成长和发展的重要性。他们愿意持续学习新的技能、学科和领域,并将学习应用于实践中,以推动个人和社会的进步。 19 | 20 | 是不是想起了玩游戏🎮时的经历? YES!你完全可以带入到开发学习中 21 | 22 | > "程序员贪婪又懒惰"——《Unix编程艺术》(The Art of Unix Programming) 23 | 24 | 然后最重要的是 **坚持** ✊ 25 | 26 | 还记得每次上课都听的迷迷糊糊的,这里为什么会报错?并发为什么每次效果不一样?`interface` 有什么用?`Context.context`是干啥的?为什么一开始要写个 `r.Register()`?会在学习的过程中遇到数不清的问题,跳进数不清的坑。但是只要坚持你回过来看发现其实都很简单,每个大佬也都是从 rookie 开始的嘛 27 | 28 | 碰到问题不可怕,找到解决问题的方式是关键,于是就引入了下一小节——**提问的智慧** 29 | 30 | ## 提问的智慧 31 | 32 | 详见 https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/main/README-zh_CN.md 33 | 34 | > 直接引用 21 级学长写的文档 🙏 35 | 36 | 你们或许在提问题的时候觉得我们很傲慢? 37 | 38 | ### 🐂🐎问题 39 | 40 | - 手机拍代码 41 | - 只说一个笼统的问题,不说细节 42 | - 夺命连环问 43 | 44 | ![img](./images/1.png) 45 | 46 | [![a277d2d2f89d0a22f4b12e425b585b2fcf547ede587345840](https://github.com/LanshanTeam/Courseware-Backend-Go-2022/raw/main/class00-%E9%80%9A%E8%AF%86%E8%AF%BE/image/a277d2d2f89d0a22f4b12e425b585b2fcf547ede587345840.jpg)](https://github.com/LanshanTeam/Courseware-Backend-Go-2022/blob/main/class00-通识课/image/a277d2d2f89d0a22f4b12e425b585b2fcf547ede587345840.jpg) 47 | 48 | ### 例子 49 | 50 | **你以为的傲慢**: 51 | 52 | - 学弟:学长我vscode装不上了怎么办? 53 | - 学长:你到官网下载安装包下载完双击它,一直点下一步就安装好了 54 | - 学弟:官网是什么 55 | - 学长:https://code.visualstudio.com/ 56 | - 学弟:下载按钮在哪? 57 | - 学长:(截图) 58 | - 学弟:好了,下载到了,然后呢? 59 | - 学长:这个都不会还是别学了 60 | - 学弟:(感觉学长很傲慢)... 61 | 62 | **实际上的傲慢:** 63 | 64 | - 学弟:学长我的电脑是64位的windows 10,我在vscode官网下载 了对应版本,在我安装过程中出现了这个问题,使用百度搜索后还是没有办法解决,你能帮我看看吗?谢谢。(同时 65 | - 贴上安装失败的图片) 66 | - 学长:这个都不会还是别学了 67 | 68 | ![img](https://github.com/LanshanTeam/Courseware-Backend-Go-2022/raw/main/class00-%E9%80%9A%E8%AF%86%E8%AF%BE/image/image-20221030123200419.png) 69 | 70 | **再举个🌰** 71 | 72 | 某论坛 73 | 74 | - A:有人有赛博朋克2077游戏资源吗? 75 | - B:有,给https://pan.baidu.com/xxxxxxxxxx 76 | - A:为啥要用百度网盘啊,我没用过 77 | - B:只有这个 78 | - A:点击哪里下载啊 79 | - B:(截了个图) 80 | - A:哦哦 81 | - 过了几个小时 82 | - A:为什么下下来不能直接玩? 83 | - B:这是个压缩包,你要解压 84 | - A:唉,我玩个游戏也太麻烦了,有什么方式能解压吗 85 | 86 | 是不是能理解了呢😄 87 | 88 | ### 我们为什么对你们提的问题感到厌烦? 89 | 90 | - 提的问题“过于简单“ 91 | - 提问题的方式不对 92 | - 遇到问题就提问 93 | 94 | ### 问问题要讲究策略 95 | 96 | 你可以根据以下顺序尝试解决你的问题: 97 | 98 | 1. 百度/Google 99 | 2. 问 chatgpt 100 | 3. 项目使用文档、API使用说明 101 | 4. CSDN/Stack Overflow 102 | 5. 尝试分析源码 103 | 104 | ### 怎么问问题? 105 | 106 | - 精确地描述问题并言之有物 107 | 108 | - 仔细、清楚地描述你的问题或者Bug的症状 109 | - 描述问题发生的环境 110 | - 描述在提问前你是怎样去研究和解决这个问题的 111 | - 描述在提问前你为了确定问题而采取的诊断步骤 112 | - 尽可能地提供一个可以`重现这个问题的可控环境`的方法 113 | 114 | - 话不在多,在于精 115 | 116 | 你需要提供精确有内容的信息 117 | 118 | 这并不是要求你简单的就把成堆的错误代码或者资料完全放在你的提问中,如果你的问题是一个很大的是程序挂掉的这样一个代码运行环境,尽量把它剪裁得越小越好。 119 | 120 | 这样做的好处至少有三点: 121 | 122 | - 第一,表现你为简化问题付出了努力,这可以使你得到回答问题的概率增加 123 | - 第二,简化问题使你更有可能得到有用的答案 124 | - 第三,在精炼你的 bug 报告的过程中,你很可能就自己找到了解决方法或者权宜之计 125 | 126 | - 清楚明确地表达你的需求 127 | 128 | 漫无边际的提问是近乎无休无止的时间黑洞。 129 | 130 | 最有可能最有能力给你有用答案的人通常也是最忙的人(他们忙是因为要亲自完成大部分工作)。所以我们对这样无休止无节制的时间黑洞是相当厌恶的。 131 | 132 | 所以,请界定一下你的问题,使我们花在辨识你的问题和回答所需要付出的时间减到最少。 133 | 134 | - 询问有关代码的问题时 135 | 136 | 千万不要动辄就要求别人帮你去调试有问题的代码,而且也不提示一下应该从何入手。 137 | 138 | 张贴几百行的代码,然后说一声:`这段代码有问题`,我们可能完全会忽略这样的问题,而且回都不想回 139 | 140 | 相比于这个,只贴几十行的代码,然后说一句:`在第七行代码之后,我觉得程序会输出xxx,但实际出现的xxx`更有可能让你得到回应 141 | 142 | 最有效描述程序代码问题的方法就是提供一段最精简的 Bug 展示的测试用例。 143 | 144 | 什么是最精简的测试用例?那是问题的缩影;就是一小个代码片段能够刚好的展示出程序的异常行为,而不包含其他令人分散注意力的代码的内容。 145 | 146 | 怎么才能找出最精简的测试用例?如果你知道哪一行或者哪一段代码会造成异常的行为,复制下来并且加入能够重现这个状况的代码(能让这段异常的代码正常运行)。如果你无法将问题缩减到一个特定区块,就复制一份代码并移除不影响产生问题行为的部分。总之,你发出的测试用例(或代码)越少越好。 147 | 148 | - 截图的方式 149 | 150 | 不要手机拍照!不要手机拍照!不要手机拍照! 151 | 152 | - 有礼貌地提问 153 | 154 | 多用`请`和`谢谢你的回答`,让回答者知道你对他们能够花费掉自己宝贵地时间来对你提供帮助心存感激。 155 | 156 | 157 | 158 | 当然,对于很多问题大家都是 0 基础,问很正常,我们还是很乐意大家提问的 159 | 160 | ## 安装软件以及一些工具的使用 161 | 162 | ### 一些懂的都懂的东西 163 | 164 | 为什么我 github 只能看运气进?为什么我 git 老是推不上去?为什么我项目拉不下来? 165 | 166 | 这些问题都是懂得都懂的,除了换镜像源之外该怎么解决呢? 167 | 168 | 挂加速器可以解决一时之需,无论是游戏加速器还是「Watt Toolkit」,都能加速 github 等一些学术网站 169 | 170 | 如果你之后有更深的需求,这里不方便说,来私聊群里的学长学姐 171 | 172 | ### 学会使用 ChatGPT 173 | 174 | ChatGPT 在22年12月初才正式进入公众视野,23年初才广为人知,但是可以说已经给程序员带来了天翻地覆的影响 175 | 176 | 除了可以用来完成水课作业,还可以对初学者起到非常大的帮助,我觉得应该可以解决 90% 的问题吧 177 | 178 | 首先选择一个环境,我其实不太推荐官网,因为官方注册账号有门槛,很多节点被墙,经常抽风。所以我比较推荐一些镜像源网站比如[这个](https://poe.com/) 179 | 180 | 登上这个网站只需要任意一个梯子就行,其次 gpt-3.5 模型是免费的,还支持其他的很多模型 181 | 182 | 像这样,你可以向它提出问题,也可以喊它给出某个功能的代码,也可以直接复制粘贴报错信息看是什么原因 183 | 184 | ![image-20231008104402198](./images/image-20231008104402198.png) 185 | 186 | 但是切记不可依赖 gpt,你会发现它很多时候还是很蠢的(比如学校的题很多都做不起),可以拿来提高效率但是不能让它替代你的工作 187 | 188 | ### Github 189 | 190 | 什么是 github ? 191 | 192 | GitHub是一个面向开发者的基于Web的版本控制和代码托管平台。它提供了一个集中存储、管理和协作开发代码的平台,使开发者能够更好地进行团队协作、版本控制和代码管理。 193 | 194 | GitHub的主要功能包括: 195 | 196 | 1. 代码托管:开发者可以将他们的代码库(仓库)上传到GitHub上进行存储和管理。这样可以确保代码的备份和安全,并且可以方便地与其他开发者共享和协作。 197 | 2. 版本控制:GitHub使用Git作为其主要的版本控制系统。Git可以追踪代码的修改历史,并提供了分支(branch)和合并(merge)等功能,使得多人协作和代码管理更加灵活和高效。 198 | 3. 协作开发:GitHub提供了丰富的协作功能,开发者可以通过提出问题(issue)、进行讨论、提交请求(pull request)等方式与团队成员交流和合作。这使得多人协作开发更加便捷和透明。 199 | 4. 社交网络:GitHub具有社交网络的特性,开发者可以关注其他开发者、关注和点赞感兴趣的项目,以及参与开源社区的活动。这种社交互动促进了知识共享、技术交流和合作机会。 200 | 5. 自动化工具:GitHub支持与其他开发工具和服务的集成,例如持续集成(Continuous Integration)和部署(Deployment)工具,可以通过自动化流程来构建、测试和部署软件。 201 | 202 | GitHub在开源社区和商业开发中都非常受欢迎。许多开源项目都托管在GitHub上,使得开发者可以共享代码、解决问题和贡献代码。同时,许多企业也使用GitHub作为内部代码托管和团队协作的平台,提高了开发效率和代码质量。 203 | 204 | 现在让我们打开 github https://github.com/ 205 | 206 | 点击右上角 Sign up 创建一个账户 207 | 208 | ![image-20231007213835122](./images/image-20231007213835122.png) 209 | 210 | 填写自己的邮箱、密码、用户名等信息,然后用邮箱验证即可完成。 211 | 212 | ![img](https://img-blog.csdnimg.cn/c07af9239da8498a9ba336ccab523c7f.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA55m95rC05bCP55Cq5LiDeWE=,size_20,color_FFFFFF,t_70,g_se,x_16) 213 | 214 | 是不是全是英文感觉看不懂🥺 215 | 216 | 我的建议是,习惯就好🤣,以后会经常碰到英文文档的 217 | 218 | 然后点击右上角头像之后点击仓库 219 | 220 | ![image-20231007214308703](./images/image-20231007214308703.png) 221 | 222 | 点击这里新建你的第一个仓库 223 | 224 | ![image-20231007214410280](./images/image-20231007214410280.png) 225 | 226 | 这些先简单填一点,开源证书什么的可以暂时不用管,然后点击创建仓库,那么以后就可以在把你的作业代码交到这个仓库里啦 227 | 228 | ![image-20231007214554512](./images/image-20231007214554512.png) 229 | 230 | ### JetBrains 231 | 232 | JetBrains是一家软件开发工具公司,专注于提供高效、智能的集成开发环境(IDE)和开发者工具。他们的产品被广泛应用于各种编程语言和技术栈的开发中。 233 | 234 | 除了马上会提到的 Goland,像 C语言可以用 Clion,Python 可以用 PyCharm,java 可以用 IntelliJ IDEA 235 | 236 | 如何下载? 237 | 238 | 看看下面的博客👇里面提到的学生认证可以不用急着弄 239 | 240 | https://blog.csdn.net/qq_25887493/article/details/124106216?ops_request_misc=&request_id=&biz_id=102&utm_term=%E5%AE%89%E8%A3%85%20jetbrains&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-1-124106216.142^v95^chatgptT3_1&spm=1018.2226.3001.4187 241 | 242 | ### Goland 243 | 244 | 可能有同学会问,为啥用 JetBrains 的 Goland ,不用 vscode 245 | 246 | 我直接粘贴 chatgpt 了 247 | 248 | ![image-20231007215945173](./images/image-20231007215945173.png) 249 | 250 | 可以在 JetBrains 里的 ToolBox 里直接快速安装 251 | 252 | 想直接下载? 253 | 254 | 看看下面的博客👇 255 | 256 | https://blog.csdn.net/Cappuccino_jay/article/details/129466743?ops_request_misc=&request_id=&biz_id=102&utm_term=%E5%AE%89%E8%A3%85%20goland&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-0-129466743.142^v95^chatgptT3_1&spm=1018.2226.3001.4187 257 | 258 | ### Git 259 | 260 | ![img](https://github.com/LanshanTeam/Courseware-Backend-Go-2022/raw/main/class00-%E9%80%9A%E8%AF%86%E8%AF%BE/image/git-github.png) 261 | 262 | ![image-20231008105637726](./images/image-20231008105637726.png) 263 | 264 | 省流:Git 是目前世界上最先进的分布式版本控制系统,用来团队协作很方便。就不用每次上传代码都靠手动 upload files 了 265 | 266 | 267 | 268 | 安装教程看这个👇 269 | 270 | https://blog.csdn.net/Passerby_Wang/article/details/120767020?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522169673342216800182730025%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=169673342216800182730025&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-4-120767020-null-null.142^v95^chatgptT3_1&utm_term=%E5%AE%89%E8%A3%85%20git&spm=1018.2226.3001.4187 271 | 272 | 然鹅我们在用 git 的时候基本上不会在 git 的客户端里敲命令,要么用 IDE 的图形化工具要么用 IDE 的命令行 273 | 274 | ![image-20231008110110760](./images/image-20231008110110760.png) 275 | 276 | 使用教程后面第二节课会详细讲,感兴趣的同学可以先看下面这个教程👇 277 | 278 | https://blog.csdn.net/weixin_55387973/article/details/123849753?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522169673357916800226593163%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=169673357916800226593163&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-123849753-null-null.142^v95^chatgptT3_1&utm_term=goland%20git&spm=1018.2226.3001.4187 279 | 280 | 感觉教程看不懂?那来玩下 git 闯关小游戏吧👇 281 | 282 | https://learngitbranching.js.org/?demo=&locale=zh_CN 283 | 284 | ### Typora 285 | 286 | #### 什么是markdown? 287 | 288 | Markdown是一门标记语言。假如你们知道HTML的话,Markdown和HTML可以认为是同类事物。简而言之,就是用纯文本的代码描述带格式文本的一种方法。 289 | 290 | 那么有了HTML,要Markdown有何用?最简单的答案是:HTML太难写了! 291 | 292 | 这样对于关注内容的作者而言友好了很多,而且即使是Markdown代码也有相当强的可读性。 293 | 294 | 于是写好Markdown后,如何将其显示成我们想要的格式呢?通常的做法是将Markdown编译为HTML。其实一些网站已经自带了这样的编译器,输入Markdown代码后直接将其转化为HTML,就可以交给浏览器渲染了。GitHub上的README以及各种.md格式文件的预览功能就是最典型的例子。 295 | 296 | Markdown 编写的文档后缀为 `.md`, `.markdown` 297 | 298 | #### 什么地方会用到markdown? 299 | 300 | Markdown可以广泛用于文档、博客、论坛等带格式文本内容的创作,习惯后使用起来会比所见即所得的HTML编辑器更加方便快捷,较Word等格式又有纯文本这一优势。 301 | 302 | #### typora又是啥 303 | 304 | 而 typora 就是一款非常方便的支持 md 格式的笔记软件,有很多功能:实时预览、数学公式、代码高亮、表格、支持HTML、流程图等等 305 | 306 | 这份文档就是在 typora 里敲出来的,反正我个人大多数情况下觉得比 word 好用多了 307 | 308 | 官网下载就完事了https://typora.io/ 309 | 310 | ## 如何高效学习 311 | 312 | > 我除了跟着学长学姐学习之外还能在哪里自学呢? 313 | 314 | 有这种自学的意识是非常好的,到了大学之后自驱力是成为强者的必要特质 315 | 316 | 接下来我会介绍一些学习心得 317 | 318 | ### 入门 Go 的一些资源 319 | 320 | 贪多嚼不烂,先推荐一些基础的: 321 | 322 | [李文周的博客](https://www.liwenzhou.com/posts/Go/golang-menu/) 323 | 324 | [go 语言圣经](https://books.studygolang.com/gopl-zh/) 325 | 326 | 还有一本难度很大的书,推荐学过数据结构,操作系统之后再来看 327 | 328 | 《Go 程序设计语言》 329 | 330 | ### Coding 能力 331 | 332 | #### 网站资源 333 | 334 | 这个是覆盖面积最广的,也是质量最参差不齐的 335 | 336 | 首先是 [**CSDN**](https://www.csdn.net/),这个东西很抽象,应该是国内最老牌的技术交流网站,但是正因如此质量参差不齐,有很多灌水,缝合,抄袭的文章。所以说在上面找资源等于 *里掏金,当然对于初学者而言还是很有帮助的 337 | 338 | 然后是字节搞的[稀土掘金](https://juejin.cn/),观感比CSDN好多了,但是也有缺陷就是有时候想搜的内容在上面搜不到 339 | 340 | 比较推荐的是 [stackoverflow](https://stackoverflow.com/),全世界知名的技术交流论坛,但是对英语能力有一定要求 341 | 342 | 然后可能有同学想看视频资源,b站也是个不错的选择,但是质量更是参差不齐,我觉得大部分看文档就能理解没必要去浪费时间看视频,除非视频资源真的讲的很好。其他诸如极客时间等等就不一一介绍了 343 | 344 | #### 学会看官方文档 345 | 346 | 官方文档其实可以说是了解一个组件最高效的方式,在有一定编程基础后看官方文档入门是不二之选 347 | 348 | 以下图为例,这是 go 的官方文档,介绍了包含的所有官方库以及其他功能 349 | 350 | ![image-20231008123718393](./images/image-20231008123718393.png) 351 | 352 | 很多组件是国外开发的,因此也是英文的,看懂其实并不难,只是一开始会有畏难情绪,能过四级就没多大阅读障碍了 353 | 354 | 如果实在看起觉得恼火可以找找有没有中文镜像站或者直接整页翻译 355 | 356 | #### 学会看源码 357 | 358 | 这个估计已经是很高级的能力了,与 Java 能用到退休的教程和生态相比,Go虽然也不是很年轻了,但是肯定也是会遇到没有教程的时候。 359 | 360 | 这时候咋办?看源码! 361 | 362 | 而且看源码还能提高你的 Coding 能力,在找工作面试的时候面试官也很有可能问到 go 的一些常见底层实现。例如看到某个库的教程觉得讲的太烂了,这个时候就可以去看看相应的源码 363 | 364 | 除此之外学习也是从“抄袭”开始的,抄的是代码逻辑,排版风格...然后把这些东西融会贯通贯通成为自己的,GitHub上就有很多开源项目供大家学习 365 | 366 | ### 时间管理能力 367 | 368 | > 觉得上了大学之后没有想象中那么轻松?觉得一周里课太多?觉得事情很多忙不过来? 369 | 370 | 这也是很多同学大一时会犯的通病 371 | 372 | 首先我们需要明确一个前提:**靠学校教的东西以后出去吃不了饭** 373 | 374 | > 如果你的目标是本科毕业直接就业,那么你绝对需要自己好好规划时间 375 | 376 | 首先说本科毕业就业,那么你对绩点就不需要那么看重了,每科能过就行 377 | 378 | 就大一上而言,需要重视的就是高数和线代。难道C语言就不重要了吗?当然很重要,重要的是它是很多人接触的第一门编程语言,能培养编程思维。说C语言不需要重视是因为学校里教的很水,拿我自己举例子,我暑假学了一点C,上课一节课没听最后期末总成绩都能上90。也就是说有这时间听老师上课讲不如自己自学。高数和线代要好好学,要不然期末突击也不太好过。 379 | 380 | 因此可以得出除了高数和线代都是水课,一周加起来也才五节课,这么来看是不是就轻松很多了呢。其他时间可以狠狠地拿来学编程了,老师管的严的把电脑搬到最后一排坐着;管的不严的emm。。你懂的 381 | 382 | > 那我想保研或者考研咋办呢 383 | 384 | 考研的话其实和上面差别不大,只是不能抱着只是为了通过考试的心态了,要正儿八经好好学**想考专业的专业课**。 385 | 386 | 但是保研的话就不一样了,我反正觉得挺坐牢的😭,每门课你都得认真对待,平时分考试分都不能落下。上课该回答问题就回答问题🙋 387 | 388 | #### 竞赛 389 | 390 | 以下仅代表我个人观点 391 | 392 | 竞赛的重要性排序依次是:保研>考研>工作,但是并不绝对 393 | 394 | 保研你没有拿的出手的东西估计只有保本校了,工作的话很多奖人家不是很看重,只是锦上添花。 395 | 396 | 然后我们再来谈谈种类,我**个人**比较倾向于分为这几类:**计算机设计类、算法类、创新创业类、数模、其他类** 397 | 398 | ##### 计算机设计类 399 | 400 | 这个我觉得对于我们搞开发的是最好打的,几个人组个小队,搓个项目,碰到比赛交上去就行。 401 | 402 | 有学长拿他们的项目打了很多比赛都取得了很好的成绩 403 | 404 | ##### 算法类 405 | 406 | 这个东西下限差不多就是蓝桥杯的水平,上限应该是ACM的水平 407 | 408 | 平时没事多练习蓝桥杯还是很好拿奖的。但是ACM就非常有难度了,需要投入大量精力并且可能对天赋也有一定要求,没有专门训练过就是去送 409 | 410 | ##### 创新创业类 411 | 412 | 就是大创、互联网+之类的。比赛周期比较长,跟开发关联其实不是很大,PPT制作、项目策划书以及答辩比较重要。如果真要搞这个建议找靠谱的老师组个团队带你打 413 | 414 | ##### 数模类 415 | 416 | 跟算法类比较相似,也有一定门槛。想拿奖也需要投入精力时间 417 | 418 | ##### 其他类 419 | 420 | 像什么大数据、物联网、嵌入式、机器人、邮政、医疗等等就不一一介绍了,关联度不是很大 421 | 422 | ## 后记 423 | 424 | ​ **选择大于努力** 425 | 426 | That's all 427 | 428 | -------------------------------------------------------------------------------- /lesson01/蓝山工作室——Golang第一节课.md: -------------------------------------------------------------------------------- 1 | # 蓝山工作室——Golang第一节课 2 | 3 | ## 前言 4 | 5 | 首先,我要热烈欢迎你们加入我们的Go语言课程。Go语言是一门强大且灵活的编程语言,它具备高性能、简单易学的特点,已经在许多领域取得了广泛的应用。本课程将帮助你掌握Go语言的核心概念、语法和最佳实践,无论你是初学者还是有一些编程经验的学员。 6 | 7 | Go语言是一门开源编程语言,由Google开发并维护。它的设计目标是简单性、效率和可读性,使得开发者可以更轻松地构建高性能的应用程序。Go语言在云计算、网络编程、大数据处理等领域表现出色,是许多知名公司和大厂的首选语言之一。 8 | 9 | 这节课的主要内容是认识:变量,常量,基本数据类型,运算符,流程控制,函数基础,fmt包 10 | 11 | ## 基础语法 12 | 13 | ### var 14 | 15 | #### 变量 16 | 17 | ##### 变量类型 18 | 变量(Variable)的功能是存储数据。不同的变量保存的数据类型可能会不一样。经过半个多世纪的发展,编程语言已经基本形成了一套固定的类型,常见变量的数据类型有:整型、浮点型、布尔型等。 19 | 20 | Go语言中的每一个变量都有自己的类型,并且变量必须经过声明才能开始使用。 21 | 22 | 1. **整数类型(Integer Types)**: 23 | - `int`:根据你的操作系统架构,可以是32位或64位的整数。 24 | - `int8`、`int16`、`int32`、`int64`:有符号整数类型,分别表示8位、16位、32位和64位整数。 25 | - `uint`:无符号整数类型,根据操作系统架构,可以是32位或64位。 26 | - `uint8`、`uint16`、`uint32`、`uint64`:无符号整数类型,分别表示8位、16位、32位和64位整数。 27 | 2. **浮点数类型(Floating-Point Types)**: 28 | - `float32`:单精度浮点数。 29 | - `float64`:双精度浮点数。 30 | 3. **复数类型(Complex Types)**: 31 | - `complex64`:包含32位实部和32位虚部的复数。 32 | - `complex128`:包含64位实部和64位虚部的复数。 33 | 4. **布尔类型(Boolean Type)**: 34 | - `bool`:表示真(true)或假(false)。 35 | 5. **字符串类型(String Type)**: 36 | - `string`:用于存储文本数据的字符序列。 37 | 6. **字节类型(Byte Type)**: 38 | - `byte`:`uint8` 的别名,通常用于表示ASCII字符。 39 | 7. **符文类型(Rune Type)**: 40 | - `rune`:`int32` 的别名,通常用于表示**Unicode字符**。 41 | - Unicode(统一码、万国码、单一码)是一种字符编码标准,它用于表示世界上几乎所有的书写系统中的字符,**包括各种文字、标点符号和特殊符号**。Unicode的目标是提供一个统一的、跨语言的字符编码系统,以消除不同字符编码之间的混乱和兼容性问题。 42 | 43 | ##### 变量声明 44 | Go语言中的变量需要声明后才能使用,同一作用域内不支持重复声明。并且Go语言的变量声明后**必须使用**。 45 | 46 | ##### 变量的初始化 47 | Go语言在声明变量的时候,会自动对变量对应的内存区域进行初始化操作。每个变量会被初始化成其类型的默认值,例如: 整型和浮点型变量的默认值为0。 字符串变量的默认值为空字符串。 布尔型变量默认为false。 切片、函数、指针变量的默认为nil。 48 | 49 | ```go 50 | var 变量名 类型 = 表达式 51 | 52 | var a = "initial" // 类型推导,不指定类型自动判断 53 | 54 | var b, c int = 1, 2 // 一次初始化多个变量 55 | 56 | var d = true // 布尔型变量 57 | 58 | var e float64 // 普通声明未赋值 59 | 60 | f := float32(e) // 类型转换初始化赋值 61 | 62 | g := a + "apple" 63 | fmt.Println(a, b, c, d, e, f) // initial 1 2 true 0 0 64 | fmt.Println(g) // initialapple 65 | ``` 66 | 67 | #### 常量 68 | 69 | 相对于变量,常量是恒定不变的值,多用于定义程序运行期间不会改变的那些值。 常量的声明和变量声明非常类似,只是把var换成了const,常量在定义的时候必须赋值。 70 | 71 | ```go 72 | const s string = "constant" 73 | const h = 500000000 74 | const i = 3e20 / h 75 | fmt.Println(s, h, i, math.Sin(h), math.Sin(i)) 76 | ``` 77 | 78 | ### for 79 | 80 | for 是设置条件并让代码进行循环的关键字 81 | 82 | #### 三段式 83 | 84 | ```go 85 | for A; B; C { 86 | // 这里是循环体,每次循环需要执行的代码在这里面 87 | } 88 | ``` 89 | 90 | `A` 位置是单次表达式,循环开始时会执行一次这里,一般用于初始化变量。 91 | 92 | `B` 位置是条件表达式,即循环条件,只要满足循环条件就会执行循环体。 93 | 94 | `C` 位置是末尾循环体,每次执行完一遍循环体之后会执行一次 `C` 中的表达式。 95 | 96 | 执行末尾循环体后将再次进行条件判断,若条件还成立,则继续重复上述循环,当条件不成立时则跳出当下for循环。 97 | 98 | 并且,你可以选择性的留空,就是让A,B,C**任何位置**为空。例如: 99 | 100 | ```go 101 | for ;i<5;{ 102 | // 循环体 103 | } 104 | ``` 105 | 106 | #### 一段式 107 | 108 | 一段式那就是只写条件 109 | 110 | ```go 111 | for A{ 112 | // 循环体 113 | } 114 | ``` 115 | 116 | `A` 位置是条件表达式,即循环条件,只要满足循环条件就会执行循环体。 117 | 118 | 例如: 119 | 120 | ```go 121 | for i<5{ 122 | // 循环体 123 | } 124 | ``` 125 | 126 | ##### break 关键词 127 | 128 | break 放在循环体中,只要执行到 break,则会立马跳出所在最里循环(注意,是所在最里循环,若嵌套,则无法跳出更外层循环) 129 | 130 | 例如: 131 | 132 | ```go 133 | for i:=1;i<4;i++{ 134 | j := i 135 | for j<4{ 136 | if j == 2{ 137 | break 138 | } 139 | } 140 | fmt.Println("hello lanshan") 141 | } 142 | ``` 143 | 144 | 上面这个函数不需要去体会其中的意思,我只是举个例子。若执行到了break,则只会跳出条件是 j < 4 这个循环,依然会执行println打印 145 | 146 | 这是总的示例,可以回顾一下: 147 | 148 | ```go 149 | package main 150 | 151 | import "fmt" 152 | 153 | func main() { 154 | i := 1 155 | for { 156 | fmt.Println("loop") 157 | break // 跳出循环 158 | } 159 | 160 | // 打印7、8 161 | for j := 7; j < 9; j++ { 162 | fmt.Println(j) 163 | } 164 | 165 | for n := 0; n < 5; n++ { 166 | if n%2 == 0 { 167 | continue 168 | // 当n模2为0时不打印,进到下一次的循环 169 | } 170 | fmt.Println(n) 171 | } 172 | // 直到i>3 173 | for i <= 3 { 174 | fmt.Println(i) 175 | i = i + 1 176 | } 177 | // for 循环嵌套 178 | for i := 0; i < 5; i++ { 179 | for j := 0; j < 5; j++ { 180 | fmt.Printf("i = %d, j = %d\n", i, j) 181 | } 182 | } 183 | } 184 | ``` 185 | 186 | ### if 187 | 188 | ```go 189 | if 条件表达式 { 190 | //当条件表达式结果为true时,执行此处代码 191 | } 192 | 193 | if 条件表达式 { 194 | //当条件表达式结果为true时,执行此处代码 195 | } else { 196 | //当条件表达式结果为false时,执行此处代码 197 | } 198 | ``` 199 | 200 | ```go 201 | package main 202 | 203 | import "fmt" 204 | 205 | func main() { 206 | // 条件表达式为false,打印出"7 是奇数" 207 | if 7%2 == 0 { 208 | fmt.Println("7 是偶数") 209 | } else { 210 | fmt.Println("7 是奇数") 211 | } 212 | 213 | // 条件表达式为ture,打印出"8 被 4 整除" 214 | if 8%4 == 0 { 215 | fmt.Println("8 被 4 整除") 216 | } 217 | 218 | // 这是一个短声明,效果等效于 219 | //num := 9 220 | //if num < 0{ 221 | // ... 222 | //} 223 | if num := 9; num < 0 { 224 | fmt.Println(num, "is negative") 225 | } else if num < 10 { 226 | fmt.Println(num, "has 1 digit") 227 | } else { 228 | fmt.Println(num, "has multiple digits") 229 | } 230 | } 231 | ``` 232 | 233 | ### switch 234 | 235 | 当分支过多的时候,使用if-else语句会降低代码的可阅读性,这个时候,我们就可以考虑使用switch语句 236 | 237 | - switch 语句用于基于不同条件执行不同动作,每一个 case 分支都是唯一的,从上至下逐一测试,直到匹配为止。 238 | - switch 语句在默认情况下 case 相当于自带 break 语句,匹配一种情况成功之后就不会执行其它的case,这一点和 c/c++ 不同 239 | - 如果我们希望在匹配一条 case 之后,继续执行后面的 case ,可以使用 fallthrough 240 | 241 | ```go 242 | package main 243 | 244 | import ( 245 | "fmt" 246 | "time" 247 | ) 248 | 249 | func main() { 250 | 251 | a := 2 252 | switch a { 253 | case 1: 254 | fmt.Println("one") 255 | case 2: 256 | // 在此打印"two"并跳出 257 | fmt.Println("two") 258 | case 3: 259 | fmt.Println("three") 260 | case 4, 5: 261 | fmt.Println("four or five") 262 | default: 263 | fmt.Println("other") 264 | } 265 | 266 | t := time.Now() 267 | switch { 268 | // 根据现在的时间判断是上午还是下午 269 | case t.Hour() < 12: 270 | fmt.Println("It's before noon") 271 | default: 272 | fmt.Println("It's after noon") 273 | } 274 | } 275 | ``` 276 | 277 | ### func 278 | 279 | 函数是指一段可以直接被另一段程序或代码引用的程序或代码,一个较大的程序一般应分为若干个程序块,每一个模块用来实现一个特定的功能。 280 | 281 | 1. **函数的声明和定义**: 282 | 283 | 在Go语言中,函数的定义以 `func` 关键字开始,然后是函数名、参数列表、返回类型和函数体。以下是一个函数的典型定义: 284 | 285 | ```go 286 | func add(x int, y int) int { 287 | return x + y 288 | } 289 | ``` 290 | 291 | 这个函数名为 `add`,接受两个整数参数 `x` 和 `y`,并返回一个整数。 292 | 293 | 2. **函数的参数**: 294 | 295 | 函数可以接受零个或多个参数,参数在参数列表中定义,并且需要指定参数的类型。例如: 296 | 297 | ```go 298 | func greet(name string) { 299 | fmt.Println("Hello, " + name) 300 | } 301 | ``` 302 | 303 | 这个函数接受一个字符串参数 `name`。 304 | 305 | 3. **函数的返回值**: 306 | 307 | 函数可以返回一个或多个值,返回值的类型也需要在函数定义中指定。如果函数没有返回值,可以将返回类型留空。例如: 308 | 309 | ```go 310 | func addAndMultiply(x, y int) (int, int) { 311 | sum := x + y 312 | product := x * y 313 | return sum, product 314 | } 315 | ``` 316 | 317 | 这个函数返回两个整数值。 318 | 319 | 4. **函数的调用**: 320 | 321 | 要调用函数,只需使用函数名并传递参数。例如: 322 | 323 | ```go 324 | result := add(3, 5) 325 | fmt.Println(result) 326 | ``` 327 | 328 | 这里我们调用了 `add` 函数,将参数 `3` 和 `5` 传递给它,并将返回值赋给 `result` 变量。 329 | 330 | ```go 331 | package main 332 | 333 | import "fmt" 334 | 335 | func add(x int, y int) int { 336 | return x + y 337 | } 338 | 339 | func main() { 340 | result := add(3, 5) 341 | fmt.Println(result) 342 | } 343 | ``` 344 | 345 | ### fmt 346 | 347 | fmt 库函数 348 | 349 | ```go 350 | package main 351 | 352 | import "fmt" 353 | 354 | type point struct { 355 | x, y int 356 | } 357 | 358 | func main() { 359 | s := "hello" 360 | n := 123 361 | p := point{1, 2} 362 | fmt.Println(s, n) // hello 123 363 | fmt.Println(p) // {1 2} 364 | 365 | fmt.Printf("s=%v\n", s) // s=hello 366 | fmt.Printf("n=%v\n", n) // n=123 367 | fmt.Printf("p=%v\n", p) // p={1 2} 368 | fmt.Printf("p=%+v\n", p) // p={x:1 y:2} 369 | fmt.Printf("p=%#v\n", p) // p=main.point{x:1, y:2} 370 | 371 | f := 3.141592653 372 | fmt.Println(f) // 3.141592653 373 | fmt.Printf("%.2f\n", f) // 3.14 374 | } 375 | ``` 376 | 377 | ## 年轻人的第一个GoProject 378 | 379 | ## 作业 380 | 381 | ### LV1 382 | 383 | 编写一个Go函数,接受两个整数作为参数,然后返回它们的和。在 `main` 函数中调用此函数并打印结果。 384 | 385 | ### LV2 386 | 387 | 编写一个Go函数,接受圆的半径作为参数,然后返回圆的面积。使用 `math` 包中的常数 Pi。在 `main` 函数中调用此函数并打印结果。 388 | 389 | 提示,引入 Pi 只需要写出`math.Pi` 390 | 391 | ### LV3 392 | 393 | 编写一个Go函数,接受一个整数作为参数,然后判断它是否为素数(质数)。在 `main` 函数中调用此函数并打印结果。提示:一个素数是只能被 1 和自身整除的正整数。 394 | 395 | ### LVX 396 | 397 | 编写一个Go函数,使用`rand`包随机选择一个1-100的数(必须每次执行的随机数都不一样),然后使用**二分法**找到这个数。 398 | 399 | tips:rand 包的使用和二分法自行研究 400 | 401 | 402 | 作业完成后将作业 GitHub 地址发送至 **nihilism0@qq.com** ,若对 GitHub 的使用有问题,可以先网上寻找解决方法,实在不行可以私信我。 403 | 404 | **提交格式(主题):2023xxxx00-邓卓-LV3** (最后LV中写出你完成的最大等级) 405 | 406 | **截止时间**:下一次上课之前 407 | -------------------------------------------------------------------------------- /lesson02/02.md: -------------------------------------------------------------------------------- 1 | # 第二节课 2 | 3 | ## 数组-array 4 | 5 | > 数组是指一系列同一类型数据的集合。数组中包含的每个数据被称为数组元素(element),一个数组包含的元素个数被称为数组的长度。 6 | 7 | - 初始化 8 | 9 | ```go 10 | // var arr [len]type 11 | // arr := [len]type{data1,data2} 12 | a := [3]int{1, 2} // 未初始化元素值为 0 13 | b := [...]int{1, 2, 3} // 通过初始化值确定数组长度 14 | c := [5]int{2: 100, 4: 200} // 通过索引号初始化元素,未初始化元素值为 0 15 | fmt.Println(a, b, c) //[1 2 0] [1 2 3] [0 0 100 0 200] 16 | 17 | //支持多维数组 18 | d := [4][2]int{{10, 11}, {20, 21}, {30, 31}, {40, 41}} 19 | e := [...][2]int{{10, 11}, {20, 21}, {30, 31}, {40, 41}} //第二维不能写"..." 20 | f := [4][2]int{1: {20, 21}, 3: {40, 41}} 21 | g := [4][2]int{1: {0: 20}, 3: {1: 41}} 22 | fmt.Println(d, e, f, g) 23 | 24 | ``` 25 | 26 | > 数组⻓度必须是常量,且是类型的组成部分。 [2]int 和 [3]int 是不同类型。 27 | 28 | - 操作数组 29 | 30 | ```go 31 | // 数组的每个元素可以通过索引下标来访问,索引下标的范围是从0开始到数组长度减1的位置。 32 | var a [10]int 33 | for i := 0; i < 10; i++ { 34 | a[i] = i + 1 35 | fmt.Printf("a[%d] = %d\n", i, a[i]) 36 | } 37 | 38 | //range具有两个返回值,第一个返回值是元素的数组下标,第二个返回值是元素的值 39 | for i, v := range a { 40 | fmt.Println("a[", i, "]=", v) 41 | } 42 | 43 | ``` 44 | 45 | 46 | 47 | ## 切片-slice 48 | 49 | > 数组的长度在定义之后无法再次修改;数组是值类型,每次传递都将产生一份副本。显然这种数据结构无法完全满足开发者的真实需求。Go语言提供了数组切片(slice)来弥补数组的不足。 50 | 51 | - 初始化 52 | 53 | ```go 54 | //初始化一个空的切片 55 | var a1 []int //就比数组少个中括号里的长度 56 | a2 := []int{} 57 | 58 | //或者用make函数 59 | //make([]T, length, capacity) //capacity省略,则和length的值相同 60 | var a3 []int = make([]int, 0) 61 | a4 := make([]int, 0, 0) 62 | 63 | a5 := []int{1, 2, 3} //创建切片并初始化值 64 | ``` 65 | 66 | - 操作切片 67 | 68 | ![](./images/img2.png) 69 | 70 | - 遍历 71 | 72 | ```go 73 | func main() { 74 | slice := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} 75 | //i是下标,v是元素的值 76 | for i, v := range slice { 77 | fmt.Printf("slice[%d]=%d, ", i, v) 78 | //slice[0]=-1, slice[1]=1, slice[2]=2, slice[3]=3, slice[4]=4, slice[5]=5, slice[6]=6, slice[7]=7, slice[8]=8, slice[9]=9, 79 | } 80 | fmt.Println("\n") 81 | } 82 | 83 | ``` 84 | 85 | - append( ) 86 | 87 | > append函数向 slice 尾部添加数据,返回新的 slice 对象: 88 | 89 | ```GO 90 | var s1 []int //创建nil切换 91 | //s1 := make([]int, 0) 92 | s1 = append(s1, 1) //追加1个元素 93 | s1 = append(s1, 2, 3) //追加2个元素 94 | s1 = append(s1, 4, 5, 6) //追加3个元素 95 | fmt.Println(s1) //[1 2 3 4 5 6] 96 | 97 | s2 := make([]int, 5) 98 | s2 = append(s2, 6) 99 | fmt.Println(s2) //[0 0 0 0 0 6] 100 | 101 | s3 := []int{1, 2, 3} 102 | s3 = append(s3, 4, 5) 103 | fmt.Println(s3)//[1 2 3 4 5] 104 | 105 | ``` 106 | 107 | > append函数会智能地底层数组的容量增长,一旦超过原底层数组容量,通常以2倍容量重新分配底层数组,并复制原来的数据 108 | 109 | ```GO 110 | func main() { 111 | var x, y []int 112 | for i := 0; i < 10; i++ { 113 | y = appendInt(x, i) 114 | fmt.Printf("%d cap=%d\t%v\n", i, cap(y), y) 115 | x = y 116 | } 117 | } 118 | ``` 119 | 120 | 输出: 121 | 122 | ```shell 123 | 0 cap=1 [0] 124 | 1 cap=2 [0 1] 125 | 2 cap=4 [0 1 2] 126 | 3 cap=4 [0 1 2 3] 127 | 4 cap=8 [0 1 2 3 4] 128 | 5 cap=8 [0 1 2 3 4 5] 129 | 6 cap=8 [0 1 2 3 4 5 6] 130 | 7 cap=8 [0 1 2 3 4 5 6 7] 131 | 8 cap=16 [0 1 2 3 4 5 6 7 8] 132 | 9 cap=16 [0 1 2 3 4 5 6 7 8 9] 133 | ``` 134 | 135 | - 切片和数组 136 | 137 | > 切片并不是数组或数组指针,它通过内部指针和相关属性引⽤数组⽚段,以实现变⻓⽅案。 138 | > 139 | > slice并不是真正意义上的动态数组,而是一个引用类型。slice总是指向一个底层array 140 | 141 | 142 | 143 | ![](./images/img1.png) 144 | 145 | 146 | 147 | ## 字典-map 148 | 149 | > map是一种无序的基于key-value的数据结构 150 | > 151 | > 类似于我们高中学过的函数y=f(x) 152 | > 153 | > 每个x就会有一个与之对应的y 154 | > 155 | > 在map中,key就可以相当于x,value相当于y 156 | 157 | - 定义 158 | 159 | ```go 160 | // map[KeyType]ValueType 161 | // KeyType:表示键的类型,只能为基本类型。 162 | // ValueType:表示键对应的值的类型。 163 | var m1 map[int]string 164 | 165 | //或者用make定义 166 | //make(map[KeyType]ValueType, [cap]) 167 | //其中cap表示map的容量,该参数不是必须的 168 | m2 := make(map[int]string) 169 | m3 := make(map[int]string,10) 170 | ``` 171 | 172 | - 使用 173 | 174 | ```go 175 | func main() { 176 | scoreMap := make(map[string]int, 8) 177 | scoreMap["张三"] = 90 178 | scoreMap["小明"] = 100 179 | fmt.Println(scoreMap) 180 | fmt.Println(scoreMap["小明"]) 181 | fmt.Printf("type of a: %T\n", scoreMap) 182 | } 183 | // map[小明:100 张三:90] 184 | // 100 185 | // type of a: map[string]int 186 | ``` 187 | 188 | ```go 189 | // map也支持在声明的时候填充元素 190 | func main() { 191 | userInfo := map[string]string{ 192 | "username": "pprof.cn", 193 | "password": "123456", 194 | } 195 | fmt.Println(userInfo) 196 | } 197 | ``` 198 | 199 | - 遍历 200 | 201 | ```go 202 | m1 := map[int]string{1: "abc", 2: "def"} 203 | //迭代遍历1,第一个返回值是key,第二个返回值是value 204 | for k, v := range m1 { 205 | fmt.Printf("%d ----> %s\n", k, v) 206 | //1 ----> abc 207 | //2 ----> def 208 | } 209 | 210 | //迭代遍历2,第一个返回值是key,第二个返回值是value(可省略) 211 | for k := range m1 { 212 | fmt.Printf("%d ----> %s\n", k, m1[k]) 213 | //1 ----> abc 214 | //2 ----> def 215 | } 216 | 217 | //判断某个key所对应的value是否存在, 第一个返回值是value(如果存在的话) 218 | value, ok := m1[1] 219 | fmt.Println("value = ", value, ", ok = ", ok) //value = abc , ok = true 220 | 221 | value2, ok2 := m1[3] 222 | fmt.Println("value2 = ", value2, ", ok2 = ", ok2) //value2 = , ok2 = false 223 | 224 | ``` 225 | 226 | 227 | 228 | - 删除 229 | 230 | ```go 231 | m1 := map[int]string{1: "abc", 2: "def", 3: "gh"} 232 | //迭代遍历1,第一个返回值是key,第二个返回值是value 233 | for k, v := range m1 { 234 | fmt.Printf("%d ----> %s\n", k, v) 235 | //1 ----> abc 236 | //2 ----> def 237 | //3 ----> gh 238 | } 239 | 240 | delete(m1, 2) //删除key值为2的map 241 | 242 | for k, v := range m1 { 243 | fmt.Printf("%d ----> %s\n", k, v) 244 | //1 ----> abc 245 | //3 ----> gh 246 | } 247 | ``` 248 | 249 | ## 函数 250 | 251 | - 函数类型 252 | 253 | > 在Go语言中,函数也是一种数据类型,我们可以通过type来定义它,它的类型就是所有拥有相同的参数,相同的返回值的一种类型。 254 | 255 | ```go 256 | type FuncType func(int, int) int //声明一个函数类型, func后面没有函数名 257 | 258 | //函数中有一个参数类型为函数类型:f FuncType 259 | func Calc(a, b int, f FuncType) (result int) { 260 | result = f(a, b) //通过调用f()实现任务 261 | return 262 | } 263 | 264 | func Add(a, b int) int { 265 | return a + b 266 | } 267 | 268 | func Minus(a, b int) int { 269 | return a - b 270 | } 271 | 272 | func main() { 273 | //函数调用,第三个参数为函数名字,此函数的参数,返回值必须和FuncType类型一致 274 | result := Calc(1, 1, Add) 275 | fmt.Println(result) //2 276 | 277 | var f FuncType = Minus 278 | fmt.Println("result = ", f(10, 2)) //result = 8 279 | } 280 | 281 | ``` 282 | 283 | 284 | 285 | - 匿名函数 286 | 287 | > 在Go里面,函数可以像普通变量一样被传递或使用,Go语言支持随时在代码里定义匿名函数。 288 | 289 | ```go 290 | package main 291 | 292 | func main() { 293 | fn := func() { println("我是一个匿名函数") } 294 | fn() 295 | } 296 | ``` 297 | 298 | 299 | 300 | - 闭包 301 | 302 | > 闭包是由函数及其相关引用环境组合而成的实体(即:闭包=函数+引用环境) 303 | > 304 | > 这就意味着当闭包被调用的时候,不管在程序什么地方调用,闭包能够使用这些常量或者变量。 305 | > 306 | > 它不关心这些捕获了的变量和常量是否已经超出了作用域,所以只有闭包还在使用它,这些变量就还会存在。 307 | 308 | ```go 309 | package main 310 | 311 | import ( 312 | "fmt" 313 | ) 314 | 315 | func a() func() int { 316 | i := 0 317 | b := func() int { 318 | i++ 319 | fmt.Println(i) 320 | return i 321 | } 322 | return b 323 | } 324 | 325 | func main() { 326 | c := a() 327 | c() 328 | c() 329 | c() 330 | } 331 | ``` 332 | 333 | 输出结果 334 | 335 | ```shell 336 | 1 337 | 2 338 | 3 339 | ``` 340 | 341 | - defer延迟调用 342 | 343 | > 关键字 defer ⽤于延迟一个函数的执行 344 | > 345 | > defer语句经常被用于处理成对的操作,如打开、关闭、连接、断开连接、加锁、释放锁。 346 | > 347 | > 如果一个函数中有多个defer语句,它们会以后进先出的顺序执行。 348 | 349 | ```go 350 | func func1(){ 351 | fmt.Println("func1调用") 352 | } 353 | func func2(){ 354 | fmt.Println("func2调用") 355 | } 356 | func func3(){ 357 | fmt.Println("func3调用") 358 | } 359 | func main(){ 360 | defer func1() 361 | defer func2() 362 | defer func3() 363 | fmt.Println("a") 364 | fmt.Println("b") 365 | } 366 | ``` 367 | 368 | 输出 369 | 370 | ```shell 371 | a 372 | b 373 | func3调用 374 | func2调用 375 | func3调用 376 | ``` 377 | 378 | 379 | 380 | ## git版本控制 381 | 382 | - 什么是git 383 | 384 | > Git 是一个开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目。 385 | 386 | - 为什么使用git 387 | 388 | > 说到版本控制,你可能见过以下类似现象 389 | 390 | ``` 391 | 毕业论文_初稿.doc 392 | 毕业论文_修改1.doc 393 | 毕业论文_修改2.doc 394 | 毕业论文_修改3.doc 395 | 毕业论文_完整版1.doc 396 | 毕业论文_完整版2.doc 397 | 毕业论文_完整版3.doc 398 | 毕业论文_最终版1.doc 399 | 毕业论文_最终版2.doc 400 | 毕业论文_死也不改版.doc 401 | ...... 402 | ``` 403 | 404 | > 以上就是使用最原始的方式进行版本控制,但是这种方式有显著缺点: 405 | > 406 | > - 多个文件,保留所有版本时,需要为每个版本保存一个文件。 407 | > - 协同操作,多人协同操作时,需要将文件打包发来发去。 408 | > - 容易丢失,被删除意味着永远失去...(可以选择网盘) 409 | > 410 | > 为了解决以上版本控制存在问题,应运而生了一批版本控制工具:VSS、CVS、SVN、Git等,其中Git应该属于绝对霸主地位。 411 | 412 | 413 | 414 | - 如何使用git 415 | 416 | 网上很多文章讲的很全面,大家可以自行搜索资料,这里推荐几个链接 417 | 418 | [goland配置git](Git版本控制及Goland使用Git教程) 419 | 420 | [git命令行练习游戏](https://learngitbranching.js.org) 421 | 422 | - git常用命令 423 | 424 | - git init 425 | 426 | > Git 使用 git init 命令来初始化一个 Git 仓库,Git 的很多命令都需要在 Git 的仓库中运行,所以 git init 是使用 Git 的第一个命令。 427 | > 428 | > 在执行完成 git init 命令后,Git 仓库会生成一个 .git 目录,该目录包含了资源的所有元数据,其他的项目目录保持不变。 429 | 430 | - git clone 431 | 432 | > 从现有仓库中拷贝项目 433 | > 434 | > `git clone https://github.com/LanshanTeam/Courseware-Backend-Go-2023` 435 | 436 | - git add 437 | 438 | > 添加文件到暂存区 439 | > 440 | > git add . 441 | 442 | - git commit 443 | 444 | > 将暂存区内容添加到仓库中 445 | > 446 | > git commit -m "this is a commit" 447 | 448 | - git push 449 | 450 | > 上传远程代码并合并 451 | 452 | ## 作业 453 | 454 | ### LV1 455 | 456 | 计算器 457 | 458 | 实现一个简单计算器 459 | 460 | 传入两个元素和一个运算符号,符号需要是一个函数类型 461 | 462 | ```go 463 | func Calculator(num1 int, num2 int, CMD func(int, int) int) int { 464 | return CMD(num1, num2) 465 | } 466 | ``` 467 | 468 | 469 | 470 | ### LV2 471 | 472 | 将goland配置git,并在github上共享一个项目 473 | 474 | 可以尝试使用git的命令行 475 | 476 | ### LV3 477 | 478 | [git命令行练习游戏](https://learngitbranching.js.org) 479 | 480 | 尝试完成以下关卡 481 | 482 | ![](./images/img3.png) 483 | 484 | ![](./images/img4.png) 485 | 486 | 作业完成后将作业 GitHub 地址发送至 **[xiaote33@qq.com](xiaote33@qq.com)** 487 | 488 | LV2-LV3截图发送即可 489 | 490 | **提交格式(主题):2023xxxx00-刘力延-LV3** (最后LV中写出你完成的最大等级) 491 | 492 | **截止时间**:下一次上课之前 493 | -------------------------------------------------------------------------------- /lesson02/images/img1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson02/images/img1.png -------------------------------------------------------------------------------- /lesson02/images/img2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson02/images/img2.png -------------------------------------------------------------------------------- /lesson02/images/img3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson02/images/img3.png -------------------------------------------------------------------------------- /lesson02/images/img4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson02/images/img4.png -------------------------------------------------------------------------------- /lesson03/第三周课件.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | > 包 4 | > 5 | > 指针 6 | > 7 | > 结构体 8 | > 9 | > 接口 10 | 11 | 12 | 13 | 14 | 15 | # 包 16 | 17 | ### 包介绍 18 | 19 | Go语言中支持模块化的开发理念,在Go语言中使用`包(package)`来支持代码模块化和代码复用。 20 | 21 | 包的引入使得我们可以去调用自己或者别人的模块代码,方便了我们的开发。 22 | 23 | 24 | 25 | 例如,在之前的课件中,我们引入了 fmt 这个包。这样使得我们可以调用 fmt 包内部的函数和变量。 26 | 27 | ```go 28 | package main 29 | 30 | import "fmt" 31 | 32 | func main(){ 33 | fmt.Println("Hello world!") 34 | } 35 | ``` 36 | 37 | 接下来详细介绍一下 38 | 39 | ### 定义包 40 | 41 | 我们可以根据自己的需要创建自定义包。一个包可以简单理解为一个存放`.go`文件的文件夹。 42 | 43 | 该文件夹下面的所有`.go`文件都要在非注释的第一行添加如下声明,声明该文件归属的包。 44 | 45 | ```go 46 | package packagename 47 | ``` 48 | 49 | 另外需要注意一个文件夹下面直接包含的文件只能归属一个包,同一个包的文件不能在多个文件夹下。 50 | 51 | 包名为`main`的包是应用程序的入口包,这种包编译后会得到一个可执行文件,而编译不包含`main`包的源代码则不会得到可执行文件。 52 | 53 | 54 | 55 | ### 可见性 56 | 57 | 在同一个包内部声明的标识符都位于同一个命名空间下,在不同的包内部声明的标识符就属于不同的命名空间。想要在包的外部使用包内部的标识符就需要添加包名前缀,例如`fmt.Println("Hello world!")`。 58 | 59 | 如果想让一个包中的标识符(如变量、常量、类型、函数等)能被外部的包使用,那么标识符必须是对外可见的(public)。在Go语言中是通过标识符的首字母大/小写来控制标识符的对外可见(public)/不可见(private)的。在一个包内部只有首字母大写的标识符才是对外可见的。 60 | 61 | 例如我们定义一个名为`demo`的包,在其中定义了若干标识符。在另外一个包中并不是所有的标识符都能通过`demo.`前缀访问到,因为只有那些首字母是大写的标识符才是对外可见的。 62 | 63 | ```go 64 | var Name string // 可在包外访问的方法 65 | var class string // 仅限包内访问的字段 66 | ``` 67 | 68 | 69 | 70 | ### 包的引入 71 | 72 | 要在当前包中使用另外一个包的内容就需要使用`import`关键字引入这个包,并且import语句通常放在文件的开头,`package`声明语句的下方。完整的引入声明语句格式如下: 73 | 74 | ```go 75 | import importname "path/to/package" 76 | ``` 77 | 78 | 其中: 79 | 80 | - importname:引入的包名,通常都省略。默认值为引入包的包名。 81 | - path/to/package:引入包的路径名称,必须使用双引号包裹起来。 82 | - *`Go语言中禁止循环导入包`* 这个一定要注意,很有可能因为这个要重新架构 83 | 84 | 一个Go源码文件中可以同时引入多个包,例如: 85 | 86 | ```go 87 | import "fmt" 88 | import "net/http" 89 | import "os" 90 | ``` 91 | 92 | 当然可以使用批量引入的方式。 93 | 94 | ```go 95 | import ( 96 | "fmt" 97 | "net/http" 98 | "os" 99 | ) 100 | ``` 101 | 102 | 103 | 104 | 如果引入一个包的时候为其设置了一个特殊`_`作为包名,那么这个包的引入方式就称为匿名引入。 105 | 106 | 一个包被匿名引入的目的主要是为了加载这个包,从而使得这个包中的资源得以初始化。 被匿名引入的包中的`init`函数将被执行并且仅执行一遍。 107 | 108 | ```go 109 | import _ "github.com/go-sql-driver/mysql" 110 | ``` 111 | 112 | 匿名引入的包与其他方式导入的包一样都会被编译到可执行文件中。 113 | 114 | 115 | 116 | 117 | 118 | # Go语言中的指针 119 | 120 | 任何程序数据载入内存后,在内存都有他们的地址,这就是指针。而为了保存一个数据在内存中的地址,我们就需要指针变量。 121 | 122 | `*Go语言中的指针不能进行偏移和运算*`,因此Go语言中的指针操作非常简单,我们只需要记住两个符号:`&`(取地址)和`*`(根据地址取值)。 123 | 124 | 125 | 126 | ## 指针地址和指针类型 127 | 128 | 每个变量在运行时都拥有一个地址,这个地址代表变量在内存中的位置。Go语言中使用`&`字符放在变量前面对变量进行“取地址”操作。 Go语言中的值类型(int、float、bool、string、array、struct)都有对应的指针类型,如:`*int`、`*int64`、`*string`等。 129 | 130 | 取变量指针的语法如下: 131 | 132 | ```go 133 | ptr := &v // v的类型为T 134 | ``` 135 | 136 | 其中: 137 | 138 | - v:代表被取地址的变量,类型为`T` 139 | - ptr:用于接收地址的变量,ptr的类型就为`*T`,称做T的指针类型。*代表指针。 140 | 141 | 举个例子: 142 | 143 | ```go 144 | func main() { 145 | a := 10 146 | b := &a 147 | fmt.Printf("a:%d ptr:%p\n", a, &a) // a:10 ptr:0xc00001a078 148 | fmt.Printf("b:%p type:%T\n", b, b) // b:0xc00001a078 type:*int 149 | fmt.Println(&b) // 0xc00000e018 150 | } 151 | ``` 152 | 153 | 我们来看一下`b := &a`的图示:![取变量地址图示](https://www.liwenzhou.com/images/Go/pointer/ptr.png) 154 | 155 | ## 指针取值 156 | 157 | 在对普通变量使用&操作符取地址后会获得这个变量的指针,然后可以对指针使用*操作,也就是指针取值,代码如下。 158 | 159 | ```go 160 | func main() { 161 | //指针取值 162 | a := 10 163 | b := &a // 取变量a的地址,将指针保存到b中 164 | fmt.Printf("type of b:%T\n", b) 165 | c := *b // 指针取值(根据指针去内存取值) 166 | fmt.Printf("type of c:%T\n", c) 167 | fmt.Printf("value of c:%v\n", c) 168 | } 169 | ``` 170 | 171 | 输出如下: 172 | 173 | ```go 174 | type of b:*int 175 | type of c:int 176 | value of c:10 177 | ``` 178 | 179 | **总结:** 取地址操作符`&`和取值操作符`*`是一对互补操作符,`&`取出地址,`*`根据地址取出地址指向的值。 180 | 181 | 变量、指针地址、指针变量、取地址、取值的相互关系和特性如下: 182 | 183 | - 对变量进行取地址(&)操作,可以获得这个变量的指针变量。 184 | - 指针变量的值是指针地址。 185 | - *`对指针变量进行取值(*)操作,可以获得指针变量指向的原变量的值`*。 186 | 187 | **指针传值示例:** 188 | 189 | ```go 190 | func modify1(x int) { 191 | x = 100 192 | } 193 | 194 | func modify2(x *int) { 195 | *x = 100 196 | } 197 | 198 | func main() { 199 | a := 10 200 | modify1(a) 201 | fmt.Println(a) // 10 202 | modify2(&a) 203 | fmt.Printl 204 | n(a) // 100 205 | } 206 | ``` 207 | 208 | 209 | 210 | # 结构体和接口 211 | 212 | Go语言中没有“类”的概念,也不支持“类”的继承等面向对象的概念。 213 | 214 | Go语言中通过结构体的内嵌再配合接口比面向对象具有更高的扩展性和灵活性。 215 | 216 | 217 | 218 | ## 类型别名和自定义类型 219 | 220 | ### 自定义类型 221 | 222 | Go语言中可以使用`type`关键字来定义自定义如`string`、`int`、`bool`等的数据类型。 223 | 224 | 自定义类型是定义了一个***全新***的类型。我们可以基于内置的基本类型定义,也可以通过struct定义。例如: 225 | 226 | ```go 227 | //将MyInt定义为int类型 228 | type MyInt int 229 | ``` 230 | 231 | 通过`type`关键字的定义,`MyInt`就是一种新的类型,它具有`int`的特性。 232 | 233 | 234 | 235 | ## 类型别名 236 | 237 | 类型别名规定:本质上是同一个类型。就像一个孩子小时候有小名,这和他的名字都指向同一个人。 238 | 239 | ```go 240 | type 类型的别名 = 类型名 241 | ``` 242 | 243 | ## 类型定义和类型别名的区别 244 | 245 | 类型别名 和原类型是同一种类型。自定义类型是一种全新的类型。 246 | 247 | 类型别名的类型只会在代码中存在,编译完成时并不会存在。 248 | 249 | 250 | 251 | > 类型别名 和 自定义类型 的意义 252 | 253 | 自定义类型 : 举个例子,我们想给 int 类型定义一个方法,但是又不想改变int本身的性质。可以基于内置的`int`类型使用type关键字可以定义新的自定义类型,然后为我们的自定义类型添加方法。 254 | 255 | 类型别名: 想象一下你有一个非常长的类型名字,比如`map[int]string`,如果在代码中反复使用这个类型,那将会变得很啰嗦。 256 | 257 | 但是如果你使用类型别名来代替它,比如`data`,那么你只需使用`Data`这个简短的名字就可以代替长长的类型名字了 258 | 259 | 260 | 261 | # 结构体 262 | 263 | Go语言中的基础数据类型可以表示一些事物的基本属性,但是当我们想表达一个事物的全部或部分属性时,这时候再用单一的基本数据类型明显就无法满足需求了,Go语言提供了一种自定义数据类型,可以封装多个基本数据类型,这种数据类型叫结构体,英文名称`struct`。 264 | 265 | 也就是我们可以通过`struct`来定义自己的类型了。 266 | 267 | 268 | 269 | ## 结构体的定义 270 | 271 | 使用`type`和`struct`关键字来定义结构体,具体代码格式如下: 272 | 273 | ```go 274 | type 类型名 struct { 275 | 字段名 字段类型 276 | 字段名 字段类型 277 | … 278 | } 279 | ``` 280 | 281 | 其中: 282 | 283 | - 类型名:标识自定义结构体的名称,在同一个包内不能重复。 284 | - 字段名:表示结构体字段名。结构体中的字段名必须唯一。 285 | - 字段类型:表示结构体字段的具体类型。 286 | 287 | 举个例子,我们定义一个`Person`(人)结构体,代码如下: 288 | 289 | ```go 290 | type person struct { 291 | name string 292 | city string 293 | age int8 294 | } 295 | ``` 296 | 297 | 这样我们就拥有了一个`person`的自定义类型。 298 | 299 | 我们通过`.`来访问结构体的字段(成员变量),例如`p1.name`和`p1.age`等。 300 | 301 | ```go 302 | var p1 person 303 | p1.name = "zero" 304 | p1.age = "18" 305 | ``` 306 | 307 | 308 | 309 | ## 结构体初始化 310 | 311 | 没有初始化的结构体,其成员变量都是对应其类型的零值。 312 | 313 | ```go 314 | type person struct { // 创建一个结构体类型,像int这种类型一样 315 | name string 316 | city string 317 | age int8 318 | } 319 | 320 | func main() { 321 | var p4 person //将结构体类型实例化,但是没有初始化 322 | fmt.Printf("p4=%#v\n", p4) //p4=main.person{name:"", city:"", age:0} 323 | } 324 | ``` 325 | 326 | ### 使用键值对初始化 327 | 328 | 使用键值对对结构体进行初始化时,键对应结构体的字段,值对应该字段的初始值。 329 | 330 | ```go 331 | p5 := person{ 332 | name: "小王子", 333 | city: "北京", 334 | age: 18, 335 | } 336 | fmt.Printf("p5=%#v\n", p5) //p5=main.person{name:"小王子", city:"北京", age:18} 337 | ``` 338 | 339 | > PS : golang 结构体的初始化有很多方式,课件有参考链接,建议大家下去看看(会用就好) 340 | 341 | 342 | 343 | ## 嵌套结构体 344 | 345 | 一个结构体中可以嵌套包含另一个结构体或结构体指针,就像下面的示例代码那样。 346 | 347 | ```go 348 | //Address 地址结构体 349 | type Address struct { 350 | Province string 351 | City string 352 | } 353 | 354 | //User 用户结构体 355 | type User struct { 356 | Name string 357 | Gender string 358 | Address Address 359 | } 360 | 361 | func main() { 362 | user1 := User{ 363 | Name: "小王子", 364 | Gender: "男", 365 | Address: Address{ 366 | Province: "山东", 367 | City: "威海", 368 | }, 369 | } 370 | fmt.Printf("user1=%#v\n", user1)//user1=main.User{Name:"小王子", Gender:"男", Address:main.Address{Province:"山东", City:"威海"}} 371 | } 372 | ``` 373 | 374 | 375 | 376 | ## 方法和接收者 377 | 378 | Go语言中的`方法(Method)`是一种作用于特定类型变量的函数。这种特定类型变量叫做`接收者(Receiver)`。 379 | 380 | 只有特定的接收者变量才可以调用对应的方法。 381 | 382 | 方法的定义格式如下: 383 | 384 | ```go 385 | func (接收者变量 接收者类型) 方法名(参数列表) (返回参数) { 386 | 函数体 387 | } 388 | ``` 389 | 390 | 其中, 391 | 392 | - 接收者变量:接收者中的参数变量名在命名时,官方建议使用接收者类型名称首字母的小写。例如,`Person`类型的接收者变量应该命名为 `p`。 393 | - 接收者类型:接收者类型和参数类似,可以是`指针类型`和`非指针类型`。 394 | - 方法名、参数列表、返回参数:具体格式与函数定义相同。 395 | 396 | 举个例子: 397 | 398 | ```go 399 | // 结构体 400 | type Person struct { 401 | name string 402 | age int 403 | } 404 | 405 | //NewPerson 构造函数 406 | func NewPerson(name string, age int) *Person { 407 | return &Person{ 408 | name: name, 409 | age: age, 410 | } 411 | } 412 | 413 | // 为 person这个类型创建一个学习的方法 414 | func (p Person) Study(){ 415 | fmt.Print("我要卷四各位,或者被各位卷四") 416 | } 417 | ``` 418 | 419 | 420 | 421 | ### 指针类型的接收者 422 | 423 | 指针类型的接收者由一个结构体的指针组成,由于指针的特性,调用方法时修改接收者指针的任意成员变量,在方法结束后,修改都是有效的。 例如我们为`Person`添加一个`SetAge`方法,来修改实例变量的年龄。 424 | 425 | ```go 426 | // SetAge 设置p的年龄 427 | // 使用指针接收者 428 | func (p *Person) SetAge(newAge int) { 429 | p.age = newAge 430 | } 431 | ``` 432 | 433 | 调用该方法: 434 | 435 | ```go 436 | func main() { 437 | p1 := NewPerson("小王子", 25) 438 | fmt.Println(p1.age) // 25 439 | p1.SetAge(30) 440 | fmt.Println(p1.age) // 30 441 | } 442 | ``` 443 | 444 | ### 值类型的接收者 445 | 446 | 当方法作用于值类型接收者时,Go语言会在代码运行时将接收者的值复制一份。在值类型接收者的方法中可以获取接收者的成员值,但修改操作只是针对副本,无法修改接收者变量本身。 447 | 448 | ```go 449 | // SetAge2 设置p的年龄 450 | // 使用值接收者 451 | func (p Person) SetAge2(newAge int8) { 452 | p.age = newAge 453 | } 454 | 455 | func main() { 456 | p1 := NewPerson("小王子", 25) 457 | p1.Dream() 458 | fmt.Println(p1.age) // 25 459 | p1.SetAge2(30) // (*p1).SetAge2(30) 460 | fmt.Println(p1.age) // 25 461 | } 462 | ``` 463 | 464 | ### 什么时候应该使用指针类型接收者 465 | 466 | 1. 需要修改接收者中的值 467 | 468 | 2. 接收者是拷贝代价比较大的大对象 469 | 470 | 3. 保证一致性,如果有某个方法使用了指针接收者,那么其他的方法也应该使用指针接收者。 471 | 472 | 473 | 474 | **注意事项:** 非本地类型不能定义方法,也就是说我们不能给别的包的类型定义方法。 475 | 476 | 477 | 478 | # 接口 479 | 480 | > 接口是一种类型 481 | 482 | 接口是一种由程序员来定义的类型,一个接口类型就是一组方法的集合,它规定了需要实现的所有方法。 483 | 484 | 相较于使用结构体类型,当我们使用接口类型说明相比于它是什么更关心**它能做什么**,而不是它是什么。 485 | 486 | ### 接口的定义 487 | 488 | 每个接口类型由任意个方法签名组成,接口的定义格式如下: 489 | 490 | ```go 491 | type 接口类型名 interface{ 492 | 方法名1( 参数列表1 ) 返回值列表1 493 | 方法名2( 参数列表2 ) 返回值列表2 494 | … 495 | } 496 | ``` 497 | 498 | 其中: 499 | 500 | - 接口类型名:Go语言的接口在命名时,一般会在单词后面添加`er`,如有写操作的接口叫`Writer`,有关闭操作的接口叫`closer`等。接口名最好要能突出该接口的类型含义。 501 | - 方法名:当方法名首字母是大写且这个接口类型名首字母也是大写时,这个方法可以被接口所在的包(package)之外的代码访问。 502 | - 参数列表、返回值列表:参数列表和返回值列表中的参数变量名可以省略。 503 | 504 | 举个例子,定义一个包含`Write`方法的`Writer`接口。 505 | 506 | ```go 507 | type Writer interface{ 508 | Write([]byte) error 509 | } 510 | ``` 511 | 512 | 当你看到一个`Writer`接口类型的值时,你不知道它是什么,唯一知道的就是可以通过调用它的`Write`方法来做一些事情。 513 | 514 | 515 | 516 | ### 实现接口的条件 517 | 518 | 接口就是规定了一个**需要实现的方法列表**,在 Go 语言中一个类型只要实现了接口中规定的所有方法,那么我们就称它实现了这个接口。 519 | 520 | 我们定义的`Singer`接口类型,它包含一个`Sing`方法。 521 | 522 | ```go 523 | // Singer 接口 可以称之为 “会唱歌的东西” 524 | type Singer interface { 525 | Sing() 526 | } 527 | ``` 528 | 529 | 我们有一个`Bird`结构体类型如下。 530 | 531 | ```go 532 | type Bird struct {} 533 | ``` 534 | 535 | 因为`Singer`接口只包含一个`Sing`方法,所以只需要给`Bird`结构体添加一个`Sing`方法就可以满足`Singer`接口的要求。 536 | 537 | ```go 538 | // Sing Bird类型的Sing方法 539 | func (b Bird) Sing() { 540 | fmt.Println("汪汪汪") 541 | } 542 | ``` 543 | 544 | 这样就称为`Bird`实现了`Singer`接口。bird 就属于singer 类型了。 545 | 546 | 547 | 548 | ### ***接口的意义*** 549 | 550 | 551 | 552 | 1️⃣ 实现多态性(Polymorphism):通过接口,可以实现多个不同类型的对象以相同的方式进行操作,这增强了代码的灵活性和可复用性。 553 | 554 | 2️⃣ 解耦合(Decoupling):接口使得模块之间的依赖关系更松散,模块只需要关注接口定义的方法,而不需要知道具体的实现细节。这有助于降低代码的耦合度,增加代码的可维护性和可测试性。 555 | 556 | 3️⃣ 扩展性(Extensibility):通过接口定义通用的行为,可以方便地对系统进行扩展和修改,而不需要改动已有的代码。当需要添加新的功能时,只需要实现接口定义的方法即可。 557 | 558 | 4️⃣ 接口断言(Interface Assertion):使用接口断言可以在运行时检查一个对象是否实现了某个接口,并根据情况进行处理。这样可以进行更灵活的类型转换和错误处理。 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | ### 小练习 567 | 568 | 你要设计一个电子商务平台,该平台有多种类型的商品,例如电子产品、家居用品和服装等。你需要设计以下结构体和功能: 569 | 570 | 1. 实现商品结构体:包含商品的名称、价格和库存数量等信息。 571 | 572 | 2. 实现接口:定义商品的库存管理功能,包括检查库存数量、更新库存数量和打印库存信息等。 573 | 574 | 3. 实现电子产品结构体:继承自商品结构体,同时具有电子产品特有的属性,例如品牌和型号。 575 | 576 | 4. 实现接口:定义电子产品结构体的库存管理功能(同上),以及打印品牌型号信息的功能。 577 | 578 | 579 | 580 | 完成后提交到邮箱saleroa@qq.com 581 | 582 | 有什么问题也可以发邮箱问哦 583 | 584 | 585 | 586 | 587 | 588 | # 参考链接 589 | 590 | 591 | 592 | [Go语言基础之包 | 李文周的博客 (liwenzhou.com)](https://www.liwenzhou.com/posts/Go/package/) 593 | 594 | [Go语言基础之指针 | 李文周的博客 (liwenzhou.com)](https://www.liwenzhou.com/posts/Go/pointer/) 595 | 596 | [Go语言基础之结构体 | 李文周的博客 (liwenzhou.com)](https://www.liwenzhou.com/posts/Go/struct/) 597 | 598 | [Go语言基础之接口 | 李文周的博客 (liwenzhou.com)](https://www.liwenzhou.com/posts/Go/interface/) 599 | 600 | 601 | 602 | -------------------------------------------------------------------------------- /lesson04/img/img01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson04/img/img01.png -------------------------------------------------------------------------------- /lesson04/img/img02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson04/img/img02.png -------------------------------------------------------------------------------- /lesson04/img/img03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson04/img/img03.png -------------------------------------------------------------------------------- /lesson04/img/img04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson04/img/img04.png -------------------------------------------------------------------------------- /lesson05/images/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson05/images/1.png -------------------------------------------------------------------------------- /lesson05/images/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson05/images/10.png -------------------------------------------------------------------------------- /lesson05/images/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson05/images/11.png -------------------------------------------------------------------------------- /lesson05/images/12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson05/images/12.png -------------------------------------------------------------------------------- /lesson05/images/13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson05/images/13.png -------------------------------------------------------------------------------- /lesson05/images/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson05/images/2.png -------------------------------------------------------------------------------- /lesson05/images/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson05/images/3.png -------------------------------------------------------------------------------- /lesson05/images/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson05/images/4.png -------------------------------------------------------------------------------- /lesson05/images/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson05/images/5.png -------------------------------------------------------------------------------- /lesson05/images/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson05/images/6.png -------------------------------------------------------------------------------- /lesson05/images/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson05/images/7.png -------------------------------------------------------------------------------- /lesson05/images/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson05/images/8.png -------------------------------------------------------------------------------- /lesson05/images/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson05/images/9.png -------------------------------------------------------------------------------- /lesson06/clain/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/gin-gonic/gin" 6 | ) 7 | 8 | func main() { 9 | r := gin.Default() 10 | //在这里使用了三个中间件 11 | r.Use(mid1(), mid2(), mid3()) 12 | r.GET("/abc", func(context *gin.Context) { 13 | fmt.Println("1") 14 | }) 15 | _ = r.Run(":8081") 16 | } 17 | 18 | func mid1() gin.HandlerFunc { 19 | return func(c *gin.Context) { 20 | fmt.Println("mid1 before") 21 | //先执行next,后执行after 22 | c.Next() 23 | fmt.Println("mid1 after") 24 | } 25 | } 26 | func mid2() gin.HandlerFunc { 27 | return func(c *gin.Context) { 28 | fmt.Println("mid2 before") 29 | c.Abort() 30 | //c.Next() 31 | fmt.Println("mid2 after") 32 | } 33 | } 34 | 35 | func mid3() gin.HandlerFunc { 36 | return func(c *gin.Context) { 37 | fmt.Println("mid3 before") 38 | c.Next() 39 | fmt.Println("mid3 after") 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lesson06/m1/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/gin-gonic/gin" 4 | 5 | func main() { 6 | r := gin.Default() 7 | r.GET("/ping", func(c *gin.Context) { 8 | c.JSON(200, gin.H{ 9 | "message": "pong", 10 | }) 11 | }) 12 | r.Run(":8080") // 监听并在 0.0.0.0:8080 上启动服务 13 | } 14 | -------------------------------------------------------------------------------- /lesson06/m2/api/middleware/cors.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import "github.com/gin-gonic/gin" 4 | 5 | func CORS() gin.HandlerFunc { 6 | return func(ctx *gin.Context) { 7 | ctx.Writer.Header().Set("Access-Control-Allow-Origin", "*") 8 | ctx.Writer.Header().Set("Access-Control-Allow-Credentials", "true") 9 | ctx.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With, token, x-access-token") 10 | ctx.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT, DELETE") 11 | 12 | if ctx.Request.Method == "OPTIONS" { 13 | ctx.AbortWithStatus(204) 14 | return 15 | } 16 | ctx.Next() 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lesson06/m2/api/middleware/jwt.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "errors" 5 | "github.com/dgrijalva/jwt-go" 6 | "net/http" 7 | "strings" 8 | 9 | "Courseware-Backend-Go-2023/lesson6/m2/model" 10 | "github.com/gin-gonic/gin" 11 | ) 12 | 13 | var Secret = []byte("Lanshan") 14 | 15 | // JWTAuthMiddleware 基于JWT的认证中间件 16 | func JWTAuthMiddleware() func(c *gin.Context) { 17 | return func(c *gin.Context) { 18 | // 客户端携带Token有三种方式 1.放在请求头 2.放在请求体 3.放在URI 19 | // 这里假设Token放在Header的Authorization中,并使用Bearer开头 20 | // 这里的具体实现方式要依据你的实际业务情况决定 21 | authHeader := c.Request.Header.Get("Authorization") 22 | if authHeader == "" { 23 | c.JSON(http.StatusOK, gin.H{ 24 | "code": 2003, 25 | "msg": "请求头中auth为空", 26 | }) 27 | c.Abort() 28 | return 29 | } 30 | // 按空格分割 31 | parts := strings.SplitN(authHeader, " ", 2) 32 | if !(len(parts) == 2 && parts[0] == "Bearer") { 33 | c.JSON(http.StatusOK, gin.H{ 34 | "code": 2004, 35 | "msg": "请求头中auth格式有误", 36 | }) 37 | c.Abort() 38 | return 39 | } 40 | // parts[1]是获取到的tokenString,我们使用之前定义好的解析JWT的函数来解析它 41 | mc, err := ParseToken(parts[1]) 42 | if err != nil { 43 | c.JSON(http.StatusOK, gin.H{ 44 | "code": 2005, 45 | "msg": "无效的Token", 46 | }) 47 | c.Abort() 48 | return 49 | } 50 | // 将当前请求的username信息保存到请求的上下文c上 51 | c.Set("username", mc.Username) 52 | c.Next() // 后续的处理函数可以用过c.Get("username")来获取当前请求的用户信息 53 | } 54 | } 55 | 56 | // ParseToken 解析JWT 57 | func ParseToken(tokenString string) (*model.MyClaims, error) { 58 | // 解析token 59 | token, err := jwt.ParseWithClaims(tokenString, &model.MyClaims{}, func(token *jwt.Token) (i interface{}, err error) { 60 | return Secret, nil 61 | }) 62 | if err != nil { 63 | return nil, err 64 | } 65 | if claims, ok := token.Claims.(*model.MyClaims); ok && token.Valid { // 校验token 66 | return claims, nil 67 | } 68 | return nil, errors.New("invalid token") 69 | } 70 | -------------------------------------------------------------------------------- /lesson06/m2/api/router.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "Courseware-Backend-Go-2023/lesson6/m2/api/middleware" 5 | "github.com/gin-gonic/gin" 6 | ) 7 | 8 | func InitRouter() { 9 | r := gin.Default() 10 | r.Use(middleware.CORS()) 11 | 12 | r.POST("/register", register) // 注册 13 | r.POST("/login", login) // 登录 14 | 15 | UserRouter := r.Group("/user") 16 | { 17 | UserRouter.Use(middleware.JWTAuthMiddleware()) 18 | UserRouter.GET("/get", getUsernameFromToken) 19 | } 20 | 21 | r.Run(":8088") // 跑在 8088 端口上 22 | } 23 | -------------------------------------------------------------------------------- /lesson06/m2/api/user.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "Class/2/api/middleware" 5 | "Class/2/dao" 6 | "Class/2/model" 7 | "Class/2/utils" 8 | "fmt" 9 | "github.com/dgrijalva/jwt-go" 10 | "github.com/gin-gonic/gin" 11 | "time" 12 | ) 13 | 14 | func register(c *gin.Context) { 15 | form := model.User{} 16 | if err := c.ShouldBind(&form); err != nil { 17 | fmt.Println(err) 18 | utils.RespSuccess(c, "verification failed") 19 | return 20 | } 21 | // 传入用户名和密码 22 | username := c.PostForm("username") 23 | password := c.PostForm("password") 24 | 25 | // 验证用户名是否重复 26 | flag := dao.SelectUser(username) 27 | fmt.Println(flag) 28 | if flag { 29 | // 以 JSON 格式返回信息 30 | utils.RespFail(c, "user already exists") 31 | return 32 | } 33 | 34 | dao.AddUser(username, password) 35 | // 以 JSON 格式返回信息 36 | utils.RespSuccess(c, "add user successful") 37 | } 38 | 39 | func login(c *gin.Context) { 40 | if err := c.ShouldBind(&model.User{}); err != nil { 41 | utils.RespFail(c, "verification failed") 42 | return 43 | } 44 | // 传入用户名和密码 45 | username := c.PostForm("username") 46 | password := c.PostForm("password") 47 | 48 | // 验证用户名是否存在 49 | flag := dao.SelectUser(username) 50 | // 不存在则退出 51 | if !flag { 52 | // 以 JSON 格式返回信息 53 | utils.RespFail(c, "user doesn't exists") 54 | return 55 | } 56 | 57 | // 查找正确的密码 58 | selectPassword := dao.SelectPasswordFromUsername(username) 59 | // 若不正确则传出错误 60 | if selectPassword != password { 61 | // 以 JSON 格式返回信息 62 | utils.RespFail(c, "wrong password") 63 | return 64 | } 65 | 66 | // 正确则登录成功 67 | // 创建一个我们自己的声明 68 | claim := model.MyClaims{ 69 | Username: username, // 自定义字段 70 | StandardClaims: jwt.StandardClaims{ 71 | ExpiresAt: time.Now().Add(time.Hour * 2).Unix(), // 过期时间 72 | Issuer: "Yxh", // 签发人 73 | }, 74 | } 75 | // 使用指定的签名方法创建签名对象 76 | token := jwt.NewWithClaims(jwt.SigningMethodHS256, claim) 77 | // 使用指定的secret签名并获得完整的编码后的字符串token 78 | tokenString, _ := token.SignedString(middleware.Secret) 79 | utils.RespSuccess(c, tokenString) 80 | } 81 | 82 | func getUsernameFromToken(c *gin.Context) { 83 | username, _ := c.Get("username") 84 | utils.RespSuccess(c, username.(string)) 85 | } 86 | -------------------------------------------------------------------------------- /lesson06/m2/dao/user.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | var database = map[string]string{ 4 | "yxh": "123456", 5 | "wx": "654321", 6 | } 7 | 8 | func AddUser(username, password string) { 9 | database[username] = password 10 | } 11 | 12 | func SelectUser(username string) bool { 13 | if database[username] == "" { 14 | return false 15 | } 16 | return true 17 | } 18 | 19 | func SelectPasswordFromUsername(username string) string { 20 | return database[username] 21 | } 22 | -------------------------------------------------------------------------------- /lesson06/m2/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "Courseware-Backend-Go-2023/lesson6/m2/api" 4 | 5 | func main() { 6 | api.InitRouter() 7 | 8 | } 9 | -------------------------------------------------------------------------------- /lesson06/m2/model/user.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import "github.com/dgrijalva/jwt-go" 4 | 5 | type User struct { 6 | Username string `form:"username" json:"username" binding:"required"` 7 | Password string `form:"password" json:"password" binding:"required"` 8 | } 9 | 10 | type MyClaims struct { 11 | Username string `json:"username"` 12 | jwt.StandardClaims 13 | } 14 | -------------------------------------------------------------------------------- /lesson06/m2/utils/response.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "net/http" 6 | ) 7 | 8 | func RespSuccess(c *gin.Context, message string) { 9 | c.JSON(http.StatusOK, gin.H{ 10 | "status": 200, 11 | "message": message, 12 | }) 13 | } 14 | 15 | func RespFail(c *gin.Context, message string) { 16 | c.JSON(http.StatusInternalServerError, gin.H{ 17 | "status": 500, 18 | "message": message, 19 | }) 20 | } 21 | -------------------------------------------------------------------------------- /lesson07/lesson7.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # MySQL 4 | 5 | ![img](https://camo.githubusercontent.com/0d321bc853789843d8659f9d694de2f1d5149d5cba6b792a114824ec98e2ebf5/68747470733a2f2f73312e617831782e636f6d2f323032322f31312f31372f7a6d6e5556732e706e67) 6 | 7 | ## 概念篇 8 | 9 | ### 什么是数据库? 10 | 11 | 数据库(Database)是指存储数据的容器,也被称为数据存储库(Data Store)。数据库能够存储大量结构化和非结构化的数据,包括文本、数字、图像、音频等各种类型的数据。 12 | 13 | 根据数据存储方式和结构的不同,数据库可以分为关系型数据库(如MySQL、Oracle、SQL Server、PostgreSQL)和非关系型数据库(如MongoDB、Cassandra、Redis、Elasticsearch)。 14 | 15 | ### MySQL介绍 16 | 17 | > MySQL是一个**关系型数据库**,一种开源关系数据库管理系统(RDBMS),它使用最常用的数据库管理语言-结构化查询语言(SQL)进行数据库管理 18 | 19 | 关系型数据库的数据都以**数据表**的形式进行存储和管理。 20 | 21 | 表是一种二维行列表格,关系型数据库中的数据通常以表的形式存储,如图所示 22 | 23 | ![img](https://camo.githubusercontent.com/e04f85a224ede31a3e08285692578a8f99b5b5b80d550fbf9e0fa2b6eaadebfe/68747470733a2f2f73312e617831782e636f6d2f323032322f31312f31392f7a4b39394a672e706e67) 24 | 25 | - `列(column)` - 表中的一个字段 ,如图中`patient_id` 。所有表都是由一个或多个列组成的 26 | 27 | - `行(row)` - 表中的一个记录,比如第一行,这条记录,记录下了小红的信息(病人号、姓名、年龄、身高体重等等) 28 | 29 | - `主键(primary key)` - 一列(或一组列),其值能够唯一标识表中每一行。 30 | 31 | > 比如图2中每个patient_id,都标识了一个**独一无二**的病人 32 | > 33 | > 又比如b站的uid,也标识了一个**独一无二**的用户 34 | 35 | #### SQL 36 | 37 | > SQL 是一种**操作数据库**的语言,包括创建数据库、删除数据库、查询记录、修改记录、添加字段等。 38 | > 39 | > SQL 是关系型数据库的标准语言,所有的关系型数据库管理系统(RDBMS)都将 SQL 作为其标准处理语言。 40 | 41 | SQL有以下用途: 42 | 43 | - 允许用户访问关系型数据库系统中的数据;(**查数据**) 44 | 45 | - 允许用户描述数据;(**自定义要查哪些数据**) 46 | 47 | - 允许用户定义数据库中的数据,并处理该数据;(**建表**) 48 | 49 | - 允许将 SQL 模块、库或者预处理器嵌入到其它编程语言中;(**在Go中使用SQL**) 50 | 51 | - 允许用户创建和删除数据库、表、数据项(记录); 52 | 53 | - 允许用户在数据库中创建视图、存储过程、函数; 54 | 55 | - 允许用户设置对表、存储过程和视图的权限; 56 | 57 | **SQL命令分为三类** 58 | 59 | 1.DDL - Data Definition Language,数据定义语言 60 | 61 | > 对数据的结构和形式进行定义,一般用于数据库和表的创建、删除、修改等。 62 | 63 | | 命令 | 说明 | 64 | | ------ | -------------------------------------------------- | 65 | | CREATE | 用于在数据库中创建一个新表、一个视图或者其它对象。 | 66 | | ALTER | 用于修改现有的数据库,比如表、记录。 | 67 | | DROP | 用于删除整个表、视图或者数据库中的其它对象 | 68 | 69 | 2.DML - Data Manipulation Language,数据处理语言 70 | 71 | > 对数据库中的数据进行处理,一般用于数据项(记录)的插入、删除、修改和查询。 72 | 73 | | 命令 | 说明 | 74 | | ------ | ------------------------------------ | 75 | | SELECT | 用于从一个或者多个表中检索某些记录。 | 76 | | INSERT | 插入一条记录。 | 77 | | UPDATE | 修改记录。 | 78 | | DELETE | 删除记录。 | 79 | 80 | 3.DCL - Data Control Language,数据控制语言 81 | 82 | | 命令 | 说明 | 83 | | --------------- | ------------------------------------------ | 84 | | COMMIT | 提交 | 85 | | SAVEPOINT | 保存点 | 86 | | ROLLBACK | 回滚 | 87 | | SET TRANSACTION | 设置当前事务的特性,它对后面的事务没有影响 | 88 | 89 | #### 数据类型 90 | 91 | MySQL 支持多种数据类型,大致可以分为三类:数值、日期/时间和字符串(字符)类型。 92 | 93 | ##### 数值类型 94 | 95 | | 类型 | 字节大小 | 96 | | --------------- | --------------------------------------- | 97 | | TINYINT | 1 | 98 | | SMALLINT | 2 | 99 | | MEDIUMINT | 3 | 100 | | INT(INTEGER) | 4 | 101 | | BIGINT | 8 | 102 | | FLOAT | 4 | 103 | | DOUBLE | 8 | 104 | | DECIMAL(小数) | 对DECIMAL(M,D)中 M>D 则为M+2,否则为D+2 | 105 | 106 | ##### 日期和时间类型 107 | 108 | | **类型** | **字节大小** | **格式** | 用途 | 109 | | --------- | ------------ | ------------------- | ------------------------ | 110 | | DATE | 3 | YYYY-MM-DD | 日期值 | 111 | | TIME | 3 | HH:MM:SS | 时间值或持续时间 | 112 | | YEAR | 1 | YYYY | 年份值 | 113 | | DATETIME | 8 | YYYY-MM-DD hh:mm:ss | 混合日期和时间值 | 114 | | TIMESTAMP | 4 | YYYY-MM-DD hh:mm:ss | 混合日期和时间值,时间戳 | 115 | 116 | ##### 字符串类型 117 | 118 | | 类型 | 字节大小 | 用途 | 119 | | ----------- | ------------ | ---------------------------- | 120 | | **CHAR** | 0-255 | 定长字符串 | 121 | | **VARCHAR** | 0-65535 | 变长字符串 | 122 | | TINYBLOB | 0-255 | <=255个字符的二进制字符串 | 123 | | TINYTEXT | 0-255 | 短文本字符串 | 124 | | BLOB | 0-65535 | 二进制形式的长文本数据 | 125 | | TEXT | 0-65535 | 长文本数据 | 126 | | MEDIUMBLOB | 0-16777215 | 二进制形式的中等长度文本数据 | 127 | | MEDIUMTEXT | 0-16777215 | 中等长度文本数据 | 128 | | LONGBLOB | 0-4294967295 | 二进制形式的极大文本数据 | 129 | | LONGTEXT | 0-4294967295 | 极大文本数据 | 130 | 131 | ## 实践篇 132 | 133 | **在开始实践前 请先安装好MySQL和配置好环境变量。** 134 | 135 | ### SQL语法 136 | 137 | 使用MySQL前当然要学会使用SQL语句。 138 | 139 | 我们先来了解一下SQL的基本规则 140 | 141 | 1. **SQL 语句要以分号`;`结尾**,在 RDBMS (关系型数据库)当中,SQL 语句是逐条执行的,一条 SQL 语句代表着数据库的一个操作 142 | 2. **SQL 语句不区分大小写**,例如,不管写成 SELECT 还是 select,解释都是一样的。表名和列名也是如此,但是,**插入到表中的数据是区分大小写的** 143 | 3. **SQL 语句的单词之间必须使用半角空格(英文空格)或换行符来进行分隔** 144 | 145 | **登录MySQL** 146 | 147 | windows系统打开cmd或windows powershell;mac和linux打开终端 148 | 149 | 输入 150 | 151 | ``` 152 | mysql -u root -p 153 | ``` 154 | 155 | root是你的用户名 156 | 157 | 然后根据提示输入密码来启动MySQL 158 | 159 | 下面来一些 SQL 语句的示例 160 | 161 | ```sql 162 | create database student; 163 | //创建数据库 164 | 165 | drop database school; 166 | //删除数据库 167 | 168 | show databases; 169 | //查看所有数据库 170 | 171 | show databases like 'school'; 172 | //查看名为 "school" 的数据库 173 | 174 | show databases like '%sch%'; 175 | //查看名字中包含sch的数据库(%表示任意个字符) 176 | 177 | show databases like 'sch%'; 178 | //查看以sch开头的数据库 179 | 180 | use school 181 | //进入数据库"school" 182 | 183 | CREATE TABLE `student` ( 184 | `id` BIGINT(20) NOT NULL AUTO_INCREMENT, 185 | `name` VARCHAR(20) DEFAULT '1', 186 | `sex` VARCHAR(8) DEFAULT '', 187 | `age` INT(11) , 188 | PRIMARY KEY(`id`) 189 | )ENGINE=InnoDB AUTO_INCREMENT=1 CHARSET=utf8mb4; 190 | //创建表"student",ENGINE=InnoDB:指定 MySQL引擎为InnoDB,AUTO_INCREMENT=1:自增的字段每次自增1,CHARSET=utf8mb4:指定编码为utf8base64以支持中文 191 | 192 | drop table student; 193 | //删除表"student" 194 | ``` 195 | 196 | 接下来我们以创建好的 student 表为例,对数据进行操作 197 | 198 | **插入 Insert** 199 | 200 | > 由于id是自增,所以添加的时候无需插入id 201 | 202 | ``` 203 | insert into 表名(列1,列2,列3,列4,...) values(值,值,值) 204 | ``` 205 | 206 | ```sql 207 | insert into student(name,sex,age) values('小明','男',18); 208 | ``` 209 | 210 | 当字段允许空时或者设置了默认值时,也可以不插入这个字段,字段会默认为空或者默认值 211 | 212 | ```sql 213 | insert into student(name,sex) values('小红','女'); 214 | ``` 215 | 216 | **查询 Select** 217 | 218 | ``` 219 | select 列1,列2 from 表名 where ...; 220 | ``` 221 | 222 | 查询所有学生 223 | 224 | ```sql 225 | select *from student; 226 | ``` 227 | 228 | 查询所有学生的姓名与年龄 229 | 230 | ```sql 231 | select name,age from student; 232 | ``` 233 | 234 | 查询男学生 235 | 236 | ```sql 237 | select name,age from student where sex='男'; 238 | ``` 239 | 240 | 如果想改变输出时的字段名 可以使用**AS** 241 | 242 | ```sql 243 | select name as '姓名' from student; 244 | ``` 245 | 246 | **子查询** 247 | 248 | 顾名思义,子查询嵌套在另一个查询语句里 249 | 250 | ```sql 251 | select name,age from student where name in( 252 | select name from student where sex='男' 253 | )order by id desc; 254 | ``` 255 | 256 | 我们将这条语句拆解一下 257 | 258 | 最外层: 259 | 260 | ```sql 261 | select name,age from student where name in(..)order by id desc; 262 | ``` 263 | 264 | 里层: 265 | 266 | ```sql 267 | select name from student where sex='男'; 268 | ``` 269 | 270 | 外层在里层结果的基础上再进行查询 271 | 272 | 最后的: 273 | 274 | ```sql 275 | order by id desc; 276 | ``` 277 | 278 | 意思是结果按id排序,降序(desc) 279 | 280 | 升序是`asc` 281 | 282 | **更新 Update** 283 | 284 | ``` 285 | update <表名> set 列1=值1,列2=值2,.... where... 286 | ``` 287 | 288 | ```sql 289 | update student set age=19,sex='女' where id=1; 290 | ``` 291 | 292 | 这里用`where`来限定要更新的记录,因为update会**更新所有**符合限定条件的记录,如果不限定,会更新所有记录 293 | 294 | 条件之间可以用`and`、`or`连接 295 | 296 | ```sql 297 | update student set age=19,sex='女' where id>=1 and name='小明'; 298 | ``` 299 | 300 | **删除 Delete** 301 | 302 | ```sql 303 | delete from student where id=1; 304 | ``` 305 | 306 | **修改数据表** 307 | 308 | 修改字段 309 | 310 | ``` 311 | alter table <表名> change <旧字段名> <新字段名> <新数据类型>; 312 | ``` 313 | 314 | ``` 315 | alter table student change name 姓名 varchar(32); 316 | ``` 317 | 318 | 删除字段 319 | 320 | ``` 321 | alter table student drop 姓名; 322 | ``` 323 | 324 | 添加字段 325 | 326 | > 需要注意的是,添加的字段会默认在表最后一列 327 | 328 | ``` 329 | alter table <表名> add <新字段名><数据类型>[约束条件]; 330 | ``` 331 | 332 | ``` 333 | alter table student add name varchar(12) default '李华' not null; 334 | ``` 335 | 336 | 在开头添加字段 337 | 338 | > 末尾加上first即可 339 | 340 | ``` 341 | alter table student add name varchar(12) default '李华' not null first; 342 | ``` 343 | 344 | 在中间添加字段 345 | 346 | > 末尾加after接字段名即可 347 | 348 | ``` 349 | alter table student add name varchar(12) default '李华' not null after id; 350 | ``` 351 | 352 | ### Go操作MySQL 353 | 354 | 上述内容是直接使用SQL语句操作MySQL,下面我们学习使用Golang来操作数据库。 355 | 356 | Go官方为我们提供了 **database/sql** 包保证SQL或类SQL数据库的泛用接口,**sqlx** 是基于标准库的扩展,在开发中 **sqlx** 包使用更加广泛,下面我们以使用 **sqlx** 包为例。 357 | 358 | 首先下载 MySQL 驱动 359 | 360 | ```go 361 | go get -u github.com/go-sql-driver/mysql 362 | ``` 363 | 364 | 然后安装 sqlx 365 | 366 | ```go 367 | go get github.com/jmoiron/sqlx 368 | ``` 369 | 370 | #### 连接数据库 371 | 372 | ```go 373 | package main 374 | 375 | import ( 376 | _ "github.com/go-sql-driver/mysql" //要导入相应驱动包,否则会报错 377 | "github.com/jmoiron/sqlx" 378 | "fmt" 379 | ) 380 | 381 | // 定义一个全局对象db 382 | var db *sqlx.DB 383 | 384 | func initDB() { 385 | var err error 386 | 387 | dsn := "username:password@tcp(127.0.0.1:3306)/database_name?charset=utf8mb4&parseTime=True&loc=Local" 388 | 389 | db, err = sqlx.Connect("mysql", dsn) 390 | if err != nil { 391 | fmt.Printf("connect DB failed, err:%v\n", err) 392 | return 393 | } 394 | 395 | fmt.Println("connecting to MySQL...") 396 | return 397 | } 398 | 399 | func main() { 400 | //初始化连接 401 | initDB() 402 | } 403 | ``` 404 | 405 | #### CRUD 406 | 407 | ##### 查询 408 | 409 | 查询单行数据 410 | 411 | ```go 412 | func queryRowDemo() { 413 | sqlStr := "select id, name, age from user where id=?" 414 | var u user 415 | err := db.Get(&u, sqlStr, 1) 416 | if err != nil { 417 | fmt.Printf("get failed, err:%v\n", err) 418 | return 419 | } 420 | fmt.Printf("id:%d name:%s age:%d\n", u.ID, u.Name, u.Age) 421 | } 422 | ``` 423 | 424 | 查询多行数据 425 | 426 | ```go 427 | func queryMultiRowDemo() { 428 | sqlStr := "select id, name, age from user where id > ?" 429 | var users []user 430 | err := db.Select(&users, sqlStr, 0) 431 | if err != nil { 432 | fmt.Printf("query failed, err:%v\n", err) 433 | return 434 | } 435 | fmt.Printf("users:%#v\n", users) 436 | } 437 | ``` 438 | 439 | ##### 插入、更新和删除 440 | 441 | 插入、更新和删除均使用 **Exec** 方法 442 | 443 | ```go 444 | // 插入数据 445 | func insertRowDemo() { 446 | sqlStr := "insert into user(name, age) values (?,?)" 447 | ret, err := db.Exec(sqlStr, "沙河小王子", 19) 448 | if err != nil { 449 | fmt.Printf("insert failed, err:%v\n", err) 450 | return 451 | } 452 | theID, err := ret.LastInsertId() // 新插入数据的id 453 | if err != nil { 454 | fmt.Printf("get lastinsert ID failed, err:%v\n", err) 455 | return 456 | } 457 | fmt.Printf("insert success, the id is %d.\n", theID) 458 | } 459 | 460 | // 更新数据 461 | func updateRowDemo() { 462 | sqlStr := "update user set age=? where id = ?" 463 | ret, err := db.Exec(sqlStr, 39, 6) 464 | if err != nil { 465 | fmt.Printf("update failed, err:%v\n", err) 466 | return 467 | } 468 | n, err := ret.RowsAffected() // 操作影响的行数 469 | if err != nil { 470 | fmt.Printf("get RowsAffected failed, err:%v\n", err) 471 | return 472 | } 473 | fmt.Printf("update success, affected rows:%d\n", n) 474 | } 475 | 476 | // 删除数据 477 | func deleteRowDemo() { 478 | sqlStr := "delete from user where id = ?" 479 | ret, err := db.Exec(sqlStr, 6) 480 | if err != nil { 481 | fmt.Printf("delete failed, err:%v\n", err) 482 | return 483 | } 484 | n, err := ret.RowsAffected() // 操作影响的行数 485 | if err != nil { 486 | fmt.Printf("get RowsAffected failed, err:%v\n", err) 487 | return 488 | } 489 | fmt.Printf("delete success, affected rows:%d\n", n) 490 | } 491 | 492 | ``` 493 | 494 | ##### 事务 495 | 496 | 事务详细介绍:[一文读懂数据库事务_数据库事务是什么-CSDN博客](https://blog.csdn.net/K346K346/article/details/114085663) 497 | 498 | 简单来说,事务(Transaction)指一个操作,由多个步骤组成,要么全部成功,要么全部失败。 499 | 500 | 下面的代码演示了一个简单的事务操作,该事物操作能够确保两次更新操作要么同时成功要么同时失败,不会存在中间状态。 501 | 502 | ```go 503 | func transactionDemo2()(err error) { 504 | tx, err := db.Beginx() // 开启事务 505 | if err != nil { 506 | fmt.Printf("begin trans failed, err:%v\n", err) 507 | return err 508 | } 509 | defer func() { 510 | if p := recover(); p != nil { 511 | tx.Rollback() 512 | panic(p) // re-throw panic after Rollback 513 | } else if err != nil { 514 | fmt.Println("rollback") 515 | tx.Rollback() // err is non-nil; don't change it 516 | } else { 517 | err = tx.Commit() // err is nil; if Commit returns error update err 518 | fmt.Println("commit") 519 | } 520 | }() 521 | 522 | sqlStr1 := "Update user set age=20 where id=?" 523 | 524 | rs, err := tx.Exec(sqlStr1, 1) 525 | if err!= nil{ 526 | return err 527 | } 528 | n, err := rs.RowsAffected() 529 | if err != nil { 530 | return err 531 | } 532 | if n != 1 { 533 | return errors.New("exec sqlStr1 failed") 534 | } 535 | sqlStr2 := "Update user set age=50 where i=?" 536 | rs, err = tx.Exec(sqlStr2, 5) 537 | if err!=nil{ 538 | return err 539 | } 540 | n, err = rs.RowsAffected() 541 | if err != nil { 542 | return err 543 | } 544 | if n != 1 { 545 | return errors.New("exec sqlStr1 failed") 546 | } 547 | return err 548 | } 549 | ``` 550 | 551 | 以上是 sqlx 包最基础的用法介绍,它更强大的功能就留给大家自行探索。 552 | 553 | 这里贴一下李文周老师的 sqlx 教程:[sqlx库使用指南 | 李文周的博客 (liwenzhou.com)](https://www.liwenzhou.com/posts/Go/sqlx/) 554 | 555 | 李文周老师介绍了 sqlx 的更多用法,上述示例代码也有一部分是直接从里面copy的 :) 556 | 557 | #### Gorm介绍 558 | 559 | - Gorm的使用并不是我们这节课的重点,所以这里就不介绍Gorm的具体用法。 560 | 561 | ##### 什么是Gorm 562 | 563 | GORM 是 Go 语言中的一个强大的对象关系映射(ORM)库,它提供了一种简单且优雅的方式来进行数据库操作。ORM 是一种编程技术,它将数据库表与程序中的对象进行映射,使得开发者可以使用面向对象的方式进行数据库的增删改查操作,而无需直接编写 SQL 语句。(关于什么是ORM,大家可以在课下阅读相关文章了解) 564 | 565 | ##### 为什么我们需要Gorm 566 | 567 | ORM的作用是在关系型数据库和对象之间作一个映射,这样,我们在具体的操作数据库的时候,就不需要再去和复杂的SQL语句打交道,只要像平时操作对象一样操作它就可以了 。 568 | 569 | 使用 ORM带来的好处有 570 | 571 | - **减少我们重复的写sql语句的实践,提高我们的工作效率,降低开发成本** 572 | - **访问数据的时候,可以使用抽象的方式,用起来非常简洁** 573 | 574 | 以使用sqlx和Gorm进行查询进行对比: 575 | 576 | ```go 577 | func queryRowDemo() { 578 | var u user 579 | err := db.First(&user, 10) 580 | // SELECT * FROM user WHERE id = 10; 581 | if err != nil { 582 | fmt.Printf("get failed, err:%v\n", err) 583 | return 584 | } 585 | fmt.Printf("id:%d name:%s age:%d\n", u.ID, u.Name, u.Age) 586 | } 587 | ``` 588 | 589 | ```go 590 | func queryRowDemo() { 591 | sqlStr := "select * from user where id=?" 592 | var u user 593 | err := db.Get(&u, sqlStr,10) 594 | if err != nil { 595 | fmt.Printf("get failed, err:%v\n", err) 596 | return 597 | } 598 | fmt.Printf("id:%d name:%s age:%d\n", u.ID, u.Name, u.Age) 599 | } 600 | ``` 601 | 602 | 这里看起来我们写出的代码并差不了几行,但在一个正式的项目开发中 我们如果使用 sqlx 进行开发 我们会大量编写sql 语句 此时的时间成本就会远高于使用 Gorm 进行开发。 603 | 604 | ##### Gorm学习 605 | 606 | Gorm官方有详尽的中文文档:[GORM 指南 | GORM - The fantastic ORM library for Golang, aims to be developer friendly.](https://gorm.io/zh_CN/docs/) 607 | 608 | 阅读官方文档是我们提高能力的有效方法🥰 609 | 610 | ## 作业 611 | 612 | 发送到邮箱nanach0@qq.com 613 | 614 | 提交格式:第七次作业-2022214012-罗一尧-Lv? 615 | 616 | **截止时间**:下一次上课之前 617 | 618 | ### Lv0 619 | 620 | 在MySQL中创建一个database,在里面创建一张数据表student,然后使用go语言操作MySQL,向里面插入十条记录,然后全部读出并打印 621 | 622 | ### Lv1 623 | 624 | 在上次作业的基础上,使用MySQL进行数据持久化(不要使用gorm,sqlx跟databse/sql均可) 625 | 626 | ### Lv2 627 | 628 | 学习MySQL与SQL理论知识(无需提交) 629 | 630 | ### Lv3 631 | 632 | 如果你学有余力,使用gin框架+MySQL实现QQ的好友功能 633 | 634 | - 登录与创建账号 635 | - 加好友 636 | - 删好友 637 | - 查看所有好友 638 | - 好友分组 639 | - 好友搜索 640 | 641 | 你可能需要知道的知识点: 642 | 643 | - 模糊查询 644 | 645 | 646 | 647 | -------------------------------------------------------------------------------- /lesson08/images/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson08/images/1.png -------------------------------------------------------------------------------- /lesson08/images/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson08/images/2.png -------------------------------------------------------------------------------- /lesson08/images/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson08/images/3.png -------------------------------------------------------------------------------- /lesson09/README.md: -------------------------------------------------------------------------------- 1 | ## 前言 2 | 3 | 俗话说,不会 linux 的后端不是好运维,后端开发人员也需要懂一点运维。 4 | 5 | 展望目前行业的发展,自动化运维和 DevOps 等理念的诞生和推广,其实也让运维和开发的边界变得更加的模糊,运维人员可能需要掌握一些开发语言来实现编写自动化运维的脚本,或者需要掌握一些开发的语言和工作流程以便更好地协作。开发人员可能也需要了解运维的内容以便更好地开发实际有用的软件以及更好地协作。 6 | 7 | ## Linux 基础 8 | 9 | ### 啥是linux? 10 | 11 | > Linux是一套免费使用和自由传播的类Unix操作系统,是一个基于POSIX和UNIX的多用户、多任务、支持多线程和多CPU的操作系统。它能运行主要的UNIX工具软件、应用程序和网络协议。它支持32位和64位硬件。Linux继承了Unix以网络为核心的设计思想,是一个性能稳定的多用户网络操作系统。 Linux操作系统诞生于1991 年10 月5 日(这是第一次正式向外公布时间)。Linux存在着许多不同的Linux版本,但它们都使用了Linux内核。Linux可安装在各种计算机硬件设备中,比如手机、平板电脑、路由器、视频游戏控制台、台式计算机、大型机和超级计算机。 严格来讲,Linux这个词本身只表示**Linux内核**,但实际上人们已经习惯了用Linux来形容整个基于Linux内核,并且使用GNU 工程各种工具和数据库的操作系统。 12 | 13 | 全世界 99% 服务器的操作系统都是 Linux 14 | 15 | ### linux 的各个发行版 16 | 17 | ![img](https://pic4.zhimg.com/80/v2-c79ecff3534b9e135aac51673172a7cf_720w.webp) 18 | 19 | Linux发行版主要有三个分支:Debian、Slackware、Redhat。 20 | (1)Debian:(以社区的方式运作) 21 | 22 | 1. Ubuntu:基于Debian开发的开源Linux操作系统,主要针对桌面和服务器; 23 | 2. Linux Mint:基于Debian和Ubuntu的Linux发行版,致力于桌面系统对个人用户每天的工作更易用,更高效,且目标是提供一种更完整的即刻可用体验。 24 | 25 | (2)slackware 26 | 27 | 1. suse:基于Slackware二次开发的一款Linux,主要用于商业桌面、服务器。 28 | 2. SLES(SUSE Linux Enterprise Server(SLES):企业服务器操作系统,是唯一与微软系统兼容的Linux操作系统。 29 | 3. OpenSUSE:由suse发展而来,旨在推进linux的广泛使用,主要用于桌面环境,用户界面非常华丽,而且性能良好。 30 | 31 | (3)Redhat 32 | 33 | 1. rhel(red hat enterprise Linux):Red Hat公司发布的面向企业用户的Linux操作系统。早起版本主要用于桌面环境,免费: 34 | 2. Fedora:基于Red Hat Linux终止发行后,红帽公司计划以Fedora来取代Red Hat Linux在个人领域的应用,而另外发行的Red Hat Enterprise Linux取代Red Hat Linux在商业应用的领域。Fedora的功能对于用户而言,它是一套功能完备、更新快速的免费操作系统,而对赞助者Red Hat公司而言,它是许多新技术的测试平台,被认为可用的技术最终会加入到Red Hat Enterprise Linux中。Fedora大约每六个月发布新版本。 35 | 3. Centos:基于Red hat Linux提供的可自由使用源代码的企业级Linux发行版本。每个版本的Centos都会获得十年的支持(通过安全更新的方式)。新版本的Centos大约每两年发行一次,而每个版本的Centos会定期(大概6个月)更新一次,以支持新的硬件。这样,建立一个安全、低维护、稳定、高预测性、高重复性的Linux环境。 36 | 37 | (4)其他发行版本: 38 | 39 | 1. Gentoo:基于linux的自由操作系统,基于Linux的自由操作系统,它能为几乎任何应用程序或需求自动地作出优化和定制。追求极限的配置、性能,以及顶尖的用户和开发者社区,都是Gentoo体验的标志特点, Gentoo的哲学是自由和选择。得益于一种称为Portage的技术,Gentoo能成为理想的安全服务器、开发工作站、专业桌面、游戏系统、嵌入式解决方案或者别的东西--你想让它成为什么,它就可以成为什么。由于它近乎无限的适应性,可把Gentoo称作元发行版。 40 | 2. Aech Linux(或称Arch):以轻量简洁为设计理念的Linux发行版。其开发团队秉承简洁、优雅和代码最小化的设计宗旨。 41 | 42 | ### linux 目录 43 | 44 | 根目录 `/` 是整个文件系统的起始点,但实际上,Linux 文件系统采用了一种层次结构来组织文件和目录。 45 | 46 | 根目录 `/` 下有许多子目录,这些子目录用于组织不同类型的文件和系统资源。以下是一些常见的子目录及其用途: 47 | 48 | - `/bin`:包含可执行文件,用于存放基本的系统命令。 49 | - `/boot`:包含启动所需的文件,例如内核和引导加载程序。 50 | - `/etc`:包含系统配置文件。 51 | - `/home`:用户的主目录,每个用户都有一个对应的子目录。 52 | - `/lib`:包含共享库文件,用于支持系统和应用程序。 53 | - `/opt`:用于安装可选软件包的目录。 54 | - `/root`:超级用户(root)的主目录。 55 | - `/sbin`:包含系统管理员使用的系统命令。 56 | - `/usr`:包含用户安装的应用程序和文件,类似于程序文件夹。 57 | - `/var`:包含可变的数据文件,例如日志文件和缓存文件。 58 | 59 | ### linux常用命令 60 | 61 | ##### 安装 62 | 63 | ###### **Ubuntu(apt)** 64 | 65 | - 安装软件包 66 | 67 | ```text 68 | sudo apt-get update # 更新软件包列表 69 | sudo apt-get install packageName # 安装软件包 70 | ``` 71 | 72 | - 删除软件包 73 | 74 | ```text 75 | sudo apt-get remove packageName # 删除软件包(保留配置文件) 76 | sudo apt-get purge packageName # 删除软件包及其配置文件 77 | ``` 78 | 79 | - 搜索软件包 80 | 81 | ```text 82 | sudo apt search packageName # 搜索软件包 83 | ``` 84 | 85 | - 更新软件包列表 86 | 87 | ```text 88 | sudo apt-get update # 更新软件包列表 89 | ``` 90 | 91 | ###### **CentOS(yum)** 92 | 93 | - 安装软件包 94 | 95 | ```text 96 | sudo yum update # 更新软件包列表(yum) 97 | sudo yum install packageName # 安装软件包(yum) 98 | ``` 99 | 100 | - 删除软件包 101 | 102 | ```text 103 | sudo yum remove packageName # 删除软件包 104 | ``` 105 | 106 | - 搜索软件包 107 | 108 | ```text 109 | sudo yum search packageName # 搜索软件包(yum) 110 | ``` 111 | 112 | - 更新软件包列表 113 | 114 | ```text 115 | sudo yum update # 更新软件包列表(yum) 116 | ``` 117 | 118 | ###### **Alpine(apk)** 119 | 120 | - 安装软件包 121 | 122 | ```text 123 | apk add packageName # 安装软件包 124 | ``` 125 | 126 | - 删除软件包 127 | 128 | ```text 129 | apk del packageName # 删除软件包 130 | ``` 131 | 132 | - 搜索软件包 133 | 134 | ```text 135 | apk search packageName # 搜索软件包 136 | ``` 137 | 138 | - 更新软件包列表 139 | 140 | ```text 141 | apk update # 更新软件包列表 142 | ``` 143 | 144 | 145 | 146 | ##### 说明书 147 | 148 | ``` 149 | man xxx 150 | ``` 151 | 152 | 153 | 154 | ##### 查看当前目录下所有文件 155 | 156 | ``` 157 | ls 158 | ## 列出所有文件(包括隐藏文件) 159 | ls -a 160 | ``` 161 | 162 | 163 | 164 | ##### 切换目录 165 | 166 | ``` 167 | #绝对路径 168 | cd /home/ 169 | #相对路径 170 | cd GoProJ 171 | ``` 172 | 173 | 174 | 175 | ##### 查看端口号占用情况 176 | 177 | ``` 178 | lsof -i:8080 179 | ``` 180 | 181 | 182 | 183 | ##### 查看所有用户所有进程 184 | 185 | ``` 186 | ps -a 187 | ``` 188 | 189 | 190 | 191 | ##### 强制杀死指定进程 192 | 193 | 课前问题:kill 命令是什么呢? 194 | 195 | ``` 196 | kill -9 12345 197 | ``` 198 | 199 | 200 | 201 | ##### 查看文本文件内容 202 | 203 | ``` 204 | cat 1.txt 205 | cat main.go 206 | ``` 207 | 208 | 209 | 210 | ##### 修改文件权限 211 | 212 | ``` 213 | chmod 733 main 214 | chmod +x main 215 | 216 | #千万不要输入 217 | chmod -Rf 777 / 218 | #千万不要输入 219 | 220 | #威力仅次于 rm -r -f / 221 | ``` 222 | 223 | [权限相关拓展](https://zhuanlan.zhihu.com/p/165091887) 224 | 225 | ##### 创建文件夹 226 | 227 | ``` 228 | mkdir goProJ 229 | ``` 230 | 231 | 232 | 233 | ##### 创建文件 234 | 235 | ``` 236 | #touch命令 237 | touch main.go 238 | #vim工具 239 | vim main.go 240 | ``` 241 | 242 | 243 | 244 | [vim入门到精通]:https://zhuanlan.zhihu.com/p/68111471 . 245 | 246 | ##### 删除文件 247 | 248 | ``` 249 | #有文件夹会一起删除 250 | rm -rf main.go 251 | ``` 252 | 253 | 254 | 255 | ##### 管道 | 256 | 257 | 竖线符号 `|` 是管道符号(Pipe Symbol)。该符号用于将一个命令的输出传递给另一个命令作为输入,以便二者之间进行数据传输和处理。 258 | 259 | ``` 260 | command1 | command2 261 | 262 | ls | grep "pattern" 263 | ``` 264 | 265 | 266 | 267 | ##### 常用快捷键 268 | 269 | ``` 270 | #停止进程 271 | ctrl + c 272 | #清屏 273 | ctrl + l 274 | ``` 275 | 276 | 277 | 278 | [进阶]:https://www.lanqiao.cn/courses/1 . 279 | 280 | ### 部署 281 | 282 | go 的程序部署非常简单(相比 Java 而言) 283 | 284 | 简单来说仅需两步:1,编译;2,运行 285 | 286 | 而如果我们想把可执行文件放到服务器上运行,还需要额外几个步骤 287 | 288 | 大致分为两种 289 | 290 | 1.编译为二进制文件,上传到服务器,服务器直接运行二进制文件 291 | 292 | 2.将源代码上传到服务器,服务器进行编译(前提是服务器配置好go环境) 293 | 294 | #### 上传二进制文件到服务器 295 | 296 | 首先,在goland终端更改环境变量 297 | 298 | ``` 299 | go env -w GOOS=linux 300 | ``` 301 | 302 | 然后,来到项目根目录,输入命令编译源代码 303 | 304 | ``` 305 | go build main.go 306 | ``` 307 | 308 | 等待几秒,你会发现项目中多了一个main文件(注意不是main.exe) 309 | 310 | 这就是编译好的二进制文件,可以在任何linux上运行 311 | 312 | 我们可以通过以下命令,将此文件上传到服务器 313 | 314 | ``` 315 | #将当前目录下main文件拷贝到127.0.0.1的/home/路径下 316 | scp main root@127.0.0.1:/home/ 317 | ``` 318 | 319 | 如果想要免密登录 配置SSH即可 320 | 321 | 1.在主机A创建密钥对 322 | 323 | ``` 324 | ssh-keygen #创建证书 325 | ``` 326 | 327 | 然后均回车(选择默认) 328 | 329 | 2、将文件上传至免登录主机B的authorized_keys 330 | 331 | ``` 332 | /root/.ssh/authorized_keys 333 | ``` 334 | 335 | #### 上传源代码到服务器 336 | 337 | 通常会先将源码上传到github再拉下来,或者使用goland一键上传 338 | 339 | [服务器go安装](https://blog.csdn.net/qq_43098070/article/details/126075629) 340 | 341 | 然后进入项目目录直接编译即可 342 | 343 | ``` 344 | go build main.go 345 | ``` 346 | 347 | #### 运行程序 348 | 349 | 现在服务器上有编译好的二进制文件了,那我们如何运行它? 350 | 351 | 只需要 352 | 353 | ``` 354 | ./main 355 | ``` 356 | 357 | 非常的简单 358 | 359 | 但是我们会发现无法运行,因为文件没有权限 360 | 361 | 这时候只需要 362 | 363 | ``` 364 | chmod +x main 365 | chmod 733 main 366 | ``` 367 | 368 | 给它高权限再次运行即可 369 | 370 | 这时候,使用云服务器的同学们需要去控制台找到安全组的配置规则,在入方向添加相应端口 371 | 372 | ##### 后台运行 373 | 374 | 如果按以上方式运行,当我们关闭窗口时会发现程序被终止了 375 | 376 | 那有什么办法可以让程序在后台运行呢? 377 | 378 | 我们可以使用 tmux,systemctl 或者 nohup+& 命令 379 | 380 | ###### nohup+& 381 | 382 | ``` 383 | nohup ./main & 384 | ``` 385 | 386 | ###### tmux 387 | 388 | [tmux](https://www.ruanyifeng.com/blog/2019/10/tmux.html) 389 | 390 | ###### systemctl 391 | 392 | [systemctl设置自己的systemd.service服务设置守护进程](https://blog.csdn.net/lonnng2004/article/details/88964763) 393 | 394 | [systemctl 详解](https://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-commands.html) 395 | 396 | ### 作业 397 | 398 | 发送到 [wanziyu@lanshan.email](mailto:wanziyu@lanshan.email) 399 | 400 | #### lv0 401 | 402 | 熟悉linux指令 403 | 404 | #### lv1 405 | 406 | 部署课件中样例到服务器,浏览器访问页面并截图 407 | 408 | 没有服务器的同学在类 unix 操作系统下编译运行并在浏览器中访问页面并截图 -------------------------------------------------------------------------------- /lesson09/example1/go.mod: -------------------------------------------------------------------------------- 1 | module main 2 | 3 | go 1.21 4 | 5 | require github.com/gin-gonic/gin v1.9.1 6 | 7 | require ( 8 | github.com/bytedance/sonic v1.9.1 // indirect 9 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect 10 | github.com/gabriel-vasile/mimetype v1.4.2 // indirect 11 | github.com/gin-contrib/sse v0.1.0 // indirect 12 | github.com/go-playground/locales v0.14.1 // indirect 13 | github.com/go-playground/universal-translator v0.18.1 // indirect 14 | github.com/go-playground/validator/v10 v10.14.0 // indirect 15 | github.com/goccy/go-json v0.10.2 // indirect 16 | github.com/json-iterator/go v1.1.12 // indirect 17 | github.com/klauspost/cpuid/v2 v2.2.4 // indirect 18 | github.com/leodido/go-urn v1.2.4 // indirect 19 | github.com/mattn/go-isatty v0.0.19 // indirect 20 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 21 | github.com/modern-go/reflect2 v1.0.2 // indirect 22 | github.com/pelletier/go-toml/v2 v2.0.8 // indirect 23 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect 24 | github.com/ugorji/go/codec v1.2.11 // indirect 25 | golang.org/x/arch v0.3.0 // indirect 26 | golang.org/x/crypto v0.9.0 // indirect 27 | golang.org/x/net v0.10.0 // indirect 28 | golang.org/x/sys v0.8.0 // indirect 29 | golang.org/x/text v0.9.0 // indirect 30 | google.golang.org/protobuf v1.30.0 // indirect 31 | gopkg.in/yaml.v3 v3.0.1 // indirect 32 | ) 33 | -------------------------------------------------------------------------------- /lesson09/example1/go.sum: -------------------------------------------------------------------------------- 1 | github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= 2 | github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= 3 | github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= 4 | github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= 5 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= 6 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= 7 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 8 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 9 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 10 | github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= 11 | github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= 12 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= 13 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 14 | github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= 15 | github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= 16 | github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= 17 | github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= 18 | github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= 19 | github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= 20 | github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= 21 | github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= 22 | github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= 23 | github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= 24 | github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= 25 | github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= 26 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 27 | github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= 28 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 29 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 30 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 31 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 32 | github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= 33 | github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= 34 | github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= 35 | github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= 36 | github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= 37 | github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= 38 | github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 39 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 40 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 41 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 42 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 43 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 44 | github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= 45 | github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= 46 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 47 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 48 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 49 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 50 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 51 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 52 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 53 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 54 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 55 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 56 | github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 57 | github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= 58 | github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 59 | github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= 60 | github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= 61 | github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= 62 | github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= 63 | golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= 64 | golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= 65 | golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= 66 | golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= 67 | golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= 68 | golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= 69 | golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= 70 | golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 71 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 72 | golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= 73 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 74 | golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= 75 | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 76 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 77 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 78 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 79 | google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= 80 | google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 81 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 82 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 83 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 84 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 85 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 86 | rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= 87 | -------------------------------------------------------------------------------- /lesson09/example1/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "net/http" 6 | ) 7 | 8 | func main() { 9 | r := gin.Default() 10 | r.GET("/hello", func(context *gin.Context) { 11 | context.JSON(200, gin.H{ 12 | "status": http.StatusOK, 13 | "info": "hello", 14 | }) 15 | }) 16 | r.Run(":8077") 17 | } 18 | -------------------------------------------------------------------------------- /lesson09/example2/baidu/a2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson09/example2/baidu/a2.png -------------------------------------------------------------------------------- /lesson09/example2/baidu/carm-baidu-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson09/example2/baidu/carm-baidu-black.png -------------------------------------------------------------------------------- /lesson09/example2/baidu/carm-baidu-blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson09/example2/baidu/carm-baidu-blue.png -------------------------------------------------------------------------------- /lesson09/example2/baidu/carm-baidu-blue.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson09/example2/baidu/carm-baidu-blue.psd -------------------------------------------------------------------------------- /lesson09/example2/baidu/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 百度一下,你就知道 8 | 256 | 257 | 258 | 新闻 259 | hao123 260 | 地图 261 | 贴吧 262 | 视频 263 | 图片 264 | 网盘 265 | 更多 266 | 267 | 268 | 269 | 270 | 271 |
272 | 273 |
274 | 275 | 276 | 281 |
283 |
成都
284 | 285 |
36℃
286 |
287 |
288 |
289 | 290 |
291 | 292 |
设置
293 |
294 |
搜索设置
295 |
高级搜索
296 |
关闭预测
297 |
隐私设置
298 |
开启播报
299 |
关闭播报
300 |
隐藏咨询
301 |
关闭播报
302 |
303 |
更换皮肤
304 |
305 | 306 |
Luckyboi
307 |
308 |
个人中心
309 |
账户设置
310 |
切换账号
311 |
退出登录
312 |
313 | 314 | 315 |
换一换
316 | 317 | 318 | 378 | 379 | -------------------------------------------------------------------------------- /lesson09/example2/baidu/tx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson09/example2/baidu/tx.png -------------------------------------------------------------------------------- /lesson09/example2/go.mod: -------------------------------------------------------------------------------- 1 | module Courseware-2022/class9/example 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/gin-contrib/cors v1.4.0 7 | github.com/gin-contrib/static v0.0.1 8 | github.com/gin-gonic/gin v1.8.1 9 | ) 10 | 11 | require ( 12 | github.com/gin-contrib/sse v0.1.0 // indirect 13 | github.com/go-playground/locales v0.14.0 // indirect 14 | github.com/go-playground/universal-translator v0.18.0 // indirect 15 | github.com/go-playground/validator/v10 v10.10.0 // indirect 16 | github.com/goccy/go-json v0.9.7 // indirect 17 | github.com/json-iterator/go v1.1.12 // indirect 18 | github.com/leodido/go-urn v1.2.1 // indirect 19 | github.com/mattn/go-isatty v0.0.14 // indirect 20 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect 21 | github.com/modern-go/reflect2 v1.0.2 // indirect 22 | github.com/pelletier/go-toml/v2 v2.0.1 // indirect 23 | github.com/ugorji/go/codec v1.2.7 // indirect 24 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect 25 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 // indirect 26 | golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 // indirect 27 | golang.org/x/text v0.3.6 // indirect 28 | google.golang.org/protobuf v1.28.0 // indirect 29 | gopkg.in/yaml.v2 v2.4.0 // indirect 30 | ) 31 | -------------------------------------------------------------------------------- /lesson09/example2/go.sum: -------------------------------------------------------------------------------- 1 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/gin-contrib/cors v1.4.0/go.mod h1:bs9pNM0x/UsmHPBWT2xZz9ROh8xYjYkiURUfmBoMlcs= 5 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 6 | github.com/gin-contrib/static v0.0.1/go.mod h1:CSxeF+wep05e0kCOsqWdAWbSszmc31zTIbD8TvWl7Hs= 7 | github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= 8 | github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= 9 | github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= 10 | github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= 11 | github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= 12 | github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= 13 | github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= 14 | github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= 15 | github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= 16 | github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= 17 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 18 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 19 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 20 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 21 | github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 22 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 23 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 24 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 25 | github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= 26 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 27 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 28 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 29 | github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= 30 | github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= 31 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 32 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= 33 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 34 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 35 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 36 | github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= 37 | github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= 38 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 39 | github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= 40 | github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= 41 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 42 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 43 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 44 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 45 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 46 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 47 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 48 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 49 | github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= 50 | github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= 51 | github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= 52 | github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= 53 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 54 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 55 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 56 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 57 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 58 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 59 | golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 60 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 61 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 62 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 63 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 64 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 65 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 66 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 67 | google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 68 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 69 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 70 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 71 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 72 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 73 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 74 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 75 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 76 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 77 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 78 | -------------------------------------------------------------------------------- /lesson09/example2/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/gin-contrib/static" 5 | "github.com/gin-gonic/gin" 6 | ) 7 | 8 | func main() { 9 | r := gin.Default() 10 | r.Use(static.Serve("/", static.LocalFile("./baidu", false))) 11 | r.Run(":8077") 12 | } 13 | -------------------------------------------------------------------------------- /lesson10/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:alpine AS builder 2 | 3 | LABEL stage=gobuilder 4 | 5 | ENV CGO_ENABLED 0 6 | ENV GOPROXY https://goproxy.cn,direct 7 | RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories 8 | 9 | RUN apk update --no-cache && apk add --no-cache tzdata 10 | 11 | WORKDIR /build 12 | 13 | ADD go.mod . 14 | ADD go.sum . 15 | RUN go mod download 16 | COPY . . 17 | RUN go build -ldflags="-s -w" -o /app/main main.go 18 | 19 | 20 | FROM scratch 21 | 22 | COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt 23 | COPY --from=builder /usr/share/zoneinfo/Asia/Shanghai /usr/share/zoneinfo/Asia/Shanghai 24 | ENV TZ Asia/Shanghai 25 | 26 | WORKDIR /app 27 | COPY --from=builder /app/main /app/main 28 | 29 | CMD ["./main"] 30 | -------------------------------------------------------------------------------- /lesson10/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | hello: 4 | image: liuxian123/hello:latest 5 | ports: 6 | - "6677:8080" # 如果你的 Go 程序在容器中监听的端口是 8080,将其映射到宿主机的 6677 端口 7 | -------------------------------------------------------------------------------- /lesson10/go.mod: -------------------------------------------------------------------------------- 1 | module lesson10 2 | 3 | go 1.21 4 | 5 | require github.com/gin-gonic/gin v1.9.1 6 | 7 | require ( 8 | github.com/bytedance/sonic v1.9.1 // indirect 9 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect 10 | github.com/gabriel-vasile/mimetype v1.4.2 // indirect 11 | github.com/gin-contrib/sse v0.1.0 // indirect 12 | github.com/go-playground/locales v0.14.1 // indirect 13 | github.com/go-playground/universal-translator v0.18.1 // indirect 14 | github.com/go-playground/validator/v10 v10.14.0 // indirect 15 | github.com/goccy/go-json v0.10.2 // indirect 16 | github.com/json-iterator/go v1.1.12 // indirect 17 | github.com/klauspost/cpuid/v2 v2.2.4 // indirect 18 | github.com/leodido/go-urn v1.2.4 // indirect 19 | github.com/mattn/go-isatty v0.0.19 // indirect 20 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 21 | github.com/modern-go/reflect2 v1.0.2 // indirect 22 | github.com/pelletier/go-toml/v2 v2.0.8 // indirect 23 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect 24 | github.com/ugorji/go/codec v1.2.11 // indirect 25 | golang.org/x/arch v0.3.0 // indirect 26 | golang.org/x/crypto v0.9.0 // indirect 27 | golang.org/x/net v0.10.0 // indirect 28 | golang.org/x/sys v0.8.0 // indirect 29 | golang.org/x/text v0.9.0 // indirect 30 | google.golang.org/protobuf v1.30.0 // indirect 31 | gopkg.in/yaml.v3 v3.0.1 // indirect 32 | ) 33 | -------------------------------------------------------------------------------- /lesson10/go.sum: -------------------------------------------------------------------------------- 1 | github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= 2 | github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= 3 | github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= 4 | github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= 5 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= 6 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= 7 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 8 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 9 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 10 | github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= 11 | github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= 12 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= 13 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 14 | github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= 15 | github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= 16 | github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= 17 | github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= 18 | github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= 19 | github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= 20 | github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= 21 | github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= 22 | github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= 23 | github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= 24 | github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= 25 | github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= 26 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 27 | github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= 28 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 29 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 30 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 31 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 32 | github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= 33 | github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= 34 | github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= 35 | github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= 36 | github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= 37 | github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= 38 | github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 39 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 40 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 41 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 42 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 43 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 44 | github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= 45 | github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= 46 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 47 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 48 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 49 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 50 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 51 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 52 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 53 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 54 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 55 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 56 | github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 57 | github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= 58 | github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 59 | github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= 60 | github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= 61 | github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= 62 | github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= 63 | golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= 64 | golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= 65 | golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= 66 | golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= 67 | golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= 68 | golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= 69 | golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= 70 | golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 71 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 72 | golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= 73 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 74 | golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= 75 | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 76 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 77 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 78 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 79 | google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= 80 | google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 81 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 82 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 83 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 84 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 85 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 86 | rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= 87 | -------------------------------------------------------------------------------- /lesson10/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "net/http" 6 | ) 7 | 8 | func main() { 9 | // 创建一个Gin路由器 10 | router := gin.Default() 11 | 12 | // 创建一个路由处理器 13 | router.GET("/", func(c *gin.Context) { 14 | c.String(http.StatusOK, "Hello, World!") 15 | }) 16 | 17 | // 启动HTTP服务器并监听端口 18 | router.Run(":8080") 19 | } 20 | -------------------------------------------------------------------------------- /lesson11/images/GOLint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson11/images/GOLint.png -------------------------------------------------------------------------------- /lesson11/images/RESTful.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson11/images/RESTful.png -------------------------------------------------------------------------------- /lesson11/images/bilibili1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson11/images/bilibili1.PNG -------------------------------------------------------------------------------- /lesson11/images/bilibili2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson11/images/bilibili2.png -------------------------------------------------------------------------------- /lesson11/images/bilibili3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson11/images/bilibili3.png -------------------------------------------------------------------------------- /lesson11/images/blue_cat_worm.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson11/images/blue_cat_worm.jpeg -------------------------------------------------------------------------------- /lesson11/images/demon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson11/images/demon.png -------------------------------------------------------------------------------- /lesson11/images/flow_chart.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson11/images/flow_chart.PNG -------------------------------------------------------------------------------- /lesson11/images/real_instance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson11/images/real_instance.png -------------------------------------------------------------------------------- /lesson11/images/structure1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson11/images/structure1.png -------------------------------------------------------------------------------- /lesson11/images/structure2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson11/images/structure2.png -------------------------------------------------------------------------------- /lesson11/images/structure3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson11/images/structure3.png -------------------------------------------------------------------------------- /lesson11/images/structure_ability.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson11/images/structure_ability.png -------------------------------------------------------------------------------- /lesson11/images/structure_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson11/images/structure_example.png -------------------------------------------------------------------------------- /lesson11/images/system_relations.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson11/images/system_relations.png -------------------------------------------------------------------------------- /lesson11/images/system_user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson11/images/system_user.png -------------------------------------------------------------------------------- /lesson11/images/tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson11/images/tree.png -------------------------------------------------------------------------------- /lesson11/images/viper.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson11/images/viper.PNG -------------------------------------------------------------------------------- /lesson11/images/zap.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson11/images/zap.PNG -------------------------------------------------------------------------------- /lesson12/images/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson12/images/1.png -------------------------------------------------------------------------------- /lesson12/images/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson12/images/10.png -------------------------------------------------------------------------------- /lesson12/images/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson12/images/11.png -------------------------------------------------------------------------------- /lesson12/images/12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson12/images/12.png -------------------------------------------------------------------------------- /lesson12/images/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson12/images/2.png -------------------------------------------------------------------------------- /lesson12/images/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson12/images/3.png -------------------------------------------------------------------------------- /lesson12/images/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson12/images/4.png -------------------------------------------------------------------------------- /lesson12/images/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson12/images/5.png -------------------------------------------------------------------------------- /lesson12/images/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson12/images/6.png -------------------------------------------------------------------------------- /lesson12/images/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson12/images/7.png -------------------------------------------------------------------------------- /lesson12/images/8.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson12/images/8.webp -------------------------------------------------------------------------------- /lesson12/images/9.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson12/images/9.webp -------------------------------------------------------------------------------- /lesson12/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "log" 8 | "time" 9 | 10 | "github.com/segmentio/kafka-go" 11 | ) 12 | 13 | var ( 14 | reader *kafka.Reader 15 | topic = "test" 16 | ) 17 | 18 | func main() { 19 | CreateTopic() 20 | ctx := context.Background() 21 | writeKafka(ctx) 22 | // go listenSignal() 23 | readKafka(ctx) 24 | } 25 | 26 | // 创建 topic 27 | func CreateTopic() { 28 | w := &kafka.Writer{ 29 | Addr: kafka.TCP("localhost:9092"), 30 | Topic: topic, 31 | AllowAutoTopicCreation: true, // 自动创建topic 32 | } 33 | 34 | messages := []kafka.Message{ 35 | { 36 | Key: []byte("Key-A"), 37 | Value: []byte("Hello World!"), 38 | }, 39 | { 40 | Key: []byte("Key-B"), 41 | Value: []byte("One!"), 42 | }, 43 | { 44 | Key: []byte("Key-C"), 45 | Value: []byte("Two!"), 46 | }, 47 | } 48 | 49 | var err error 50 | const retries = 3 51 | // 重试3次 52 | for i := 0; i < retries; i++ { 53 | ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 54 | defer cancel() 55 | 56 | err = w.WriteMessages(ctx, messages...) 57 | if errors.Is(err, kafka.LeaderNotAvailable) || errors.Is(err, context.DeadlineExceeded) { 58 | time.Sleep(time.Millisecond * 250) 59 | continue 60 | } 61 | 62 | if err != nil { 63 | log.Fatalf(" create Topic error %v", err) 64 | } 65 | break 66 | } 67 | 68 | // 关闭Writer 69 | if err := w.Close(); err != nil { 70 | log.Fatal("failed to close writer:", err) 71 | } 72 | } 73 | 74 | // 生产消息 75 | func writeKafka(ctx context.Context) { 76 | writer := kafka.Writer{ 77 | Addr: kafka.TCP("127.0.0.1:9092"), 78 | Topic: topic, 79 | Balancer: &kafka.Hash{}, //负载均衡算法 80 | WriteTimeout: 1 * time.Second, 81 | //kafka操作不应该影响正常服务的调用,所以设置响应限时 82 | RequiredAcks: kafka.RequireNone, 83 | // 最简单,但是最不安全的 不需要 acks 84 | 85 | //AllowAutoTopicCreation: true, 86 | // topic 没有创建的话可以设置为true让自动创建 87 | // 但是工作当中,你只是使用者,无权创建,此处只是演示 88 | } 89 | defer writer.Close() 90 | // 函数允许传入不定长的消息,原子性操作 91 | if err := writer.WriteMessages(ctx, 92 | kafka.Message{Key: []byte("1"), Value: []byte("h")}, 93 | kafka.Message{Key: []byte("2"), Value: []byte("e")}, 94 | kafka.Message{Key: []byte("3"), Value: []byte("l")}, 95 | kafka.Message{Key: []byte("4"), Value: []byte("l")}, 96 | kafka.Message{Key: []byte("5"), Value: []byte("o")}, 97 | ); err != nil { 98 | // 一开始 topic 没有创建,写入肯定失败, 意料之中 ,让循环三次尝试 99 | fmt.Printf("批量写入kafka失败:%v\n", err) 100 | } else { 101 | fmt.Printf("success") 102 | } 103 | } 104 | 105 | func readKafka(ctx context.Context) { 106 | reader = kafka.NewReader(kafka.ReaderConfig{ 107 | // 一个 broker 就是一个服务器上运行的kafka实例 108 | Brokers: []string{"localhost:9092"}, 109 | Topic: topic, 110 | // 每一秒上报一次当前读取的位置 111 | CommitInterval: 1 * time.Second, 112 | // 每个group只能消费一份确定的topic的数据,消费者需要用group id说明是哪个group在消费 113 | GroupID: "server_team", 114 | // 历史第一条消息开始消费 还是 该消费者上线后的最新消息开始 115 | StartOffset: kafka.FirstOffset, // 历史第一条 116 | }) 117 | // defer reader.Close() 这一行根本执行不到,与writer不同 118 | 119 | // 死循环一直读取消息 120 | for { 121 | if message, err := reader.ReadMessage(ctx); err != nil { 122 | fmt.Printf("读取kafka失败:%v\n", err) 123 | break 124 | } else { 125 | fmt.Printf("topic=%s,partition=%s,offset=%d,key=%d,value=%d", message.Topic, message.Partition, message.Offset, message.Key, message.Value) 126 | } 127 | } 128 | } 129 | 130 | // // 当系统杀进程的时候,就需要把reader主动停下 131 | // func listenSignal() { 132 | // c := make(chan os.Signal) 133 | // signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) 134 | // sig := <-c 135 | // fmt.Printf("接受到信号:%s", sig.String()) 136 | // if reader != nil { 137 | // reader.Close() 138 | // } 139 | // os.Exit(0) 140 | // } 141 | -------------------------------------------------------------------------------- /lesson12/消息队列.md: -------------------------------------------------------------------------------- 1 | ### 一、消息队列(MQ)概述 2 | 3 | 消息队列(Message Queue),是分布式系统中重要的组件,其通用的使用场景可以简单地描述为: 4 | 5 | > 当不需要立即获得结果,但是并发量又需要进行控制的时候,大概就是需要使用消息队列的时候。 6 | 7 | 消息队列主要解决了应用耦合、异步处理、流量削锋等问题。 8 | 9 | ### 二、消息队列使用场景 10 | 11 | #### 2.1 异步处理 12 | 13 | 多应用对消息队列中同一消息进行处理,应用间并发处理消息,相比串行处理,减少处理时间; 14 | 15 | 具体场景:用户为了使用某个应用,进行注册,系统需要发送注册邮件并验证短信。对这两个操作的处理方式有两种:串行及并行。 16 | 17 | (1)串行方式:新注册信息生成后,先发送注册邮件,再发送验证短信; 18 | 19 | 20 | 21 | 在这种方式下,需要最终发送验证短信后再返回给客户端。 22 | 23 | 24 | 25 | (2)并行处理:新注册信息写入后,由发短信和发邮件并行处理; 26 | 27 | 28 | 29 | 在这种方式下,发短信和发邮件 需处理完成后再返回给客户端。 30 | 31 | 假设以上三个子系统处理的时间均为50ms,且不考虑网络延迟,则总的处理时间: 32 | 33 | > 串行:50+50+50=150ms 并行:50+50 = 100ms 34 | 35 | 若使用消息队列: 36 | 37 | 38 | 39 | 并在写入消息队列后立即返回成功给客户端,则总的响应时间依赖于写入消息队列的时间,而写入消息队列的时间本身是可以很快的,基本可以忽略不计,因此总的处理时间相比串行提高了2倍,相比并行提高了一倍; 40 | 41 | 42 | 43 | #### 2.2 应用耦合 44 | 45 | 多应用间通过消息队列对同一消息进行处理,避免调用接口失败导致整个过程失败; 46 | 47 | 具体场景:用户使用QQ相册上传一张图片,人脸识别系统会对该图片进行人脸识别,一般的做法是,服务器接收到图片后,图片上传系统立即调用人脸识别系统,调用完成后再返回成功,如下图所示: 48 | 49 | 50 | 51 | 该方法有如下缺点: 52 | 53 | 54 | 55 | - 人脸识别系统被调失败,导致图片上传失败; 56 | - 延迟高,需要人脸识别系统处理完成后,再返回给客户端,即使用户并不需要立即知道结果; 57 | - 图片上传系统与人脸识别系统之间互相调用,需要做耦合; 58 | 59 | 60 | 61 | 若使用消息队列: 62 | 63 | 64 | 65 | 66 | 67 | 客户端上传图片后,图片上传系统将图片信息如uin、批次写入消息队列,直接返回成功;而人脸识别系统则定时从消息队列中取数据,完成对新增图片的识别。 68 | 69 | 此时图片上传系统并不需要关心人脸识别系统是否对这些图片信息的处理、以及何时对这些图片信息进行处理。事实上,由于用户并不需要立即知道人脸识别结果,人脸识别系统可以选择不同的调度策略,按照闲时、忙时、正常时间,对队列中的图片信息进行处理。 70 | 71 | 72 | 73 | #### 2.3 限流削峰 74 | 75 | 广泛应用于秒杀或抢购活动中,避免流量过大导致应用系统挂掉的情况; 76 | 77 | 具体场景:购物网站开展秒杀活动,一般由于瞬时访问量过大,服务器接收过大,会导致流量暴增,相关系统无法处理请求甚至崩溃。而加入消息队列后,系统可以从消息队列中取数据,相当于消息队列做了一次缓冲。 78 | 79 | 80 | 81 | 该方法有如下优点: 82 | 83 | 1. 请求先入消息队列,而不是由业务处理系统直接处理,做了一次缓冲,极大地减少了业务处理系统的压力; 84 | 2. 队列长度可以做限制,事实上,秒杀时,后入队列的用户无法秒杀到商品,这些请求可以直接被抛弃,返回活动已结束或商品已售完信息; 85 | 86 | 87 | 88 | #### 2.4 消息驱动的系统 89 | 90 | 系统分为消息队列、消息生产者、消息消费者,生产者负责产生消息,消费者(可能有多个)负责对消息进行处理; 91 | 92 | 具体场景:用户新上传了一批照片, 人脸识别系统需要对这个用户的所有照片进行聚类,聚类完成后由对账系统重新生成用户的人脸索引(加快查询)。这三个子系统间由消息队列连接起来,前一个阶段的处理结果放入队列中,后一个阶段从队列中获取消息继续处理。 93 | 94 | 95 | 96 | 该方法有如下优点: 97 | 98 | - 避免了直接调用下一个系统导致当前系统失败; 99 | - 每个子系统对于消息的处理方式可以更为灵活,可以选择收到消息时就处理,可以选择定时处理,也可以划分时间段按不同处理速度处理; 100 | 101 | 102 | 103 | 104 | 105 | ### **三、消息系统分类** 106 | 107 | 我们知道常见的消息系统有RabbtMQ、RocketMQ、Kafka、ZeroMQ等,但是这些消息系统中所使用的消息模式如下两种: 108 | 109 | #### 3.1 **Peer-to-Peer (Queue)** 110 | 111 | 简称PTP队列模式,也可以理解为点到点。例如单发邮件,我发送一封邮件给小徐,我发送过之后邮件会保存在服务器的云端,当小徐打开邮件客户端并且成功连接云端服务器后,可以自动接收邮件或者手动接收邮件到本地,当服务器云端的邮件被小徐消费过之后,云端就不再存储(这根据邮件服务器的配置方式而定)。 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | **Peer-to-Peer模式工作原理:** 120 | 121 | 1. 消息生产者`Producer1`生产消息到`Queue`,然后`Consumer1`从Queue中取出并且消费消息。 122 | 2. 消息被消费后,`Queue`将不再存储消息,其它所有`Consumer`不可能消费到已经被其它Consumer消费过的消息。 123 | 3. `Queue`支持存在多个`Producer`,但是对一条消息而言,只会有一个`Consumer`可以消费,其它Consumer则不能再次消费。 124 | 4. 但`Consumer`不存在时,消息则由`Queue`一直保存,直到有`Consumer`把它消费。 125 | 126 | #### 3.2 **Publish/Subscribe(Topic)** 127 | 128 | 简称发布/订阅模式。例如我微博有30万粉丝,我今天更新了一条微博,那么这30万粉丝都可以接收到我的微博更新,大家都可以消费我的消息。 129 | 130 | > 注:以下图示中的`Pushlisher`是错误的名词,正确的为`Publisher` 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | **Publish/Subscribe模式工作原理:** 139 | 140 | 1. 消息发布者`Publisher`将消息发布到主题`Topic`中,同时有多个消息消费者 `Subscriber`消费该消息。 141 | 2. 和PTP方式不同,发布到`Topic`的消息会被所有订阅者消费。 142 | 3. 当发布者发布消息,不管是否有订阅者,都不会报错信息。 143 | 4. 一定要先有消息发布者,后有消息订阅者。 144 | 145 | **注意:Kafka所采用的就是发布/订阅模式,被称为一种高吞吐量、持久性、分布式的发布订阅的消息队列系统。** 146 | 147 | 148 | 149 | ### 四、kafka 介绍 150 | 151 | #### 3.1 Kafka是什么? 152 | 153 | > Kafka是Apache旗下的一款分布式流媒体平台,Kafka是一种高吞吐量、持久性、分布式的发布订阅的消息队列系统。 它最初由LinkedIn(领英)公司发布,使用Scala语言编写,与2010年12月份开源,成为Apache的顶级子项目。 154 | 155 | **Kafka三大特点** 156 | 157 | 1.**高吞吐量**:可以满足每秒百万级别消息的生产和消费。 158 | 159 | 2.**持久性**:有一套完善的消息存储机制,确保数据高效安全且持久化。 160 | 161 | 3.**分布式**:基于分布式的扩展;Kafka的数据都会复制到几台服务器上,当某台故障失效时,生产者和消费者转而使用其它的Kafka。 162 | 163 | 164 | 165 | #### 3.2 概念介绍 166 | 167 | - **Producer:**消息和数据的生产者,主要负责生产`Push`消息到指定的Topic中。 168 | 169 | - **Consumer:**消息和数据的消费者,主要负责主动到已订阅的Topic中拉取消息并消费。 170 | 171 | - **Broker:**Kafka节点就是被称为Broker,Broker主要负责创建Topic,存储Producer所发布的消息,记录消息处理的过程,现是将消息保存到内存中,然后持久化到磁盘。 172 | 173 | - **Topic:**同一个Topic的消息可以分布在一个或多个Broker上,一个Topic包含一个或者多个Partition分区,数据被存储在多个Partition中。 174 | 175 | - **Partition:**分区,在这里被称为Topic物理上的分组,一个Topic在Broker中被分为1个或者多个Partition,也可以说为每个Topic包含一个或多个Partition,分区在创建Topic的时候可以指定。分区才是真正存储数据的单元。 176 | 177 | 178 | #### 3.3 Kafka集群 179 | 180 | **副本:** 副本是对分区(partition)的备份,在集群中,不同的副本会被部署在不同的broker上,其中一个作为leader,其它的是follower。 181 | 182 | kafka的读写操作都发生在**leader**上,leader负责把数据同步给**follower**,当leader挂了,经过主从选举,从多个follower中选举产生一个leader,follower接收leader同步的数据。 183 | 184 | **isr**:可以同步和已同步的节点都存到isr集合中(如果isr中节点的性能较差,会被踢出isr集合) 185 | 186 | 187 | 188 | 189 | 190 | kafka 集群中 (kafka——cluster), 有多个 **broker** (kafka 进程),部署在多台服务器上 (也可以部署在一台服务器,学习用)。 191 | 192 | **topic** 是一个逻辑概念 ,消息会发送到 指定的某个 topic ,然后 由某个 topic 下面的 实物 **partition** 来接收。 193 | 194 | 同一个 partition 的副本 分布在不同的服务器上,其中有一个被选为 **leader** ,leader down掉后,从同步比较好的副本中,选一个当 leader 。 195 | 196 | **集群的消息发送和接收** 197 | 198 | 分区分消费组的集群消费中的一些细节: 199 | 200 | 201 | 202 | - 一个partition只能被一个消费组中的同一个消费者消费,保证了partition范围内消息的有序性,但是无法保证整体消息的有序性。 203 | 204 | ``` 205 | Partition 1: --------------[消息1]-------------------------> 206 | 207 | Partition 2: -->[消息2]--->[消息3]--->[消息4]--->[消息5]---> 208 | 209 | Partition 3: --------------[消息6]--------->[消息7]---------> 210 | ``` 211 | 212 | 每个partition 中的消息是有序的,但是多个partition中,无法保证消息有序。 213 | 214 | - partition的数量要多于同一个消费组中的消费者数量,否则多的消费者消费不到消息。 215 | - 如果消费者挂了,就会触发rebalance机制,让其它消费者来消费该分区 216 | 217 | 218 | 219 | 220 | 221 | ### 五、Kafka集群的Controller、Rebalance、HW 222 | 223 | #### Controller 224 | 225 | 每个broker启动时都会向zk创建一个临时序号节点,获得序号最小的那个broker将会成为集群中的controller,controller会负责以下三件事: 226 | 227 | - 当集群中有一个副本的leader挂掉后,需要在集群中选举出一个新的leader,选举的规则是从isr集合中的最左边获得; 228 | - 当集群中有 broker 新增或减少时,controller 会同步信息给其它的 broker ; 229 | - 当集群中有分区新增或减少时,controller会同步信息给其它broker。 230 | 231 | #### Rebalance 232 | 233 | 前提:消费组中的消费者没有指明分区来消费 234 | 235 | 触发条件:当消费组中的消费者和分区的关系发生变化时(比如某个消费者不消费原本分区,或者是新增/减少消费者/分区) 236 | 237 | **分区分配策略:**在rebalance之前,分区使用以下三种策略进行分配 238 | 239 | - **range**:根据公式计算每个消费者消费哪几个分区(前面的消费者是分区总数/消费者数量 +1,之后的消费者是分区总数/消费者数量) 240 | 241 | > 例如,如果有3个消费者,6个分区,那么可能的分配方式是:消费者1负责分区0-1,消费者2负责分区2-3,消费者3负责分区4-5。 242 | 243 | - **轮询**:把分区轮流分给消费者 244 | 245 | > 例如,如果有3个消费者,6个分区,Round Robin分配策略可能会让消费者1负责分区0和3,消费者2负责分区1和4,消费者3负责分区2和5。 246 | 247 | - **sticky:**粘合策略,当发生rebalance时,尽量减少分区的移动,优先考虑让已有消费者继续消费它们之前消费的分区,减少资源消耗。如果这个策略没开,就会进行全部的重新分配。 248 | 249 | #### HW 和 LEO 机制 250 | 251 | LEO(log-end-offset) 是某个副本最后消息的位置。 252 | 253 | HW(HighWatermark) 是已完成同步的位置。 254 | 255 | leader和follower都会保存并负责更新自己的 hw 。消息写入leader且 ISR 中所有都同步完这个消息后,hw才会变化。消费者最多都读到 hw 的位置,即使 leader 的 leo 大于 hw。这样的机制能够保证消息不丢失。 256 | 257 | > 例如,消费者消费到了 leader 的最新的还没有同步的消息,消费之后同步完成,然后leader 挂了,这时换了leader后,消息会被重复消费 258 | 259 | 260 | 261 | 262 | 263 | ### 作业 264 | 265 | - 使用docker部署kafka,尝试kafka指令。 266 | - 完成使用go操作kafka 的demo。 267 | - 使用消息队列将之前的项目进行优化。 268 | 269 | 270 | 271 | ### 参考资料 272 | 273 | [常见消息队列简单介绍](https://cloud.tencent.com/developer/article/1006035) 274 | 275 | [Go操作Kafka之kafka-go | 李文周的博客 (liwenzhou.com)](https://www.liwenzhou.com/posts/Go/kafka-go/) 276 | 277 | [go-kafka](https://github.com/segmentio/kafka-go) 278 | 279 | [kafka官网](https://kafka.apache.org/) 280 | 281 | [黑马程序员kafka](https://www.bilibili.com/video/BV19y4y1b7Uo/?vd_source=23db46b6191c8462c4060f596df4e99e) 282 | 283 | 284 | 285 | -------------------------------------------------------------------------------- /lesson13/lesson13.md: -------------------------------------------------------------------------------- 1 | # 微服务入门 2 | 3 | ## 单体应用 4 | 5 | 与微服务相对的另一个概念是传统的「单体式应用程序」( Monolithic application ),单体式应用内部包含了所有需要的服务。而且各个服务功能模块有很强的耦合性,也就是相互依赖彼此,很难拆分和扩容。 6 | 7 | ### 单体应用的优点 8 | 9 | - 开发简洁,功能都在单个程序内部,便于软件设计和开发规划。 10 | - 容易部署,程序单一不存在分布式集群的复杂部署环境,降低了部署难度。 11 | - 容易测试,没有各种复杂的服务调用关系,都是内部调用方便测试。 12 | 13 | ### 单体应用的缺点 14 | 15 | 单体架构随着功能增多,不可避免的是研发效能的降低:研发周期变长、研发资源占用增多。从而引发的情况是:新员工培训时间增多、员工加班时间变长、员工要求涨薪或者跳槽。到了这种情况就说明,单体架构已经不能够满足企业发展需要,这个时候,需要升级架构来提升研发效能,比如微服务架构。 16 | 17 | ## 微服务 18 | 19 | > 2014年,[Martin Fowler](https://link.juejin.cn?target=https%3A%2F%2Fzh.wikipedia.org%2Fwiki%2FMartin_Fowler) 与 [James Lewis](https://link.juejin.cn?target=https%3A%2F%2Fzh.wikipedia.org%2Fw%2Findex.php%3Ftitle%3DJames_Lewis%26action%3Dedit%26redlink%3D1) 共同提出了微服务的概念,定义了微服务是由以单一应用程序构成的小服务,自己拥有自己的行程与轻量化处理,服务依业务功能设计,以全自动的方式部署,与其他服务使用 HTTP API 通信。同时服务会使用最小的规模的集中管理 (例如 [Docker](https://link.juejin.cn?target=https%3A%2F%2Fzh.wikipedia.org%2Fwiki%2FDocker)) 能力,服务可以用不同的编程语言与数据库等组件实现 。「维基百科」 20 | 21 | 微服务是一种架构风格: 22 | 23 | 1. 一组小服务 24 | 2. 每个服务运行在独立进程中 25 | 3. 服务之间使用轻量级通信 26 | 4. 服务可独立部署 27 | 5. 是基于业务能力实现 28 | 6. 无集中式管理 29 | 30 | 业界对于微服务还有一句更加剪短的描述:**微服务是一组小而自治的服务**。 31 | 32 | ### 微服务优点 33 | 34 | 优点很多,我们介绍几个比较重要的。 35 | 36 | #### 隔离性 37 | 38 | 一个服务不可用不会导致另一个服务也瘫痪,因为各个服务是相互独立和自治的系统。这在单体应用程序中是做不到的,单体应用程序中某个模块瘫痪,必将导致整个系统不可用,当然,单体程序也可以在不同机器上部署同样的程序来实现备份,不过,同样存在上面说的资源浪费问题。 39 | 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 | 68 | 按用户界面拆分:将整个系统按照不同的用户界面进行拆分,每个用户界面对应一个微服务。这种方式能够实现快速迭代和响应用户需求的能力。 69 | 70 | 按技术栈拆分:将整个系统按照不同的技术栈进行拆分,每个技术栈对应一个微服务。这种方式能够提高开发效率和降低系统的复杂度。 71 | 72 | 按性能拆分:将整个系统按照不同的性能需求进行拆分,每个需求对应一个微服务。这种方式能够提高系统的性能和可扩展性。 73 | 74 | ## RPC 75 | 76 | ### 什么是RPC 77 | 78 | ![img](https://github.com/LanshanTeam/Courseware-Backend-Go-2022/raw/main/class11-%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8ERPC%E6%A1%86%E6%9E%B6/images/rpc.jpg) 79 | 80 | RPC是指远程过程调用,是Remote Procedure Call三个单词的缩写,功能就是本地的函数一样去调远程函数,远程一般是指通过网络从远程计算机程序上请求服务,也可以在在宿主机下通过网络进行不同架构下的互相请求服务。 81 | 82 | RPC在分布式或者微服务架构上非常常用,RPC让不同服务之间的服务调用像本地调用一样简单高效,RPC是一种网络协议,是一种规范,每个大厂几乎都有自己研发的RPC协议。 83 | 84 | ### 为什么要用RPC 85 | 86 | 如果我们开发简单的单一应用,逻辑简单、用户不多、流量不大,那我们用不着。 87 | 88 | 当我们的系统访问量增大、业务增多时,我们会发现一台单机运行此系统已经无法承受。此时,我们可以将业务拆分成几个互不关联的应用,分别部署在各自机器上,以划清逻辑并减小压力。此时,我们也可以不需要RPC,因为应用之间是互不关联的。 89 | 90 | 当我们的业务越来越多、应用也越来越多时,自然的,我们会发现有些功能已经不能简单划分开来或者划分不出来。此时,可以将公共业务逻辑抽离出来,将之组成独立的服务Service应用 。而原有的、新增的应用都可以与那些独立的Service应用 互,以此来完成完整的业务功能。 91 | 92 | 所以此时,我们急需一种高效的应用程序之间的通讯手段来完成这种需求,服务之间的调用需要各种场景和因素的考虑,内部原理非常复杂和繁琐,同时在集群情况下,服务的负载均衡,熔断,限流等都是需要去考虑的,这时候就需要一个集服务注册发现、负载均衡、序列化协议、RPC通信协议、Socket通信、异步调用、熔断降级等技术为一体的技术去完成这些公共功能,所以你看,RPC大显身手的时候来了! 93 | 94 | 其实描述的场景也是服务化 、微服务和分布式系统架构的基础场景。即RPC框架就是实现以上结构的有力方式。 95 | 96 | ### 为什么不用HTTP 97 | 98 | ![img](https://github.com/LanshanTeam/Courseware-Backend-Go-2022/raw/main/class11-%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8ERPC%E6%A1%86%E6%9E%B6/images/rpc-framework-vs-rest.webp) 99 | 100 | 为什么有HTTP还需要RPC呢?RPC那么复杂为什么不用HTTP请求. 101 | 102 | 首先需要指正,这两个并不是并行概念。RPC 是一种**设计**,就是为了解决**不同服务之间的调用问题**,完整的 RPC 实现一般会包含有 **传输协议** 和 **序列化协议** 这两个。 103 | 104 | 而 HTTP 是一种传输协议,RPC 框架完全可以使用 HTTP 作为传输协议,也可以直接使用 TCP,使用不同的协议一般也是为了适应不同的场景。 105 | 106 | RPC主要是为了解决特定的问题或者节约成本的角度出发的。 107 | 108 | 使用 TCP 和使用 HTTP 各有优势: 109 | 110 | **传输效率**: 111 | 112 | - TCP,通常自定义上层协议,可以让请求报文体积更小 113 | - HTTP:如果是基于HTTP 1.1 的协议,请求中会包含很多无用的内容 114 | 115 | **性能消耗**,主要在于序列化和反序列化的耗时 116 | 117 | - TCP,可以基于各种序列化框架进行,效率比较高 118 | - HTTP,大部分是通过 json 来实现的,字节大小和序列化耗时都要更消耗性能 119 | 120 | **跨平台**: 121 | 122 | - TCP:通常要求客户端和服务器为统一平台 123 | - HTTP:可以在各种异构系统上运行 124 | 125 | **总结**: RPC 的 TCP 方式主要用于公司内部的服务调用,性能消耗低,传输效率高。HTTP主要用于对外的异构环境,浏览器接口调用,APP接口调用,第三方接口调用等。 126 | 127 | ### 框架需要解决的问题 128 | 129 | ![img](https://github.com/LanshanTeam/Courseware-Backend-Go-2022/raw/main/class11-%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8ERPC%E6%A1%86%E6%9E%B6/images/discovery-problem.jpg) 130 | 131 | RPC是指远程过程调用,也就是说两台服务器A,B,一个应用部署在A服务器上,想要调用B服务器上应用提供的函数/方法,由于**不在一个内存空间,不能直接调用**,需要通过网络来表达调用的语义和传达调用的数据。 132 | 133 | 比如说,一个方法可能是这样定义的:func GetUserProfile(userId int64) *Profile 那么: 134 | 135 | 第一,**首先,要解决通讯的问题**,主要是通过在客户端和服务器之间建立TCP连接(socket),远程过程调用的所有交换的数据都在这个连接里传输。连接可以是按需连接,调用结束后就断掉,也可以是长连接,多个远程过程调用共享同一个连接。 136 | 137 | 第二,**要解决寻址的问题**,也就是说,A服务器上的应用怎么告诉底层的RPC框架,如何连接到B服务器(如主机或IP地址)以及特定的端口,方法的名称名称是什么,这样才能完成调用。 138 | 139 | 第三,当A服务器上的应用发起远程过程调用时,方法的参数需要通过底层的网络协议如TCP传递到B服务器,由于网络协议是基于二进制的,内存中的参数的值要序列化成二进制的形式,也就是**序列化**(Serialize)或编组(marshal),通过寻址和传输将序列化的二进制发送给B服务器。 140 | 141 | 第四,B服务器收到请求后,需要对参数进行反序列化(序列化的逆操作),恢复为内存中的表达方式,然后找到对应的方法(寻址的一部分)进行本地调用,然后得到返回值。 142 | 143 | 第五,返回值还要发送回服务器A上的应用,也要经过序列化的方式发送,服务器A接到后,再反序列化,恢复为内存中的表达方式,交给A服务器上的应用 144 | 145 | **总的来说可以归纳为以下几步:** 146 | 147 | 1. 远程服务之间建立通讯协议 148 | 2. 寻址:服务器(如主机或IP地址)以及特定的端口,方法的名称名称是什么 149 | 3. 通过序列化和反序列化进行数据传递 150 | 4. 将传递过来的数据通过java反射原理定位接口方法和参数 151 | 5. 暴露服务:用map将寻址的信息暴露给远方服务(提供一个endpoint *URI或者一个前端展示页面*) 152 | 6. 多线程并发请求业务 153 | 154 | ### 基本组成 155 | 156 | ![img](https://github.com/LanshanTeam/Courseware-Backend-Go-2022/raw/main/class11-%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8ERPC%E6%A1%86%E6%9E%B6/images/service-discovery.png) 157 | 158 | RPC框架需要的最基本的三个要素: 159 | 160 | - ServiceProvider: 服务提供方,提供相关服务接口。 161 | - ServiceConsumer: 服务消费方,消费服务提供方的接口。 162 | - Registry: 注册中心,用于进行服务的注册、发现、治理、高可用。 163 | 164 | #### 注册中心 165 | 166 | 注册中心是RPC框架中的管理者和协调者角色,虽然在远程过程调用中服务消费者会不经过注册中心,会直接向服务提供者发送请求,但是随着我们的服务方越来越多,每个服务的实例也不断变化的,且每个服务的地址,端口等信息是需要通知到消费方的,所以我们需要一个类似“管家”的角色,来负责管理服务注册和发现的工作,这个“管家”我们称之为注册中心。 167 | 168 | 一个合格的注册中心需要具备包括缓存和持久化服务提供方数据,动态更新服务提供者信息,动态监听服务提供方节点变化,推送节点变化到消费方,查询服务提供方数据等功能。 169 | 170 | #### 服务提供方(RPC服务端) 171 | 172 | 其需要对外提供服务接口,一个服务方需要包括启动连接注册中心,注册相关信息到注册中心,提供服务下线和更新机制,维护服务名和服务的映射,序列化和反序列化,启动通信等。 173 | 174 | #### 服务消费方(RPC客户端) 175 | 176 | 服务消费方需要具备可以从注册中心拉取服务列表,缓存服务列表,动态监听和更新服务列表的功能,还需要具备针对于服务的负载均衡策略,序列化和反序列化,根据约定的通信协议进行调用等。 177 | 178 | ### 服务注册与发现 179 | 180 | 简单来说,当服务A需要依赖服务B时,我们就需要告诉服务A,哪里可以调用到服务B,这就是服务注册发现要解决的问题。 181 | 182 | ![img](https://github.com/LanshanTeam/Courseware-Backend-Go-2022/raw/main/class11-%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8ERPC%E6%A1%86%E6%9E%B6/images/service-discovery.png) 183 | 184 | - `Service B` 把自己注册到 `Service Registry` 叫做 **服务注册** 185 | - `Service A` 从 `Service Registry` 发现 `Service B` 的节点信息叫做 **服务发现** 186 | 187 | #### 服务注册 188 | 189 | 服务注册是针对服务端的,服务启动后需要注册,分为几个部分: 190 | 191 | - 启动注册 192 | - 定时续期 193 | - 退出撤销 194 | 195 | ##### 启动注册 196 | 197 | 当一个服务节点起来之后,需要把自己注册到 `Service Registry` 上,便于其它节点来发现自己。注册需要在服务启动完成并可以接受请求时才会去注册自己,并且会设置有效期,防止进程异常退出后依然被访问。 198 | 199 | ##### 定时续期 200 | 201 | 定时续期相当于 `keep alive`,定期告诉 `Service Registry` 自己还在,能够继续服务。 202 | 203 | ##### 退出撤销 204 | 205 | 当进程退出时,我们应该主动去撤销注册信息,便于调用方及时将请求分发到别的节点。同时,go-zero 通过自适应的负载均衡来保证即使节点退出没有主动注销,也能及时摘除该节点。 206 | 207 | ### 服务发现 208 | 209 | 服务发现是针对调用端的,一般分为两类问题: 210 | 211 | - 存量获取 212 | - 增量侦听 213 | 214 | 还有一个常见的工程问题是 215 | 216 | - 应对服务发现故障 217 | 218 | 当服务发现服务(比如 etcd, consul, nacos等)出现问题的时候,我们不要去修改已经获取到的 endpoints 列表,从而可以更好的确保 etcd 等宕机后所依赖的服务依然可以正常交互。 219 | 220 | #### 存量获取 221 | 222 | ![img](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f2fc0a889be0421883d707493e327527~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp) 223 | 224 | 当 `Service A` 启动时,需要从 `Service Registry` 获取 `Service B` 的已有节点列表:`Service B1`, `Service B2`, `Service B3`,然后根据自己的负载均衡算法来选择合适的节点发送请求。 225 | 226 | #### 增量侦听 227 | 228 | 上图已经有了 `Service B1`, `Service B2`, `Service B3`,如果此时又启动了 `Service B4`,那么我们就需要通知 `Service A` 有个新增的节点。如图: 229 | 230 | ![img](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/71497af0435442e3acffff9dfd67cb86~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp) 231 | 232 | #### 应对服务发现故障 233 | 234 | 对于服务调用方来说,我们都会在内存里缓存一个可用节点列表。不管是使用 `etcd`,`consul` 或者 `nacos` 等,我们都可能面临服务发现集群故障,以 `etcd` 为例,当遇到 `etcd` 故障时,我们就需要冻结 `Service B` 的节点信息而不去变更,此时一定不能去清空节点信息,一旦清空就无法获取了,而此时 `Service B` 的节点很可能都是正常的,并且 `go-zero` 会自动隔离和恢复故障节点。 235 | 236 | ![img](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5af33ecc71dc44639825092ea0a81e4f~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp) 237 | 238 | 对服务发现模式的介绍:[微服务中服务注册和发现的可行性方案 - 掘金 (juejin.cn)](https://juejin.cn/post/7028348724447281189?searchId=202403092351383768936C87D074711FFA) 239 | 240 | ## 框架推荐 241 | 242 | ### Go-zero 243 | 244 | go-zero(收录于 CNCF 云原生技术全景图:https://landscape.cncf.io/?selected=go-zero)是一个集成了各种工程实践的 web 和 rpc 框架。通过弹性设计保障了大并发服务端的稳定性,经受了充分的实战检验。 245 | 246 | ![img](https://raw.githubusercontent.com/zeromicro/zero-doc/main/doc/images/go-zero.png) 247 | 248 | [GitHub](https://github.com/zeromicro/go-zero) 249 | 250 | [官方文档](https://go-zero.dev/) 251 | 252 | [go-zero-looklook](https://github.com/Mikaelemmmm/go-zero-looklook) 253 | 254 | ### Kitex 255 | 256 | Kitex [kaɪt’eks] 字节跳动内部的 Golang 微服务 RPC 框架,具有**高性能**、**强可扩展**的特点,在字节内部已广泛使用。如果对微服务性能有要求,又希望定制扩展融入自己的治理体系,Kitex 会是一个不错的选择。 257 | 258 | [Github](https://github.com/cloudwego/kitex) 259 | 260 | [官方文档](https://www.cloudwego.io/zh/docs/kitex/) 261 | 262 | [biz-demo](https://github.com/cloudwego/biz-demo) 263 | 264 | ### 学习项目推荐 265 | 266 | Herzt&Kitex:[ViolaPioggia/GoYin: 第六届字节跳动青训营后端极简版抖音(二等奖🥈) (github.com)](https://github.com/ViolaPioggia/GoYin) 267 | 268 | (万神🤩) 269 | 270 | Go-zero:[GophersTeam/GopherTok: Gopher小队的极简抖音 (github.com)](https://github.com/GophersTeam/GopherTok) (显哥🥰) 271 | 272 | ## 阅读 273 | 274 | [Thrift & IDL 介绍 - 掘金 (juejin.cn)](https://juejin.cn/post/6844903971086139400) 275 | 276 | [写给go开发者的gRPC教程-protobuf基础 - 掘金 (juejin.cn)](https://juejin.cn/post/7191008929986379836) 277 | 278 | ## 作业 279 | 280 | 阅读你感兴趣的框架文档 尝试读懂推荐项目代码 学会写IDL文件 281 | 282 | 学有余力可以写demo练手 :) (无需提交) 283 | 284 | -------------------------------------------------------------------------------- /lesson14/lesson14.md: -------------------------------------------------------------------------------- 1 | # Go-zero 快速搭建微服务demo 2 | 3 | ## 前言 4 | 5 | 我认为框架的学习,快速搭建微服务demo,看官方文档自己摸索足矣,主要讲一些踩坑经验和进阶的学习 6 | 7 | ## 官方文档(必看)!!! 8 | 9 | 直接看快速开始,跟着做就可以了 10 | 11 | https://go-zero.dev/ 12 | 13 | ## 社区群(里面大佬多,不懂的可以去问一下) 14 | 15 | https://go-zero.dev/docs/reference/about-us 16 | 17 | ## 项目推荐 18 | 19 | 如果快速开始过了之后,能搭建一个小demo,那么就可以看大佬们的项目深入学习啦,这里我推荐两个,一个是looklook,可以学习一些规范和很多技术栈,包括链路追踪用的jeager。日志收集然后可视化呈现的elk,云原生监控prometheus,k8s..... 再一个就是电商项目lebron,这个对业务的学习是很有帮助的,尤其是秒杀那一块,能学到很多高并发处理手段。 20 | 21 | ### lookook(规范&&技术栈) 22 | 23 | > 项目地址:https://github.com/Mikaelemmmm/go-zero-looklook 24 | > 25 | > LookLook文档地址:https://github.com/Mikaelemmmm/go-zero-looklook/tree/main/doc/chinese 26 | 27 | ### lebron(业务处理&&高并发设计) 28 | 29 | > 项目地址:https://github.com/zhoushuguang/lebron 30 | > 31 | > 系列博客:https://learnku.com/articles/68472 32 | 33 | ## 一些视频教学 34 | 35 | looklook作者讲解的入门教学视频,可以看一下 36 | 37 | [go-zero入门视频](https://www.bilibili.com/video/BV1LS4y1U72n/?spm_id_from=333.788&vd_source=23d8be6eab81d2d14531293e81d6a5dc) 38 | 39 | go-zero分布式缓存设计,这个万总讲的很好,**必看** 40 | 41 | [go-zero分布式缓存设计](https://www.bilibili.com/video/BV1Rv411L72P/?spm_id_from=333.337.search-card.all.click&vd_source=23d8be6eab81d2d14531293e81d6a5dc) 42 | 43 | ## 一些tips 44 | 45 | ### 并发神器mr包 46 | 47 | 可以用mr.Finish()去并发rpc请求,相当于封装好的goroutine 48 | 49 | 还有MapReduce,也值得学习一些,下面有讲解文章可以看看 50 | 51 | https://juejin.cn/post/7031483035820556295 52 | 53 | 视频推荐:https://www.bilibili.com/video/BV1eQ4y1W7PV?p=4&vd_source=23d8be6eab81d2d14531293e81d6a5dc 54 | 55 | 这个佬的视频都可以看,**很硬核很低层** 56 | 57 | ### go-zero实现的分布式锁 58 | 59 | [官方文档demo](https://go-zero.dev/docs/tutorials/redis/redis-lock) 60 | 61 | 博客解读源码实现 62 | 63 | https://juejin.cn/post/7026544325030838303 64 | 65 | 视频推荐:https://www.bilibili.com/video/BV1Pm4y1b76u?p=1&vd_source=23d8be6eab81d2d14531293e81d6a5dc 66 | 67 | 这里有介绍etcd实现的,很值得看看 68 | 69 | ## 作业 70 | 71 | * Lv1 用go-zero随便写个服务demo 72 | * Lv2 把looklook和lebron过一遍 73 | * LvX 自己深入go-zero的一些底层设计实现源码、按照自己的兴趣广泛深入发展,重点是**深入**...... 74 | -------------------------------------------------------------------------------- /lesson15/README.md: -------------------------------------------------------------------------------- 1 | ## 架构设计 2 | 3 | 在前两节课当中,相信大家都熟悉了微服务,但是微服务并不是万金油,通常会带来更高的维护成本和更复杂的架构设计难度,如果架构设计的不好,那么很容易就会写成屎山,也不能充分发挥微服务的优势。 4 | 5 | 传统企业应用大多是单体架构,而单体架构则大多是三层架构。三层架构解决了程序内代码间调用复杂、代码职责不清的问题,但这种分层是逻辑概念,在物理上它是中心化的集中式架构,并不适合分布式微服务架构。 6 | 7 | DDD 分层架构中的要素其实和三层架构类似,只是在 DDD 分层架构中,这些要素被重新归类,重新划分了层,确定了层与层之间的交互规则和职责边界。 8 | 9 | [什么是 DDD](https://blog.csdn.net/qq_16498553/article/details/110848831) 10 | 11 | [DDD 实战课](http://learn.lianglianglee.com/专栏/DDD实战课) 12 | 13 | ![img](./images/java_error_in_idea.png) 14 | 15 | DDD 分层架构在用户接口层引入了 DTO,给前端提供了更多的可使用数据和更高的展示灵活性。 16 | 17 | DDD 分层架构对三层架构的业务逻辑层进行了更清晰的划分,改善了三层架构核心业务逻辑混乱,代码改动相互影响大的情况。DDD 分层架构将业务逻辑层的服务拆分到了应用层和领域层。应用层快速响应前端的变化,领域层实现领域模型的能力。 18 | 19 | 另外一个重要的变化发生在数据访问层和基础层之间。三层架构数据访问采用 DAO 方式;DDD 分层架构的数据库等基础资源访问,采用了仓储(Repository)设计模式,通过依赖倒置实现各层对基础资源的解耦。 20 | 21 | 如何判断微服务设计是否合理呢?其实很简单,只需要看它是否满足这样的情形就可以了:随着业务的发展或需求的变更,在不断重新拆分或者组合成新的微服务的过程中,不会大幅增加软件开发和维护的成本,并且这个架构演进的过程是非常轻松、简单的。 22 | 23 | 这也是微服务设计的重点,就是看微服务设计是否能够支持架构长期、轻松的演进。 24 | 25 | ### **微服务还是小单体?** 26 | 27 | 有些项目团队在将集中式单体应用拆分为微服务时,首先进行的往往不是建立领域模型,而只是按照业务功能将原来单体应用的一个软件包拆分成多个所谓的“微服务”软件包,而这些“微服务”内的代码仍然是集中式三层架构的模式,“微服务”内的代码高度耦合,逻辑边界不清晰,这里我们暂且称它为“小单体微服务”。 28 | 29 | 下面这张图也很好地展示了这个过程。 30 | 31 | 而随着新需求的提出和业务的发展,这些小单体微服务会慢慢膨胀起来。当有一天你发现这些膨胀了的微服务,有一部分业务功能需要拆分出去,或者部分功能需要与其它微服务进行重组时,你会发现原来这些看似清晰的微服务,不知不觉已经摇身一变,变成了臃肿油腻的大单体了,而这个大单体内的代码依然是高度耦合且边界不清的。 32 | 33 | ![img](./images/1280X1280.PNG) 34 | “辛辛苦苦好多年,一夜回到解放前啊!”这个时候你就需要一遍又一遍地重复着从大单体向单体微服务重构的过程。想想,这个代价是不是有点高了呢? 35 | 36 | 其实这个问题已经很明显了,那就是边界。 37 | 38 | 这种单体式微服务只定义了一个维度的边界,也就是微服务之间的物理边界,本质上还是单体架构模式。微服务设计时要考虑的不仅仅只有这一个边界,别忘了还要定义好微服务内的逻辑边界和代码边界,这样才能得到你想要的结果。 39 | 40 | 那现在你知道了,我们一定要避免将微服务设计为小单体微服务,那具体该如何避免呢?清晰的边界人人想要,可该如何保证呢?DDD 已然给出了答案。 41 | 42 | 微服务的拆分可以参考领域模型,也可以参考聚合,因为聚合是可以拆分为微服务的最小单位的。但实施过程是否一定要做到逻辑边界与物理边界一致性呢?也就是说聚合是否也一定要设计成微服务呢?答案是不一定的,这里就涉及到微服务过度拆分的问题了。 43 | 44 | 微服务的过度拆分会使软件维护成本上升,比如:集成成本、发布成本、运维成本以及监控和定位问题的成本等。在项目建设初期,如果你不具备较强的微服务管理能力,那就不宜将微服务拆分过细。当我们具备一定的能力以后,且微服务内部的逻辑和代码边界也很清晰,你就可以随时根据需要,拆分出新的微服务,实现微服务的架构演进了。 45 | 46 | 当然,还要记住一点,微服务内聚合之间的服务调用和数据依赖需要符合高内聚松耦合的设计原则和开发规范,否则你也不能很快完成微服务的架构演进。 47 | 48 | ![img](./images/1280X1280%20(1).PNG) 49 | ### **微服务设计**原则 50 | 51 | 微服务设计原则中,如高内聚低耦合、复用、单一职责等这些常见的设计原则在此就不赘述了,主要强调下面这几条: 52 | 53 | **第一条:要领域驱动设计**,而不是数据驱动设计,也不是界面驱动设计。 54 | 55 | 微服务设计首先应建立领域模型,确定逻辑和物理边界以及领域对象后,然后才开始微服务的拆分和设计。而不是先定义数据模型和库表结构,也不是前端界面需要什么,就去调整核心领域逻辑代码。在设计时应该将外部需求从外到内逐级消化,尽量降低对核心领域层逻辑的影响。 56 | 57 | **第二条:要边界清晰的微服务,而不是泥球小单体。** 58 | 59 | 微服务上线后其功能和代码也不是一成不变的。随着需求或设计变化,领域模型会迭代,微服务的代码也会分分合合。边界清晰的微服务,可快速实现微服务代码的重组。微服务内聚合之间的领域服务和数据库实体原则上应杜绝相互依赖。你可通过应用服务编排或者事件驱动,实现聚合之间的解耦,以便微服务的架构演进。 60 | 61 | **第三条:要职能清晰的分层,而不是什么都放的大箩筐。** 62 | 63 | 分层架构中各层职能定位清晰,且都只能与其下方的层发生依赖,也就是说只能从外层调用内层服务,内层通过封装、组合或编排对外逐层暴露,服务粒度也由细到粗。应用层负责服务的组合和编排,不应有太多的核心业务逻辑,领域层负责核心领域业务逻辑的实现。各层应各司其职,职责边界不要混乱。在服务演进时,应尽量将可复用的能力向下层沉淀。 64 | 65 | **第四条:要做自己能 hold 住的微服务,而不是过度拆分的微服务。** 66 | 67 | 微服务过度拆分必然会带来软件维护成本的上升,比如:集成成本、运维成本、监控和定位问题的成本。企业在微服务转型过程中还需要有云计算、DevOps、自动化监控等能力,而一般企业很难在短时间内提升这些能力,如果项目团队没有这些能力,将很难 hold 住这些微服务。 68 | 69 | 如果在微服务设计之初按照 DDD 的战略设计方法,定义好了微服务内的逻辑边界,做好了架构的分层,其实我们不必拆分太多的微服务,即使是单体也未尝不可。随着技术积累和能力提升,当我们有了这些能力后,由于应用内有清晰的逻辑边界,我们可以随时轻松地重组出新的微服务,而这个过程不会花费太多的时间和精力。 70 | 71 | ## 服务注册与发现 72 | 73 | ### 什么是服务注册与发现? 74 | 75 | 一个后台系统,存在两个服务 A 和 B, A 服务需要调用 B 服务,就需要建立和 B 服务的连接,通常需要 B 服务的地址。 76 | 77 | 在计算机领域,服务的地址通常是由 ip 地址 和 端口 port 来组合表达。 78 | 79 | 此时,问题就来了:A 服务怎么知道 B 服务的地址(ip + port)呢? 80 | 81 | #### 简单方式 82 | 83 | 服务较少时,可以使用简单粗暴的方式: 84 | 85 | - 硬编码:代码中写死服务地址(ip+port)。 86 | - 给每个服务分配一个唯一的名字,并将其对应的地址(ip+port)一起写入configure文件。 所有服务的名字 : 地址都被列在这个配置文件中,一目了然。需要的时候,就根据名字来查询对应的地址。 87 | 88 | ![img](./images/af08e7a2-c965-4084-a417-ada9ee07d899.webp) 89 | 配置形式的地址管理 90 | 91 | 如果地址发生变更,则更新所有服务机器上的配置文件。 92 | 93 | ![img](./images/4e760b81-7bfb-437a-83f7-c054ea6b414c.webp) 94 | 配置更新 95 | 96 | #### DNS 查询 97 | 98 | 缺点是不适用于大量服务的场景并且通常有一定的起效时间 99 | 100 | 而且功能比较单一,只能用来获取服务地址 101 | 102 | #### 服务发现 103 | 104 | 当进入微服务时代,服务的数量急剧增多(相对独立的功能都会被拆分为一个个服务),服务之间像齿轮一样密切配合。而云计算大行其道的现在,服务的地址也是经常变化的,尤其是 k8s 自动调度的引入,服务经常会被调度到不同的机器,这样,服务的地址变化更频繁。 105 | 106 | 此时,我们就需要引入一种称为 ServiceRegistry 的服务注册与发现的组件。本质上,我们就是需要一种随时注册、更新、获取并监听其他服务地址变更的机制。 107 | 108 | ![img](./images/caec8684-c117-4521-a6bd-a124a795e819.webp) 109 | 110 | ![img](./images/dfbb50b0-6a41-4045-9e47-6a77ffc2d5b3.png) 111 | #### 服务发现有什么优势? 112 | 113 | ##### 灵活配置 114 | 115 | 类似于 DNS 解析,我们只需要对应的服务名就可以获取到服务地址,同时通常情况下起效快,延迟低。 116 | 117 | ##### 健康检查 118 | 119 | 如果说直接用 ip+port 的形式或者 DNS ,如果一个实例挂掉了,调用者依然会请求这个地址,然后就会返回错误,造成了很大的资源浪费、降低了易用性。但是使用了服务发现的话,服务发现中心会定期地对各个实例进行健康检查,如果发现故障则作上标记或者直接下线。 120 | 121 | ![img](./images/7f37cdb4-faa1-4341-9daa-9f24646f4443.png) 122 | ##### 简单的配置中心 123 | 124 | 以 consul 和 nacos 为例,consul 允许存储 kv 键值对,nacos 也允许 存储 json 、yaml 等格式的数据,也就是说在不需要高级配置中心功能(灰度发布等等)的情况下使用服务注册中心来存储配置也是一个不错的选择。 125 | 126 | ![img](./images/d0a32a7c-8b86-4083-a156-4570db6f3018.png) 127 | ##### 负载均衡 128 | 129 | - 随机负载均衡:从可用的服务实例中随机选择一个来处理请求,简单且容易实现,但无法保证均衡和高性能。 130 | - 轮询负载均衡:按照轮询的方式依次选择可用的服务实例来处理请求,确保每个实例平均分担负载,但无法考虑实例的性能差异。 131 | - 权重负载均衡:为每个服务实例分配一个权重值,根据权重来决定选择哪个实例处理请求,可以根据实例的性能和资源情况进行动态调整。 132 | - 基于哈希的负载均衡:根据请求的特征或标识计算哈希值,将请求映射到特定的服务实例,可以实现请求的精确路由和会话保持。 133 | - 最少连接负载均衡:选择当前连接数最少的服务实例来处理请求,以实现负载均衡和性能优化。 134 | - 动态负载均衡:根据实时的系统负载和性能指标来动态调整和分配请求,例如根据 CPU 使用率、内存利用率等指标进行决策。 135 | 136 | ###### 一致性哈希算法实现负载均衡 137 | 138 | ![img](./images/930904ca-0525-41af-b5e5-314fd377dfce.png) 139 | 140 | ![img](./images/398cdbe8-1006-48b3-bba6-b73768c02aca.png) 141 | 142 | 参考资料: 143 | 144 | https://zhuanlan.zhihu.com/p/161277955 145 | 146 | https://zhuanlan.zhihu.com/p/675020068 147 | 148 | [什么是一致性哈希](https://www.xiaolincoding.com/os/8_network_system/hash.html#如何通过虚拟节点提高均衡度) 149 | 150 | ## Kitex && Hertz 151 | 152 | https://cloudwego.cn/docs/kitex/ 153 | 154 | https://cloudwego.cn/docs/hertz/ 155 | 156 | https://github.com/cloudwego/kitex-examples 157 | 158 | https://github.com/cloudwego/hertz-examples 159 | 160 | [袁神去年的教程(缺少时效性,仅供参考)](https://juejin.cn/post/7216255521372110909) 161 | 162 | 163 | 164 | we重邮代码走读 -------------------------------------------------------------------------------- /lesson15/images/1280X1280 (1).PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson15/images/1280X1280 (1).PNG -------------------------------------------------------------------------------- /lesson15/images/1280X1280.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson15/images/1280X1280.PNG -------------------------------------------------------------------------------- /lesson15/images/398cdbe8-1006-48b3-bba6-b73768c02aca.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson15/images/398cdbe8-1006-48b3-bba6-b73768c02aca.png -------------------------------------------------------------------------------- /lesson15/images/4e760b81-7bfb-437a-83f7-c054ea6b414c.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson15/images/4e760b81-7bfb-437a-83f7-c054ea6b414c.webp -------------------------------------------------------------------------------- /lesson15/images/7f37cdb4-faa1-4341-9daa-9f24646f4443.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson15/images/7f37cdb4-faa1-4341-9daa-9f24646f4443.png -------------------------------------------------------------------------------- /lesson15/images/930904ca-0525-41af-b5e5-314fd377dfce.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson15/images/930904ca-0525-41af-b5e5-314fd377dfce.png -------------------------------------------------------------------------------- /lesson15/images/af08e7a2-c965-4084-a417-ada9ee07d899.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson15/images/af08e7a2-c965-4084-a417-ada9ee07d899.webp -------------------------------------------------------------------------------- /lesson15/images/caec8684-c117-4521-a6bd-a124a795e819.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson15/images/caec8684-c117-4521-a6bd-a124a795e819.webp -------------------------------------------------------------------------------- /lesson15/images/d0a32a7c-8b86-4083-a156-4570db6f3018.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson15/images/d0a32a7c-8b86-4083-a156-4570db6f3018.png -------------------------------------------------------------------------------- /lesson15/images/dfbb50b0-6a41-4045-9e47-6a77ffc2d5b3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson15/images/dfbb50b0-6a41-4045-9e47-6a77ffc2d5b3.png -------------------------------------------------------------------------------- /lesson15/images/java_error_in_idea.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson15/images/java_error_in_idea.png -------------------------------------------------------------------------------- /lesson16/README.md: -------------------------------------------------------------------------------- 1 | # 第十六节课 2 | 3 | ### 负载均衡 4 | 5 | --- 6 | 7 | 负载均衡(Load Balance,简称 LB)是高并发、高可用系统必不可少的关键组件,目标是 尽力将网络流量平均分发到多个服务器上,以提高系统整体的响应速度和可用性。 8 | 9 | **负载均衡的主要作用如下:** 10 | 11 | **高并发:**负载均衡通过算法调整负载,尽力均匀的分配应用集群中各节点的工作量,以此提高应用集群的并发处理能力(吞吐量)。 12 | 13 | **伸缩性:**添加或减少服务器数量,然后由负载均衡进行分发控制。这使得应用集群具备伸缩性。 14 | 15 | **高可用:**负载均衡器可以监控候选服务器,当服务器不可用时,自动跳过,将请求分发给可用的服务器。这使得应用集群具备高可用的特性。 16 | 17 | 18 | 19 | ### 反向代理 20 | 21 | --- 22 | 23 | 反向代理(Reverse Proxy)方式是指以 代理服务器 来接受网络请求,然后 将请求转发给内网中的服务器,并将从内网中的服务器上得到的结果返回给网络请求的客户端。 24 | 25 | 与反向代理相对的就是正向代理。 26 | 27 | 正向代理:发生在 **客户端**,是由用户主动发起的。vpn软件就是典型的正向代理,客户端通过主动访问代理服务器,让代理服务器获得需要的外网数据,然后转发回客户端。 28 | 29 | 反向代理:发生在 **服务端**,用户不知道代理的存在。 30 | 31 | nginx就是反向代理的一个主流产品。 32 | 33 | 34 | 35 | ### 限流 熔断 降级 36 | 37 | --- 38 | 39 | 在分布式系统中,**熔断**、**限流**和**服务降级**是关键的容错设计模式,用于保护系统免受突发流量、故障或性能问题的影响。 40 | 41 | 让我们深入了解这些概念: 42 | 43 | 1. **限流**: 44 | 45 | - 当系统的处理能力无法应对外部请求的突发流量时,为了避免系统崩溃,必须采取限流措施。 46 | - 限流指标可以是: 47 | - **TPS**(每秒事务数):按事务完成数量来限制流量。 48 | - **HPS**(每秒请求数):服务端每秒收到的客户端请求数量。 49 | - **QPS**(每秒查询请求数):服务端每秒响应的客户端查询数量。 50 | - 常用的限流方法包括流量计数器、滑动时间窗口、漏桶算法和令牌桶算法。 51 | 52 | 2. **熔断**: 53 | 54 | - 当流量过大或下游服务出现问题时,熔断会自动断开与下游服务的交互,以避免影响整个系统。 55 | - 熔断可以自我恢复,通过自我诊断下游系统是否已修复或上游流量是否减少至正常水平来判断。 56 | 57 | 3. **服务降级**: 58 | 59 | - 降级是从系统内部的平级服务或业务维度考虑,用于保护其他正常使用的功能。 60 | 61 | - 当服务出现问题或影响核心流程性能时,需要暂时屏蔽掉,待高峰或问题解决后再打开。 62 | 63 | 64 | 65 | > 3者的关系这样理解: 66 | > 拿下棋比喻: 67 | > 限流: 相当于尽量避免同时和两三个人同时下 68 | > 熔断:相当于你的一颗卒被围死了,就不要利用其它棋去救它了,弃卒保帅,否则救他的棋也可能被拖死 69 | > 降级:相当于尽量不要走用处不大的棋了,浪费走棋机会(资源),使已经过河的棋有更多的走棋机会(资源)发挥最大作用 70 | 71 | 72 | 73 | kitex中使用 [负载均衡](https://www.cloudwego.io/zh/docs/kitex/tutorials/service-governance/loadbalance/) [熔断](https://www.cloudwego.io/zh/docs/kitex/tutorials/service-governance/circuitbreaker/) [限流](https://www.cloudwego.io/zh/docs/kitex/tutorials/service-governance/limiting/) 74 | 75 | 76 | 77 | **Sentinel** 是一款面向分布式服务架构的轻量级流量控制产品 78 | 79 | Sentinel 的主要特点: 80 | 81 | 1. **流量控制**:Sentinel 可以限制外部请求的流量,防止系统因突发流量而崩溃。 82 | 2. **实时监控**:它提供实时监控功能,您可以在控制台中查看接入应用的运行情况。 83 | 3. **开源生态**:Sentinel 可以与其他开源框架/库(如 Spring Cloud、Dubbo、gRPC)整合。 84 | 85 | [sentinel文档](https://sentinelguard.io/zh-cn/docs/golang/quick-start.html) 86 | 87 | 88 | 89 | ### Nginx 90 | 91 | --- 92 | 93 | Nginx是一个高性能、开源的HTTP服务器和反向代理服务器。它可以作为一个独立的Web服务器,也可以作为其他Web服务器的反向代理。Nginx以其出色的性能和高度的可扩展性而闻名,是许多高流量网站的首选服务器。 94 | 95 | Nginx的特点包括: 96 | 97 | - 高性能:Nginx采用事件驱动的方式处理请求,可以处理大量的并发请求。 98 | - 可扩展性:Nginx可以通过添加模块来扩展其功能,支持各种第三方模块。 99 | - 反向代理:Nginx可以作为反向代理服务器,可以隐藏真实服务器的地址,从而提高网络安全性。 100 | - 负载均衡:Nginx可以实现负载均衡,可以将请求分配到多个服务器上,从而提高网站的性能和可靠性。 101 | 102 | Nginx是一个功能强大而灵活的服务器,适合用于构建高性能、可扩展的Web应用程序。 103 | 104 | 在Nginx中实现反向代理非常简单。只需要在Nginx配置文件中添加一个`proxy_pass`指令即可。例如,要将所有`/api`请求代理到`http://localhost:3000`,可以使用以下配置: 105 | 106 | ``` 107 | location /api { 108 | proxy_pass ; 109 | } 110 | ``` 111 | 112 | 113 | 114 | 这将使Nginx将所有`/api`请求代理到`http://localhost:3000`。在这个例子中,Nginx充当了客户端和服务器之间的中转站,将所有请求代理到真实的服务器上,然后将响应返回给客户端。 115 | 116 | 除了`proxy_pass`指令之外,Nginx还提供了许多其他的反向代理指令,包括: 117 | 118 | - `proxy_set_header`:设置HTTP请求头。 119 | - `proxy_redirect`:重定向代理请求。 120 | - `proxy_cache`:缓存代理请求的响应。 121 | - `proxy_connect_timeout`:设置代理连接超时时间。 122 | 123 | 通过使用这些指令,可以轻松地配置Nginx作为反向代理服务器,并提供高性能、可扩展的Web应用程序。 124 | 125 | 126 | 127 | ### Traefik 128 | 129 | --- 130 | 131 | Traefik是一款现代的、基于容器的反向代理和负载均衡器。它可以自动发现容器、自动配置路由和HTTPS证书,并提供了许多其他的高级功能,如动态配置、健康检查和访问日志记录等。 132 | 133 | Traefik的特点包括: 134 | 135 | - 自动化:Traefik可以自动发现容器,并自动配置路由和HTTPS证书。它还可以自动调整流量,将请求分配到最少的容器上,并在容器出现问题时自动将流量转移到健康的容器上。 136 | - 动态配置:Traefik支持动态配置,可以通过REST API或文件变更自动配置路由和负载均衡策略。它还支持多种后端服务,如Docker、Kubernetes、Mesos、Consul等。 137 | - 高可用性:Traefik可以实现高可用性,可以将请求分配到多个Traefik实例上,从而实现负载均衡和故障转移。 138 | - 访问日志记录:Traefik可以记录访问日志,并将其发送到各种日志记录服务中。 139 | 140 | Traefik的配置文件非常简单,可以使用以下格式: 141 | 142 | ``` 143 | # traefik.toml 144 | [entryPoints] 145 | [entryPoints.http] 146 | address = ":80" 147 | 148 | [api] 149 | dashboard = true 150 | 151 | [docker] 152 | endpoint = "unix:///var/run/docker.sock" 153 | domain = "example.com" 154 | ``` 155 | 156 | 157 | 158 | 这将启用Traefik的API和仪表板,并配置Traefik使用Docker作为后端服务。 159 | 160 | 通过使用Traefik,可以轻松地实现反向代理和负载均衡,并提供高可用性、动态配置和访问日志记录等高级功能。 161 | 162 | [Traefik文档](https://www.traefik.tech/) 163 | 164 | 165 | 166 | ### Kong 167 | 168 | --- 169 | 170 | Kong是一个高性能的API网关,它可以管理微服务架构中的API通信。 171 | 172 | - **核心组件**:Kong的主要组件包括Kong Server(基于nginx的服务器,用来接收API请求)、数据库(Apache Cassandra或PostgreSQL,用来存储操作数据)以及Kong dashboard(官方推荐的UI管理工具)。 173 | - **插件系统**:Kong通过插件来扩展其功能,这些插件可以在API请求响应循环的生命周期中被执行。插件使用Lua编写,目前已有多个基础功能插件,如HTTP基本认证、密钥认证、CORS、TCP、UDP、文件日志、API请求限流、请求转发以及Nginx监控。 174 | - **可扩展性**:Kong可以通过简单地添加更多的服务器来进行横向扩展,这意味着它可以在较低负载的情况下处理任何请求。 175 | - **模块化**:Kong的模块化设计允许通过添加新的插件进行扩展,这些插件可以通过RESTful Admin API轻松配置。 176 | - **跨基础架构运行**:Kong可以在云或内部网络环境中部署,包括单个或多个数据中心设置,以及public、private或invite-only APIs。 177 | - **集群和高可用性**:Kong支持集群部署,集群中的节点通过gossip协议自动发现其他节点。当通过一个Kong节点的管理API进行变更时,也会通知其他节点。 178 | 179 | [Kong文档](https://docs.konghq.com/) 180 | 181 | 182 | 183 | ### Nginx.conf 184 | 185 | --- 186 | 187 | 删除注释长这样 188 | 189 | ``` 190 | worker_processes 1; 191 | 192 | 193 | events { 194 | worker_connections 1024; 195 | } 196 | 197 | 198 | http { 199 | include mime.types; 200 | default_type application/octet-stream; 201 | 202 | sendfile on; 203 | 204 | keepalive_timeout 65; 205 | 206 | server { 207 | listen 80; 208 | server_name localhost; 209 | 210 | location / { 211 | root html; 212 | index index.html index.htm; 213 | } 214 | 215 | 216 | error_page 500 502 503 504 /50x.html; 217 | location = /50x.html { 218 | root html; 219 | } 220 | } 221 | } 222 | ``` 223 | 224 | 结构 225 | 226 | ``` 227 | ... #全局块 228 | 229 | events { #events块 230 | ... 231 | } 232 | 233 | http #http块 234 | { 235 | ... #http全局块 236 | server #server块 237 | { 238 | ... #server全局块 239 | location [PATTERN] #location块 240 | { 241 | ... 242 | } 243 | location [PATTERN] 244 | { 245 | ... 246 | } 247 | } 248 | server 249 | { 250 | ... 251 | } 252 | ... #http全局块 253 | } 254 | ``` 255 | 256 | 1、**全局块**:配置影响nginx全局的指令。一般有运行nginx服务器的用户组,nginx进程pid存放路径,日志存放路径,配置文件引入,允许生成worker process数等。 257 | 258 | 2、**events块**:配置影响nginx服务器或与用户的网络连接。有每个进程的最大连接数,选取哪种事件驱动模型处理连接请求,是否允许同时接受多个网路连接,开启多个网络连接序列化等。 259 | 260 | 3、**http块**:可以嵌套多个server,配置代理,缓存,日志定义等绝大多数功能和第三方模块的配置。如文件引入,mime-type定义,日志自定义,是否使用sendfile传输文件,连接超时时间,单连接请求数等。 261 | 262 | 4、**server块**:配置虚拟主机的相关参数,一个http中可以有多个server。 263 | 264 | 5、**location块**:配置请求的路由,以及各种页面的处理情况。 265 | 266 | 267 | 268 | ### 作业 269 | 270 | lv1:尝试在自己的项目中实现负载均衡,限流或者熔断 271 | 272 | lv2:在docker中用nginx模拟实现负载均衡,涉及到网络部分可自行学习docker network 273 | 274 | -------------------------------------------------------------------------------- /lesson17/img/agent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson17/img/agent.png -------------------------------------------------------------------------------- /lesson17/img/component.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson17/img/component.png -------------------------------------------------------------------------------- /lesson17/img/gateway.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson17/img/gateway.png -------------------------------------------------------------------------------- /lesson17/img/jaeger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson17/img/jaeger.png -------------------------------------------------------------------------------- /lesson17/img/nocollector.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson17/img/nocollector.png -------------------------------------------------------------------------------- /lesson17/img/otel_strcut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson17/img/otel_strcut.png -------------------------------------------------------------------------------- /lesson17/img/pipeline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson17/img/pipeline.png -------------------------------------------------------------------------------- /lesson17/img/prometheus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson17/img/prometheus.png -------------------------------------------------------------------------------- /lesson17/img/trace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson17/img/trace.png -------------------------------------------------------------------------------- /lesson17/img/trace1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson17/img/trace1.png -------------------------------------------------------------------------------- /lesson17/img/trace2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson17/img/trace2.png -------------------------------------------------------------------------------- /lesson17/img/trace3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson17/img/trace3.png -------------------------------------------------------------------------------- /lesson17/img/trace4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson17/img/trace4.png -------------------------------------------------------------------------------- /lesson17/img/trace5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson17/img/trace5.png -------------------------------------------------------------------------------- /lesson17/img/trace_collector.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson17/img/trace_collector.png -------------------------------------------------------------------------------- /lesson17/后端 Go 第十七次课.md: -------------------------------------------------------------------------------- 1 | ## 可观测性 2 | ### 什么是可观测性? 3 | 4 | 在计算机系统和软件领域,可观测性的含义是:可以从系统和应用对外输出的信息(指标、日志和链路),来帮助我们了解应用程序的内部系统状态和运行情况。 5 | 6 | 但可观测性并不只是简单的数据采集,它更强调对这些数据的**关联和分析**,这能够帮助我们**快速定位**,找到问题的根本原因。当然,最终的目的是**保障系统的可靠性,达到服务质量的目标**。 7 | 8 | ### 为什么需要可观测性? 9 | 10 | 软件架构从单体应用到三层架构(前端 Web,应用 App 层,以及后端的数据库),再到现在的微服务架构,无疑是越来越复杂了。分布式架构具有可扩展性、可维护性、灵活性等多种优点,但是这种部署方案也出现一些问题: 11 | 12 | 1、排查问题难度大,周期长 13 | 2、特定场景难复现 14 | 3、系统性能瓶颈分析较难 15 | 16 | 以前针对简单的系统架构,常用的维护手段还是建立监控,然后制作一些仪表盘来展示各个层面指标的情况,再加上一些告警对各种指标的阈值进行监控和通知,但是现在,不仅排查故障的难度越来越大,错误还有了更多潜在的组合,传统的排障方式越来越没有效率。所以复杂性的增加使得我们越来越有必要为系统和应用构建可观测性。 17 | 18 | 假设现在你运营一个分布式的电商平台下,这个项目的订单处理涉及多个微服务和数据库(用户服务、支付服务、购物车服务等等)。如果订单处理的调用链路上某个服务出现故障或者延迟,导致订单处理失败或者变慢,进而影响客户体验,那么这个问题究竟是出在哪个服务上呢?如果没有良好的观测机制,就无法追踪整个订单处理事务的全过程,因而很难定位到问题。 19 | 20 | 这种情况下,如果通过实时监控系统,就可以追踪到订单的处理流程,识别潜在的瓶颈或故障点,及时进行调整和修复。 21 | 22 | ### 可观测性=指标+日志+链路追踪? 23 | 24 | 指标(metrics),日志(logs)和链路追踪(Tracing)是可观测性的三大支柱,先来看看它们的含义: 25 | #### Metrics 26 | 27 | **是在⼀段时间内测量的数值**。它包括特定属性,例如时间戳、名称、键和值。和⽇志不同,指标在默认情况下是结构化的,这会让查询和优化存储变得更加容易。 28 | 29 | 例如:2022/05/20 12:48:22,CPU usage user,23.87%,它就表示 CPU 运行在用户态的时间占比在这一刻为 23.87%。 30 | #### Logs 31 | 32 | **日志:是对特定时间发生的事件的文本记录**。日志一般是非结构化字符串,会在程序执行期间被写入磁盘。每个请求会产生一行或者多行的日志,每个日志行可能包含 1-5 个维度的有用数据(例如客户端 IP,时间戳,调用方法,响应码等等)。当系统出现问题时,⽇志通常也是排错⾸先查看的地⽅。 33 | #### Traces 34 | 35 | **有时候也被称为分布式追踪(Distributed Tracing),表示请求通过分布式系统的端到端的路径**。当请求通过主机系统时, 它执⾏的每个操作被称为“跨度”(Span)。一个 Trace 是 Spans 的有向无环图。链路追踪一般会通过一个可视化的瀑布图展现出来。瀑布图展示了用于调试的请求的每个阶段,以及每个部分的开始时间和持续时长。 36 | 37 | 举个分布式调用的例子:客户端发起请求,请求首先到达负载均衡器,经过认证服务、系统服务,然后请求资源,最终返回结果。 38 | 39 | ![trace](img/trace.png) 40 | ##### Trace 实现的基本原理 41 | 42 | 想要实现调用链,就要为**每次调用做个标识**,然后**将服务按标识大小排列**,可以更清晰地看出调用顺序,我们暂且将该标识命名为 spanid 43 | 44 | ![trace1](img/trace1.png) 45 | 46 | 实际场景中,我们需要知道某次请求调用的情况,所以只有spanid还不够,得为**每次请求做个唯一标识**,这样才能根据标识查出本次请求调用的所有服务,而这个标识我们命名为 traceid。 47 | 48 | ![trace2](img/trace2.png) 49 | 50 | 现在根据spanid可以轻易地知道被调用服务的先后顺序,但无法体现调用的层级关系,正如下图所示,多个服务可能是逐级调用的链条,也可能是同时被同一个服务调用。例如: 51 | 52 | ![trace3](img/trace3.png) 53 | 54 | 所以应该每次都记录下是谁调用的,我们用parentid作为这个标识的名字。 55 | 56 | ![trace4](img/trace4.png) 57 | 58 | 到现在,已经知道调用顺序和层级关系了,但是接口出现问题后,还是不能找到出问题的环节,如果某个服务有问题,那个被调用执行的服务一定耗时很长,要想**计算出耗时**,上述的三个标识还不够,还需要加上**时间戳**,时间戳可以更精细一点,精确到微秒级。只记录发起调用时的时间戳还算不出耗时,要记录下服务**返回时的时间戳**,**才能算出时间差** 59 | 60 | ![trace5](img/trace5.png) 61 | 62 | 其实 span 除了上面说的这几个必选参数外,还可以记录一些其他信息,比如发起调用服务名称,被调用服务名称、返回结果、IP 等等。但是这些信息是在 span 内自己上报给 Collector 或者 Backend Server,而不会在链路上传递,在链路上传递的主要是 trace_id 和 span_id 等用于在跨度间建立关系的信息(也就是 [Span Context](https://opentelemetry.io/docs/concepts/signals/traces/#span-context))。这样子,Collector 或者 Backend Server 就有了一个链路的很多 span,而这些 span 都有 span_id、trace_id,于是就可以通过这些信息构建出一个完整的调用链了。 63 | 64 | ![trace_collector](img/trace_collector.png) 65 | 66 | 然而,仅仅是收集这些数据类并不能保证系统的可观测性,尤其是当你彼此独⽴地使⽤它们时。**从根本上来说,指标、日志和链路追踪只是数据类型,与可观测性无关。** 67 | 68 | 可观测性应该是我们构建的系统的一种属性,就像可用性、高可用性和稳定性这些一样,能够帮助我们更好地理解和解释系统当前所处的任何状态,无论这种状态或者问题是否在之前出现过。这就要求我们的收集的数据支持高基数(值的唯一性高)和高维度(键的数量多) 69 | 70 | ## OpenTelemetry 71 | 72 | ### 什么是 OpenTelemetry? 73 | 74 | OpenTelemetry 简称 OTel, 是 CNCF ( Cloud Native Computing Foundation,云原生计算基金会,是一个开源软件基金会,致力于云原生技术的普及和可持续发展) 的一个可观测性项目。 75 | 76 | OpenTelemetry 旨在提供可观测性领域的标准化方案,解决遥测数据的数据建模、采集、处理、导出等标准化问题,并能够将这些数据发送到你选择的后端(开源或商业均可)。这样你就可以建立不被供应商锁定的可观测,灵活地更改后端,不需要再次更改代码,重新进行插桩(Instrument)了。 77 | 78 | ### OpenTelemetry 的简要历史 79 | 80 | 在 OpenTelemetry 出现之前,还出现过 OpenTracing 和 OpenCensus 两套标准。目前在应用性能监控有 Jaeger、Pinpoint、Zipkin 等多个开源产品,商业玩家也有很多,可谓竞争非常激烈。然而,这也带来了一个问题,那就是每一家都有一套自己的数据采集标准和 SDK,实现上各不相同,很难实现厂商或者技术中立。 81 | 82 | OpenTracing 制定了一套与平台和厂商无关的协议标准,让开发人员能够方便地添加或更换底层 APM 的实现。 83 | 84 | 另一套标准 OpenCensus 是谷歌发起的,它和 OpenTracing 最大的不同之处在于,除了链路追踪,它还把指标也包括进来。这样就可以在 OpenCensus 上做基础的指标监控了。除此之外相较于 OpenTracing 制定规范,OpenCensus 不仅制定规范,还包含了 Agent 和 Collector。 85 | 86 | OpenTracing 和 OpenCensus,这两套框架都有很多追随者,而且二者都想统一对方。但是从功能和特性上来看,它们各有优缺点,半斤八两。 87 | 88 | 于是就有了 OpenTelemetry,OpenTelemetry 可以同时兼容 OpenTracing 和 OpenCensus,开发人员再也无需在 OpenTracing 和 OpenCensus 之间进行选择。OpenTelemetry 提供一个统一的集合。 89 | 90 | ### OpenTelemetry 的核心组件 91 | 92 | 93 | ![component](img/component.png) 94 | 95 | >Collector 收集器 96 | 97 | OpenTelemetry 收集器是一个独立于供应商的代理,可接收、处理和导出遥测数据。它支持以多种格式接收遥测数据,以及在导出前对遥测数据进行处理和筛选。 98 | 99 | >Language SDKs 语言 SDK 100 | 101 | OpenTelemetry 语言 SDK 允许您使用 OpenTelemetry API 以某种语言生成遥测数据并将数据导出到后端。 102 | 103 | >Instrument libraries 插桩库 104 | 105 | OpenTelemetry 支持一系列广泛的组件,这些组件能够针对所支持的语言使用热门的库和框架生成相关的遥测数据,例如HTTP客户端、数据库驱动程序、消息队列等。 106 | 107 | >Exporter 导出器 108 | 109 | 导出器能够将插桩与您的后端配置分离开,由于无需更改插桩设定,所以能更轻松地更改后端。导出器还能将遥测数据上传到不止一个后端。 110 | 111 | >OpenTelemetry Protocol(OTLP) OpenTelemetry 协议 112 | 113 | SDK 和 Collector 传递遥测信号使用的传输协议,用于将数据导出到后端或其他收集器。OTLP 可以使用 gRPC (OTLP/gRPC) 或 HTTP (OTLP/HTTP)。虽然在 Opentelemetry 的项目中组件支持了 Zipkin v2 或 Jaeger Thrift 的协议格式的实现,但是都是以第三方贡献库的形式提供的。只有 OTLP是 Opentelemetry 官方原生支持的格式。 114 | 115 | 1. `OTLP/HTTP` 使用 `HTTP POST` 请求将遥测数据从客户端发送到服务器。实现可以使用HTTP/1.1或HTTP/2传输,请求正文是可以是用 Protobuf 编码(proto3 标准)或者 JSON 格式(proto3 标准定义的 JSON Mapping)的 `ExportXXXServiceRequest` 消息,HTTP 默认端口为 4318,路径默认配置: 116 | - Trace:`v1/traces` 117 | - Metric:`v1/metrics` 118 | - Log:`v1/logs` 119 | 2. `OTLP/gRPC` 建立底层GRPC传输后,客户端开始使用`Export*ServiceRequest`消息,发送遥测数据,gRPC 默认端口为 4317 120 | ### OpenTelemetry Collector 121 | 122 | OpenTelemetry Collector 是OpenTelemetry 项目中的一个代理软件,作为遥测数据的中转站,能够对遥测数据做一些预处理的逻辑 123 | 124 | #### 部署模式 125 | 126 | 1. **不使用OpenTelemetry Collector**:OpenTelemetry Collector 并不是必须的,我们可以直接使用OpenTelemetry 客户端SDK发送遥测数据到监控组件中,比如将 trace 数据发送到 jaeger,发送 metric 数据到 prometheus 127 | 128 | ![nocollector](img/nocollector.png) 129 | 130 | 2. **Agent 模式**:在应用程序和后端监控组件(jaeger、prometheus)之间部署上OpenTelemetry Collector,OpenTelemetry Collector 和应用程序之间传输遥测数据(Collector 支持几个主流的开源协议),存在的问题是如果应用程序产生的遥测数据太多,一个OpenTelemetry Collector 已经不能满足快速处理数据的要求 131 | 132 | ![agent](img/agent.png) 133 | 134 | 3. **Gateway 模式**:通过在多个OpenTelemetry Collector 前面部署一个拥有负载均衡功能的OpenTelemetry Collector 来让分发发往整个集群的遥测数据。 135 | 136 | ![gateway](img/gateway.png) 137 | 138 | #### Pipeline 139 | 140 | Collector 采用 Pipeline 方式来接收、处理和导出数据,每个 Collector 可配置一个或多个 Pipeline,每个 pipeline 包含如下组件: 141 | - receiver 142 | - processor 143 | - exporter 144 | 145 | 每个 receiver 可能被多个 pipeline 共用,多个 pipeline 也可能包含同一个 exporter 146 | 147 | >pipeline 148 | 149 | 一个pipeline定义了一条数据流动的路径:从 Collector 数据接收开始,然后进一步的处理或修改,到最后通过exporter导出。 150 | 151 | pipeline能够处理3种类型数据:trace、metric和logs。数据类型是需要pipeline配置的一个属性。在pipeline中receiver、processor和exporter必须支持某个特定的数据类型,一个 pipeline 可以表示为: 152 | 153 | ![pipeline](img/pipeline.png) 154 | 155 | 一个经典的 pipeline 配置如下: 156 | 157 | ```yaml 158 | service: 159 | pipelines: # section that can contain multiple subsections, one per pipeline 160 | traces: # type of the pipeline 161 | receivers: [otlp, jaeger, zipkin] 162 | processors: [memory_limiter, batch] 163 | exporters: [otlp, jaeger, zipkin] 164 | ``` 165 | 166 | 这个例子定义了一个"trace"类型数据的pipeline,包含3个receiver、2个processor和3个exporter 167 | 168 | >receiver 169 | 170 | receiver 监听某个网络端口并且接收观测数据。通常会配置一个 receiver 将接收到的数据传送到一个 pipeline 中,当然也可能是同一个 receiver 将接收到的数据传送到多个 pipeline 中。 171 | 172 | 如上面的 `otlp` receiver 示例配置如下: 173 | 174 | ```yaml 175 | receivers: 176 | otlp: 177 | protocols: 178 | grpc: 179 | endpoint: localhost:4317 180 | ``` 181 | 182 | >exporter 183 | 184 | exporter 通常将收到数据发送到某个网络上地址。允许一个 pipeline 中配置多个同类型的 exporter 185 | 186 | 如上面的 `jaeger` exporter 的示例配置如下 187 | 188 | ```yaml 189 | exporters: 190 | jaeger: 191 | protocols: 192 | grpc: 193 | endpoint: localhost:14250 194 | ``` 195 | 196 | >processor 197 | 198 | 一个 pipeline 可以包含多个**串联**的 processor。 199 | 200 | processor能够在转发数据之前对其进行转换(例如增加或删除spans中的属性),也可以丢弃数据,也可以生成新数据 201 | 202 | 如上面的 `batch` processor 的示例配置: 203 | 204 | ```yaml 205 | processors: 206 | batch: 207 | send_batch_size: 10000 208 | timeout: 10s 209 | ``` 210 | 211 | 这段配置就描述了一个批处理 processor 的配置 212 | ## Jaeger 213 | 214 | ### 简介 215 | 216 | ![jaeger](img/jaeger.png) 217 | 218 | [Jaeger](https://www.jaegertracing.io/)  是受到 ​ ​Dapper​​​ 和 ​ ​OpenZipkin​​​ 启发的由 ​ ​Uber Technologies​​ 作为开源发布的分布式跟踪系统,兼容 OpenTracing 以及 Zipkin 追踪格式,目前已成为 CNCF 基金会的开源项目。其前端采用React语言实现,后端采用GO语言实现,适用于进行链路追踪,分布式跟踪消息传递,分布式事务监控、问题分析、服务依赖性分析、性能优化等场景。 219 | 220 | ### 快速开始 221 | 222 | > all-in-one Docker 镜像包含 **Jaeger UI**、**jaeger-collector**、**jaeger-query** 和 **jaeger-agent**,以及一个内存存储组件。适用于快速体验 Jaeger ,由于所有追踪数据存储在内存中,不适用于生产环境 223 | 224 | ```shell 225 | docker run --rm --name jaeger \ 226 | -e COLLECTOR_ZIPKIN_HOST_PORT=:9411 \ 227 | -p 6831:6831/udp \ 228 | -p 6832:6832/udp \ 229 | -p 5778:5778 \ 230 | -p 16686:16686 \ 231 | -p 4317:4317 \ 232 | -p 4318:4318 \ 233 | -p 14250:14250 \ 234 | -p 14268:14268 \ 235 | -p 14269:14269 \ 236 | -p 9411:9411 \ 237 | jaegertracing/all-in-one:1.56 238 | ``` 239 | 240 | 容器启动后,使用浏览器打开 [http://localhost:16686](http://localhost:16686/) 即可访问 Jaeger UI。 241 | 242 | all-in-one 端口文档如下: 243 | 244 | |Port|Protocol|Component|Function| 245 | |---|---|---|---| 246 | |6831|UDP|agent|accept `jaeger.thrift` over Thrift-compact protocol (used by most SDKs)| 247 | |6832|UDP|agent|accept `jaeger.thrift` over Thrift-binary protocol (used by Node.js SDK)| 248 | |5775|UDP|agent|(deprecated) accept `zipkin.thrift` over compact Thrift protocol (used by legacy clients only)| 249 | |5778|HTTP|agent|serve configs (sampling, etc.)| 250 | ||||| 251 | |16686|HTTP|query|serve frontend| 252 | ||||| 253 | |4317|HTTP|collector|accept OpenTelemetry Protocol (OTLP) over gRPC| 254 | |4318|HTTP|collector|accept OpenTelemetry Protocol (OTLP) over HTTP| 255 | |14268|HTTP|collector|accept `jaeger.thrift` directly from clients| 256 | |14250|HTTP|collector|accept `model.proto`| 257 | |9411|HTTP|collector|Zipkin compatible endpoint (optional)| 258 | ## Prometheus 259 | ### 简介 260 | 261 | ![prometheus](img/prometheus.png) 262 | 263 | [Prometheus](https://github.com/prometheus) 是由前 Google 工程师从 2012 年开始在 Soundcloud 以开源软件的形式进行研发的系统监控和告警工具包,自此以后,许多公司和组织都采用了 Prometheus 作为监控告警工具。Prometheus 的开发者和用户社区非常活跃,它现在是一个独立的开源项目,可以独立于任何公司进行维护。 264 | 265 | Prometheus 的主要优势有: 266 | 267 | - 由指标名称和和键/值对标签标识的时间序列数据组成的多维数据模型。 268 | - 强大的[查询语言 PromQL](https://github.com/yangchuansheng/prometheus-handbook/tree/c6e1e12588ec63c20345090368b37654ef30922a/4-prometheus/basics.html)。 269 | - 不依赖分布式存储;单个服务节点具有自治能力。 270 | - 时间序列数据是服务端通过 HTTP 协议主动拉取获得的。 271 | - 也可以通过中间网关来[推送时间序列数据](https://github.com/yangchuansheng/prometheus-handbook/tree/c6e1e12588ec63c20345090368b37654ef30922a/5-instrumenting/pushing.html)。 272 | - 可以通过静态配置文件或服务发现来获取监控目标。 273 | - 支持多种类型的图表和仪表盘。 274 | 275 | ### 快速上手 276 | 277 | 可以使用 Docker 快速部署 278 | 279 | ```shell 280 | docker run \ 281 | -p 9090:9090 \ 282 | -v /path/to/prometheus.yml:/etc/prometheus/prometheus.yml \ 283 | prom/prometheus 284 | ``` 285 | 286 | 容器启动后,使用浏览器打开 [http://localhost:9090](http://localhost:9090/) 即可访问 Prometheus UI,如果想要添加数据源,需要配置 prometheus.yml 287 | 288 | ## 作业 289 | 290 | **提交地址**: mull1on0910@gmail.com 291 | **提交格式**:第十七次作业-学号-姓名 292 | **截止时间**:下一次上课之前 293 | ### Lv1 294 | 295 | 按照官方文档的[快速上手](https://opentelemetry.opendocs.io/docs/instrumentation/go/getting-started/)教程,自己动手做一下,加深 OpenTelemetry 每个组件的理解 296 | ### Lv2 297 | 298 | 在完成快速上手 rolldice 服务的基础上,添加 exporter,将 trace 发送到 jaeger 和 prometheus,参照官方文档的 [exporter教程](https://opentelemetry.opendocs.io/docs/instrumentation/go/exporters/) 299 | 300 | 或者可以部署 OpenTelemetry Collector,通过 Collector 发送到 jaeger 和 prometheus 301 | ### Lv3 302 | 303 | 了解一下 ELK,最好可以自己尝试部署一个 304 | 305 | ## 参考链接 306 | 307 | 1. [什么是 OpenTelemetry](https://www.elastic.co/cn/what-is/opentelemetry) 308 | 2. [深入浅出可观测性](https://learn.lianglianglee.com/%e4%b8%93%e6%a0%8f/%e6%b7%b1%e5%85%a5%e6%b5%85%e5%87%ba%e5%8f%af%e8%a7%82%e6%b5%8b%e6%80%a7/00%20%e5%bc%80%e7%af%87%e8%af%8d%20%e5%8f%af%e8%a7%82%e6%b5%8b%e6%80%a7%ef%bc%8c%e8%ae%a9%e5%bc%80%e5%8f%91%e5%92%8c%e7%bb%b4%e6%8a%a4%e7%b3%bb%e7%bb%9f%e7%9a%84%e4%bd%a0%e6%af%8f%e6%99%9a%e9%83%bd%e8%83%bd%e7%9d%a1%e4%b8%aa%e5%a5%bd%e8%a7%89%ef%bc%81.md) 309 | 3. [李文周-Go OpenTelemetry快速指南](https://www.liwenzhou.com/posts/Go/openTelemetry-go/) 310 | 4. [OpenTelemetry Collector 简介](https://blog.csdn.net/u_geek/article/details/128389731) 311 | 5. [OpenTelemetry Collector 部署模式](https://www.cnblogs.com/hobbybear/p/18006757) 312 | 6. [微服务之链路追踪原理](https://developer.aliyun.com/article/1317806) 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | -------------------------------------------------------------------------------- /lesson18/images/cap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson18/images/cap.png -------------------------------------------------------------------------------- /lesson18/images/leader_turn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson18/images/leader_turn.png -------------------------------------------------------------------------------- /lesson18/images/snowflake.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson18/images/snowflake.png -------------------------------------------------------------------------------- /lesson18/images/tcc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LanshanTeam/Courseware-Backend-Go-2023/0d8692d7379426618f6e60f53da71a5bcd814685/lesson18/images/tcc.png --------------------------------------------------------------------------------