燕舞蝶 發表於 2022-1-19 13:46:00

带你十天轻松搞定 Go 微服务系列(一)

<p>本文开始,我们会出一个系列文章跟大家详细展示一个 go-zero 微服务示例,整个系列分十篇文章,目录结构如下:</p>
<ol>
<li><strong>环境搭建(本文)</strong></li>
<li>服务拆分</li>
<li>用户服务</li>
<li>产品服务</li>
<li>订单服务</li>
<li>支付服务</li>
<li>RPC 服务 Auth 验证</li>
<li>服务监控</li>
<li>链路追踪</li>
<li>分布式事务</li>
</ol>
<p>期望通过本系列带你在本机利用 Docker 环境利用 go-zero 快速开发一个商城系统,让你快速上手微服务。</p>
<p>完整示例代码:https://github.com/nivin-studio/go-zero-mall</p>
<h2 id="1-环境要求">1 环境要求</h2>
<ul>
<li><code>Golang</code> 1.15+</li>
<li><code>Etcd</code></li>
<li><code>Redis</code></li>
<li><code>Mysql</code></li>
<li><code>Prometheus</code></li>
<li><code>Grafana</code></li>
<li><code>Jaeger</code></li>
<li><code>DTM</code></li>
</ul>
<h2 id="2-docker-本地开发环境搭建">2 <code>Docker</code> 本地开发环境搭建</h2>
<p>为了方便开发调试,我们使用 <code>Docker</code> 构建本地开发环境。<code>Windows</code> 和 <code>macOS</code> 系统可下载 <code>Docker Desktop</code> 安装使用,具体下载安装方法可自行搜索相关教程。</p>
<p>这里我们使用 <code>Docker Compose</code> 来编排管理我们的容器,创建如下目录:</p>
<pre><code>gonivinck
├── dtm                   # DTM 分布式事务管理器
│   ├── config.yml      # DTM 配置文件
│   └── Dockerfile
├── etcd                  # Etcd 服务注册发现
│   └── Dockerfile
├── golang                # Golang 运行环境
│   └── Dockerfile
├── grafana               # Grafana 可视化数据监控
│   └── Dockerfile
├── jaeger                # Jaeger 链路追踪
│   └── Dockerfile
├── mysql               # Mysql 服务
│   └── Dockerfile
├── mysql-manage          # Mysql 可视化管理
│   └── Dockerfile
├── prometheus            # Prometheus 服务监控
│   ├── Dockerfile
│   └── prometheus.yml    # Prometheus 配置文件
├── redis               # Redis 服务
│   └── Dockerfile
├── redis-manage          # Redis 可视化管理
│   └── Dockerfile
├── .env                  # env 配置
└── docker-compose.yml
</code></pre>
<h3 id="21-编写-dockerfile">2.1 编写 <code>Dockerfile</code></h3>
<p>在 <code>go-zero</code> 的微服务中采用 <code>grpc</code> 进行服务间的通信,而 <code>grpc</code> 的编写就需要用到 <code>protoc</code> 和翻译成 <code>go</code> 语言 <code>rpc stub</code> 代码的插件 <code>protoc-gen-go</code>。</p>
<p>为了提高开发效率,减少代码的出错率,缩短业务开发的工作量,<code>go-zero</code> 还提供了 <code>goctl</code> 代码生成工具。</p>
<p>因此,我们需要将 <code>protoc</code>, <code>protoc-gen-go</code>, <code>goctl</code>, 给提前安装到 <code>golang</code> 的容器中,以便后续使用。</p>
<p>所以 <code>golang</code> 容器的 <code>Dockerfile</code> 代码如下:</p>
<pre><code class="language-Dockerfile">FROM golang:1.17

LABEL maintainer="Ving &lt;ving@nivin.cn&gt;"

ENV GOPROXY https://goproxy.cn,direct

# 安装必要的软件包和依赖包
USER root
RUN sed -i 's/deb.debian.org/mirrors.tuna.tsinghua.edu.cn/' /etc/apt/sources.list &amp;&amp; \
    sed -i 's/security.debian.org/mirrors.tuna.tsinghua.edu.cn/' /etc/apt/sources.list &amp;&amp; \
    sed -i 's/security-cdn.debian.org/mirrors.tuna.tsinghua.edu.cn/' /etc/apt/sources.list &amp;&amp; \
    apt-get update &amp;&amp; \
    apt-get upgrade -y &amp;&amp; \
    apt-get install -y --no-install-recommends \
    curl \
    zip \
    unzip \
    git \
    vim

# 安装 goctl
USER root
RUN GOPROXY=https://goproxy.cn/,direct go install github.com/tal-tech/go-zero/tools/goctl@cli

