网络知识 娱乐 从前慢-谷粒商城篇章1

从前慢-谷粒商城篇章1

分布式基础篇

在这里插入图片描述

1 项目简介

1.1 项目背景

1.2 电商模式

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

1.2.1 B2B 模式

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

1.2.2 B2C 模式

B2C(Business to Consumer) 就是我们经常看到的供应商直接把商品买个
用户,即 “商对客” 模式,也就是我们呢说的商业零售,直接面向消费销
售产品和服务,如苏宁易购,京东,天猫,小米商城

1.2.3 C2B 模式

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

1.2.4 C2C 模式

C2C (Customer to Consumer) 客户之间把自己的东西放到网上去卖 。
如淘宝、咸鱼

1.2.5 O2O 模式

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

1.3 谷粒商城

谷粒商城是一个B2C模式的电商平台,销售自营商品给客户

1.4 项目架构图

1.4.1 项目微服务架构图

在这里插入图片描述

前后分离开发,分为内网部署和外网部署,外网是面向公众访问的。
访问前端项目,可以有手机APP,电脑网页;内网部署的是后端集群,
前端在页面上操作发送请求到后端,在这途中会经过Nginx集群,
Nginx把请求转交给API网关(springcloud gateway)(网关可以根据当
前请求动态地路由到指定的服务,看当前请求是想调用商品服务还是购
物车服务还是检索服务),从路由过来如果请求很多,可以负载均衡地调
用商品服务器中一台(商品服务复制了多份),当商品服务器出现问题也
可以在网关层面对服务进行熔断或降级(使用阿里的sentinel组件),网关
还有其他的功能如认证授权、限流(只放行部分到服务器)等。

到达服务器后进行处理(springboot为微服务),服务与服务可能会相互
调用(使用feign组件),有些请求可能经过登录才能进行(基于OAuth2.0的
认证中心。安全和权限使用springSecurity控制)

服务可能保存了一些数据或者需要使用缓存,我们使用redis集群(分片+哨兵集
群)。持久化使用mysql,读写分离和分库分表。

服务和服务之间会使用消息队列(RabbitMQ),来完成异步解耦,分布式事务
的一致性。有些服务可能需要全文检索,检索商品信息,使用ElaticSearch。

服务可能需要存取数据,使用阿里云的对象存储服务OSS。

项目上线后为了快速定位问题,使用ELK对日志进行处理,使用LogStash收
集业务里的各种日志,把日志存储到ES中,用Kibana可视化页面从ES中检
索出相关信息,帮助我们快速定位问题所在。

在分布式系统中,由于我们每个服务都可能部署在很多台机器,服务和服务
可能相互调用,就得知道彼此都在哪里,所以需要将所有服务都注册到注册
中心。服务从注册中心发现其他服务所在位置(使用阿里Nacos作为注册
中心)。

每个服务的配置众多,为了实现改一处配置相同配置就同步更改,就需要配
置中心,也使用阿里的Nacos,服务从配置中心中动态取配置。

服务追踪,追踪服务调用链哪里出现问题,使用springcloud提供的Sleuth、
Zipkin、Metrics,把每个服务的信息交给开源的Prometheus进行聚合分析,
再由Grafana进行可视化展示,提供Prometheus提供的AlterManager实时
得到服务的警告信息,以短信/邮件的方式告知服务开发人员。

还提供了持续集成和持续部署。项目发布起来后,因为微服务众多,每一个都打
包部署到服务器太麻烦,有了持续集成后开发人员可以将修改后的代码提交到
github,运维人员可以通过自动化工具Jenkins Pipeline将github中获取的代码打
包成docker镜像,最终是由k8s集成docker服务,将服务以docker容器的方式运行。

1.4.2 微服务划分图

在这里插入图片描述

反映了需要创建的微服务以及相关技术。

前后分离开发。前端项目分为admin-vue(工作人员使用的后台管理系统)、
shop-vue(面向公众访问的web网站)、app(公众)、小程序(公众)

商品服务:商品的增删改查、商品的上下架、商品详情
支付服务
优惠服务
用户服务:用户的个人中心、收货地址
仓储服务:商品的库存
秒杀服务
订单服务:订单增删改查
检索服务:商品的检索ES
中央认证服务:登录、注册、单点登录、社交登录
购物车服务
后台管理系统:添加优惠信息等

