微服务保护(Sentinel)

news2024/12/26 10:43:49

1.雪崩

微服务链路上某个服务出现了问题,结果导致整个微服务调用链上所有服务都出现了问题,这就是雪崩。


2.解决雪崩问题的常见方式有四种

1.超时处理设定超时时间,请求超过一定时间没有响应就返回错误信息,不会无休止等待

2.舱壁模式限定每个业务能使用的线程数,避免耗尽整个tomcat资源,因此也叫线程隔离

3.熔断降级由断路器统计业务执行的异常比例,如果超出阈值则会熔断该业务,拦截访问该业务的一切请求。

4.流量控制:限制业务访问的QPS,避免服务因流量的突增而故障。QPS:每秒钟处理的请求的数量

注意

1.流量控制可以避免因瞬间高并发流量而导致服务故障。

2.超时处理,线程隔离,熔断降级避免因服务故障引起的雪崩问题

3.超时处理只是缓解了雪崩问题。(比如在1个请求的超时时间过程中又来了其他请求,随着时间推移,还是会耗尽服务的资源)

4.线程隔离由于会创建很多的线程,会造成一定的资源浪费

5.只有流量控制是预防雪崩,其他三种都是防止已有服务故障,然后又传递到其他的服务中去。


3.线程保护技术

注意

1.线程池隔离

             一个业务请求进入tomcat中的时候,给每一个被隔离的业务创建独立的线程池,所以比直接的方式会多出很多很多线程,虽然隔离性好,但是由于线程的数量增多,会造成更多的CPU上下文切换的消耗,所以整个服务性能会下降

2.信号量隔离

                  当业务请求进入tomcat后,不会创建独立线程池,而是统计当前业务已经使用了几个线程了,限定线程的数量,当业务请求数量超过了这个量就会拒绝,限制每个业务能使用的线程数量,减少了线程的创建,在隔离的基础上并没有影响性能


4.Sentinel

阿里巴巴开源的一款微服务流量控制组件: https://sentinelguard.io/zh-cn/index.html
在GitHub下载,使用时直接启动jar包即可: java -jar sentinel-dashboard-1.8.1.jar
然后账号密码都默认为 sentinel

1.微服务整合Sentinel

1.引入sentinel依赖

 <!--sentinel,整合Sentinel-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>

2.配置控制台地址

spring:
  cloud:
    sentinel:
      transport:
        #配置控制台地址
        dashboard: localhost:8080

3.访问微服务的任意端口,触发sentinel监控


5.限流规则

1.簇点链路

就是 项目内的调用链路(请求->springmvc->controller->service->mapper->),链路中被监控的每个接口就是一个资源。 默认情况下sentinel会监控SpringMvc的每一个端点(Endpoint)(可以理解成controller中的方法),因此SpringMVC的每一个端点就是调用链路中的一个资源。流控,熔断等都是针对簇点链路中的资源来设置的。

2.流控模式

在添加限流时,点击高级选项,可以选择三种流控模式:

直接:统计当前资源的请求,触发阈值时对当前资源直接限流,也是默认的模式。

关联:统计与当前资源相关的另一个资源,触发阈值时,对当前资源限流。

链路:统计从指定链路访问到本资源的请求,触发阈值时,对指定链路限流。


1.关联模式

关联模式使用场景:比如用户支付时需要修改订单状态,同时用户要查询订单。查询和修改操作会争抢数据库锁,产生竞争。业务需求是优先支付和更新订单的业务,因此当修改订单业务触发阈值时,需要对查询订单业务限流

注意:满足什么条件可以使用关联模式:

1.两个有竞争关系的资源。

2.一个优先级较高,一个优先级较低。


2.链路模式

只针对从指定链路访问到本资源的请求做统计,判断是否超过阈值。

场景:有查询订单和创建订单业务,两者都需要查询商品。针对从查询订单进入到查询商品的请求统计,并设置限流。(使其不影响创建订单时的性能)

注意

1.Sentinel默认只标记Controller中的方法为资源,如果要标记其他方法(service层),需要利用@SentinelResource注解

 @SentinelResource("goods") //标记一个资源并起一个名字:goods
    public void queryGoods(){
        System.err.println("查询商品");
    }

