1. 项目简介

1.1 项目背景

1.1.1 电商模式

市面上有5种常见的电商模式:B2B,B2C,C2B,C2C,O2O

B2B模式

B2B(Business to Business),是指商家与商家建立的商业关系。如:阿里巴巴

B2C模式

B2C(Business to Consumer),就是我们经常看到的供应商直接把商品卖给用户,即“商对客”模式,也就是通常说的商业零售,直接面向消费者销售产品和服务。 如:苏宁易购、京东、天猫、小米商城。当前这个项目谷粒商城就是一个B2C模式的电商平台,销售自营商品给客户。

C2B模式

C2B(Customer to Business),即消费者对企业。先有消费者需求产生而后有企业生产,即先有消费者提出需求,后有生产企业按需求组织生产

C2C模式

C2C(Customer to Consumer),客户之间自己把东西放上网去卖,如:淘宝,闲鱼

O2O模式

O2O即 Online To offline,也即将线下商务的机会与互联网结合在了一起,让互联网成为线下交易的前台。线上快速支付,线下优质服务。如:饿了么,美团,淘票票,京东到家

1.2 项目架构图

1.2.1 项目微服务架构图

外网部署:面向公众访问,部署前端项目
内网部署:后台集群

1. 项目前期准备 [1-15] - 图1

1.2.2 项目微服务划分图

1. 项目前期准备 [1-15] - 图2

1.3 项目技术&特色

  1. 前后分离开发,并开发基于vue的后台管理系统

  2. SpringCloud全新的解决方案

  3. 应用监控、限流、网关、熔断降级等分布式方案,全方位涉及

  4. 透彻讲解分布式事务、分布式锁等分布式系统的难点

  5. 分析高并发场景的编码方式,线程池,异步编排等使用

  6. 压力测试与性能优化

  7. 各种集群技术的区别以及使用

  8. CI/CD使用

  9. ……

1.4 项目前置要求

  1. 熟悉 SpringBoot以及常见整合方案

  2. 了解SpringCloud

  3. 熟悉git,maven

  4. 熟悉 linux,redis,docker基本操作

  5. 了解html,css,js,vue

  6. 熟练使用idea开发项目

2. 分布式基础概念

2.1 微服务

微服务架构风格,就像是把一个单独的应用程序开发为一套小服务,每个小服务运行在自己的进程中,并使用轻量级机制通信,通常是HTTP API。这些服务围绕业务能力来构建,并通过完全自动化部署机制来独立部署。这些服务使用不同的编程语言书写,以及不同数据存储技术,并保持最低限度的集中式管理。

简而言之:拒绝大型单体应用,基于业务边界进行服务微化拆分,各个服务独立部署运行。

2.2 集群&分布式&节点

分布式:是指将不同的业务分布在不同的地方。
集群:指的是将几台服务器集中在一起,实现同一业务。
节点:指的是集群中的一个服务器。

集群是个物理形态,分布式是个工作方式。只要是一堆机器,就可以叫集群,他们是不是一起协作着干活,这个谁也不知道。《分布式系统原理与范型》定义:分布式系统是若干独立计算机的集合,这些计算机对于用户来说就像单个相关系统。分布式系统(distributed system)是建立在网络之上的软件系统。

分布式中的每一个节点,都可以做集群。而集群并不一定就是分布式的。

例如:京东是一个分布式系统,众多业务运行在不同的机器,所有业务构成一个大型的业务集群。每一个小的业务,比如用户系统,访问压力大的时候一台服务器是不够的。我们就应该将用户系统部署到多个服务器,也就是每一个业务系统也可以做集群化。但是在多个服务器上的用户系统组成的集群,并不是分布式的。

2.3 远程调用

在分布式系统中,各个服务可能处于不同主机,但是服务之间不可避免的需要互相调用,我们称为远程调用。

SpringCloud中使用HTTP+JSON的方式完成远程调用

2.4 负载均衡

分布式系统中,A服务需要调用B服务,B服务在多台机器中都存在,A调用任意一个服务器均可完成功能。为了使每一个服务器都不要太忙或者太闲,我们可以负载均衡的调用每一个服务器,提升网站的健壮性。