# 安装 protoc
USER root
RUN curl -L -o /tmp/protoc.zip https://github.com/protocolbuffers/protobuf/releases/download/v3.19.1/protoc-3.19.1-linux-x86_64.zip &amp;&amp; \
    unzip -d /tmp/protoc /tmp/protoc.zip &amp;&amp; \
    mv /tmp/protoc/bin/protoc $GOPATH/bin

# 安装 protoc-gen-go
USER root
RUN go get -u github.com/golang/protobuf/protoc-gen-go@v1.4.0

# $GOPATH/bin添加到环境变量中
ENV PATH $GOPATH/bin:$PATH

# 清理垃圾
USER root
RUN apt-get clean &amp;&amp; \
    rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* &amp;&amp; \
    rm /var/log/lastlog /var/log/faillog

# 设置工作目录
WORKDIR /usr/src/code

EXPOSE 8000
EXPOSE 8001
EXPOSE 8002
EXPOSE 8003
EXPOSE 9000
EXPOSE 9001
EXPOSE 9002
EXPOSE 9003
</code></pre>
<p>其他服务容器 <code>Dockerfile</code> 无需特殊处理,只要基于现有的镜像即可。</p>
<table>
<thead>
<tr>
<th>服务</th>
<th>基于的镜像</th>
</tr>
</thead>
<tbody>
<tr>
<td>DTM</td>
<td>yedf/dtm</td>
</tr>
<tr>
<td>Etcd</td>
<td>bitnami/etcd</td>
</tr>
<tr>
<td>Mysql</td>
<td>mysql:5.7</td>
</tr>
<tr>
<td>Redis</td>
<td>redis:5.0</td>
</tr>
<tr>
<td>Mysql Manage</td>
<td>phpmyadmin/phpmyadmin</td>
</tr>
<tr>
<td>Redis Manage</td>
<td>erikdubbelboer/phpredisadmin</td>
</tr>
<tr>
<td>Prometheus</td>
<td>bitnami/prometheus</td>
</tr>
<tr>
<td>Grafana</td>
<td>grafana/grafana</td>
</tr>
<tr>
<td>Jaeger</td>
<td>jaegertracing/all-in-one:1.28</td>
</tr>
</tbody>
</table>
<h3 id="22-编写-env-配置文件">2.2 编写 <code>.env</code> 配置文件</h3>
<pre><code># 设置时区
TZ=Asia/Shanghai
# 设置网络模式
NETWORKS_DRIVER=bridge


# PATHS ##########################################
# 宿主机上代码存放的目录路径
CODE_PATH_HOST=./code
# 宿主机上Mysql Reids数据存放的目录路径
DATA_PATH_HOST=./data


# MYSQL ##########################################
# Mysql 服务映射宿主机端口号,可在宿主机127.0.0.1:3306访问
MYSQL_PORT=3306
MYSQL_USERNAME=admin
MYSQL_PASSWORD=123456
MYSQL_ROOT_PASSWORD=123456

# Mysql 可视化管理用户名称,同 MYSQL_USERNAME
MYSQL_MANAGE_USERNAME=admin
# Mysql 可视化管理用户密码,同 MYSQL_PASSWORD
MYSQL_MANAGE_PASSWORD=123456
# Mysql 可视化管理ROOT用户密码,同 MYSQL_ROOT_PASSWORD
MYSQL_MANAGE_ROOT_PASSWORD=123456
# Mysql 服务地址
MYSQL_MANAGE_CONNECT_HOST=mysql
# Mysql 服务端口号
MYSQL_MANAGE_CONNECT_PORT=3306
# Mysql 可视化管理映射宿主机端口号,可在宿主机127.0.0.1:1000访问
MYSQL_MANAGE_PORT=1000


# REDIS ##########################################
# Redis 服务映射宿主机端口号,可在宿主机127.0.0.1:6379访问
REDIS_PORT=6379

# Redis 可视化管理用户名称
REDIS_MANAGE_USERNAME=admin
# Redis 可视化管理用户密码
REDIS_MANAGE_PASSWORD=123456
# Redis 服务地址
REDIS_MANAGE_CONNECT_HOST=redis
# Redis 服务端口号
REDIS_MANAGE_CONNECT_PORT=6379
# Redis 可视化管理映射宿主机端口号,可在宿主机127.0.0.1:2000访问
REDIS_MANAGE_PORT=2000


# ETCD ###########################################
# Etcd 服务映射宿主机端口号,可在宿主机127.0.0.1:2379访问
ETCD_PORT=2379