2.Sentinel默认会将Controller方法做context整合,导致链路模式的流控失效,需要修改application.yml,添加配置

spring:
  cloud:
    sentinel:
      #关闭context整合
      web-context-unify: false

总结

直接模式:对当前资源限流

关联模式:高优先级资源触发阈值,对低优先级资源限流

链路模式:阈值统计时,只统计从指定资源进入当前资源的请求,是对请求来源的限流


3.流控效果

快速失败:达到阈值后,新的请求会被立即拒绝并抛出FlowException异常。是默认的处理方式。

warm up:预热模式,对超出阈值的请求同样是拒绝并抛出异常。但这种模式阈值会动态变化,从一个较小值逐渐增加到最大阈值。

排队等待:让所有的请求按照先后次序排队执行,两个请求的间隔不能小于指定时长


1.warm up

是应对服务冷启动(为了避免冷启动那一刻过高并发导致服务故障)的一种方案。请求阈值初始值是(最大阈值)threshold/(冷启动因子,默认是3)coldFactor,持续指定时长后,逐渐提高到threshold值。而coldFactor的默认值是3.

2.排队等待

当请求超过QPS阈值时,快速失败和warm up会拒绝新的请求并抛出异常。而排队等待则是让所有请求进入一个队列中,然后按阈值允许的时间间隔依次执行。后来的请求必须等待前面的执行完成,如果请求预期的等待时间超过最大时长,则会被拒绝。(流量整形)
例子:QPS = 5,意味着每200ms处理一个队列中的请求;timeout = 2000,意味着预期等待超过2000ms的请求会被拒绝并抛出异常

总结

快速失败:QPS超过阈值时,拒绝新的请求

warm up:  QPS超过阈值时,拒绝新的请求;QPS阈值是逐渐提升的,可以避免冷启动时高并发导致服务宕机

排队等待:请求会进入队列,按照阈值允许的时间间隔依次执行请求;如果请求预期等待时长超过超时时间,直接拒绝。


4.热点参数限流

之前的限流是统计 访问某个资源的所有请求,判断是否超过QPS阈值。而热点参数限流是 分别统计参数值相同的请求,判断是否超过QPS阈值。

注意

热点参数限流对默认的SpringMVC资源无效。只有用@SentinelResource声明的资源才可以配置热点参数限流

@SentinelResource("hot")
    @GetMapping("{orderId}")
    public Order queryOrderByUserId(@PathVariable("orderId") Long orderId) {
        // 根据id查询订单并返回
        return orderService.queryOrderById(orderId);
    }

6.隔离和降级

1.FeignClient整合Sentinel