常见的负载均衡算法:

轮询:为第一个请求选择健康池中的第一个后端服务器,然后按顺序往后依次选择,直到最后一个,然后循环。

最小连接:优先选择连接数最少,也就是压力最小的后端服务器,在会话较长的情况下可以考虑采取这种方式。

散列:根据请求源的IP的散列(hash)来选择要转发的服务器。这种方式可以一定程度上保证特定用户能连接到相同的服务器。如果你的应用需要处理状态而要求用户能连接到和之前相同的服务器,可以考虑采取这种方式。

2.5 服务注册/发现&注册中心

A服务调用B服务,A服务并不知道B服务当前在哪几台服务器有,哪些正常的,哪些服务已经下线。解决这个问题可以引入注册中心。

1. 项目前期准备 [1-15] - 图3

如果某些服务下线,我们其他人可以实时的感知到其他服务的状态,从而避免调用不可用的服务。

2.6 配置中心

每一个服务最终都有大量的配置,并且每个服务都可能部署在多台机器上。我们经常需要变更配置,我们可以让每个服务在配置中心获取自己的配置。

1. 项目前期准备 [1-15] - 图4

2.7 服务熔断&服务降级

在微服务架构中,微服务之间通过网络进行通信,存在相互依赖,当其中一个服务不可用时,有可能会造成雪崩效应。要防止这样的情况,必须要有容错机制来保护服务。

比如:如下项目,如果库存服务出现故障,会导致商品服务响应不及时,商品服务堵塞,会导致订单服务积压,一个服务不可用,导致整个服务链阻塞。高并发情况下,如果没有进行熔断处理,过多请求积压或阻塞,最终会导致服务器资源耗尽。

1. 项目前期准备 [1-15] - 图5

服务熔断

设置服务的超时,当被调用的服务经常失败到达某个阙值,我们可以开启断路保护机制,后来的请求不再去调用这个服务。本地直接返回默认的数据。

服务降级

在运维期间,当系统处于高峰期,系统资源紧张,我们可以让非核心业务降级运行。降级:某些服务不处理,或者简单处理【抛异常、返回NULL、调用Mock数据、调用Fallback处理逻辑】。

2.8 API网关

在微服务架构中,APl Gateway作为整体架构的重要组,它抽象了微服务中都需要的公共功能,同时提供了客户端负载均衡,服务自动熔断,灰度发布,统一认证,限流监控,日志统计等丰富的功能,帮助我们解决很多API管理难题。

3. 环境搭建

3.1 安装Linux虚拟机

使用VirtualBox + Vagrant创建linux虚拟机:

  1. 下载并安装VirtualBox【virtualbox官网】和Vagrant【vagrant官网】

  2. 打开window cmd窗口,运行vagrant init centos/7,初始化一个centos7系统

  3. 初始化完成之后,运行vagrant up启动虚拟机。当看到下图内容,就表示虚拟机启动成功,Ctrl + C 退出之后,使用vagrant ssh连接虚拟机。系统默认root用户的密码是vagrant。【vagrant其他常用命令】

1. 项目前期准备 [1-15] - 图6

! 出现问题:虚拟机启动时卡在private key,问题描述和解决方案5-1【问题描述】

  1. 虚拟机的ip地址不是固定ip,开发不方便。打开window cmd窗口,执行ipconfig命令查看VirtualBox的IP地址

1. 项目前期准备 [1-15] - 图7

之后在C:\Users\用户名\文件夹下打开Vagrantfile文件,找到下面一行,修改IP地址,保持网关与VirtualBox的IP地址网关一致

  1. config.vm.network "private_network", ip: "192.168.56.10"

3.2 安装docker

Docker是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux或Windows操作系统的机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。【Docker官网】