1.5 项目技术&特色

前后分离开发,并开发基于vue的后台管理系统
SpringCloud全新的解决方案
应用监控、限流、网关、熔断降级等分布式方案,全方位涉及
透彻讲解分布式事务,分布式锁等分布式系统的难点
压力测试与性能优化
各种集群技术的区别以及使用
CI/CD 使用

1.6 项目前置要求

1.6.1 学习项目的前置知识

熟悉SpringBoot以及常见整合方案
了解SpringCloud
熟悉 git  maven
熟悉 linux redis docker 基本操作
了解 html,css,js,vue
熟练使用idea开发项目

2 分布式基础概念

2.1 微服务

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

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

在这里插入图片描述

2.2 集群&分布式&节点

集群是个物理状态,分布式是个工作方式

只要是一堆机器,也可以叫做集群,他们是不是一起协作干活,
这谁也不知道。

《分布式系统原理与范型》定义:

分布式系统是若干独立计算机的集合,这些计算机对于用户来说像单个系统
分布式系统 (distributed system) 是建立网络之上的软件系统

分布式是指根据不同的业务分布在不同的地方

集群指的是将几台服务器集中在一起,实现同一业务

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

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

节点:集群中的一个服务器

2.3 远程调用

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

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

在这里插入图片描述

2.4 负载均衡

在这里插入图片描述

分布式系统中,A 服务需要调用B服务,B服务在多台机器中都存在, 
A调用任意一个服务器均可完成功能

为了使每一个服务器都不要太或者太闲,我们可以负载均衡调用每一个服务器,
提升网站的健壮性

常见的负载均衡算法:
轮询:为第一个请求选择健康池中的每一个后端服务器,然后按顺序往后依
次选择,直到最后一个,然后循环

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

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

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

在这里插入图片描述

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

2.6 配置中心

在这里插入图片描述

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

配置中心用来集中管理微服务的配置信息

2.7 服务熔断&服务降级

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

在这里插入图片描述

rpc
情景:
订单服务 --> 商品服务 --> 库存服务

库存服务出现故障导致响应慢,导致商品服务需要等待,可能等到10s后库存服
务才能响应。库存服务的不可用导致商品服务阻塞,商品服务等的期间,订单服
务也处于阻塞。一个服务不可用导致整个服务链都阻塞。如果是高并发,第一个
请求调用后阻塞10s得不到结果,第二个请求直接阻塞10s。更多的请求进来导致
请求积压,全部阻塞,最终服务器的资源耗尽。导致雪崩

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

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

2.8 API 网关

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

在这里插入图片描述

3 环境搭建

3.1 安装jdk1.8

1 下载并解压jdk1.8
tar  -zxvf  jdk-8u281-linux-x64.tar.gz(检查本机有没有jdk有的话卸载掉。安装上自己的jdk)

2 配上环境变量
vim /etc/profile
# 添加一下两行代码
export JAVA_HOME=jdk的位置
export PATH=$PATH:$JAVA_HOME/bin

3 使配置生效
source  /etc/profile

3.2 安装docker(CentOS)

1 卸载系统之前的docker 
sudo yum remove docker 
                  docker-client 
                  docker-client-latest 
                  docker-common 
                  docker-latest 
                  docker-latest-logrotate 
                  docker-logrotate 
                  docker-engine

2  设置存储库
sudo yum install -y yum-utils
sudo yum-config-manager 
    --add-repo 
    https://download.docker.com/linux/centos/docker-ce.repo

3  安装DOCKER引擎
sudo yum install docker-ce docker-ce-cli containerd.io

4  启动Docker.
sudo systemctl start docker

5 配置镜像加速
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": ["https://chqac97z.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker

3.3 安装mysql

1 拉去mysql镜像
sudo docker pull mysql:8.0

2 启动mysql容器
# --name指定容器名字 -v目录挂载 -p指定端口映射  -e设置mysql参数 -d后台运行
sudo docker run --name mysql -v /usr/local/mysql/data:/var/lib/mysql -v /usr/local/mysql:/etc/mysql/conf.d -v /usr/local/mysql/log:/var/log/mysql  -e MYSQL_ROOT_PASSWORD=root  -p 3306:3306 -d mysql:8.0