虽然 限流可以尽量避免 服务器因过高并发而引起服务故障,但服务还会 因为其他原因而故障。而要将这些故障 控制在一定的范围内,避免雪崩,就要靠 线程隔离熔断降级手段了。(不管是线程隔离还是熔断降级,都是对客户端(调用方)的保护。
SpringCloud中, 微服务调用都是通过Feign来实现的,因此做客户端保护必须整合Feign和Sentinel

1.修改application.yml文件,开启Feign的Sentinel功能

feign:
  #开启Feign对Sentinel的支持
  sentinel:
    enabled: true

2.给FeignClient编写失败后的降级逻辑

1.FallbackClass,无法对远程调用的异常做处理。

2.FallbackFactory,可以对远程调用的异常做处理。(用这个)

步骤一:实现FallbackFactory

/**
 * 实现FallbackFactory
 */
@Slf4j
public class UserClientFallbackFactory implements FallbackFactory<UserClient> {
    @Override
    public UserClient create(Throwable throwable) {
        //创建UserClient接口实现类,实现其中的方法,编写失败降级的处理逻辑
        return new UserClient() {
            @Override
            public User findById(Long id) {
                // 记录异常信息
                log.error("查询用户失败",throwable);
                //根据业务需要返回默认的数据,这里是空用户
                return new User();
            }
        };
    }
}

步骤2:将UserClientFallbackFactory类在配置类中注册成一个Bean

@Bean
    public UserClientFallbackFactory userClientFallbackFactory(){
        return new UserClientFallbackFactory();
    }

    步骤3:在UserClient接口中使用UserClientFallbackFactory

@FeignClient(value = "userservice",fallbackFactory = UserClientFallbackFactory.class)
public interface UserClient {

    @GetMapping("/user/{id}")
    User findById(@PathVariable("id") Long id);
}

总结

1.在application.yml中配置:feign.sentienl.enable=true

2.给FeignClient编写FallbackFactory并注册为Bean

3.将FallbackFactory配置到FeignClient


2.线程隔离(舱壁模式)

线程池隔离
       一个请求过来,会给它依赖的每个服务都创建一个线程池,不会使用请求自己的线程,而是让请求在每个线程池中都取出一个线程,然后用这些线程去调用feign的客户端,这样,服务就隔离了,如果有个服务出现了故障,就是线程池满了,然后就不会再接受新的请求,所以不会把调用方的资源给耗尽了,所以就把故障给隔离了。
信号量隔离(Sentinel默认采用):( 网关基本采用信号量模式)
         不会去创建新的线程,而是用请求自己的线程去调用feign的客户端,但是会在请求进入时做一个判断,计数器记录一个信号量,每有一个请求进入,计数器就减一,当计数器为0了再有新的请求进入就会被拒绝。当请求处理完,信号还要还回去。

优点

缺点

场景

信号量隔离

轻量级,无额外开销

不支持主动超时

不支持异步调用

高频调用

高扇出

线程池隔离

支持主动超时

支持异步调用

线程的额外开销比较大

低扇出

QPS:就是每秒的请求数
线程数:是该资源能使用的tomcat线程数的最大值,也就是通过限制线程数量,实现舱壁模式

总结

1.线程隔离的两种手段

信号量隔离

线程池隔离

2.信号量隔离特点

基于计数器模式,简单,开销小

3.线程池隔离特点

基于线程池模式,有额外开销,但隔离控制更强


3.熔断降级

熔断降级是解决雪崩问题的重要手段,其思路是由 断路器统计服务调用的异常比例,
慢请求比例,如果超出阈值则会熔断该服务。即拦截访问该服务的一切请求;而当服务恢复
时。断路器会放行访问该服务的请求。
断路器通过内部的状态机来实现熔断和放行,当断路器的状态为 Closed的时候,断路器
不会拦截任何请求,但是断路器会去 统计异常的比例,如果发现异常的比例过高达到了阈值就会
切换状况到 Open拦截进入该服务的一些请求这就是熔断,熔断会持续一段时间,当熔断时间结束
就会切换状态到 Half-Open这时会 尝试放行一次请求,根据这次请求的结果来决定接下来的行为,
如果请求失败会再次进入OPen状态拦截请求进入熔断,一直循环直到请求成功,切换状态到Closed,
这时就可以放行该服务的请求了。
            达成熔断的条件就是 熔断策略
            失败阈值和熔断时间由我们配置。

熔断策略

详细说明

慢调用

业务的响应时长大于指定时长的请求认定为慢调用请求。在指定时间内,如果请求数量超过设定的最小数量,慢调用比例大于设定的阈值,则触发熔断。

异常比例

异常比例或异常数:统计指定时间内的调用,如果调用次数超过请求数,并且出现异常的比例达到设定的比例阈值(或超过指定异常数),则触发熔断。

异常数

比较的是异常数量。


总结

Sentinel熔断降级的策略

慢调用比例:超过指定时长的调用为慢调用,统计单位时长内慢调用的比例,超过阈值则熔断

异常比例:统计单位时长内异常调用的比例,超过阈值则熔断

异常数:统计单位时长内异常调用的次数,超过则熔断


7.授权规则

1.授权规则

授权规则可以对调用方的来源做控制,有 白名单黑名单两种方式
               白名单:来源在白名单内的调用者允许访问
               黑名单:来源在黑名单内的调用者不允许访问

假如我们只允许从网关进来的请求可以访问,那么流控应用中就应该写网关的名称

注意:Sentinel是通过RequestOriginParser(请求来源解析器)这个接口的parseOrigin来获取请求的来源的。但是这个接口的访问结果都是default,所以我们需要想办法自己实现这个接口来实现从网关进入的请求和浏览器进入的请求返回不同的结果。

实现方法:给网关过来的请求带上额外的请求头,即可以区分网关和浏览器的请求。利用网关的过滤器添加名为origin的getway头。(约定好即可)

1.实现RequestOriginParser接口方法

/**
 * 请求来源解析器
 */
@Component
public class HeaderOriginParser implements RequestOriginParser {

    @Override
    public String parseOrigin(HttpServletRequest request) {
        //1.获得请求头
        String origin = request.getHeader("origin");
        //2.非空判断
        if (StringUtils.isEmpty(origin)){
            origin="blank";//blank的是浏览器传入的请求
        }
        return origin;//不为blank的是网关传入的请求
    }
}

2.在网关中,利用网关的过滤器添加名为gateway的origin头

spring:
  cloud:
    gateway:
      default-filters:
        - AddRequestHeader=origin,gateway #添加一个名为origin的请求头,值为gateway

2.自定义异常结果

默认情况下,发生限流,降级,授权拦截时,都会抛出异常到调用方。如果要自定义异常时
的返回结果,需要实现 BlockExceptionHandler接口。
(阻塞异常处理器)
/**
 * 阻塞异常处理器
 */
@Component
public class SentinelExceptionHandler implements BlockExceptionHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
        String msg="未知异常";
        int status=429;

        if (e instanceof FlowException){
            msg="请求被限流了";
        } else if(e instanceof ParamFlowException){
            msg="请求被热点参数限流了";
        }else if(e instanceof DegradeException){
            msg="请求被降级了";
        }else if(e instanceof AuthorityException){
            msg="没有权限访问";
            status=401;
        }

        response.setContentType("application/json;charset=utf-8");
        response.setStatus(status);
        response.getWriter().println("{\"msg\":"+msg+",\"status\":"+status+"}");
    }
}