我们当前示例是在CentOS7中安装Docker。不同的环境安装Docker,可查看Docker的官网。【安装Docker】

  1. 卸载旧版本

    sudo yum remove docker \
                   docker-client \
                   docker-client-latest \
                   docker-common \
                   docker-latest \
                   docker-latest-logrotate \
                   docker-logrotate \
                   docker-engine
    
  2. 设置存储库 ```shell

    安装yum-utils包(提供yum-config-manager实用程序)

    sudo yum install -y yum-utils

设置稳定的存储库

sudo yum-config-manager \ —add-repo \ https://download.docker.com/linux/centos/docker-ce.repo


3. 安装Docker
```shell
sudo yum install docker-ce docker-ce-cli containerd.io
  1. 启动Docker

    sudo systemctl start docker
    
  2. 设置Docker开机自启

    sudo systemctl enable docker
    
  3. 为docker配置镜像加速

到阿里云的官网,在产品与服务中找到容器镜像服务,点进去之后找到镜像加速器,执行适合自己的脚本即可。

1. 项目前期准备 [1-15] - 图8

1. 项目前期准备 [1-15] - 图9

sudo mkdir -p /etc/docker

sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": ["https://1kubux0d.mirror.aliyuncs.com"]
}
EOF

sudo systemctl daemon-reload

sudo systemctl restart docker

3.3 docker安装mysql

  1. 下载mysql镜像文件

    # sudo docker pull 镜像名称:版本号
    sudo docker pull mysql:5.7
    
  2. 创建实例并启动mysql ```shell docker run -p 3306:3306 —name mysql \ -v /mydata/mysql/log:/var/log/mysql \
    -v /mydata/mysql/data:/var/lib/mysql \ -v /mydata/mysql/conf:/etc/mysql \ -e MYSQL_ROOT_PASSWORD=root \ -d mysql:5.7

每次docker run都会启动一个容器,容器与容器之间互相隔离,当前启动的这个容器是mysql完整的运行环境,

相当于这个容器是一个linux,mysql就装这个linux里面,意思是我们的centos中又运行了一个linux环境, 这个环境是mysql的容器,里面装了mysql

-p 3306:3306:将容器的3306端口映射到主机的3306端口

docker run -p 3306:3306 —name mysql \

# 将日志文件挂载到主机,这些文件本来在容器内部,每次查看都要去容器内部查看很麻烦,
  所以将容器内部的文件挂载到linux上

-v linux外部文件夹:容器内部文件夹

-v /mydata/mysql/log:/var/log/mysql \

将配置文件挂载到主机

-v /mydata/mysql/data:/var/lib/mysql \

将配置文件挂载到主机

-v /mydata/mysql/conf:/etc/mysql \  

初始化root用户的密码

-e MYSQL_ROOT_PASSWORD=root \

后台运行

-d mysql:5.7

执行上述命令,当看到如下内容,证明mysql已经启动完成,可通过docker ps查看启动的镜像

![](https://gitee.com/yaosy-seven/note_images/raw/master/img/20211230113018.png#crop=0&crop=0&crop=1&crop=1&height=149&id=F1GSy&originHeight=244&originWidth=1314&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=800)

3. 修改mysql的配置
```shell
[client]
default-character-set=utf8

[mysql]
default-character-set=utf8

[mysqld]
init_connect="SET collation_connection = utf8_unicode_ci"
init_connect="SET NAMES utf8"
character-set-server=utf8
collation-server=utf8_unicode_ci
skip-character-set-client-handshake
skip-name-resolve
  1. 进入mysql容器

    docker exec -it mysql bin/bash
    
  2. 启动mysql

    docker start mysql
    

3.4 docker安装redis

  1. 下载镜像文件

    docker pull redis
    
  2. 创建实例并启动 ```shell

    先创建文件,在执行启动命令

    mkdir -p /mydata/redis/conf

touch /mydata/redis/conf/redis.conf

docker run -p 6379:6379 —name redis\ -v /mydata/redis/data:/data\ -v /mydata/redis/conf/redis.conf:/etc/redis/redis.conf\ -d redis redis-server /etc/redis/redis.conf


当看到如下内容,证明redis启动成功,可通过docker ps查看启动的镜像

![](https://gitee.com/yaosy-seven/note_images/raw/master/img/20211230144711.png#crop=0&crop=0&crop=1&crop=1&height=99&id=chhgL&originHeight=173&originWidth=1047&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=600)

3. 配置redis持久化[【redis官网查看其他配置】](https://redis.io/)

修改/mydata/redis/conf/redis.conf文件
```shell
appendonly yes   # 开启aof持久化,如果不配置持久化,数据存在内存中,关机之后数据就没有了
  1. 使用redis镜像执行redis-cli命令连接
    docker exec -it redis redis-cli
    