3 使用su - root(切换为root,这样就不用每次都sudo来赐予了)
su - root
4 进入mysql容器
docker exec -it 容器名称|容器id bin/bash

3.4 安装redis

1 在docker hub搜索redis镜像
docker search redis

2 拉取redis镜像到本地
docker pull redis:6.0.10

3 修改需要自定义的配置(docker-redis默认没有配置文件,
自己在宿主机建立后挂载映射)
创建并修改/usr/local/redis/redis.conf
bind 0.0.0.0 开启远程权限
appendonly yes 开启aof持久化

4 启动redis服务运行容器
docker run --name redis  -v /usr/local/redis/data:/data  -v /usr/local/redis/redis.conf:/usr/local/etc/redis/redis.conf -p 6379:6379 -d redis:6.0.10  redis-server /usr/local/etc/redis/redis.conf 

解释: -v /usr/local/redis/data:/data  # 将数据目录挂在到本地保证数据安全
 -v /root/redis/redis.conf:/usr/local/etc/redis/redis.conf   # 将配置文件挂在到本地修改方便
 
5  直接进去redis客户端。
docker exec -it redis redis-cli

3.5 Maven

在maven配置文件配置
配置阿里云镜像

	
		nexus-aliyun
		central
		Nexus aliyun
		http://maven.aliyun.com/nexus/content/groups/public
	


配置 jdk 1.8 编译项目

	
		jdk-1.8
		
			true
			1.8
		
		
			1.8
			1.8
			1.8
		
	

3.6 安装开发插件(可选-方便开发)

vscode

Auto Close Tag  
Auto Rename Tag 
Chinese 
ESlint 
HTML CSS Support
HTML Snippets
JavaScript (ES6) code snippets
Live Server
open in brower
Vetur

idea
lombok、mybatisx

3.7 安装git

# 配置用户名
git config --global user.name "username"  //(名字,随意写)

# 配置邮箱
git config --global user.email "55333@qq.com" // 注册账号时使用的邮箱

# 配置ssh免密登录
ssh-keygen -t rsa -C "55333@qq.com"

三次回车后生成了密钥,也可以查看密钥
cat ~/.ssh/id_rsa.pub


浏览器登录码云后,个人头像上点设置、然后点ssh公钥、随便填个标题,然后赋值
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC6MWhGXSKdRxr1mGPZysDrcwABMTrxc8Va2IWZyIMMRHH9Qn/wy3PN2I9144UUqg65W0CDE/thxbOdn78MygFFsIG4j0wdT9sdjmSfzQikLHFsJ02yr58V6J2zwXcW9AhIlaGr+XIlGKDUy5mXb4OF+6UMXM6HKF7rY9FYh9wL6bun9f1jV4Ydlxftb/xtV8oQXXNJbI6OoqkogPKBYcNdWzMbjJdmbq2bSQugGaPVnHEqAD74Qgkw1G7SIDTXnY55gBlFPVzjLWUu74OWFCx4pFHH6LRZOCLlMaJ9haTwT2DB/sFzOG/Js+cEExx/arJ2rvvdmTMwlv/T+6xhrMS3 894548575@qq.com

# 测试
ssh -T git@gitee.com

# 测试成功
Hi unique_perfect! You've successfully authenticated, but GITEE.COM does not provide shell access.

3.8 创建仓库

在码云新建仓库,仓库名gulimall,选择语言java,在.gitignore选中maven,
许可证选Apache-2.0,开发模型选生产/开发模型,开发时在dev分支,
发布时在master分支,创建如图所示

在这里插入图片描述

3.9 新建项目并创建出以下服务模块

在IDEA中New Project from version control Git  复制刚才项目的地址,如https://gitee.com/yxj/gulimall.git

创建以下模块
商品服务product
存储服务ware
订单服务order
优惠券服务coupon
用户服务member
每个模块导入web和openFeign

如下图所示

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

总体效果

在这里插入图片描述

创建父模块:在gulimall中创建pom.xml

<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.atguigu.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-product</module>
        <module>gulimall-ware</module>
    </modules>


</project>

在maven窗口刷新,并点击+号,找到刚才的pom.xml添加进来,发现多了个root。这样比如运行root的clean命令,其他项目也一起clean了。

