2025-04-16-Wed-T-Java项目-商城

1.分布式基础(全栈开发篇)

  • 架构图

1.1 项目介绍

1.1.1 项目背景

1)电商模式

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

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

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

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

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

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

2)谷粒商城

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

1.1.2 项目架构图

项目微服务架构图

微服务划分图

1.1.3 项目技术&特色

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

1.1.4 业务基础概念

SPU与SKU

SPU: Standard Product Unit(标准化产品单元)

SPU是商品信息聚合的最小单位, 是一组可复用, 易于检索的标准化信息的集合, 该集合描述了一个产品的特性

SKU: Stock Keeping Unit (库存量单位)

SKU是库存进出量的基本单元, 可以是件, 盒, 托盘等为单位. SKU这是对大型连锁超市(配送中心)物流管理的一个必要的方法. 现在已经被引申为产品统一编号的简称, 每种产品均对应唯一的SKU号.

1.2 分布式基础概念

1.2.1 微服务

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

1.2.2 集群&分布式&节点

集群是个物理形态,分布式是个工作方式。
只要是一堆机器,就可以叫集群,他们是不是一起协作着干活,这个谁也不知道;

《分布式系统原理与范型》定义:
“分布式系统是若干独立计算机的集合,这些计算机对于用户来说就像单个相关系统”
分布式系统(distributed system)是建立在网络之上的软件系统。

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

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

1.2.3 远程调用

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

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

1.2.4 负载均衡

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

常见的负载均衡算法:

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

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

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

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

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

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

1.2.6 配置中心

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

1.2.7 服务熔断&服务降级

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

1)服务熔断

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

2)服务降级

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

1.2.8 API 网关

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

1.3 环境搭建

1.3.1 安装linux 虚拟机

下载&安装VirtualBox https://www.virtualbox.org/,主机需要开启CPU 虚拟化

在cmd窗口输入vagrant, 如果出现命令提示,说明安装成功

# 打开window cmd 窗口,运行vagrant init centos/7,即可初始化一个centos7 系统
vagrant init centos/7
# 运行vagrant up 即可启动虚拟机。系统root 用户的密码是vagrant
vagrant up
  • vagrant 其他常用命令
vagrant ssh # 自动使用vagrant 用户连接虚拟机。
vagrant upload source [destination] [name|id] #上传文件

https://www.vagrantup.com/docs/cli/init.html Vagrant 命令行

默认虚拟机的ip 地址不是固定ip,开发不方便, 可以修改Vagrantfile (在init的文件夹下)

  # Create a private network, which allows host-only access to the machine
  # using a specific IP.
  # config.vm.network "private_network", ip: "192.168.33.10"
config.vm.network "private_network", ip: "192.168.56.2" # 这里的ip 是物理机对应网卡的一个不同的ip,需要在物理机下使用ipconfig -all命令找到(找到的是192.168.56.1,则设置一个192.168.56.*即可)

重新使用vagrant up (或者vagrant restart)启动机器即可。然后再vagrant ssh 连接机器

# 测试
# 1. 在虚拟机中使用ip addr查看虚拟机ip
ip addr
# 2. 在主机使用ipconfig -all 查看主机ip
ipconfig -all

# 3. 在虚拟机中ping主机、在主机中ping虚拟机。测试网络连通性
ping host
ping vm

默认只允许ssh 登录方式,为了后来操作方便,文件上传等,我们可以配置允许账号密码登录
Vagrant ssh 进去系统之后
vi /etc/ssh/sshd_config
修改PasswordAuthentication yes/no
重启服务service sshd restart
以后可以使用提供的ssh 连接工具直接连接

注意:VirtualBox 会与包括但不限于如下软件冲突,需要卸载这些软件,然后重启电脑;
冲突的软件:红蜘蛛,360,净网大师(有可能)等
修改linux 的yum 源
1)、备份原yum 源
sudo mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup
2)、使用新yum 源
sudo curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
3)、生成缓存
yum makecache

1.3.2 安装docker

Docker 安装文档:https://docs.docker.com/install/linux/docker-ce/centos/

1、卸载系统之前的docker

sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine

2、安装Docker-CE

  • 安装必须的依赖
sudo yum install -y yum-utils \
device-mapper-persistent-data \
lvm2
  • 设置docker repo 的yum 位置
# 设置阿里docker镜像仓库地址
sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
  • 安装docker,以及docker-cli
sudo yum install docker-ce docker-ce-cli containerd.io

3、启动docker

sudo systemctl start docker

4、设置docker 开机自启

sudo systemctl enable docker

5、测试docker 常用命令,注意切换到root 用户下

https://docs.docker.com/engine/reference/commandline/docker/

6、配置docker 镜像加速

阿里云,容器镜像服务
针对Docker 客户端版本大于1.10.0 的用户
您可以通过修改daemon 配置文件/etc/docker/daemon.json 来使用加速器

sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": ["https://odol8len.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker

1.3.3 docker 安装mysql

1、下载镜像文件

docker pull mysql:5.7

2、创建实例并启动

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/conf.d \
-e MYSQL_ROOT_PASSWORD=root \
-d mysql:5.7


# --privileged=true \
参数说明:
-p 3306:3306:将容器的3306 端口映射到主机的3306 端口
-v /mydata/mysql/conf:/etc/mysql:将配置文件夹挂载到主机
-v /mydata/mysql/log:/var/log/mysql:将日志文件夹挂载到主机
-v /mydata/mysql/data:/var/lib/mysql/:将配置文件夹挂载到主机
-e MYSQL_ROOT_PASSWORD=root:初始化root 用户的密码
--restart=always # docker 重启时容器自启动
docker update mysql --restart=always
MySQL 配置
vi /mydata/mysql/conf/my.cnf
[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


# 注意:解决MySQL 连接慢的问题
# 在配置文件中加入如下,并重启mysql
[mysqld]
skip-name-resolve # skip-name-resolve:跳过域名解析

3、通过容器的mysql 命令行工具连接

docker exec -it mysql mysql -uroot -proot

4、设置root 远程访问

grant all privileges on *.* to 'root'@'%' identified by 'root' with grant option;
flush privileges;

5、进入容器文件系统

docker exec -it mysql /bin/bash

1.3.4 docker 安装redis

1、下载镜像文件

docker pull redis

2、创建实例并启动

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 自描述文件:
https://raw.githubusercontent.com/antirez/redis/4.0/redis.conf

3、使用redis 镜像执行redis-cli 命令连接

docker exec -it redis redis-cli
# 防火墙操作
# 1、查看防火墙状态
systemctl status firewalld

#2、开启防火墙
systemctl start firewalld.service 

# 3、开机启动防火墙
systemctl enable firewalld.service

#4、ip白名单方式:只允许指定的IP(或ip段)访问指定的端口
	#(1)指定的IP段访问指定的端口
firewall-cmd --permanent --add-rich-rule="rule family="ipv4" source address="192.xx.xx.32/24" port protocol="tcp" port="22" accept"

	#(2)指定的IP访问指定的端口
firewall-cmd --permanent --add-rich-rule="rule family="ipv4" source address="192.168.19.26/32" port protocol="tcp" port="21" accept"

#5、开启指定的某一个端口,允许其他任意IP访问指定的端口
firewall-cmd --permanent --add-port=22/tcp
firewall-cmd --permanent --add-port=23/tcp
# 设置完规则后执行
firewall-cmd --list-all
# 查看防火墙规则,查看设置是否已添加
# 如已添加则执行
firewall-cmd --reload 
#使得更改生效
# 如若没生效重启防火墙
systemctl restart firewalld.service

1.3.5 开发环境统一

1、Maven

配置阿里云镜像
<mirrors>
    <mirror>
        <id>nexus-aliyun</id>
        <mirrorOf>central</mirrorOf>
        <name>Nexus aliyun</name>
        <url>http://maven.aliyun.com/nexus/content/groups/public</url>
    </mirror>
</mirrors>
配置jdk1.8 编译项目
<profiles>
    <profile>
        <id>jdk-1.8</id>
        <activation>
            <activeByDefault>true</activeByDefault>
            <jdk>1.8</jdk>
        </activation>
        <properties>
            <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>
</profiles>

2、Idea&VsCode

idea 安装lombok、mybatisx 插件
Vscode 安装开发必备插件

Vetur —— 语法高亮、智能感知、Emmet 等
包含格式化功能, Alt+Shift+F (格式化全文),Ctrl+K Ctrl+F(格式化选中代码,两个Ctrl
需要同时按着)
EsLint —— 语法纠错
Auto Close Tag —— 自动闭合HTML/XML 标签
Auto Rename Tag —— 自动完成另一侧标签的同步修改
JavaScript(ES6) code snippets — — ES6 语法智能提示以及快速输入, 除js 外还支
持.ts,.jsx,.tsx,.html,.vue,省去了配置其支持各种包含js 代码文件的时间
HTML CSS Support —— 让html 标签上写class 智能提示当前项目所支持的样式
HTML Snippets —— html 快速自动补全
Open in browser —— 浏览器快速打开
Live Server —— 以内嵌服务器方式打开
Chinese (Simplified) Language Pack for Visual Studio Code —— 中文语言包

3、安装配置git

1、下载git;https://git-scm.com
2、配置git,进入git bash

配置用户名

git config --global user.name "username" //(名字)

配置邮箱

git config --global user.email "username@email.com" //(注册账号时用的邮箱)
3、配置ssh 免密登录
https://gitee.com/help/articles/4181#article-header0
进入git bash;使用:ssh-keygen -t rsa -C "xxxxx@xxxxx.com"命令。连续三次回车。
一般用户目录下会有
或者cat ~/.ssh/id_rsa.pub
登录进入gitee,在设置里面找到SSH KEY 将.pub 文件的内容粘贴进去
使用ssh -T git@gitee.com 测试是否成功即可
Git+码云教程https://gitee.com/help/articles/4104

4、逆向工程使用

1、导入项目逆向工程
2、下载人人开源后台管理系统脚手架工程
(1) 导入工程,创建数据库
(2) 修改工程shiro 依赖为SpringSecurity
(3) 删除部分暂时不需要的业务
3、下载人人开源后台管理系统vue 端脚手架工程
(1) vscode 导入前端项目
(2) 前后端联调测试基本功能

1.3.6 创建项目微服务

商品服务、仓储服务、订单服务、优惠券服务、用户服务
共同:
1)、web、openfeign
2)、每一个服务,包名com.atguigu.gulimall.xxx(product/order/ware/coupon/member)
3)、模块名:gulimall-coupon