# PROMETHEUS #####################################
# Prometheus 服务映射宿主机端口号,可在宿主机127.0.0.1:3000访问
PROMETHEUS_PORT=3000


# GRAFANA ########################################
# Grafana 服务映射宿主机端口号,可在宿主机127.0.0.1:4000访问
GRAFANA_PORT=4000


# JAEGER #########################################
# Jaeger 服务映射宿主机端口号,可在宿主机127.0.0.1:5000访问
JAEGER_PORT=5000


# DTM #########################################
# DTM HTTP 协议端口号
DTM_HTTP_PORT=36789
# DTM gRPC 协议端口号
DTM_GRPC_PORT=36790
</code></pre>
<h3 id="23-编写-docker-composeyml-配置文件">2.3 编写 <code>docker-compose.yml</code> 配置文件</h3>
<pre><code class="language-yaml">version: '3.5'
# 网络配置
networks:
backend:
    driver: ${NETWORKS_DRIVER}

# 服务容器配置
services:
golang:                              # 自定义容器名称
    build:
      context: ./golang                  # 指定构建使用的 Dockerfile 文件
    environment:                         # 设置环境变量
      - TZ=${TZ}
    volumes:                           # 设置挂载目录
      - ${CODE_PATH_HOST}:/usr/src/code# 引用 .env 配置中 CODE_PATH_HOST 变量,将宿主机上代码存放的目录挂载到容器中 /usr/src/code 目录
    ports:                               # 设置端口映射
      - "8000:8000"
      - "8001:8001"
      - "8002:8002"
      - "8003:8003"
      - "9000:9000"
      - "9001:9001"
      - "9002:9002"
      - "9003:9003"
    stdin_open: true                     # 打开标准输入,可以接受外部输入
    tty: true
    networks:
      - backend
    restart: always                      # 指定容器退出后的重启策略为始终重启

etcd:                                  # 自定义容器名称
    build:
      context: ./etcd                  # 指定构建使用的 Dockerfile 文件
    environment:
      - TZ=${TZ}
      - ALLOW_NONE_AUTHENTICATION=yes
      - ETCD_ADVERTISE_CLIENT_URLS=http://etcd:2379
    ports:                               # 设置端口映射
      - "${ETCD_PORT}:2379"
    networks:
      - backend
    restart: always

mysql:
    build:
      context: ./mysql
    environment:
      - TZ=${TZ}
      - MYSQL_USER=${MYSQL_USERNAME}                  # 设置 Mysql 用户名称
      - MYSQL_PASSWORD=${MYSQL_PASSWORD}            # 设置 Mysql 用户密码
      - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}    # 设置 Mysql root 用户密码
    volumes:
      - ${DATA_PATH_HOST}/mysql:/var/lib/mysql      # 引用 .env 配置中 DATA_PATH_HOST 变量,将宿主机上存放 Mysql 数据的目录挂载到容器中 /var/lib/mysql 目录
    ports:
      - "${MYSQL_PORT}:3306"                        # 设置容器3306端口映射指定宿主机端口
    networks:
      - backend
    restart: always

redis:
    build:
      context: ./redis
    environment:
      - TZ=${TZ}
    volumes:
      - ${DATA_PATH_HOST}/redis:/data               # 引用 .env 配置中 DATA_PATH_HOST 变量,将宿主机上存放 Redis 数据的目录挂载到容器中 /data 目录
    ports:
      - "${REDIS_PORT}:6379"                        # 设置容器6379端口映射指定宿主机端口
    networks:
      - backend
    restart: always

mysql-manage:
    build:
      context: ./mysql-manage
    environment:
      - TZ=${TZ}
      - PMA_ARBITRARY=1
      - MYSQL_USER=${MYSQL_MANAGE_USERNAME}               # 设置连接的 Mysql 服务用户名称
      - MYSQL_PASSWORD=${MYSQL_MANAGE_PASSWORD}         # 设置连接的 Mysql 服务用户密码
      - MYSQL_ROOT_PASSWORD=${MYSQL_MANAGE_ROOT_PASSWORD} # 设置连接的 Mysql 服务 root 用户密码
      - PMA_HOST=${MYSQL_MANAGE_CONNECT_HOST}             # 设置连接的 Mysql 服务 host,可以是 Mysql 服务容器的名称,也可以是 Mysql 服务容器的 ip 地址
      - PMA_PORT=${MYSQL_MANAGE_CONNECT_PORT}             # 设置连接的 Mysql 服务端口号
    ports:
      - "${MYSQL_MANAGE_PORT}:80"                         # 设置容器80端口映射指定宿主机端口,用于宿主机访问可视化web
    depends_on:                                           # 依赖容器
      - mysql                                             # 在 Mysql 服务容器启动后启动
    networks:
      - backend
    restart: always

