微服务架构
单体架构
所有功能集成在一起,打成一个jar包部署
- 架构简单
- 部署成本低
- 协作能力差、
- 发布效率低
- 系统可用性差
面对高并发时容易卡顿

300个进程连续访问,tomcat资源耗尽,访问非常卡。
微服务架构
把单体架构中的功能拆分为多个独立的项目
- 粒度小
- 团队自制
- 服务自制 (分别打包,分别部署) 数据隔离
单体架构的拆分
什么时候需要拆分
单体架构快速开发,规模扩大后拆分
确定的项目,资金充足,可直接选择微服务架构
怎么拆
- 高内聚 : 职责尽量单一
- 低耦合 : 服务独立,减少对其他微服务的依赖
- 横向拆分 按照业务模块拆分
- 横向拆分 抽取公共服务提高复用性
结构
- 独立project 适合超大型项目
- Maven聚合
两个项目的联系
通过网络请求联系 访问两个数据库,通过请求获取数据
如何用java发送网络请求
RestTemplate 工具 (存在一些问题)
注册中心
- 注册服务信息
- 订阅服务
- 负载均衡
- 发送请求,远程调用
- 所有服务定期向服务中心发送请求,没收到请求就会剔除这个服务
- 推送变更
Nacos(注册中心组件 ) 阿里巴巴 Nacos官网
部署
可参考我的另一篇文章
https://laning.com.cn/archives/nacosbu-shu
服务注册
- 引入依赖

<!--nacos 服务注册发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> <!-- 版本号在父工程中定义-->
</dependency>
- 配置yaml文件

- 配置自己的服务器/虚拟机ip
- 之后重新启动会自动进行服务注册

多服务启动。

服务发现
- 与服务注册相同 前两部需要引入依赖和 配置yaml文件
- 加入bean方法
@Bean public RestTemplate restTemplate() { return new RestTemplate(); } - 注入要调用的类中

- 获取服务示例,写负载均衡

OpenFeign
声明式的http客户端 ,帮助java发送http请求
入门
- 引入依赖
<!--openFeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--负载均衡器-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
- 启动类加入注解 (@EnableFeignClients)
- 编写FeignClient

- 在service中注入itemClient
- 调用方法


连接池
底层实现原理是用 Client 接口实现 效率低
- 引入依赖

- 配置开关
feign:
okhttp:
enabled: true # 开启OKHttp功能
OKHttp 中有连接池效率高
最佳方案

- 使用新的模块 (耦合度高)

创建新模块(引入依赖, 加入dto , client)