1)、从gitee 初始化一个项目

运行nacos容器

docker run -d --name nacos -p 8848:8848 --restart=always -e MODE=standalone -d nacos/nacos-server:1.3.2

# docker cp nacos:/home/nacos/logs/ /mydata/nacos/
docker cp nacos:/home/nacos/conf/ /mydata/nacos/
docker cp nacos:/home/nacos/data/ /mydata/nacos/


docker rm -f nacos

docker run -d \
  --name nacos \
  -p 8848:8848 \
  -e MODE=standalone \
  -v /mydata/nacos/conf/:/home/nacos/conf/ \
  -v /mydata/nacos/data/:/home/nacos/data/ \
  --restart=always \
  nacos/nacos-server:1.3.2

docker run -d \
  --name nacos \
  -p 8848:8848 \
  -e MODE=standalone \
  -v /mydata/nacos/data/:/home/nacos/data/ \
  --restart=always \
  nacos/nacos-server:1.3.2

Docker安装nacos(图文并茂,避免踩坑,一步到位)_docker 安装nacos并且配置文件挂载-CSDN博客

  • 服务发现
/**
 * 1. 整合mybatis plus
 *      1) 导入依赖 mybatis plus boot starter
 *      2) 配置
 *          1. 配置数据源:数据库
 *              1. 导入数据库驱动:写入common
 *              2. 配置数据源: application.yaml
 *          2. 配置mybatis plus
 *              1. 启动类配置@MapperScan
 *              2. 告诉mybatis-plus,sql映射文件xml在哪: application.yaml
 */

要注意nacos集群所在的server,一定要关闭防火墙,否则容易出现各种问题。

搭建nacos集群,然后分别启动各个微服务,将它们注册到Nacos中。

  application:
    name: gulimall-coupon
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.137.14
  • 使用openfen

1)、引入open-feign

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

2)、编写一个接口,告诉SpringCLoud这个接口需要调用远程服务

修改“com.bigdata.gulimall.coupon.controller.CouponController”,添加以下controller方法:

    @RequestMapping("/member/list")
    public R memberCoupons(){
        CouponEntity couponEntity = new CouponEntity();
        couponEntity.setCouponName("discount 20%");
        return R.ok().put("coupons",Arrays.asList(couponEntity));
    }

新建“com.bigdata.gulimall.member.feign.CouponFeignService”接口

@FeignClient("gulimall_coupon")
public interface CouponFeignService {
    @RequestMapping("/coupon/coupon/member/list")
    public R memberCoupons();
}

修改“com.bigdata.gulimall.member.GulimallMemberApplication”类,添加上"@EnableFeignClients":

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients(basePackages = "com.bigdata.gulimall.member.feign")
public class GulimallMemberApplication {

    public static void main(String[] args) {
        SpringApplication.run(GulimallMemberApplication.class, args);
    }
}

​ 声明接口的每一个方法都是调用哪个远程服务的那个请求

3)、开启远程调用功能

com.bigdata.gulimall.member.controller.MemberController

    @RequestMapping("/coupons")
    public R test(){
        MemberEntity memberEntity=new MemberEntity();
        memberEntity.setNickname("zhangsan");
        R memberCoupons = couponFeignService.memberCoupons();

        return memberCoupons.put("member",memberEntity).put("coupons",memberCoupons.get("coupons"));
    }

  • 配置中心

1)修改“gulimall-coupon”模块

添加pom依赖:

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

创建bootstrap.properties文件,该配置文件会优先于“application.yml”加载。

spring.application.name=gulimall-coupon
spring.cloud.nacos.config.server-addr=192.168.137.14:8848

2)传统方式

为了详细说明config的使用方法,先来看原始的方式

创建“application.properties”配置文件,添加如下配置内容:

coupon.user.name="zhangsan"
coupon.user.age=30

修改“com.bigdata.gulimall.coupon.controller.CouponController”文件,添加如下内容:

    @Value("${coupon.user.name}")
    private String name;
    @Value("${coupon.user.age}")
    private Integer age;

    @RequestMapping("/test")
    public R getConfigInfo(){
       return R.ok().put("name",name).put("age",age);
    }

启动“gulimall-coupon”服务:

访问:http://localhost:7000/coupon/coupon/test>

![1587716583668](C:/Users/fei/AppData/Local/Temp/fe7397b3-337b-4060-a13f-8b450cfa0160_Guli Mall(包含代码、课件、sql).rar.160/Guli Mall/文档/三阶段大纲md/images/1587716583668.png)

这样做存在的一个问题,如果频繁的修改application.properties,在需要频繁重新打包部署。下面我们将采用Nacos的配置中心来解决这个问题。

3)nacos config

1、在Nacos注册中心中,点击“配置列表”,添加配置规则:

![1587716911435](C:/Users/fei/AppData/Local/Temp/fe7397b3-337b-4060-a13f-8b450cfa0160_Guli Mall(包含代码、课件、sql).rar.160/Guli Mall/文档/三阶段大纲md/images/1587716911435.png)

DataID:gulimall-coupon

配置格式:properties

文件的命名规则为:${spring.application.name}-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}

${spring.application.name}:为微服务名

${spring.profiles.active}:指明是哪种环境下的配置,如dev、test或info

${spring.cloud.nacos.config.file-extension}:配置文件的扩展名,可以为properties、yml等

2、查看配置:

![1587717125580](C:/Users/fei/AppData/Local/Temp/fe7397b3-337b-4060-a13f-8b450cfa0160_Guli Mall(包含代码、课件、sql).rar.160/Guli Mall/文档/三阶段大纲md/images/1587717125580.png)

3、修改“com.bigdata.gulimall.coupon.controller.CouponController”类,添加“@RefreshScope”注解

@RestController
@RequestMapping("coupon/coupon")
@RefreshScope
public class CouponController {

这样都会动态的从配置中心读取配置.

4、访问:http://localhost:7000/coupon/coupon/test

![1587717485283](C:/Users/fei/AppData/Local/Temp/fe7397b3-337b-4060-a13f-8b450cfa0160_Guli Mall(包含代码、课件、sql).rar.160/Guli Mall/文档/三阶段大纲md/images/1587717485283.png)

能够看到读取到了nacos 中的最新的配置信息,并且在指明了相同的配置信息时,配置中心中设置的值优先于本地配置。

4)Nacos支持三种配置加载方方案

Nacos支持“Namespace+group+data ID”的配置解决方案。

详情见:https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-docs/src/main/asciidoc-zh/nacos-config.adoc

Namespace方案

通过命名空间实现环境区分

下面是配置实例:

1、创建命名空间:

“命名空间”—>“创建命名空间”:

![1587718802109](C:/Users/fei/AppData/Local/Temp/fe7397b3-337b-4060-a13f-8b450cfa0160_Guli Mall(包含代码、课件、sql).rar.160/Guli Mall/文档/三阶段大纲md/images/1587718802109.png)

创建三个命名空间,分别为dev,test和prop

2、回到配置列表中,能够看到所创建的三个命名空间

![1587718889316](C:/Users/fei/AppData/Local/Temp/fe7397b3-337b-4060-a13f-8b450cfa0160_Guli Mall(包含代码、课件、sql).rar.160/Guli Mall/文档/三阶段大纲md/images/1587718889316.png)

下面我们需要在dev命名空间下,创建“gulimall-coupon.properties”配置规则:

![1587719108947](C:/Users/fei/AppData/Local/Temp/fe7397b3-337b-4060-a13f-8b450cfa0160_Guli Mall(包含代码、课件、sql).rar.160/Guli Mall/文档/三阶段大纲md/images/1587719108947.png)

3、访问:http://localhost:7000/coupon/coupon/test

![1587721184218](C:/Users/fei/AppData/Local/Temp/fe7397b3-337b-4060-a13f-8b450cfa0160_Guli Mall(包含代码、课件、sql).rar.160/Guli Mall/文档/三阶段大纲md/images/1587721184218.png)

并没有使用我们在dev命名空间下所配置的规则,而是使用的是public命名空间下所配置的规则,这是怎么回事呢?

查看“gulimall-coupon”服务的启动日志:

2020-04-24 16:37:24.158  WARN 32792 --- [           main] c.a.c.n.c.NacosPropertySourceBuilder     : Ignore the empty nacos configuration and get it based on dataId[gulimall-coupon] & group[DEFAULT_GROUP]
2020-04-24 16:37:24.163  INFO 32792 --- [           main] c.a.nacos.client.config.utils.JVMUtil    : isMultiInstance:false
2020-04-24 16:37:24.169  INFO 32792 --- [           main] b.c.PropertySourceBootstrapConfiguration : Located property source: [BootstrapPropertySource {name='bootstrapProperties-gulimall-coupon.properties,DEFAULT_GROUP'}, BootstrapPropertySource {name='bootstrapProperties-gulimall-coupon,DEFAULT_GROUP'}]

"gulimall-coupon.properties",默认就是public命名空间中的内容中所配置的规则。

4、指定命名空间

如果想要使得我们自定义的命名空间生效,需要在“bootstrap.properties”文件中,指定使用哪个命名空间:

spring.cloud.nacos.config.namespace=a2c83f0b-e0a8-40fb-9b26-1e9d61be7d6d

这个命名空间ID来源于我们在第一步所创建的命名空间

![1587718802109](C:/Users/fei/AppData/Local/Temp/fe7397b3-337b-4060-a13f-8b450cfa0160_Guli Mall(包含代码、课件、sql).rar.160/Guli Mall/文档/三阶段大纲md/images/1587718802109.png)

5、重启“gulimall-coupon”,再次访问:http://localhost:7000/coupon/coupon/test

![1587720311349](C:/Users/fei/AppData/Local/Temp/fe7397b3-337b-4060-a13f-8b450cfa0160_Guli Mall(包含代码、课件、sql).rar.160/Guli Mall/文档/三阶段大纲md/images/1587720311349.png)

但是这种命名空间的粒度还是不够细化,对此我们可以为项目的每个微服务module创建一个命名空间。

6、为所有微服务创建命名空间

![1587720714101](C:/Users/fei/AppData/Local/Temp/fe7397b3-337b-4060-a13f-8b450cfa0160_Guli Mall(包含代码、课件、sql).rar.160/Guli Mall/文档/三阶段大纲md/images/1587720714101.png)

7、回到配置列表选项卡,克隆pulic的配置规则到coupon命名空间下

![1587720883244](C:/Users/fei/AppData/Local/Temp/fe7397b3-337b-4060-a13f-8b450cfa0160_Guli Mall(包含代码、课件、sql).rar.160/Guli Mall/文档/三阶段大纲md/images/1587720883244.png)

切换到coupon命名空间下,查看所克隆的规则:

![1587720963699](C:/Users/fei/AppData/Local/Temp/fe7397b3-337b-4060-a13f-8b450cfa0160_Guli Mall(包含代码、课件、sql).rar.160/Guli Mall/文档/三阶段大纲md/images/1587720963699.png)

8、修改“gulimall-coupon”下的bootstrap.properties文件,添加如下配置信息

spring.cloud.nacos.config.namespace=7905c915-64ad-4066-8ea9-ef63918e5f79

这里指明的是,读取时使用coupon命名空间下的配置。

9、重启“gulimall-coupon”,访问:http://localhost:7000/coupon/coupon/test

![1587721184218](C:/Users/fei/AppData/Local/Temp/fe7397b3-337b-4060-a13f-8b450cfa0160_Guli Mall(包含代码、课件、sql).rar.160/Guli Mall/文档/三阶段大纲md/images/1587721184218.png)

DataID方案

通过指定spring.profile.active和配置文件的DataID,来使不同环境下读取不同的配置,读取配置时,使用的是默认命名空间public,默认分组(default_group)下的DataID。

默认情况,Namespace=public,Group=DEFAULT GROUP,默认Cluster是DEFAULT

Group方案

通过Group实现环境区分

实例:通过使用不同的组,来读取不同的配置,还是以上面的gulimall-coupon微服务为例

1、新建“gulimall-coupon.properties”,将它置于“tmp”组下

![1587721616021](C:/Users/fei/AppData/Local/Temp/fe7397b3-337b-4060-a13f-8b450cfa0160_Guli Mall(包含代码、课件、sql).rar.160/Guli Mall/文档/三阶段大纲md/images/1587721616021.png)

2、修改“bootstrap.properties”配置,添加如下的配置

spring.cloud.nacos.config.group=tmp

3、重启“gulimall-coupon”,访问:http://localhost:7000/coupon/coupon/test

![1587721844449](C:/Users/fei/AppData/Local/Temp/fe7397b3-337b-4060-a13f-8b450cfa0160_Guli Mall(包含代码、课件、sql).rar.160/Guli Mall/文档/三阶段大纲md/images/1587721844449.png)

5)同时加载多个配置集

当微服务数量很庞大时,将所有配置都书写到一个配置文件中,显然不是太合适。对此我们可以将配置按照功能的不同,拆分为不同的配置文件。

如下面的配置文件:

server:
  port: 7000

spring:
  datasource:
    #MySQL配置
    driverClassName: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://192.168.137.14:3306/gulimall_sms?useUnicode=true&characterEncoding=UTF-8&useSSL=false
    username: root
    password: root

  application:
    name: gulimall-coupon
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.137.14:8848



