黑马商城微服务实战开发(微服务保护和分布式事务)
1.雪崩问题
微服务调用链路中某个服务故障,引起整个链路中的所有微服务都不可用,这就是雪崩。

雪崩产生的原因:

解决思路:

服务保护方案:
1.请求限流

2.线程隔离

3.服务熔断

总结:
解决雪崩问题的常见处理方案

2.Sentinel(服务保护技术)
2.1如何使用过sentinel
下载jar包

控制台命令行启动
java "-Dserver.port=8090" "-Dcsp.sentinel.dashboard.server=localhost:8090" "-Dproject.name=sentinel-dashboard" -jar "sentinel-dashboard.jar"
就可以在localhost:8090接口访问到sentinel的控制台
2.2微服务整合
1.引入sentinel依赖
<!--sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
2.配置控制台,修改yaml文件
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8090
3.重新访问端口就能看到簇点链路
所谓簇点链路,就是单机调用链路,是一次请求进入服务后经过的每一个被Sentinel监控的资源。默认情况下,Sentinel会监控SpringMVC的每一个Endpoint(接口)。
不过,需要注意的是,我们的SpringMVC接口是按照Restful风格设计,因此购物车的查询、删除、修改等接口全部都是/carts路径,默认情况下Sentinel会把路径作为簇点资源的名称,无法区分路径相同但请求方式不同的接口,查询、删除、修改等都被识别为一个簇点资源,这显然是不合适的,所以我们可以选择打开Sentinel的请求方式前缀,把请求方式 + 请求路径作为簇点资源名:
首先,在cart-service的application.yml中添加下面的配置:
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8090
http-method-specify: true #是否将请求方式作为资源名称
3.请求限流
在簇点链路后面点击流控按钮,即可对其做限流配置:


这样就把查询购物车列表这个簇点资源的流量限制在了每秒6个,也就是最大QPS为6.
拒绝访问状态码429(限流)
4.线程隔离
4.1正常使用
限流可以降低服务器压力,尽量减少因并发流量引起的服务故障的概率,但并不能完全避免服务故障。一旦某个服务出现故障,我们必须隔离对这个服务的调用,避免发生雪崩。
比如,查询购物车的时候需要查询商品,为了避免因商品服务出现故障导致购物车服务级联失败,我们可以把购物车业务中查询商品的部分隔离起来,限制可用的线程资源:
暂时无法在飞书文档外展示此内容
这样,即便商品服务出现故障,最多导致查询购物车业务故障,并且可用的线程资源也被限定在一定范围,不会导致整个购物车服务崩溃。
所以,我们要对查询商品的FeignClient接口做线程隔离。
需要注意的是,默认情况下SpringBoot项目的tomcat最大线程数是200,允许的最大连接是8492,单机测试很难打满。所以需要修改tomcat
server:
port: 8082
tomcat:
threads:
max: 50 # 允许的最大线程数
accept-count: 50 # 最大排队等待数量
max-connections: 100 # 允许的最大连接

测试发现:
此时如果我们通过页面访问购物车的其它接口,例如添加购物车、修改购物车商品数量,发现不受影响:
响应时间非常短,这就证明线程隔离起到了作用,尽管查询购物车这个接口并发很高,但是它能使用的线程资源被限制了,因此不会影响到其它接口。
4.2 fallback
上述只是查询业务的报错还是没有处理,这个我们可以利用fallback对异常做处理

案列:
给feignClient编写fallback逻辑
1.自定义FallbackFactory,编写对某个FeignClient的fallback逻辑
package com.hmall.api.client.fallback;
import com.hmall.api.client.ItemClient;
import com.hmall.api.dto.ItemDTO;
import com.hmall.api.dto.OrderDetailDTO;
import com.hmall.common.utils.CollUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.openfeign.FallbackFactory;
import java.util.Collection;
import java.util.List;
@Slf4j
public class ItemClientFallbackFactory implements FallbackFactory<ItemClient> {
@Override
public ItemClient create(Throwable cause) {
return new ItemClient() {
@Override
public List<ItemDTO> queryItemByIds(Collection<Long> ids) {
log.error("查询商品信息失败", cause);
return CollUtils.emptyList();
}
@Override
public void deductStock(List<OrderDetailDTO> details) {
log.error("扣减库存失败!!", cause);
throw new RuntimeException(cause);
}
};
}
}
2.将刚才定义的ItemClientFallbackFactory注册成一个bean
package com.hmall.api.config;
import com.hmall.api.client.fallback.ItemClientFallbackFactory;
import org.springframework.context.annotation.Bean;
public class DefaultFeignConfig {
@Bean
public ItemClientFallbackFactory itemClientFallbackFactory() {
return new ItemClientFallbackFactory();
}
}
3.在ItemClient接口中使用ItemClientFallbackFactory
package com.hmall.api.client;
import com.hmall.api.client.fallback.ItemClientFallbackFactory;
import com.hmall.api.dto.ItemDTO;
import com.hmall.api.dto.OrderDetailDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.Collection;
import java.util.List;
@FeignClient(value = "item-service",fallbackFactory = ItemClientFallbackFactory.class)
public interface ItemClient {
@GetMapping("/items")
List<ItemDTO> queryItemByIds(@RequestParam("ids") Collection<Long> ids);
@PutMapping("/items/deduct")
void deductStock(@RequestParam("details") List<OrderDetailDTO> details);
}
4.修改cart-service模块的application.yml文件,开启Feign的sentinel功能:
feign:
sentinel:
enabled: true # 开启feign对sentinel的支持
接下来,点击查询商品的FeignClient对应的簇点资源后面的流控按钮:


流控规则

5.服务熔断
对资源的一种保护,知道它已经挂了,就不走这个服务了,直接走fallback。

熔断思路

在客户端相应路径添加熔断规则即可

6.分布式事务

7. 认识seate
解决分布式事务的方案有很多,但实现起来都比较复杂,因此我们一般会使用开源的框架来解决分布式事务问题。在众多的开源分布式事务框架中,功能最完善、使用最多的就是阿里巴巴在2019年开源的Seata了。
seate解决思路

Seata也不例外,在Seata的事务管理中有三个重要的角色:
- TC (Transaction Coordinator) - 事务协调者:维护全局和分支事务的状态,协调全局事务提交或回滚。
- TM (Transaction Manager) - 事务管理器:定义全局事务的范围、开始全局事务、提交或回滚全局事务。
- RM (Resource Manager) - 资源管理器:管理分支事务,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。

8.微服务集成seata
黑马资料中有部署seata教程(所以部署跳过)
1.引入依赖
为了方便各个微服务集成seata,我们需要把seata配置共享到nacos,因此trade-service模块不仅仅要引入seata依赖,还要引入nacos依赖:
<!--统一配置管理-->
<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>
依赖不需要引入common模块中,只需要引入需要的模块,这里引入了item-server,trade-server,cart-server
2.改造配置
首先在nacos上添加一个共享的seata配置,命名为shared-seata.yaml:

具体yaml:
seata:
registry: # TC服务注册中心的配置,微服务根据这些信息去注册中心获取tc服务地址
type: nacos # 注册中心类型 nacos
nacos:
server-addr: 192.168.203.129: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"
然后在这个三个模块aplicatioin.yaml和bootstrap.yaml进行改造
因为nacos已经配置好了yaml文件所以在代码中配置就会变的很简单
nacos中的配置

这里以cart-server示例:
application.yaml
server:
port: 8082
feign:
okhttp:
enabled: true
sentinel:
enabled: true # 开启feign的sentinel
hm:
db:
database: hm-cart
swagger:
title: "黑马商城购物车服务相关接口"
package: com.hmall.cart.controller
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8090 #配置sentinel控制台地址
http-method-specify: true #是否将请求方式作为资源名称
bootstrap.yaml:
spring:
application:
name: cart-service
profiles:
active: dev
cloud:
nacos:
server-addr: 192.168.203.129:8848
config:
file-extension: yaml
shared-configs:
- data-id: shared-jdbc.yaml
- data-id: shared-swagger.yaml
- data-id: shared-log.yaml
- data-id: shared-seata.yaml
item-server的例子:
application.yaml:
server:
port: 8081
hm:
db:
database: hm-item
swagger:
title: "黑马商城商品服务相关接口"
package: com.hmall.item.controller
bootstrap.yaml:
spring:
application:
name: item-service
profiles:
active: dev
cloud:
nacos:
server-addr: 192.168.203.129:8848
config:
file-extension: yaml
shared-configs:
- data-id: shared-jdbc.yaml
- data-id: shared-swagger.yaml
- data-id: shared-log.yaml
- data-id: shared-seata.yaml
trade就不展示了,最后在docker上查看seata日志,能发现这三个服务已经被注册成TM、RM
9.XA模式
XA 规范 是 X/Open 组织定义的分布式事务处理(DTP,Distributed Transaction Processing)标准,XA 规范 描述了全局的TM与局部的RM之间的接口,几乎所有主流的数据库都对 XA 规范 提供了支持。seata的xa模式如下:

XA模式的优点是什么?
- 事务的强一致性,满足ACID原则
- 常用数据库都支持,实现简单,并且没有代码侵入
XA模式的缺点是什么?
- 因为一阶段需要锁定数据库资源,等待二阶段结束才释放,性能较差
- 依赖关系型数据库实现事务
实现步骤:
1.首先,我们要在配置文件中指定要采用的分布式事务模式。我们可以在Nacos中的共享shared-seata.yaml配置文件中设置:
直接进nacos管理,改yaml
seata:
data-source-proxy-mode: XA

2.其次,我们要利用@GlobalTransactional标记分布式事务的入口方法:

10.AT模式
Seata主推的是AT模式,AT模式同样是分阶段提交的事务模型,不过弥补了XA模式中资源锁定周期过长的缺陷。

AT模式与XA模式最大的区别是什么?
- XA模式一阶段不提交事务,锁定资源;AT模式一阶段直接提交,不锁定资源。
- XA模式依赖数据库机制实现回滚;AT模式利用数据快照实现数据回滚。
- XA模式强一致;AT模式最终一致
实现步骤:
1.在所需要的数据库中创建有关AT模式的undo_log表

-- 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';
其实seata默认不填就是AT模式:

更多推荐

所有评论(0)