如果虚拟机关闭再重启之后,redis和mysql没有自动启动,也可通过下面的命令设置开机自启,只要虚拟机重启,mysql和redis都会自动启动

docker update mysql —restart=always docker update redis —restart=always

3.5 开发环境统一

3.5.1 Maven

开发要求jdk版本1.8以上

1. 项目前期准备 [1-15] - 图10

一个完整的Maven settings.xml配置:

<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">

    <!-- 本地仓库的位置 -->
    <localRepository>E:\MySelf\Java\WorkSpace\Java\repository</localRepository>

    <!-- Apache Maven 配置 -->
    <pluginGroups/>
    <proxies/>

    <!-- 私服发布的用户名密码 -->
    <servers>
        <server>
            <id>releases</id>
            <username>deployment</username>
            <password>He2019</password>
        </server>
        <server>
            <id>snapshots</id>
            <username>deployment</username>
            <password>He2019</password>
        </server>
    </servers>

    <!-- 阿里云镜像 -->
    <mirrors>
        <mirror>
            <id>alimaven</id>
            <name>aliyun maven</name>
            <!-- https://maven.aliyun.com/repository/public/ -->
            <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
            <mirrorOf>central</mirrorOf>
        </mirror>
    </mirrors>

    <!-- 配置: java8, 先从阿里云下载, 没有再去私服下载  -->
    <profiles>
        <!-- 全局JDK1.8配置 -->
        <profile>
            <id>jdk1.8</id>
            <activation>
                <activeByDefault>true</activeByDefault>
                <jdk>1.8</jdk>
            </activation>
            <properties>
                <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
                <maven.compiler.source>1.8</maven.compiler.source>
                <maven.compiler.target>1.8</maven.compiler.target>
                <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
            </properties>
        </profile>


        <!-- Nexus私服配置: 第三方jar包下载, 比如oracle的jdbc驱动等 -->
        <profile>
            <id>dev</id>
            <repositories>
                <repository>
                    <id>nexus</id>
                    <url>http://nexus.hepengju.cn:8081/nexus/content/groups/public/</url>
                    <releases>
                        <enabled>true</enabled>
                    </releases>
                    <snapshots>
                        <enabled>true</enabled>
                    </snapshots>
                </repository>
            </repositories>
            <pluginRepositories>
                <pluginRepository>
                    <id>public</id>
                    <name>Public Repositories</name>
                    <url>http://nexus.hepengju.cn:8081/nexus/content/groups/public/</url>
                </pluginRepository>
            </pluginRepositories>
        </profile>

        <!-- 阿里云配置: 提高国内的jar包下载速度 -->
        <profile>
            <id>ali</id>
            <repositories>
                <repository>
                    <id>alimaven</id>
                    <name>aliyun maven</name>
                    <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
                    <releases>
                        <enabled>true</enabled>
                    </releases>
                    <snapshots>
                        <enabled>true</enabled>
                    </snapshots>
                </repository>
            </repositories>
            <pluginRepositories>
                <pluginRepository>
                    <id>alimaven</id>
                    <name>aliyun maven</name>
                    <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
                </pluginRepository>
            </pluginRepositories>
        </profile>

    </profiles>

    <!-- 激活配置 --> 
    <activeProfiles>
        <activeProfile>jdk1.8</activeProfile>
        <activeProfile>dev</activeProfile>
        <activeProfile>ali</activeProfile>
    </activeProfiles>
</settings>

3.5.2 IDea&VsCode

IDea配置自己的Maven,并且安装Lombok和MyBatisX两个插件。后台管理系统的前端工程使用VsCode开发,所以需要去VsCode官网下载并安装VsCode。安装完成之后,再给VsCode安装如下插件。【VsCode官网】