mybatis-plus:
  global-config:
    db-config:
      id-type: auto
  mapper-locations: classpath:/mapper/**/*.xml


我们可以将,

数据源有关的配置写到一个配置文件中:

spring:
  datasource:
    #MySQL配置
    driverClassName: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://192.168.137.14:3306/gulimall_sms?useUnicode=true&characterEncoding=UTF-8&useSSL=false
    username: root
    password: root

和框架有关的写到另外一个配置文件中:

mybatis-plus:
  global-config:
    db-config:
      id-type: auto
  mapper-locations: classpath:/mapper/**/*.xml

也可以将上面的这些配置交给nacos来进行管理。

实例:将“gulimall-coupon”的“application.yml”文件拆分为多个配置,并放置到nacos配置中心

1、创建“datasource.yml”,用于存储和数据源有关的配置

spring:
  datasource:
    #MySQL配置
    driverClassName: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://192.168.137.14:3306/gulimall_sms?useUnicode=true&characterEncoding=UTF-8&useSSL=false
    username: root
    password: root

在coupon命名空间中,创建“datasource.yml”配置

![1587722798375](C:/Users/fei/AppData/Local/Temp/fe7397b3-337b-4060-a13f-8b450cfa0160_Guli Mall(包含代码、课件、sql).rar.160/Guli Mall/文档/三阶段大纲md/images/1587722798375.png)

2、将和mybatis相关的配置,放置到“mybatis.yml”中

mybatis-plus:
  global-config:
    db-config:
      id-type: auto
  mapper-locations: classpath:/mapper/**/*.xml

![1587722710432](C:/Users/fei/AppData/Local/Temp/fe7397b3-337b-4060-a13f-8b450cfa0160_Guli Mall(包含代码、课件、sql).rar.160/Guli Mall/文档/三阶段大纲md/images/1587722710432.png)

3、创建“other.yml”配置,保存其他的配置信息

server:
  port: 7000

spring:
  application:
    name: gulimall-coupon
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.137.14:8848

![1587722998265](C:/Users/fei/AppData/Local/Temp/fe7397b3-337b-4060-a13f-8b450cfa0160_Guli Mall(包含代码、课件、sql).rar.160/Guli Mall/文档/三阶段大纲md/images/1587722998265.png)

现在“mybatis.yml”、“datasource.yml”和“other.yml”共同构成了微服务的配置。

4、修改“gulimall-coupon”的“bootstrap.properties”文件,加载“mybatis.yml”、“datasource.yml”和“other.yml”配置

spring.cloud.nacos.config.extension-configs[0].data-id=mybatis.yml
spring.cloud.nacos.config.extension-configs[0].group=dev
spring.cloud.nacos.config.extension-configs[0].refresh=true

spring.cloud.nacos.config.extension-configs[1].data-id=datasource.yml
spring.cloud.nacos.config.extension-configs[1].group=dev
spring.cloud.nacos.config.extension-configs[1].refresh=true


spring.cloud.nacos.config.extension-configs[2].data-id=other.yml
spring.cloud.nacos.config.extension-configs[2].group=dev
spring.cloud.nacos.config.extension-configs[2].refresh=true

"spring.cloud.nacos.config.ext-config"已经被废弃,建议使用“spring.cloud.nacos.config.extension-configs”

5、注释“application.yml”文件中的所有配置

6、重启“gulimall-coupon”服务,然后访问:http://localhost:7000/coupon/coupon/test

![1587724212905](C:/Users/fei/AppData/Local/Temp/fe7397b3-337b-4060-a13f-8b450cfa0160_Guli Mall(包含代码、课件、sql).rar.160/Guli Mall/文档/三阶段大纲md/images/1587724212905.png)

7、访问:http://localhost:7000/coupon/coupon/list,查看是否能够正常的访问数据库

![1587724350548](C:/Users/fei/AppData/Local/Temp/fe7397b3-337b-4060-a13f-8b450cfa0160_Guli Mall(包含代码、课件、sql).rar.160/Guli Mall/文档/三阶段大纲md/images/1587724350548.png)

小结:

1)、微服务任何配置信息,任何配置文件都可以放在配置中心;

2)、只需要在bootstrap.properties中,说明加载配置中心的哪些配置文件即可;

3)、@Value, @ConfigurationProperties。都可以用来获取配置中心中所配置的信息;

4)、配置中心有的优先使用配置中心中的,没有则使用本地的配置。

2)、创建各个微服务项目

1)、了解人人开源项目,快速搭建后台脚手架
2)、修改代码调整为我们的业务逻辑
3)、创建各个微服务以及数据库

2. 分布式高级 (微服务架构篇)

2.1 Elasticsearch