redis-manage:
    build:
      context: ./redis-manage
    environment:
      - TZ=${TZ}
      - ADMIN_USER=${REDIS_MANAGE_USERNAME}         # 设置 Redis 可视化管理的用户名称
      - ADMIN_PASS=${REDIS_MANAGE_PASSWORD}         # 设置 Redis 可视化管理的用户密码
      - REDIS_1_HOST=${REDIS_MANAGE_CONNECT_HOST}   # 设置连接的 Redis 服务 host,可以是 Redis 服务容器的名称,也可以是 Redis 服务容器的 ip 地址
      - REDIS_1_PORT=${REDIS_MANAGE_CONNECT_PORT}   # 设置连接的 Redis 服务端口号
    ports:
      - "${REDIS_MANAGE_PORT}:80"                     # 设置容器80端口映射指定宿主机端口,用于宿主机访问可视化web
    depends_on:                                       # 依赖容器
      - redis                                       # 在 Redis 服务容器启动后启动
    networks:
      - backend
    restart: always

prometheus:
    build:
      context: ./prometheus
    environment:
      - TZ=${TZ}
    volumes:
      - ./prometheus/prometheus.yml:/opt/bitnami/prometheus/conf/prometheus.yml# 将 prometheus 配置文件挂载到容器里
    ports:
      - "${PROMETHEUS_PORT}:9090"                     # 设置容器9090端口映射指定宿主机端口,用于宿主机访问可视化web
    networks:
      - backend
    restart: always

grafana:
    build:
      context: ./grafana
    environment:
      - TZ=${TZ}
    ports:
      - "${GRAFANA_PORT}:3000"                        # 设置容器3000端口映射指定宿主机端口,用于宿主机访问可视化web
    networks:
      - backend
    restart: always

jaeger:
    build:
      context: ./jaeger
    environment:
      - TZ=${TZ}
    ports:
      - "${JAEGER_PORT}:16686"                        # 设置容器16686端口映射指定宿主机端口,用于宿主机访问可视化web
    networks:
      - backend
    restart: always

dtm:
    build:
      context: ./dtm
    environment:
      - TZ=${TZ}
    entrypoint:
      - "/app/dtm/dtm"
      - "-c=/app/dtm/configs/config.yaml"
    volumes:
      - ./dtm/config.yml:/app/dtm/configs/config.yaml # 将 dtm 配置文件挂载到容器里
    ports:
      - "${DTM_HTTP_PORT}:36789"
      - "${DTM_GRPC_PORT}:36790"
    networks:
      - backend
    restart: always