总结

获取请求来源的接口

RequestOriginParser

处理BlockException的接口

BlockExceptionHandler


8.规则持久化

1.规则管理模式

推送模式

说明

优点

缺点

原始模式

API 将规则推送至客户端并直接更新到内存中,扩展写数据源(WritableDataSource),默认就是这种

简单,无任何依赖

不保证一致性;规则保存在内存中,重启即消失。严重不建议用于生产环境

Pull模式

扩展写数据源(WritableDataSource), 客户端主动向某个规则管理中心定期轮询拉取规则,这个规则中心可以是 RDBMS、文件 等

简单,无任何依赖,规则持久化

不保证一致性;实时性不保证,拉取过于频繁也可能会有性能问题。

Push模式

扩展读数据源(ReadableDataSource),规则中心统一推送,客户端通过注册监听器的方式时刻监听变化,比如使用 Nacos、Zookeeper 等配置中心。这种方式有更好的实时性和一致性保证。生产环境下一般采用 push 模式的数据源

规则持久化,一致性

引入第三方依赖

原始模式:控制台配置的规则直接推送到Sentinel客户端,也就是我们的应用。然后保存在内存中,服务重启则丢失
pull模式:控制台将配置的规则推送到Sentinel客户端,而客户端会将配置规则保存在本地文件或数据库中。以后会定时去本地文件或数据库查询,更新本地规则。
push模式:控制台将配置规则推送到远程配置中心,例如Nacos,Sentinel客户端监听Nacos,获取配置变更的推送消息,完成本地配置更新。

总结

原始模式:保存到内存中

pull模式:保存在本地文件或数据库,定时去读取

push模式:保存在nacos,监听变更实时更新


2.实现push模式

1.引入依赖,引入sentinel监听nacos依赖

<!--引入sentinel监听nacos依赖-->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>

  2.配置nacos地址,在yml文件中配置nacos地址以及监听的配置信息

spring:
  cloud:
    sentinel:
      datasource:
        flow:
          nacos:
            server-addr: localhost:8848 # nacos地址
            dataId: orderservice-flow-rules #配置文件名称
            groupId: SENTINEL_GROUP         #配置组
            # 限流规则
            rule-type: flow # 还可以是:degrade降级,authority授权,param-flow热点参数限流
    #        degrade:
    #          nacos:
    #            server-addr: localhost:8848 # nacos地址
    #            dataId: orderservice-degrade-rules #配置文件名称
    #            groupId: SENTINEL_GROUP         #配置组
    #            # 限流规则
    #            rule-type: degrade # 还可以是:degrade降级,authority授权,param-flow热点参数限流