1. 项目前期准备 [1-15] - 图11

3.5.3 安装配置git

去Git官网下载Git并安装,安装完成之后,进入Git Bash对Git进行配置。【Git官网】

# 1. 在Git bash中配置用户名
git config --global user.name "yaosy"

# 2. 在Git bash中配置邮箱
git config --global user.email "yaosy_8080@163.com"

# 3. 配置ssh免密登录

# 3.1 在Git bash中生成秘钥,输入下面命令,三次回车
ssh-keygen -t rsa -C "yaosy_8080@163.com"

# 3.2 在Git bash中查看公钥
cat ~/.ssh/id_rsa.pub

# 3.3 打开下面网址,去码云的设置页面将公钥粘贴进去,公钥标题任意
https://gitee.com/profile/sshkeys

# 3.4 在Git bash中测试是否配置成功
ssh -T git@gitee.com

# 出现如下内容,说明免密登录设置成功
<!--
    The authenticity of host 'gitee.com (212.64.62.183)' can't be established.
    ED25519 key fingerprint is SHA256:+ULzij2u99B9eWYFTw1Q4ErYG/aepHLbu96PAUCoV88.
    This key is not known by any other names

    # 输入yes
    Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
    Warning: Permanently added 'gitee.com' (ED25519) to the list of known hosts.
    Hi yaosy_seven! You've successfully authenticated, but GITEE.COM does not provide shell access.
-->

3.6 创建项目微服务

3.6.1 从码云初始化一个项目

  1. 去码云新建一个仓库

1. 项目前期准备 [1-15] - 图12

  1. 在idea中将git中的初始化项目拉取下来:URL中填写仓库地址,之后点击Clone

1. 项目前期准备 [1-15] - 图13

3.6.2 创建各个微服务项目

商品服务(product),仓储服务(ware),订单服务(order),优惠券服务(coupon),会员服务(member)

分别创建以上服务,每个服务都如下操作:首先点击New Module,使用Spring的初始化向导进行创建

1. 项目前期准备 [1-15] - 图14

1. 项目前期准备 [1-15] - 图15

由于是微服务项目,所以先选择导入微服务必要的组件

1. 项目前期准备 [1-15] - 图16

1. 项目前期准备 [1-15] - 图17

全部创建之后的项目结构如下图:

1. 项目前期准备 [1-15] - 图18

在总工程Guli_Mall下创建一个pom.xml文件,将其他子服务做统一管理

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>com.yaosy.gulimall</groupId>
    <artifactId>gulimall</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>gulimall</name>
    <description>聚合服务</description>
    <packaging>pom</packaging>

    <modules>
        <module>gulimall_coupon</module>
        <module>gulimall_member</module>
        <module>gulimall_order</module>
        <module>gulimall_produt</module>
        <module>gulimall_ware</module>
    </modules>
</project>

文件创建完成之后,点击下图的 + 号,将刚创建的pom文件添加进来,添加以后,会多一个root的gulimall。这样操作的目的是为了对子项目进行聚合管理,后续只需要操作root下的clean,就可以对所有工程进行clean

1. 项目前期准备 [1-15] - 图19

此外修改总工程下的.gitignore文件,添加以下配置,表示这些文件不用提交到Git

target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties
# https://github.com/takari/maven-wrapper#usage-without-binary-jar
.mvn/wrapper/maven-wrapper.jar

# 添加以下配置

**/mvnw
**/mvnw.cmd

**/.mvn
**/target

.idea
**/.gitignore

3.7 数据库初始化

不同的服务操作自己的数据库,所以我们根据服务创建以下数据库:

# 创建数据库时,字符集选择utf8mb64,这个可以兼容utf8,解决很多乱码问题

gulimall_oms
gulimall_pms
gulimall_ums
gulimall_sms
gulimall_wms

之后根据以下sql在对应的数据库中创建表格:

gulimall_oms.sqlgulimall_pms.sqlgulimall_sms.sqlgulimall_ums.sqlgulimall_wms.sql