</code></pre>
<h3 id="24-构建与运行">2.4 构建与运行</h3>
<ul>
<li>使用 <code>docker-compose</code> 命令来构建和启动运行我们的服务容器,在根目录执行如下命令:</li>
</ul>
<pre><code class="language-bash">$ docker-compose up -d
</code></pre>
<ul>
<li>
<p>容器构建中<br>
<img src="https://oscimg.oschina.net/oscnet/up-04e9c8f915fa80bcfc5b5b29c6980d3d197.png" alt="" loading="lazy"></p>
</li>
<li>
<p>在 <code>Windows</code> 系统容器构建中出现如下图所示,请选择 <code>Share it</code> 这将允许 <code>Windows</code> 的文件目录挂载到容器目录中。<br>
<img src="https://oscimg.oschina.net/oscnet/up-4916417944b4f3f3cd9c3704edb4c54df7b.png" alt="" loading="lazy"></p>
</li>
<li>
<p>容器已启动运行<br>
<img src="https://oscimg.oschina.net/oscnet/up-4916417944b4f3f3cd9c3704edb4c54df7b.png" alt="" loading="lazy"></p>
<p><img src="https://oscimg.oschina.net/oscnet/up-061e446012bb5d869f18590c834c76717ac.png" alt="" loading="lazy"></p>
</li>
</ul>
<h3 id="25-容器说明">2.5 容器说明</h3>
<table>
<thead>
<tr>
<th>容器名称</th>
<th>暴露端口</th>
<th>host地址</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>golang</td>
<td>8000:8000<br>8001:8001<br>8002:8002<br>8003:8003<br>9000:9000<br>9001:9001<br>9002:9002<br>9003:9003</td>
<td>golang</td>
<td>在生产环境中微服务一般都是集群部署,可能一个微服务一台服务器,也可能一个微服务一个容器。为了方便开发调试,我们将在 <code>golang</code> 容器中启动所有微服务,并为它们分配监听不同的端口号以示区分。<br>80:开头的端口号我们将用于 <code>api</code> 服务 <br>90:开头的端口号我们将用于 <code>rpc</code> 服务</td>
</tr>
<tr>
<td>dtm</td>
<td>36789:36789<br>36790:36790</td>
<td>dtm</td>
<td><code>dtm</code> 的 <code>http</code> 协议和 <code>grpc</code> 协议服务端口号,供客户端交互使用。<br>此项目中我们只在 <code>Docker</code> 内部容器之间访问使用,所以也可以不暴露端口号给宿主机</td>
</tr>
<tr>
<td>etcd</td>
<td>2379:2379</td>
<td>etcd</td>
<td><code>Etcd</code> <code>http api</code> 服务端口号,供客户端交互使用。<br>此项目中我们只在 <code>Docker</code> 内部容器之间访问使用,所以也可以不暴露端口号给宿主机</td>
</tr>
<tr>
<td>mysql</td>
<td>3306:3306</td>
<td>mysql</td>
<td><code>Mysql</code> 服务默认端口号,宿主机可通过 <code>127.0.0.1:3306</code> 进行数据库的连接</td>
</tr>
<tr>
<td>redis</td>
<td>6379:6379</td>
<td>redis</td>
<td><code>Redis</code> 服务默认端口号,宿主机可通过 <code>127.0.0.1:6379</code> 进行数据库的连接</td>
</tr>
<tr>
<td>mysql-manage</td>
<td>1000:80</td>
<td>mysql-manage</td>
<td><code>phpMyAdmin</code> <code>web</code> 服务端口号,可以在宿主机 <code>127.0.0.1:1000</code> 访问</td>
</tr>
<tr>
<td>redis-manage</td>
<td>2000:80</td>
<td>redis-manage</td>
<td><code>phpRedisAdmin</code> <code>web</code> 服务端口号,可以在宿主机 <code>127.0.0.1:2000</code> 访问</td>
</tr>
<tr>
<td>prometheus</td>
<td>3000:9090</td>
<td>prometheus</td>
<td><code>Prometheus</code> <code>web</code> 服务端口号,可以在宿主机 <code>127.0.0.1:3000</code> 访问</td>
</tr>
<tr>
<td>grafana</td>
<td>4000:3000</td>
<td>grafana</td>
<td><code>Grafana</code> <code>web</code> 服务端口号,可以在宿主机 <code>127.0.0.1:4000</code> 访问</td>
</tr>
<tr>
<td>jaeger</td>
<td>5000:16686</td>
<td>jaeger</td>
<td><code>Jaeger</code> <code>web</code> 服务端口号,可以在宿主机 <code>127.0.0.1:5000</code> 访问</td>
</tr>
</tbody>
</table>
<h3 id="26-访问验证">2.6 访问验证</h3>
<ul>
<li>
<p><code>Mysql</code> 访问验证<br>
<img src="https://oscimg.oschina.net/oscnet/up-8721f9707f76a9d1f80f83df8498adf5b3d.png" alt="" loading="lazy"></p>
</li>
<li>
<p><code>Redis</code> 访问验证<br>
<img src="https://oscimg.oschina.net/oscnet/up-f1d40e21fccea69d7d7995a6b36208ab574.png" alt="" loading="lazy"></p>
</li>
<li>
<p><code>Prometheus</code> 访问验证<br>
<img src="https://oscimg.oschina.net/oscnet/up-f2f1578e380ca1c49594211dd7a211bce58.png" alt="" loading="lazy"></p>
</li>
<li>
<p><code>Grafana</code> 访问验证<br>
<img src="https://oscimg.oschina.net/oscnet/up-1b73cdc078dcd4688b57df0981f689bb2dd.png" alt="" loading="lazy"></p>
</li>
<li>
<p><code>Jaeger</code> 访问验证<br>
<img src="https://oscimg.oschina.net/oscnet/up-829844abdb68d673dc1d6fda690e82d8661.png" alt="" loading="lazy"></p>
</li>
</ul>
<h2 id="项目地址">项目地址</h2>
<p>https://github.com/zeromicro/go-zero</p>
<p>欢迎使用 <code>go-zero</code> 并 <strong>star</strong> 支持我们!</p>
<h2 id="微信交流群">微信交流群</h2>
<p>关注『<strong>微服务实践</strong>』公众号并点击 <strong>交流群</strong> 获取社区群二维码。</p><br><br>
来源:https://www.cnblogs.com/kevinwan/p/15821971.html
頁: [1]
查看完整版本: 带你十天轻松搞定 Go 微服务系列(一)