3.修改sentinel-dashboard源码(默认不支持nacos的持久化)

4.重新编译,打包


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/403845.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

人工智能简单应用1-OCR分栏识别:两栏识别三栏识别都可以,本地部署完美拼接

大家好&#xff0c;我是微学AI&#xff0c;今天给大家带来OCR的分栏识别。 一、文本分栏的问题 在OCR识别过程中&#xff0c;遇到文字是两个分栏的情况确实是一个比较常见的问题。通常情况下&#xff0c;OCR引擎会将文本按照从左到右&#xff0c;从上到下的顺序一行一行地识别…

软考高项——信息文档管理

信息文档管理信息文档管理文档分类文档质量等级文档管理的规则和方法信息文档管理 信息文档管理的总线索包括&#xff1a; 1&#xff09;文档分类 2&#xff09;文档质量等级 3&#xff09; 文档分类 1、开发文档 开发过程中用到的文档 &#xff08;可行性报告、任书、需求、…

Django实践-06导出excel/pdf/echarts

文章目录Django实践-06导出excel/pdf/echartsDjango实践-06导出excel/pdf/echarts导出excel安装依赖库修改views.py添加excel导出函数修改urls.py添加excel/运行测试导出pdf安装依赖库修改views.py添加pdf导出函数修改urls.py添加pdf/生成前端统计图表修改views.py添加get_teac…

Qt读xml文件

QXmlStreamReaderQXmlStreamReader类通过简单的流式API为我们提供了一种快速的读取xml文件的方式。他比Qt自己使用的SAX解析方式还要快。所谓的流式读取即将一个xml文档读取成一系列标记的流&#xff0c;类似于SAX。而QXmlStreamReader类和SAX的主要区别就是解析这些标记的方式…

Linux自动化交互命令expect测试

介绍 expect 是由Don Libes基于Tcl&#xff08;Tool Command Language &#xff09;语言开发的&#xff0c;主要应用于自动化交互式操作的场景&#xff0c;借助Expect处理交互的命令&#xff0c;可以将交互过程如&#xff1a;ssh登录&#xff0c;ftp登录等写在一个脚本上&#…

RabbitMQ系列(1)--RabbitMQ简介

1、RabbitMQ概念RabbitMQ是一个消息中间件&#xff0c;不对消息进行处理&#xff0c;只对消息做接收、存储和转发。2、RabbitMQ四大核心概念(1)生产者产生数据发送信息的程序(2)交换机交换机是RabbitMQ中一个非常重要的部件&#xff0c;接收来着生产者的消息并把消息推送到队列…

PMP项目管理项目沟通管理

目录1 项目沟通管理2 规划沟通管理3 管理沟通4 监督沟通1 项目沟通管理 项目沟通管理包括通过开发工件&#xff0c;以及执行用于有效交换信息的各种活动&#xff0c;来确保项目及其相关方的信息需求得以满足的各个过程。项目沟通管理由两个部分组成&#xff1a;第一部分是制定…

云企业网CEN介绍与实践

云企业网CEN介绍 云企业网&#xff08;Cloud Enterprise Network&#xff09;是一款能快速构建混合云和分布式业务系统的全球网络服务。 运行在云厂商的私有全球网络上实现跨地域专有网络间&#xff0c;专有网络与本地数据中心间的私网通信 提供高效、稳定的网络传输服务适用…

【网络】什么是RPC?RPC与HTTP有什么关系?

文章目录RPC是什么RPC和HTTP的关系和区别[附]关于REST论文中提到的"HTTP不是RPC"重点参考 凤凰架构-远程过程调用 既然有HTTP为什么还要有RPC&#xff1f; RPC是什么 RPC(Remote Procedure Call)&#xff1a;即远程过程调用&#xff0c;目的是为了让计算机能够跟调用…

Android中实现滑动的7种方法

