一、项目前言
1.1 什么是微服务
微服务架构风格,就像是把一个单独的应用程序开发为一套小服务,每个小服务运行在自己的进程中,并使用轻量级机制通信,通常是HTTP API。这些服务围绕业务能力来构建,并通过完全自动化部署机制来独立部署。这些服务使用不同的编程语言书写,以及不同数据存储技术,并保持最低限度的集中式管理。(个人理解就是把大的项目拆分,服务拆分部署)
1.2 微服务的基础概念
1.2.1 服务注册/发现&注册中心
A服务调用B服务,A服务并不知道B服务当前在哪几台服务器有,那些是正常的,那些服务已经下线。我们可以引入配置中心来解决这个问题,本项目用的是nacos配置中心的主要作用是服务的注册和发现。
1.2.2 服务熔断&服务降级
在微服务架构中,微服务之间通过网络进行通信,存在相互依赖,当其中一个服务不可用时,可能会导致其他服务等待,比如a服务 -->b服务 --> c服务,因为c服务宕机,有可能会造成雪崩效应。要防止这样的情况,必须要有容错机制来保护服务。为了防止这样的情况一般会有这样两种方法来处理。
- 服务熔断
设置服务的超时,当被调用的服务经常失败到达某个阈值,我们可以开启断路保护机制,后来的请求不再去调用这个服务。本地直接返回默认的数据 - 服务的降级
简单来说就是有些业务不处理或者简单处理(抛异常等)
1.2.3 网关
提供了客户端负载均衡,服务自动熔断,灰度发布,统—认证,限流流控,日志统计等丰富的功能,帮助我们解决很多API管理难题。
1.2.4 项目架构
1.2.5 微服务划分
二、环境搭建
2.1 安装虚拟机(vgrant)
- 下载安装VirtualBox https://www.virtualbox.org/,要开启CPu虚拟化(一路next就好)
- 下载&安装Vagrant ==(https://app. vagrantup.com/boxes/search Vagrant官方镜像仓库 https://www. vagrantup.com/doknloads.html Vagrant下载)有了他我们可以很方便的构建一个centos7 当然也有其他系统镜像
- 打开window cmd窗口(可以直接win+r打开),运行Vagrant init centos/7,即可初始化一个centos7系统
- 运行vagrant up即可启动虚拟机。系统root用户的密码是vagrant(博主这边老是失败比较烦,最后用了vamware来代替 )
- 连接的话出来用连接工具在windows命令行中也可以连接通过 vagrant ssh exit退出
https://blog.csdn.net/u013761049/article/details/106965188/一直卡在最后一步并且虚拟化也设置了,还是没有用的可以用一下这位大佬的解决方案 升级一下版本博主就是这样解决的
2.2 虚拟机网络配置
主要就是为了我们在访问一个中间件的时候 可以直接通过ip加端口号来直接使用不需要我们一个个端口映射去配置,示意图如下:
步骤
- config.vm.network “private_network”, ip: "192.168.56.10"找到生成的Vagrantfile中的这段配置
- 进入cmd 输入ipconfig找到 以太网适配器 VirtualBox Host-Only Network: 查看ipv4地址 修改上述的配置
- 检测是否配置成功:用我们的虚拟机去和宿主机相互ping
2.3 虚拟机安装docker
#卸载系统之前的docker
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
sudo yum install -y yum-utils
# 配置镜像
sudo yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo
sudo yum install docker-ce docker-ce-cli containerd.io
sudo systemctl start docker
# 设置开机自启动
sudo systemctl enable docker
# 查看docker版本
docker -v
# 查看镜像
sudo docker images
# 配置阿里云镜像加速
# https://cr.console.aliyun.com/cn-qingdao/instances/mirrors
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
2.4 docker安装mysql
# --拉取镜像不写版本默认最新
sudo docker pull mysql:5.7
# --查看镜像
sudo docker images
# --name指定容器名字 -v目录挂载 -p指定端口映射 -e设置mysql参数这边给了一个默认密码 -d后台运行
sudo 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
# --查看容器
sudo docker ps
# 进入容器内部 可以理解为一个单独的容器 “mysql就是一个linux”
docker exec -it mysql bin/bash
exit;
# 修改配置文件
vi /mydata/mysql/conf/my.conf
[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
docker restart mysql
2.5 docker安装redis
# 创建关联的数据卷文件夹
mkdir -p /mydata/redis/conf
touch /mydata/redis/conf/redis.conf
docker pull redis
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 exec -it redis redis-cli
vim /mydata/redis/conf/redis.conf
# redis默认数据在内存 重启就没了 开启持久化
appendonly yes
docker restart redis
# 启动redis客户端
docker exec -it redis redis-cli
# 设置开机自启动
docker update redis --restart=always
三、项目搭建
3.1项目结构
3.1.1父pom
<?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.atguigu.gulimall</groupId>
<artifactId>gulimall</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>gulimall</name>
<description>聚合服务</description>
<packaging>pom</packaging>
<properties>
<java.version>1.8</java.version>
</properties>
<modules>
<module>gulimall-coupon</module>
<module>gulimall-member</module>
<module>gulimall-order</module>
<module>gulimall-product</module>
<module>gulimall-ware</module>
<module>renren-fast</module>
<module>renren-generator</module>
<module>gulimall-common</module>
<module>gulimall-gateway</module>
<module>gulimall-third-party</module>
<module>gulimall-search</module>
</modules>
</project>
3.2后台管理的搭建(人人开源-https://gitee.com/renrenio)
- 去码云下载下来 加入我们的项目中(记得刷新maven下载依赖)
- 运行项目提供的sql 刷一下数据
- 配置文件修改自己的mysql信息
<modules>
<module>gulimall-coupon</module>
<module>gulimall-member</module>
<module>gulimall-order</module>
<module>gulimall-product</module>
。。。。。。。
<module>renren-fast</module>
</modules
3.3后台管理前端的搭建(人人开源)
- 确保你们的电脑有node环境,没有要下载一个(不要用12.0,可以用12.1)
- 安装完成 命令行输入node -v 检查是不是配置好了
- 配置国内镜像
- npm install拉取依赖(拉取失败 可以参考这个博主的https://www.cnblogs.com/misscai/p/12809404.html)
node -v
npm config set registry http://registry.npm.taobao.org/
3.4模块搭建和逆向工程
3.4.1模块搭建(略根据视频搭建引入相关依赖就好)
3.4.2逆向工程(根据数据库来实现crud基本代码和一些实体类)
- 下载代码(renren-gentor项目)
- 刷新下载maven依赖
- 修改application.yml
- 这边换成你自己的数据库信息
url: jdbc:mysql://192.168.56.10:3306/gulimall-pms?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: root
- 然后修改generator.properties()
# 主目录
mainPath=com.atguigu
#包名
package=com.atguigu.gulimall
#模块名
moduleName=product
#作者
author=lujun
#email
email=502598771@qq.com
#表前缀(类名不会包含表前缀) # 我们的pms数据库中的表的前缀都pms
# 如果写了表前缀,每一张表对于的javaBean就不会添加前缀了
tablePrefix=pms_
选择我们要生成的表
3.4.3模块配置
common(每个模块通用的一些东西)
- 在common项目的pom.xml中添加
<!-- mybatisPLUS-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.2</version>
</dependency>
<!--简化实体类,用@Data代替getset方法-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
</dependency>
<!-- httpcomponent包。发送http请求 -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.4.13</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
报错看缺少什么依赖去renrenfast来找
书写各个模块的配置一般都有 以product模块为例
- 配置
spring:
datasource:
username: root
password: root
url: jdbc:mysql://192.168.56.10:3306/gulimall-sms?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis-plus:
mapper-locations: classpath:/mapper/**/*.xml
global-config:
db-config:
id-type: auto
# 逻辑删除值
logic-delete-value: 1
logic-not-delete-value: 0
server:
port: 10000
classpath 和 classpath* 区别: classpath:只会到你的class路径中查找找文件;
classpath*:不仅包含class路径,还包括jar文件中(class路径)进行查找classpath*的使用:当项目中有多个classpath路径,还要加载多个classpath路径下(一般不会)的文件,就发挥了作用,如果不加,则表示仅仅加载第一个classpath路径。
- 然后在主启动类上加上注解@MapperScan(“com.atguigu.gulimall.product.dao”)
- 测试
@SpringBootTest
class gulimallProductApplicationTests {
@Autowired
BrandService brandService;
@Test
void contextLoads() {
BrandEntity brandEntity = new BrandEntity();
brandEntity.setDescript("111");
brandEntity.setName("222");
brandService.save(brandEntity);
System.out.println("保存成功");
}
}
四、springcloudAlibaba
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
4.1 nacos
4.1.1 nacos作为服务中心
nacos作为我们的注册中心和配置中心。(还有eruka和duboo可以对比学习)
注册中心文档:https://github.com/alibaba/spring-cloud-alibaba/tree/master/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example
- 下载 然后直接bin里面找到stratup运行就行
- 在项目使用里使用写
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
。再指定applicatin.name
告诉注册到nacos中以什么命名 - 在我们的项目中应为都要使用所以在common的依赖里面加一下就好
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
- 在启动类加上 @EnableDiscoveryClient 注解开启服务注册与发现功能
- 配置展示
spring:
datasource:
username: root
password: root
url: jdbc:mysql://192.168.56.10:3306/gulimall-sms?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
driver-class-name: com.mysql.cj.jdbc.Driver
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
application:
name: gulimall-product
mybatis-plus:
mapper-locations: classpath:/mapper/**/*.xml
global-config:
db-config:
id-type: auto
logic-delete-value: 1
logic-not-delete-value: 0
server:
port: 10000
4.1.1 nacos作为配置中心
我们还可以用nacos作为配置中心。配置中心的意思是不在application.properties或者yml等文件中配置了,而是放到nacos配置中心公用,这样可以很便利的改一些公用的配置
- common中添加依赖 nacos配置中心
- 在coupons项目中创建/src/main/resources/bootstrap.properties
# 对应nacos里的配置文件名
spring.application.name=gulimall-coupon
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
- 登录nacos
- 我们可以动态加载就是修改了配置不需要重启项目 加@RefreshScope就好了
配置中心进阶
在nacos还可以配置
- 命名空间:用作配置隔离。(相当于我们的配置环境)
- 默认public。默认新增的配置都在public空间下
# 可以选择对应的命名空间 # 写上对应环境的命名空间ID
spring.cloud.nacos.config.namespace=xx
- 更改环境也很简单修改配置即可
# 更改配置分组
spring.cloud.nacos.config.group=DEFAULT_GROUP
- 配置集 一组相关或不相关配置项的集合
- 配置集ID:类似于配置文件名,即Data ID
- 配置分组:默认所有的配置集都属于DEFAULT_GROUP。
:每个微服务创建自己的命名空间,然后使用配置分组区分环境(dev/test/prod)
spring.cloud.nacos.config.namespace=xxx
spring-cloud.nacos.config-group=dev
加载多配置集
场景 如果配置过多 就不能写一个yml里面我们要进行拆分 比如数据源配置 框架配置等
在配置中中用spring.cloud.nacos.config.extension-configs[]
写明每个配置集
spring.cloud.nacos.config.extension-configs[0].data-id=datasource.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=mybatis.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
4.2 openfeign远程调用(a服务掉b服务)
feign是一个声明式的HTTP客户端,他的目的就是让远程调用更加简单。给远程服务发的是HTTP请求。
使用:
- 依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
- 假设我们现在在cuppon服务中有这么一个接口 我们在会员服务中想要调用他
@RequestMapping("coupon/coupon")
public class CouponController {
@Autowired
private CouponService couponService;
@RequestMapping("/member/list")
public R membercoupons(){
CouponEntity couponEntity = new CouponEntity();
couponEntity.setCouponName("满11减50");
return R.ok().put("coupons",Arrays.asList(couponEntity));
}
- 在member的启动类加注解@EnableDiscoveryClient
@EnableFeignClients(basePackages="com.atguigu.gulimall.member.feign")//扫描接口方法注解
@EnableDiscoveryClient
@SpringBootApplication
public class gulimallMemberApplication {
public static void main(String[] args) {
SpringApplication.run(gulimallMemberApplication.class, args);
}
}
- com.atguigu.gulimall.member.feign包下新建类
@FeignClient("gulimall-coupon") //指定nacos中的服务名,具体是调用coupon服务的/coupon/coupon/member/list对应的方法
public interface CouponFeignService {
// 远程服务的url
@RequestMapping("/coupon/coupon/member/list")
public R membercoupons();//得到一个R对象
- 注入CouponFeignService就可以使用了
4.2 gateway网关
- 整个服务入口,网关动态地管理服务,他能从注册中心中实时地感知某个服务上线还是下线。
- 网关是请求流量的入口,常用功能包括路由转发,权限校验,限流控制等。springcloud gateway取代了zuul网关。
使用 - 创建网关服务 在父pom中加入其依赖
- 在gateway服务中开启注册服务发现@EnableDiscoveryClient 吧网关放入注册中心中
- 书写配置文件
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
spring.application.name=gulimall-gateway
server.port=88
- bootstrap.properties 填写nacos配置中心地址
spring.application.name=gulimall-gateway
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
spring.cloud.nacos.config.namespace=xxx
spring:
cloud:
gateway:
routes:
- id: test_route
uri: https://www.baidu.com
predicates:
- Query=url,baidu
- id: qq_route
uri: https://www.qq.com
predicates:
- Query=url,qq
- id: product_route
uri: lb://gulimall-product
predicates:
- Path=/api/product/**
filters:
- RewritePath=/api/(?<segment>.*),/$\{segment}
- id: third_party_route
uri: lb://gulimall-third-party
predicates:
- Path=/api/thirdparty/**
filters:
- RewritePath=/api/thirdparty/(?<segment>.*),/$\{segment}
- id: member_route
uri: lb://gulimall-member
predicates:
- Path=/api/member/**
filters:
- RewritePath=/api/(?<segment>.*),/$\{segment}
- id: ware_route
uri: lb://gulimall-ware
predicates:
- Path=/api/ware/**
filters:
- RewritePath=/api/(?<segment>.*),/$\{segment}
- id: admin_route
uri: lb://renren-fast
predicates:
- Path=/api/**
filters: # 这段过滤器和验证码有关,api内容缓存了/renren-fast,还得注意/renren-fast也注册到nacos中
- RewritePath=/api/(?<segment>.*),/renren-fast/$\{segment}
## 前端项目,/api前缀。开来到网关后断言先匹配到,过滤器修改url,比如跳转到renren微服务,所以要注意renren后端项目也注册到 nacos里
## http://localhost:88/api/captcha.jpg http://localhost:8080/renren-fast/captcha.jpg
## http://localhost:88/api/product/category/list/tree http://localhost:10000/product/category/list/tree