2.1.1 基本概念

  • index

动词: 相当于mysl中的insert

名词: 相当于mysql中的database

  • type

在index中,可以定义一个或多个类型

类似于Mysql中的table, 每一种类型的数据放在一起

  • document

保存在某个index下, 某个type的一个数据. document是json格式的

2.2 Docker 镜像安装

# 安装Elasticsearch
docker pull elasticsearch:7.4.2 # 存储和检索数据
# 安装kibana 
docker pull kibana:7.4.2 #  可视化检索数据

free -m # 查看linux内存情况
# 设置挂载路径和配置
mkdir -p /mydata/elasticsearch/config
mkdir -p /mydata/elasticsearch/data
chmod -R 777 /mydata/elasticsearch
echo "http.host:0.0.0.0" >> /mydata/elasticsearch/config/elasticsearch.yml # 任何机器都可访问es服务


docker run --name elasticsearch -p 9200:9200 -p 9300:9300 \
-e "discovery.type=single-node" \
-e ES_JAVA_OPS="-Xms64m -Xmx512m" \
-v /mydata/elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \
-v /mydata/elasticsearch/data:/usr/share/elasticsearch/data \
-v /mydata/elasticsearch/plugins:/usr/share/elasticsearch/plugins \
--restart=always \
-d elasticsearch:7.4.2

docker run --name kibana -e ELASTICSEARCH_HOSTS:http://192.168.56.2:9200 -p 5601:5601 \
-v /mydata/kibana/config/kibana.yml:/usr/share/kibana/config/kibana.yml \
--restart=always
-d kibana:7.4.2


# 更新为开机自启
docker update elasticsearch --restart=always
docker update kibana --restart=alw

2.3 初步检索

_cat

GET
url/_cat/nodes
url/_cat/health
url/_cat/master
url/_cat/indices # 查看所有索引

索引与文档json

PUT or POST # 创建和更新
url/customer/external/1 # customer索引下的external类型保存1号数据
{ "name": "jack"}


DELETE  删除
# 删除id为1的文档
url/customer/external/1 
# 删除type
url/customer/external
# 删除index
url/customer

批量操作

POST customer/external/_bulk
{"index":{"_id":"1"}}
{"name":"jack"}
{"index":{"_id":"2"}}
{"name":"tom"}

Nginx镜像安装

# 先启动一个nginx镜像,用于复制配置文件
docker run -p 80:80  --name nginx -d nginx:1.10
docker container cp nginx:/etc/nginx .
docker stop nginx
docker rm nginx
docker run --restart=always --name nginx -p 80:80 \
-v /mydata/nginx/html:/usr/share/nginx/html \
-v /mydata/nginx/logs:/var/log/nginx \
-v /mydata/nginx/conf:/etc/nginx \
-d nginx:1.10

docker update nginx --restart=always

2.4. 压力测试

指标:

  • 响应时间

  • HPS: 每秒单击次数 Hits Per Second

  • TPS: 系统每秒处理交易数

  • QPS: 系统每秒处理查询次数

    • 如果某些业务有且仅有一个请求连接, 则QPS=TPS=HPS
    • TPS衡量整个业务流程, QPS衡量接口查询次数, HPS描述对服务器的单击请求
  • TPS,QPS,HPS越大越好

  • 一般情况下:

    • 金融行业: 1000TPS~50000TPS, 普通业务,不包含互联网业务
    • 保险行业: 100TPS~100000TPS, 不包含互联网业务
    • 制造业: 10TPS ~ 5000TPS
    • 互联网电子商务: 100000TPS ~ 1000000TPS
    • 互联网中型网站: 1000TPS ~ 50000TPS
    • 互联网小型网站: 500TPS ~ 10000TPS

缓存与分布式锁

2.5 RabitMQ

docker run -d --name rabbitmq -p 5671:5671 -p 5672:5672 -p 4369:4369 -p 25672:25672 -p 15671:15671 -p 15672:15672 ----restart=always rabbitmq:management

RabbitMQ整合Spring Boot

  1. 引入依赖
<!--引入RabbitMQ : AMQP(高级消息队列协议) Advanced-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

依赖引入之后,RabbitAutoConfiguration就会自动生效。

给容器中自动配置: RabbitTemplate, AmqpAdmin, CachingConnectionFactory等

  1. application yaml配置
spring:
  rabbitmq:
    host: 192.168.56.2
    port: 5672
    virtual-host: "/"
    username: guest
    password: guest

启动类添加注解:

@EnableRabbit
  1. 测试RabbitMQ
    @Autowired
    AmqpAdmin amqpAdmin;

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Test
    void createExchange(){
        DirectExchange directExchange = new DirectExchange("java.direct",true,false);
        amqpAdmin.declareExchange(directExchange);
        log.info("exchange创建成功 java.exchange");
    }

    @Test
    void createQueue(){
        Queue queue = new Queue("java.queue", true, false, false);
        amqpAdmin.declareQueue(queue);
        log.info("Queue创建成功");
    }
    @Test
    void createBinding(){
        Binding chinese = new Binding("chinese", Binding.DestinationType.QUEUE, "java.direct", "china-java", new HashMap<>());
        Binding binding = new Binding("java.queue", Binding.DestinationType.QUEUE, "java.direct", "java.message", new HashMap<>());
        amqpAdmin.declareBinding(binding);
        amqpAdmin.declareBinding(chinese);
        log.info("binding 创建成功");
    }

    @Test
    void sendMessage(){
        rabbitTemplate.convertAndSend("java.direct","china-java","this message is from java app");
        rabbitTemplate.convertAndSend("java.direct","java.message","this is message from java and this is only to java routing key");
        log.info("消息发送成功");
    }
  1. 监听消息
  • 类 + 方法上
@RabbitListener(queues = {"java.queue"}) // 必须开启@EnableRabbit, 注解需要再组件类或其内部的方法中
  • 方法上
@RabbitHandler //必须开启@EnableRabbit, 只能标注在方法上 

// 用法
@RabbitListener(queues = "queue")
public class MessageHandler {
    @RabbitHandler
    public void handleMessage(String message) {
        System.out.println("Received string message: " + message);
    }
    @RabbitHandler
    public void handleMessage(Integer message) {
        System.out.println("Received integer message: " + message);
    }
}

RabbitMQ消息确认机制 - 可靠抵达

发送端(producer)

  • confirmCallback: exchange接收到后会触发
  • returnCallback:queue接受到之后会触发

接收端(consumer)

  • ack:consumer接收到之后会触发

confirmCallback使用

# 1. 配置yaml
spring:
  rabbitmq:
    publisher-confirm-type: correlated # correlated 模式通过 CorrelationData 对象关联消息与确认结果,支持异步回调处理。
// 2. 配置RabbitTemplate
@Configuration
public class RabbitConfig {

    @Autowired
    private RabbitTemplate rabbitTemplate;
    @PostConstruct // RabbitConfig对象创建完成之后,进行本方法调用
    public void initRabbitTemplate(){
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            
            // 只要消息被交换机接受到了就会回调本方法
            @Override // 当前消息id,消息成功否?,失败原因
            public void confirm(CorrelationData correlationData, boolean b, String s) {
                System.out.println("确认回调, " + co);
            }
        });
    }
    
    
     /**
     * 使消息发送到rabbitmq时自动转化为json格式
     * @return
     */
    // @Bean
    // public MessageConverter messageConverter(){
    //     return new Jackson2JsonMessageConverter();
    // }
}

retrunCallback使用

# 1. yaml 配置
spring:
  rabbitmq:
	publisher-returns: true
    template:	
      mandatory: true  # 只要抵达队列,以异步优先回调returnCallback
/**
* 只要没有抵达队列,就会发送回调
*/
rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
    @Override
    //投递失败的详细信息, 回复状态码,回复内容,交换机,路由键
    public void returnedMessage(Message message, int i, String s, String s1, String s2) {
        System.out.println("Return回调:" + message + "  i:" + i + " s:" + s + " s1:" + s1 + " s2:" + s2 );
    }
});

//Return回调:(Body:'"\"HELLO�LLBACK\""' MessageProperties [headers={__TypeId__=java.lang.String}, contentType=application/json, contentEncoding=UTF-8, contentLength=0, receivedDeliveryMode=PERSISTENT, priority=0, deliveryTag=0])  i:312 s:NO_ROUTE s1:my.exchange.topic s2:chinese

ack消息确认机制使用

# 1. yaml配置
spring:
  rabbitmq:
    listener:
      simple:
        acknowledge-mode: manual # 手动确认接受消息,不确认不成功处理
// 2. 手动确认接受
@RabbitListener(queues = {"chinese"})
public void receiveMessage(Message message, Channel channel){
    System.out.println(message);
    long deliveryTag = message.getMessageProperties().getDeliveryTag(); // 通道内自增的
    System.out.println("DeliveryTag ==> " + deliveryTag);

    try {
        channel.basicAck(deliveryTag,false); // 不批量ack
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

//注意: idea直接关闭程序不算是真正宕机,它会执行之后的ack代码

2.6 订单业务

2..6.1 订单中心

电商系统涉及到3流,分别是信息流,资金流、物流。而订单系统作为中枢将三者有机集合起来。

订单模块是电商系统的枢纽,在订单这个环节上需求获取多个模块的数据和信息,同时对这些信息进行加工处理后流向下个环节。这一系列就构成了订单的信息流通。

3. 高可用集群(架构师提升篇)

posted @ 2025-11-23 21:37  飞↑  阅读(2)  评论(0)    收藏  举报