修改总项目的.gitignore,把小项目里的垃圾文件在提交的时候忽略掉
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties
.mvn/wrapper/maven-wrapper.jar

**/mvnw
**/mvnw.cmd

**/.mvn
**/target

.idea


**/.gitignore

**/README.md

3.10 创建数据库

创建数据库之前需要启动docker服务

sudo docker ps
sudo docker ps -a
# 这两个命令的差别就是后者会显示  【已创建但没有启动的容器】

# 我们接下来设置我们要用的容器每次都是自动启动
sudo docker update redis --restart=always
sudo docker update mysql --restart=always
# 如果不配置上面的内容的话,我们也可以选择手动启动
sudo docker start mysql
sudo docker start redis
# 如果要进入已启动的容器
sudo docker exec -it mysql /bin/bash
接着创建数据库
然后接着去sqlyog直接我们的操作,在左侧root上右键建立数据库:
字符集选utf8mb4,他能兼容utf8且能解决一些乱码的问题。分别
建立了下面数据库

gulimall_oms
gulimall_pms
gulimall_sms
gulimall_ums
gulimall_wms

所有的数据库数据再复杂也不建立外键,因为在电商系统里,数据量大,
做外键关联很耗性能。
gulimall-oms.sql

drop table if exists oms_order;

drop table if exists oms_order_item;

drop table if exists oms_order_operate_history;

drop table if exists oms_order_return_apply;

drop table if exists oms_order_return_reason;

drop table if exists oms_order_setting;

drop table if exists oms_payment_info;

drop table if exists oms_refund_info;

/*==============================================================*/
/* Table: oms_order                                             */
/*==============================================================*/
create table oms_order
(
   id                   bigint not null auto_increment comment 'id',
   member_id            bigint comment 'member_id',
   order_sn             char(32) comment '订单号',
   coupon_id            bigint comment '使用的优惠券',
   create_time          datetime comment 'create_time',
   member_username      varchar(200) comment '用户名',
   total_amount         decimal(18,4) comment '订单总额',
   pay_amount           decimal(18,4) comment '应付总额',
   freight_amount       decimal(18,4) comment '运费金额',
   promotion_amount     decimal(18,4) comment '促销优化金额(促销价、满减、阶梯价)',
   integration_amount   decimal(18,4) comment '积分抵扣金额',
   coupon_amount        decimal(18,4) comment '优惠券抵扣金额',
   discount_amount      decimal(18,4) comment '后台调整订单使用的折扣金额',
   pay_type             tinyint comment '支付方式【1->支付宝;2->微信;3->银联; 4->货到付款;】',
   source_type          tinyint comment '订单来源[0->PC订单;1->app订单]',
   status               tinyint comment '订单状态【0->待付款;1->待发货;2->已发货;3->已完成;4->已关闭;5->无效订单】',
   delivery_company     varchar(64) comment '物流公司(配送方式)',
   delivery_sn          varchar(64) comment '物流单号',
   auto_confirm_day     int comment '自动确认时间(天)',
   integration          int comment '可以获得的积分',
   growth               int comment '可以获得的成长值',
   bill_type            tinyint comment '发票类型[0->不开发票;1->电子发票;2->纸质发票]',
   bill_header          varchar(255) comment '发票抬头',
   bill_content         varchar(255) comment '发票内容',
   bill_receiver_phone  varchar(32) comment '收票人电话',
   bill_receiver_email  varchar(64) comment '收票人邮箱',
   receiver_name        varchar(100) comment '收货人姓名',
   receiver_phone       varchar(32) comment '收货人电话',
   receiver_post_code   varchar(32) comment '收货人邮编',
   receiver_province    varchar(32) comment '省份/直辖市',
   receiver_city        varchar(32) comment '城市',
   receiver_region      varchar(32) comment '区',
   receiver_detail_address varchar(200) comment '详细地址',
   note                 varchar(500) comment '订单备注',
   confirm_status       tinyint comment '确认收货状态[0->未确认;1->已确认]',
   delete_status        tinyint comment '删除状态【0->未删除;1->已删除】',
   use_integration      int comment '下单时使用的积分',
   payment_time         datetime comment '支付时间',
   delivery_time        datetime comment '发货时间',
   receive_time         datetime comment '确认收货时间',
   comment_time         datetime comment '评价时间',
   modify_time          datetime comment '修改时间',
   primary key (id)
);