在cart-service中删除dto和client ,导入hm-api的依赖
<!--hm-api-->
<dependency>
<groupId>com.heima</groupId>
<artifactId>hm-api</artifactId>
<version>1.0.0</version>
</dependency>
在启动类中加入,需要扫描的api包(不然扫描不到 client)
@EnableFeignClients(basePackages = "com.hmall.api.client")
日志
只有级别为debug时才会输出 , 且日志有四个级别
- NONE 不记录任何日志 (默认)
- BASIC 仅记录请求的方法,URL ,响应码和时间
- HEADERS 在BASIC基础上额外记录了请求和响应头的信息
- FULL 记录所有请求和响应的明细, 包括头信息、请求体、元数据
全局配置
package com.hmall.api.config;
import feign.Logger;
import org.springframework.context.annotation.Bean;
public class DefaultFeignConfig {
@Bean
public Logger.Level feignLogLevel(){
return Logger.Level.FULL;
}
}
网关
- 网络的关口, 负责请求的路由,转发和身份校验。
微服务必不可少的组件, 从注册中心拉取各种微服务地址发送给前端
网关路由
- 创建网关微服务
- 引入SpringCloudGateway、NacosDiscovery依赖
- 编写启动类
- 配置网关路由
spring cloud gateway
配置文件
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>hmall</artifactId>
<groupId>com.heima</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>hm-gateway</artifactId>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<!--common-->
<dependency>
<groupId>com.heima</groupId>
<artifactId>hm-common</artifactId>
<version>1.0.0</version>
</dependency>
<!--网关-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--nacos discovery-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--负载均衡-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
server:
port: 8080
spring:
application:
name: gateway
cloud:
nacos: # 注册中心
server-addr: 192.168.100.132
gateway:
routes:
- id: item-service # 路由的ID,没有固定规则但要求唯一,建议配合服务名
uri: lb://item-service # 目标微服务的地址,lb表示负载均衡
predicates: # 断言,即判断该请求是否需要转发
- Path=/items/**,/search/** # 匹配的路径-
- id: user-service
uri: lb://user-service
predicates:
- Path=/addresses/**,/users/**
路由规则
- id 路由的唯一标识
- uri 目标路由地址
- predicates 路由断言, 判断是否是当前路由
- filters 路由过滤器,对请求或响应做特殊处理
身份验证
- 在网关做jwt校验
自定义过滤器
- GatewayFilter 路由过滤器选择生效
- GlobalFilter 全局过滤器, 声明后全局生效
-
package com.hmall.gateway.filters; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.http.HttpHeaders; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; @Component public class MyGlobalFilter implements GlobalFilter { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { // TODO 模拟实现登录逻辑 ServerHttpRequest request = exchange.getRequest(); HttpHeaders headers = request.getHeaders(); System.out.println("请求头:" + headers); // 放行 return chain.filter(exchange); } public int getOrder() { // 过滤器优先级 , 值越小优先级越高 return 0; } }
配置管理(Nacos)
配置共享

在nacos中编写配置文件
配置热更新
修改微服务时无需重启使配置生效
动态路由
服务保护和分布式事务
雪崩问题
- 由于某个服务故障导致所有微服务都不可用
- 服务提供者出现故障
- 调用者没有做好异常处理 , 导致自身出现故障
- 级联失败 ,集群出现故障
解决方案
- 请求限流
限制访问的并发量 , 避免出现故障,使用限流器
- 线程隔离
限定每个业务能使用的线程数量将故障业务隔离
- 服务熔断
断路器统计监测请求异常 ,熔断业务,拦截请求使用 fallback 的逻辑
Sentinel
安装
- 安装 sentinel 为一个jar包

打开cmd,执行这个命令

java -Dserver.port=8090 -Dcsp.sentinel.dashboard.server=localhost:8090 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar
访问本机8090端口
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2025-11-14 11:09:25.772 ERROR 15684 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
Web server failed to start. Port 8090 was already in use.
Action:
Identify and stop the process that's listening on port 8090 or configure this application to listen on another port.
这个错误就是8090端口被占用了
java -Dserver.port=8091 -Dcsp.sentinel.dashboard.server=localhost:8090 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar
启动8091端口

账号密码都为 sentinel
引入依赖
<!--sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8090

限流
限流测试

- 在簇点链路设置单机阈值 , 进行测试, 我设置为6 ,一秒之内只能处理6个请求,超出的请求会报错
- 使用JMeter进行测试,1秒发送10个请求 ,可以看到6个成功 ,4个失败,成功率大概60%
- 失败返回状态码为 429 限流错误


线程隔离

Fallback
线程隔离 , 无法得到线程所执行的方法
- 定义 FallbackFactory 继承 FallbackFactory< T >
- 泛型为需要的 Client 方法

- 注册Bean
- 添加在Client注解
@FeignClient(value = "item-service" , fallbackFactory = ItemClientFallbackFactory.class , configuration = DefaultFeignConfig.class)
feign:
sentinel:
enabled: true # 开启Sentinel功能
- 添加sentinel 配置 使远程Client调用也成为簇点规则, 配置远程调用的规则。
这样查询失败后不再是异常 ,返回空结果 ,不影响其他调用。
异常为 0%

服务熔断
拦截访问该服务的所有请求 ,直接走Fallback ,当服务恢复时放行访问。
- 断路器(closed , Open , Half-Open)

RT 超过RT算慢 , 比例阈值 慢的比例超过多少进行熔断 , 熔断时长Open状态维持的时间
分布式事务(难)
一次调用需要多个服务合作完成,必须同时成功或者失败,这就是分布式事务
Seata
- TC 单独的微服务
- TM 引入依赖
- RM 引入依赖
TC 服务的部署 , 在虚拟机环境部署。
- 在虚拟机mysql中执行sql脚本
- 首先检查nacos容器在哪个网络下
docker inspect nacos
# 这个就是网络名 bridge 和 heima
"Networks": {
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"MacAddress": "02:42:ac:11:00:02",
"NetworkID": "609f9badbe96938b3d091aef8b978390509467a62d9331b2b6e35e5200d33bdd",
"EndpointID": "568a59f42ae260105b9f94bf101c460d72c93fb4ff8dc53c078f1f54262cdaa5",
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"DriverOpts": null,
"DNSNames": null
},
"heima": {
"IPAMConfig": {},
"Links": null,
"Aliases": [],
"MacAddress": "02:42:ac:12:00:02",
"NetworkID": "e3bb39e2165d24c719c49a9946c6643565b8a6bd79a48dcfc690aeb4d37a9acf",
"EndpointID": "e7ebc7937eb5a4fdb6ce0f8e36a268af0d67920d7034eaebb7848c57fedc10ce",
"Gateway": "172.18.0.1",
"IPAddress": "172.18.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"DriverOpts": {},
"DNSNames": [
"mysql",
"5d758047248d"
]
}
}
我的mysql 和 nacos 都在 heima 这个网络下
如果不在可以用,这个指令添加到一个网络中
docker network connect [网络名] [容器名]
- 导入seata的tar包和配置文件
- 执行docker命令, $$ ip地址需要改为自己的 , 我这个版本是 1.5.2, 大家可以使用最新版的, 我因为tar包是1.5.2所以就用1.5.2 , 这个网络名也需要改为自己的哦
docker run --name seata \
-p 8099:8099 \
-p 7099:7099 \
-e SEATA_IP=192.168.100.132 \
-v ./seata:/seata-server/resources \
--privileged=true \
--network heima \
-d \
seataio/seata-server:1.5.2


seata-server 这个服务添加成功了
访问虚拟机的7099端口

账号密码都是admin,这样我们就成功启动这个TC服务
在微服务中集成 Seata
- 引入依赖
<!--统一配置管理-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--读取bootstrap文件-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<!--seata-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
- yaml配置(抽取到 nacos) 把需要的服务引入nacos的配置
seata:
registry: # TC服务注册中心的配置,微服务根据这些信息去注册中心获取tc服务地址
type: nacos # 注册中心类型 nacos
nacos:
server-addr: 192.168.100.132:8848 # nacos地址
namespace: "" # namespace,默认为空
group: DEFAULT_GROUP # 分组,默认是DEFAULT_GROUP
application: seata-server # seata服务名称
username: nacos
password: nacos
tx-service-group: hmall # 事务组名称
service:
vgroup-mapping: # 事务组与tc集群的映射关系
hmall: "default"


XA模式
XA规范描述了全局TM和局部RM之间的接口
- 实现简单,满足ACID的规则
- 常用数据库都支持
- 在第一阶段锁定数据库资源,结束才会释放,性能较差。
XA模式 的使用方法
- 配置yaml文件 , 开启XA模式
seata:
data-source-proxy-mode: XA
- 在方法上使用 @GlobalTransactional 注解
AT模式
AT模式执行完立刻提交sql , 弥补XA模型资源锁定缺陷。在执行sql之前,记录快照 。
- TC判断执行成功,删除快照
- TC判断执行失败,恢复快照
- AT模式,服务如果出现异常,在回滚之前会存在一段时间数据异常。(但是持续时间较短 ,发生服务异常概率也少低 , 一般不用考虑)
AT模式 的使用方法
- 为每个微服务提供,一个快照的表
-- for AT mode you must to init this sql for you business database. the seata server not need it.
CREATE TABLE IF NOT EXISTS `undo_log`
(
`branch_id` BIGINT NOT NULL COMMENT 'branch transaction id',
`xid` VARCHAR(128) NOT NULL COMMENT 'global transaction id',
`context` VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization',
`rollback_info` LONGBLOB NOT NULL COMMENT 'rollback info',
`log_status` INT(11) NOT NULL COMMENT '0:normal status,1:defense status',
`log_created` DATETIME(6) NOT NULL COMMENT 'create datetime',
`log_modified` DATETIME(6) NOT NULL COMMENT 'modify datetime',
UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE = InnoDB
AUTO_INCREMENT = 1
DEFAULT CHARSET = utf8mb4 COMMENT ='AT transaction mode undo table';
- 在配置文件配置AT模式(不设置,自动也是AT)
seata:
data-source-proxy-mode: AT # 或者直接不写
XA模式 强一致 , AT模式 最终一致 , 但 AT模式效率比XA模式高。所以需要看情况选择(一般为AT模式)如果对一致要求非常高,那就选择XA模式。
结束
经过一个月的时间, 我的微服务这篇总算是写完了。吧唧吧唧 ☆: .。. o(≧▽≦)o .。.:☆
下一篇为中间件 MQ的学习。
数据分析还没学完 悲(;´д`)ゞ
Bay Bay!