Android中实现滑动的7种方法前置知识Android坐标系视图坐标系触控事件---MotionEvent获取坐标的方法实现滑动的7种方法layout方法offsetLeftAndRight()和offsetTopAndBottom()LayoutParamsscrollTo和scrollByScroller属性动画ViewDragHelper参考前置知识 Android坐标系 Andro…

【C++进阶】面向对象

程序 编写程序是为了让计算机解决现实生活中的实际问题。pascal之父、结构化程序设计先驱Niklaus Wirth提出程序 算法 数据结构。程序是完成一定功能的一些列有序指令的集合。指令 操作码 指令。将指令按一定的顺序进行整合&#xff0c;就形成了程序。 机器语言与汇编语言…

软件测试的案例分析 - 闰年5

文章目的 显示不同的博客能获得多少博客质量分 &#xff08;这是关于博客质量分的测试 https://www.csdn.net/qc) 这个博客得了 83 分。怎么才能得到更多分数&#xff1f; 正文 我们谈了不少测试的名词, 软件是人写的, 测试计划和测试用例也是人写的, 人总会犯错误。错误发生…

Shiro学习认证和授权

Shiro学习笔记 认证 思路 获取当前的Subject&#xff0c;调用SecurityUtils.getSubject()方法&#xff1b;判断当前用户是否被认证&#xff0c;即是否已经登陆&#xff0c;调用Subject的isAuthenticated()方法进行判断&#xff1b;若没有认证&#xff0c;则把用户名和密码封…

C++11线程、互斥量以及条件变量

文章目录前言1、创建第一个线程2、线程对象的生命周期、等待和分离3、线程创建的多种方式4、互斥量4.1 独占的互斥量std::mutex4.2 递归独占互斥量recursive_mutex4.3 带超时的互斥量std::timed_mutex和std::recursive_timed_mutex4.4 std::lock_guard和std::unique_lock5、cal…

CSS常用内容总结(扫盲)

文章目录前言相关概念【了解】脚本语言什么是脚本语言脚本语言有什么特点常见的脚本语言什么是动态语言&#xff0c;什么是静态语言动态语言和静态语言两者之间有何区别CSSCSS是什么CSS的特点一、CSS代码怎么写基本语法规则引入方式内部样式内联样式表外部样式代码风格二、CSS的…

JavaWeb——进程详解

目录 一、操作系统 1、定义&#xff1a; 2、操作系统的基本功能&#xff1a; 二、进程 1、定义&#xff1a; 三、进程管理 1、PCB定义 &#xff08;1&#xff09;、身份标识 &#xff08;2&#xff09;、内存指针 &#xff08;3&#xff09;、文件描述符 2、操作系统…

Hadoop入门常见面试题与集群时间同步操作

目录 一&#xff0c;常用端口号 Hadoop3.x &#xff1a; Hadoop2.x&#xff1a; 二&#xff0c;常用配置文件&#xff1a; Hadoop3.x: Hadoop2.x: 集群时间同步&#xff1a; 时间服务器配置&#xff08;必须root用户&#xff09;&#xff1a; &#xff08;1&#xff09…

1639_perror的函数功能以及简单测试

全部学习汇总&#xff1a; GreyZhang/g_unix: some basic learning about unix operating system. (github.com) 继续分析之前的shell程序代码&#xff0c;看到了一个fork1的实现。 Fork之前还是看过的&#xff0c;但是也已经忘得差不多了&#xff0c;这个fork1就是fork的一种应…

4.Spring Cloud (Hoxton.SR8) 学习笔记—Nacos微服务治理

本文目录如下&#xff1a;一、Nacos微服务治理Nacos 下载 与 启动Spring Cloud 集成 NacosIDEA 同一个 Application 启动多次一、Nacos微服务治理 Nacos 下载 与 启动 https://github.com/alibaba/nacos/releases Nacos 下载与启动: F:\ProgramFiles\nacos\bin> .\startup…

MyBatis操作数据库

目录 MyBatis 功能架构 学习MyBatis 第一个MyBatis查询 1、创建数据库和表 2、搭建MyBatis开发环境 2.1、在项目中添加MyBatis框架 2.2、配置数据库连接信息 2.3、配置MyBatis中xml的保存路径&#xff08;规则&#xff09; 3、添加业务代码 3.1、创建实体类 3.2、构…