alter table oms_order comment '订单';

/*==============================================================*/
/* Table: oms_order_item                                        */
/*==============================================================*/
create table oms_order_item
(
   id                   bigint not null auto_increment comment 'id',
   order_id             bigint comment 'order_id',
   order_sn             char(32) comment 'order_sn',
   spu_id               bigint comment 'spu_id',
   spu_name             varchar(255) comment 'spu_name',
   spu_pic              varchar(500) comment 'spu_pic',
   spu_brand            varchar(200) comment '品牌',
   category_id          bigint comment '商品分类id',
   sku_id               bigint comment '商品sku编号',
   sku_name             varchar(255) comment '商品sku名字',
   sku_pic              varchar(500) comment '商品sku图片',
   sku_price            decimal(18,4) comment '商品sku价格',
   sku_quantity         int comment '商品购买的数量',
   sku_attrs_vals       varchar(500) comment '商品销售属性组合(JSON)',
   promotion_amount     decimal(18,4) comment '商品促销分解金额',
   coupon_amount        decimal(18,4) comment '优惠券优惠分解金额',
   integration_amount   decimal(18,4) comment '积分优惠分解金额',
   real_amount          decimal(18,4) comment '该商品经过优惠后的分解金额',
   gift_integration     int comment '赠送积分',
   gift_growth          int comment '赠送成长值',
   primary key (id)
);

alter table oms_order_item comment '订单项信息';

/*==============================================================*/
/* Table: oms_order_operate_history                             */
/*==============================================================*/
create table oms_order_operate_history
(
   id                   bigint not null auto_increment comment 'id',
   order_id             bigint comment '订单id',
   operate_man          varchar(100) comment '操作人[用户;系统;后台管理员]',
   create_time          datetime comment '操作时间',
   order_status         tinyint comment '订单状态【0->待付款;1->待发货;2->已发货;3->已完成;4->已关闭;5->无效订单】',
   note                 varchar(500) comment '备注',
   primary key (id)
);

alter table oms_order_operate_history comment '订单操作历史记录';

/*==============================================================*/
/* Table: oms_order_return_apply                                */
/*==============================================================*/
create table oms_order_return_apply
(
   id                   bigint not null auto_increment comment 'id',
   order_id             bigint comment 'order_id',
   sku_id               bigint comment '退货商品id',
   order_sn             char(32) comment '订单编号',
   create_time          datetime comment '申请时间',
   member_username      varchar(64) comment '会员用户名',
   return_amount        decimal(18,4) comment '退款金额',
   return_name          varchar(100) comment '退货人姓名',
   return_phone         varchar(20) comment '退货人电话',
   status               tinyint(1) comment '申请状态[0->待处理;1->退货中;2->已完成;3->已拒绝]',
   handle_time          datetime comment '处理时间',
   sku_img              varchar(500) comment '商品图片',
   sku_name             varchar(200) comment '商品名称',
   sku_brand            varchar(200) comment '商品品牌',
   sku_attrs_vals       varchar(500) comment '商品销售属性(JSON)',
   sku_count            int comment '退货数量',
   sku_price            decimal(18,4) comment '商品单价',
   sku_real_price       decimal(18,4) comment '商品实际支付单价',
   reason               varchar(200) comment '原因',
   description述         varchar(500) comment '描述',
   desc_pics            varchar(2000) comment '凭证图片,以逗号隔开',
   handle_note          varchar(500) comment '处理备注',
   handle_man           varchar(200) comment '处理人员',
   receive_man          varchar(100) comment '收货人',
   receive_time         datetime comment '收货时间',
   receive_note         varchar(500) comment '收货备注',
   receive_phone        varchar(20) comment '收货电话',
   company_address      varchar(500) comment '公司收货地址',
   primary key (id)
);

alter table oms_order_return_apply comment '订单退货申请';

/*==============================================================*/
/* Table: oms_order_return_reason                               */
/*==============================================================*/
create table oms_order_return_reason
(
   id                   bigint not null auto_increment comment 'id',
   name                 varchar(200) comment '退货原因名',
   sort                 int comment '排序',
   status               tinyint(1) comment '启用状态',
   create_time          datetime comment 'create_time',