SpringCloud+CloudAlibaba微服务初阶入门

内容目录


微服务

为什么要用微服务?

随着时代的发展,传统一体制的软件架构若在某一时间段迎来爆炸性的并发访问(如春运抢票、双十一抢货),将会导致软件的后台系统面临崩溃。(例如当年的12306网,抢票有多卡,过来人都懂的),而如今12306进行了微服务系统的重构,可以看出后台系统优化的效果显而易见
那么微服务为什么这么有益呢?我简单来举个例子,如下图是一体制服务与微服务主要差距示意图(此图为简图,不够严谨勿喷)。
在这里插入图片描述
可以看出、实际上微服务最大优势是把一台机器上的压力,分给了多台机器、所以软件变得更好,有可能不是承载服务的服务器变的更好了,而是服务器变的更多罢了。
如下是微服务体系完整构想者的博客。
马丁福勒个人博客官网: 链接.
在这里插入图片描述
当然如果你英文不算OK 小编也为您准备了中文版本
中文版微服务马丁文章: 链接.
读完后,你也许就已经初步了解了微服务。下面会介绍一些我们微服务的生态系统SpringCloud的旧版与新版服务组件,这些组件就像人的骨骼,血液,将微小的服务进行组成拼装为健康完整的服务。

SpringCloud生态(旧版)

如今微服务最为流行的为Spring官网开发出的SpringCloud生态
下面是一些有关SpringCloud技术相关网站在此列出,供您参考
1.SpringCloud 中文参考文档连接。—》点我.
2.SpringCloud 社区 ——》 点我.
3.SpringCloud 版本技术选择依据 ——》 点我.
将返回的json运行整理后,如下图。
SpringCloud与SpringBoot的版本选择依据。
在这里插入图片描述
4.SpringCloud H版本官方开发文档(小编以下实验应用版本)
官网链接.

项目搭建

规范的创建父工程

1.创建一个Maven
在这里插入图片描述
注意maven这里最好使用自己的本地的 ,版本最好为3.5以上

在这里插入图片描述
设置项目编码,全部设置为UTF-8
在这里插入图片描述
配置注解生效
在这里插入图片描述
配置java编译版本
在这里插入图片描述
配置项目过滤(此步可略)
在这里插入图片描述

父模块的pom的常用依赖

<!-- 统一Jar包和版本号的管理 -->
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <junit.version>4.12</junit.version>
    <log4j.version>1.2.17</log4j.version>
    <lombok.version>1.16.18</lombok.version>
    <mysql.version>5.1.47</mysql.version>
    <druid.version>1.1.16</druid.version>
    <mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version>
  </properties>

  <dependencyManagement>
    <dependencies>
      <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-dependencies -->
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-dependencies</artifactId>
        <version>2.2.2.RELEASE</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>

      <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-dependencies -->
      <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-dependencies</artifactId>
        <version>Hoxton.SR1</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>

      <!-- https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-alibaba-dependencies -->
      <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-alibaba-dependencies</artifactId>
        <version>2.1.0.RELEASE</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>

      <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
      <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>${mysql.version}</version>
      </dependency>

      <!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
      <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>${druid.version}</version>
      </dependency>

      <!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->
      <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>${mybatis.spring.boot.version}</version>
      </dependency>

      <!-- https://mvnrepository.com/artifact/junit/junit -->
      <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>${junit.version}</version>
      </dependency>

      <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
      <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>${lombok.version}</version>
        <scope>provided</scope>
      </dependency>

      <!-- https://mvnrepository.com/artifact/log4j/log4j -->
      <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>${log4j.version}</version>
      </dependency>

    </dependencies>
  </dependencyManagement>

创建子模块

创建第一个子模块具体步骤如下。

1.建module(maven)

在这里插入图片描述

2.改pom

添加子模块需要的依赖

<dependencies>
 <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-test</artifactId>
   <scope>test</scope>
 </dependency>
</dependencies>
3.写Yml

在这里插入图片描述
以下为yml文件必须配置的,其他配置按项目需求配置即可。

#端口号
server:
  port: 8001
#服务名
spring:
  application:
    name: cloud-payment-chuang
4.主启动
@SpringBootApplication
public class PaymentMain8001 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentMain8001.class,args);
    }
}

5.分层编写业务流程

1.创建SQL
在这里插入图片描述
2.entitie
例1:创建数据库映射实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Payment implements Serializable {
    private int id;
    private String serial;
    
}

例2:创建响应数据实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult<T> {
    private Integer code;
    private String message;
    private T data;

    public CommonResult(Integer code,String message){
        this(code,message,null);
    }
}

3.dao(/Mapper)
例3.1:创建增查Mapper接口

@Mapper
@Repository
public interface PaymentMapper {
    public int create(Payment payment);
    public Payment getPaymentById(@Param("id") int id);
}

例3.2:编写Mapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.chuang.springcloud.dao.PaymentMapper">
    <insert id="create" parameterType="Payment" useGeneratedKeys="true" keyProperty="id">
        insert into payment(serial) values(#{serial});
    </insert>

<!--  进行下映射,防止有不规范等操作-->
    <resultMap id="BaseResultMap" type="com.chuang.springcloud.entities.Payment">
<!--  column:数据库   property:java中   jdbcType:为数据库里数据类型-->
        <id column="id" property="id" jdbcType="INTEGER"></id>
        <id column="serial" property="serial" jdbcType="VARCHAR"></id>
    </resultMap>
    <select id="getPaymentById" parameterType="int" resultMap="BaseResultMap">
        select * from payment where id=#{id};
    </select>
</mapper>

4.service
例4.1: service接口

public interface PaymentService {
    public int create(Payment payment);
    public Payment getPaymentById(@Param("id") int id);
}

例4.2: service层接口实现

@Service
public class PaymentServiceImpl implements PaymentService {
    @Autowired
    private PaymentMapper paymentMapper;
    @Override
    public int create(Payment payment) {
        return paymentMapper.create(payment);
    }
    @Override
    public Payment getPaymentById(int id) {
        return paymentMapper.getPaymentById(id);
    }
}

5.controller层
例5.1:调用业务层进行事务处理,并与前端交互

@RestController
@Slf4j
public class PaymentController {
    @Autowired
    private PaymentService paymentService;
    //业务调用并与前端交互
    @PostMapping(value = "/payment/create")
    public CommonResult create(Payment payment){
        int result = paymentService.create(payment);
        log.info("********插入结果:"+result);
        if(result > 0){
            return new CommonResult(200,"插入成功",result);
        }
        else {
            return new CommonResult(400,"插入数据失败",null);
        }
    }
    @GetMapping(value = "/payment/get/{id}")
    public CommonResult getPaymentById(@PathVariable("id") int id){
        Payment payment = paymentService.getPaymentById(id);
        log.info("********插入结果:"+payment);
        if(payment != null){
            return new CommonResult(200,"查询成功",payment);
        }
        else {
            return new CommonResult(400,"无此记录ID"+id,null);
        }
    }
}

例5.2:访问controller层接口查询结果如下
在这里插入图片描述

注册中心

背景介绍

如果只有两个服务,我们可以通过RestTemplate足以调用。但是当服务多了,我们就需要用到服务治理了(就像医生和病人,如果只有一个医生和一个病人,我么无需挂号。但现实是我们有多个医生与病人,此时我们需要个前台进行挂号,来管理,这个前台就是——服务治理中心)。
当今主流的注册中心组件Eureka、Zookeeper、Consul 、Nacos。
服务治理中心的作用:管理服务与服务之间的依赖关系、可以实现服务调用、负载均衡、服务注册与发现、容错等。

1.Eureka(已停更)

SpringCloud 封装了Netflix公司的Eureka模块来实现服务治理。
在这里插入图片描述
Eureka充当注册中心的理念图
在这里插入图片描述

Springcloud配置Eureka

1.新建一个Moudle 如图

在这里插入图片描述

2.pom文件导入依赖
<dependency>
  <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>

3.yml 文件配置Eureka

server:
  port: 7001

eureka:
  instance:
    hostname: localhost #eureka服务端实例名称
  client:
    register-with-eureka: false  #false 不像注册中心注册自己
    fetch-registry: false #false 表示自己端就是注册中心,维护服务,不需要去检索服务。
    service-url:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
      #设置与Eureka Server 交互的地址查询服务,注册服务需要依赖的地址
4.配置启动类,添加注解
@SpringBootApplication
@EnableEurekaServer   //开启Eureka服务
public class EurekaMain7001 {
    public static void main(String[] args) {
        SpringApplication.run(EurekaMain7001.class,args);
    }
}
5.访问到页面,即配置成功。

在这里插入图片描述

6. 服务向Eureka注册

将需要注册的服务中pom文件里添加如下依赖

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
7.yml文件添加如下
eureka:
  client:
    register-with-eureka: true    #表示是否将自己注册进Eureka
    fetch-registry: true  #表示是否从Eureka抓取自己的信息,默认为true.集群时则必须设置为true
    service-url:
      defaultZone: http://localhost:7001/eureka
8.主启动类添加监听注解
@EnableEurekaClient

此时查看Eureka可见服务注册信息如下
在这里插入图片描述

9.Eureka集群配置**

原理:相互注册,相互守望。(配置略)

10.服务集群注册配置

只需要保证新增服务与原本服务的,Spring.application.name (即服务名)保证一致即可。都在服务中心进行注册。
(小编是在本地新建个不同端口的module为例,访问注册中心如下图)
可以看到红线的服务有两个,分别是两个端口不同的支付服务。
在这里插入图片描述

11.集群的负载均衡

此时需要在RestTemplate 添加负载均衡机制(默认为轮询)
在这里插入图片描述
需要以服务名+业务接口进行访问业务。(配置集群后,只认服务名)。
修改Eureka显示服务的主机名称以及端口号,只需要在该服务的yml中添加
payment8001为主机端口名等。

instance:
    instance-id: payment8001

2.Zookeeper注册中心配置

pom文件添加依赖

把Eureka的依赖替换为Zookeeper的其余的配置不变。
服务端、客户端添加如下pom

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
 </dependency>
版本不兼容问题

注:保持版本一致(小编引入的需要版本为zookeeper3.5.3)但是我想用3.4.9需要如下配置
在这里插入图片描述
3.4.9版本配置pom

	<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
        <!--  删除自带的zookeeper3.5.3-->
            <exclusions>
                <exclusion>
                    <groupId>org.apache.zookeeper</groupId>
                    <artifactId>zookeeper</artifactId>
                </exclusion>
            </exclusions>

	</dependency>
<!--  添加zookeeper3.4.9-->
<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.4.9</version>
</dependency>
yml配置
spring:
  cloud:
    zookeeper:
      connect-string: ip+端口
主启动类添加注解

该注解用于向使用consul或者zookeeper作为注册中心时注册服务。

@EnableDiscoveryClient

3.Consul注册中心配置

pom文件依赖

服务端客户端依赖如下

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
yml文件配置
spring:
  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        service-name: ${spring.application.name}
主启动类添加注解
@EnableDiscoveryClient

负载均衡调用

背景介绍

为了在集群服务的压力稳定,保护各个服务下集群的安全性,实现各个集群机器的压力相等,因此在微服务中负载均衡是必不可少的。

1.Ribbon简介

Ribbon是客户端负载均衡的工具,主要功能是提供客户端的软件负载均衡算法和服务调用。
Ribbon为进程内的负载均衡,它只是个类库,集成于消费方的进程,消费方通过它来获取到服务提供方的地址。

Springcloud配置Ribbon

注:eureka-client依赖已自动配置了Ribbon,故无需导入依赖。

如果注册中心不是Eureka则需要引入
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
    <version>2.2.1.RELEASE</version>
</dependency>
Ribbon核心组件IRule

基于IRule落地实现的机制如下图
在这里插入图片描述

项目Value
RoundRobinRule轮询
RandomRule随机
WeightedResponseTimeRule响应速度越快的服务权重越大
RetryRule先轮询,若失败,获取可用的服务
RestAvailableRule过滤掉断路器跳闸的服务,选择并发量最小的服务
AvailabilityFilteringRule多次访问故障会pass,重新选择并发量最小的服务
ZoneAvoidanceRule默认规则,根据Server的可用性进行选择
负载规则的自定义

注意:
该配置类不能放在@ComponentScan所扫描的当前包,及其子包下。
具体放在如下图所示
在这里插入图片描述
内容为

@Configuration
public class MyselfRule {
    @Bean
    public IRule myRule(){
        return new RandomRule();//定义为随机
    }
}

主启动类需要指定该模式

// 设置服务名,与规则
@RibbonClient(name = "CLOUD-PAYMENT-CHUANG",configuration = MyselfRule.class)

2.OpenFeign简介

说到OpenFeign,有必要了解下其前身Feign。
Feign是一个声明式WebService客户端,它的使用方法式定义一个接口,然后在其上面添加注解,Feign可以与Eureka和Ribbon组合使用,以支持负载均衡。使开发更加简洁。
在Feign不在持续跟新后,社区推出了OpenFeign,可以理解为对Feign的升级而出的新产品。
使用方法为:微服务调用接口+@FeignClient注解

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

在依赖中可以看到openfeign中已经整合了ribbon.
在这里插入图片描述

主启动类添加
@EnableFeignClients
业务类编写规则概述

service层 ——创建业务接口,添加注解。

@Component
@FeignClient(value = "CLOUD-PAYMENT-CHUANG") //指定该业务访问的服务名为。
public interface PaymentFeignService {
    @GetMapping( value = "/payment/get/{id}") //服务提供者的接口
    public CommonResult<Payment> getPaymentById(@PathVariable("id") int id);
}

controller层 调用服务即可。

@RestController
public class OrderFeignController {
    @Autowired
    private PaymentFeignService paymentFeignService;
    @GetMapping( value = "/consumer/payment/get/{id}")
    public CommonResult<Payment> getPaymentById(@PathVariable("id") int id){
        return paymentFeignService.getPaymentById(id);
    }
}

测试结果为,默认适配ribbon的轮询机制。

OpenFeign的超时控制。

何为超时控制?
比如在支付服务提供方,处理业务时间需要三秒钟,但是当消费端通过OpenFeign调用支付服务,进行消费时。OpenFeign默认只能等待1S,此时会报超时的错误。那么我们该如何进行对OpenFeign的超时控制呢?
Yml 设置超时时间

#设置Openfeign客户端超时时间(Openfeign默认支持ribbon)
ribbin:
#  建立连接所用时间
  ReadTimeout: 5000
#  建立连接后,从服务端读取可用资源的时间
  ConnectTimeout: 5000
OpenFeign的日志打印功能

工作中由于调用接口过多,因此往往能打印出详细的日志,会有很大的方便。
首先添加一个Config类

@Configuration
public class FeignConfig {
   @Bean
   Logger.Level feignLoggerLevel(){
       return Logger.Level.FULL;
   }
}

yml文件添加日志级别

logging:
  level:
    #日志以什么级别,监控哪个接口。
    com.chuang.springcloud.service.PaymentFeignService: debug

Hystrix断路器(已停更)

背景介绍

为什么微服务需要断路器?
举个例子。假设,淘宝的搜索物品、添加购物车、结算购物车这三个服务是一条行为链。我们通过微服务架构,将每个服务放置在不同的服务器上。假设添加购物车的服务器断电,那么会直接使该平台所有服务雪崩,导致平台崩溃。这时我们需要个断路器,一个临时的服务,与该行为链进行对接,从而不会使整个淘宝平台处于崩溃,因此Hystrix诞生,用于解决该问题。
Hystrix简介
断路器本身是一种开关,当某个单元发生故障之后,通过断路器的监控(保险丝),像调用方返回一个可预期的、可处理的备选响应。从而达到,服务方的线程不会被长时间、不必要的占用,从而避免故障在分布式中蔓延,导致服务雪崩。

Hystrix相关概念

服务降级
由于某种原因导致服务无法正常使用,给予用户友好提示。
服务降级触发条件
程序运行异常、超时、服务熔断触发服务降级、线程池,信号量满载也会触发服务降级
服务熔断
当服务达到最大访问量时,服务直接拒绝访问,然后掉用服务降级,并给予友好的提示。
服务限流
秒杀高并发等操作,严禁一窝蜂的过来拥挤,使并发排队,一秒N个,有序执行。

配置Hystrix服务降级

pom依赖
<dependency>
      <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
@HystrixCommand 进行标记

备胎理论
当某个接口方法被压垮时,会调用备胎方法进行处理,有效的提高软件用户友好程度

 @HystrixCommand(
 //指定备胎方法
 fallbackMethod = "备胎方法",commandProperties = {
//设置超时时间
 @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "5000")
    })
public String Info_timeout(Integer id){}
@EnableHystrix
主启动类开启Hystrix

添加如下注解

@EnableHystrix

注:Hystrix服务降级,我们可以用在客户端或者服务端,通常我们会对客户端进行降级保护
注:Hystrix标记的服务,如果自身代码报错,也会直接进入到
“备胎方法”

解决代码膨胀

代码膨胀的由来
如果我们每个接口,都需要一个“备胎方法”会造成代码膨胀,这违反我们的设计原则,为了解决此类问题,如下实操即可。
解决
首先定义一个全局的备胎处理类,然后在需要的类上添加如下标签。

@DefaultProperties(defaultFallback = "指定全局默认方法")

最后只需要在需在需要备胎方法的接口上添加如下注解

@HystrixCommand
降低代码耦合

耦合过高的由来
通过以上我们会把接口方法,与备用方法放入在一个类中,这会使得接口方法与备胎方法的耦合度变高。
通过feign降低耦合
在与服务通讯时,我们创建过feign的服务接口(interface),我们只需要创建其实现类,并通过如下代码进行指定即可。
在这里插入图片描述
fallback 指定该服务下的接口在熔断、异常、超时的情况下feign默认调用的方法,这里指定PaymentFallbackServic为备胎方法。

@FeignClient(value = "feign调用的eureka的服务",fallback = PaymentFallbackService.class)

feign的yml文件需要开启hystrix

feign:
  hystrix:
    enabled: true

总结:通过如上的步骤使用feign指定备胎方法,解决了代码高耦合的弊端。

服务熔断深入详解

服务熔断的技术思想,来源于马丁福勒的技术博客。这里提出了熔断服务的三种状态为,开、断、半开。如下图。
在这里插入图片描述
熔断打开
请求不在进行调用当前服务,内部设置时钟一般为MTTR(平均故障处理时间),当打开时长达到所设置的时钟,则进入半熔状态。
熔断关闭
不会对服务进行熔断
熔断半开
部分请求根据规则调用当前服务,如果请求成功,如果请求成功并且符合当前规则,则认为服务恢复正常,并关闭熔断。
服务熔断的配置与服务降级相似

// 服务熔断
    @HystrixCommand(fallbackMethod = "指定服务熔断后备胎方法",commandProperties = {
            @HystrixProperty(name = "circuitBreaker.enabled",value = "true"), //是否开启短路器
            @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"), //请求次数
            @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"), //时间窗口期
            @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"), //失败率达到多少后,自动跳闸
    })

:时间窗口期,相当于断路器几秒一刷新,如果在某一时间窗口期内,请求20次错误率大于60%,触发熔断,那么在该时间窗口期内,正常请求也会被杜绝接收,直到电路器,判断服务恢复正常,才给与放行。
断路器的属性如何查找与设置,可以搜索HystrixCommandProperties带该类进行查看。如下图
在这里插入图片描述

HystrixDashboard图形化界面

作为前些年最火的Hystrix,最为方便的是可以通过图形化界面进行服务的熔断监控。下面将带领你配置HystrixDashboard
pom引入

<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

启动类开启

@EnableHystrixDashboard

访问ip+端口/hystrix
参见豪猪哥
在这里插入图片描述
被监控的服务配置
在启动类添加如下代码

@Bean
    public ServletRegistrationBean getServlet(){
        HystrixMetricsStreamServlet streamservlet = new HystrixMetricsStreamServlet();
        ServletRegistrationBean<HystrixMetricsStreamServlet> registrationbean = new ServletRegistrationBean<>(streamservlet);
        registrationbean.setLoadOnStartup(1);
        registrationbean.addUrlMappings("/hystrix.stream");
        registrationbean.setName("HystrixMetricsStreamServlet");
        return registrationbean;

    }

按如下添加地址
在这里插入图片描述
对被监听的服务发送请求效果如下
在这里插入图片描述

微服务之网关

Gateway

网关在微服务开发中的位置如下。
在这里插入图片描述

Gateway背景

最早netflix公司引领微服务,研发出Zuul1网关,并在市场引领风骚很多年,但是在公司即将推出Zuul2的时候,由于研发Zuul2的方向问题,很多大佬意见不统一,导致项目一直处于搁浅状态。最终以三位大佬跳槽离开为该项目画上遗憾的句号。但是神仙打架,平民遭殃,苦了程序员劳苦大众。因此Spring官方决定自己研发出属于自己的网关,解救众生。因此Gateway被慢慢的孕育而出。而Zuul技术被完全抛弃。因此小编我也就不再介绍Zuul了,直接介绍Gateway好啦。

Gateway简介

SpringCloud Gataway 作为SpringCloud生态系统的网关,目标是替代Zuul
在SpringCloud2.0版本中没有对Zuul2.0的框架进行集成。为了提高网关性能
SpringCloud Gataway 是基于WebFulx框架实现的,而WebFulx框架底层是使用的高性能的Reactor模式通讯框架Netty。实现安全、监控、指标、限流等特性。最后,注意。Gateway是非阻塞异步模型。

Gateway网关搭建配置

POM引入
<!-- gateway -->
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
yml配置路由、断言

yml配置路由、断言 代码解析如下。

spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      routes:
        - id: payment_routh  #路由的ID,没有固定的要求,但必须唯一
#          uri: http://localhost:8001 #匹配后提供服务的路由地址
          uri: lb://CLOUD-PAYMENT-CHUANG     #ribbon 会根据服务名前的lb标记进行负载均衡
          predicates:
            - Path=/payment/get/**  #断言,路径相匹配的进行路由
            - After=2020-10-12T17:15:38.818+08:00[Asia/Shanghai]  #意思为在此时间后访问路由生效。befor\between大意如此。
#            - Cookie=username,chuang     #访问该路由访问该接口必须要Cookie "username=chuang"
#            - Header=X-Request-Id,\d+    #请求头要有X-Request-Id属性,并且值为整数。
#            - Host=**.chuang.com          # -H "Host:www.chuang.com" 请求头必须有该信息才可访问
#            - Method=GET  #该路由只有get请求可以可以访问,屏蔽其余请求。
#            - Query=username, \d+ #要求请求需要有参数username并且参数需要为整数才可以路由

        - id: payment_routh2  #路由的ID,没有固定的要求,但必须唯一
          uri: http://localhost:8004 #匹配后提供服务的路由地址
          predicates:
            - Path=/payment/hystrix/ok/**  #断言,路径相匹配的进行路由
配置Filter

yml 配置Filter
yml 配置Filter有多种配置属性,具体参考官网链接: 点这.
自定义Filter
自定义Filter主要需要实现两个接口,举例配置一个全局过滤,需要token进行验证。代码参考如下。

@Component
public class MyLogGateWayFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("***********进入过滤器:"+new Date());
        String token=exchange.getRequest().getQueryParams().getFirst("token");
        if(token == null){
            System.out.println("没有登录,请先登录");
            exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }


    //加载过滤器的顺序  数字越小优先级越高
    @Override
    public int getOrder() {
        return 0;
    }
}

SpringCloud Config配置中心

背景介绍

由于微服务会将整体服务拆分成一个个子服务,假若每个服务的粒度较小,因此系统中会出现大量的服务、以及服务配置信息。所以一套集中式的、动态的配置管理设施是必不可少的。

Config简介

SpringCloud Config为微服务的架构中的微服务提供了中心化的外部配置支持,配置服务器为各个不同微服务应用的所有环境提供了一个中心化的外部配置(git\github)

Config服务配置github

pom引入
<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-config-server</artifactId>
</dependency>
yml制定
spring:
  application:
    name: cloud-config-center
  cloud:
    config:
      server:
        git:
          uri: https://github.com/chuangge123/springcloud-practice1-config.git #获取github上仓库名字
          ####搜索目录
          search-paths:
            - springcloudconfig
          force-pull: true
          username: 你的github用户名
          password: 密码
      #读取分支
      label: master
启动类添加注解
// An highlighted block
@EnableConfigServer

通过以上配置,运行Config服务,通过服务ip+/分支+文件名,进行访问github上的具体文件。

SpringCloud_Bus消息总线

总线简介

在微服务架构系统中,通常会使用轻量级的消息代理来构建一个共用的消息主体,并让系统中所有的微服务都连上来,由于该主体中产生的消息会被所有的实例监听和消费,监听总线的所以实例,都会得到总线上的消息。
ConfigClient实例都监听MQ中同一个topic(默认是springcloudbus),当一个服务刷新数据的时候,它会把这个信息放入到topic中,这样监听同一个Topic的服务都会接受大信息,并更新自身配置。

Config中心添加Bus配置

添加引入POM
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
yml新增修改
rabbitmq:
  host: localhost
  port: 5672
  username: chuang
  password: 123456
  virtual-host: host1

#暴露BUS刷新配置端点
management:
  endpoints:
    web:
      exposure:
        include: 'bus-refresh'

客户端需要新增如上pom、并新增yml如下。

rabbitmq:
  host: localhost
  port: 5672
  username: chuang
  password: 123456
  virtual-host: host1

启动配置中心与监听者,并发送更新post 会做到所有监听者服务配置与配置中心一起刷新。

SpringCloud Stream

背景介绍

现如今市面上有四种常用的消息中间件,分别为ActiveMQ、RabbitMQ、RocketMQ、kafka.导致在同一个项目中可能会含有多个消息中间件的存在,比如项目中JavaEE通常会用RabbitMQ 而大数据部分会用Kafka,这样会使得运维人员必须掌握四种消息中间件。于是一种可以可以实现在四种消息中间件来回切换的技术便孕育而生。

SpringCloud Stream 简介

SpringCloud Stram 作用可概况为一句话“屏蔽底层消息中间件的差异,降低切换成本,统一消息的编辑模式”。 具体介绍参考官网即可官网链接.
技术文档参考点这.

Stram 编码API与常用注解

在这里插入图片描述

Stream工作原理以及流程分析图

在这里插入图片描述

SpringCloud Stream 配置

pom引入依赖

<dependency>
 <groupId>org.springframework.cloud</groupId>
 <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
yml配置如下
spring:
  application:
    name: stream-provider
  cloud:
    stream:
      binders: # 绑定MQ服务信息(此处我们是RabbitMQ)
        cityRabbitBinder: # 给Binder定义的名称,⽤于后⾯的关联
          type: rabbit # MQ类型,如果是Kafka的话,此处配置kafka
          environment: # MQ环境配置(⽤户名、密码等)
            spring:
              rabbitmq:
                host: localhost
                port: 5672
                username: guest
                password: guest
                virtual-host: host1
      bindings: # 关联整合通道和binder对象
        output: # output是我们定义的通道名称,此处不能乱改   如果是消费端定义为input即可
          destination: cityExchange # 要使⽤的Exchange名称(消息队列主题名称)
          content-type: text/plain # application/json # 消息类型设置,⽐如json
binder: cityRabbitBinder # 关联MQ服务
生产者端配置
@EnableBinding(Source.class) //定义消息的推送管道
public class MessageProviderimpl implements IMessageProvider {
    @Autowired
    private MessageChannel output;  //消息发送管道 与yml文件互相呼应
    @Override
    public String send() {
        String serial = UUID.randomUUID().toString();
        output.send(MessageBuilder.withPayload(serial).build());
        System.out.println("*************serial:"+serial);
        return null;
    }
}
消费者端配置
@Component
@EnableBinding(Sink.class)
public class ReceiveMessageListennerController {
    @Value("${server.port}")
    private String serverPort;
    @StreamListener(Sink.INPUT)
    public void input(Message<String> message){
        System.out.println("消费者,-------》接收到消息:"+message.getPayload()+"\t port:"+serverPort);
    }
}

总结:掌握stream 可以根据绑定的不同消息队列进行,消息通讯。以上配置仅对rabbitmq进行绑定,同理我们绑定kafka也是如此步骤操作即可。是一款很强大、值得学习的cloud组件。

SpringCloud之Sleuth链路监控

背景介绍

在微服务框架中,一个由客户端发起的请求在后端系统中会经历多个不同服务的结点调用来协同产生最后的请求结果,每一个段的请求通信组合起一条复杂的分布式服务调用链路,这些链路中,如果一环出现高延时或错误,都会引起整个请求最终失败,因此一个关于请求链路监控的技术孕育而生。

Sleuth简介

Springcloud_Sleuth 提供了一套完整的服务跟踪解决方案,在分布式系统中提供追踪解决方案并且兼容支持了zipkin.官网介绍、以及官网链接.

Sleuth之zipkin搭建

下载安装Zipkin

版本选择地址.
下载好后执行如下图、即为安装成功。
在这里插入图片描述

服务配置ZipKin

pom引入依赖

前台访问地址:http://localhost:9411/zipkin/

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
yml添加配置
  zipkin:
    base-url: http://localhost:9411
    sleuth:
      sampler:
        #采样率值介于01之间。1表示全部采样
        probability: 1

2020SpringCloud组件升级

在这里插入图片描述

SpringCloud_Alibaba 全新生态

技术介绍

官网链接.
SpringCloudAlibaba_github技术博客.请点击官网查看(中文)。

Nacos

Nacos简介

一个易于构建云原生应用的动态服务发现、配置管理和服务管理平台。说白Nacos就是注册中心+配置中心的组合(Nacos=Eureka+Config+Bus)。Nacos官网

Nacos安装并运行

下载地址.
下载后运行如下
在这里插入图片描述
启动成功后访问 http://localhost:8848/nacos
默认账户密码均为“nacos”在这里插入图片描述

Nacos配置

父pom引入
<dependency>
       <groupId>com.alibaba.cloud</groupId>
       <artifactId>spring-cloud-alibaba-dependencies</artifactId>
       <version>2.1.0.RELEASE</version>
       <type>pom</type>
       <scope>import</scope>
</dependency>
子pom引入
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
Yml 配置
spring:
  application:
    name: nacos-provider-chuang
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
management:
  endpoints:
    web:
      exposure:
        include: '*'
主启动类配置
@EnableDiscoveryClient
Nacos负载均衡

Nacos 自带集成ribbon所以是使用ribbon进行负载均衡的。
在这里插入图片描述

Nacos CP与AP模式选择

Nacos 支持CP与AP模式的切换(可以理解为它继承了Eureka与Zookeper双重特点,并进行模式切换)
模式选择
一般来说如果不需要存储服务级别的信息且服务实例是通过nacos-client注册,并能够保持心跳上报,那么可以选择AP模式。
如果需要服务级别编辑或者存储配置信息,那么选择CP模式,例如K8S服务和DNS服务适用于CP.

Nacos之服务配置中心

pom添加
<dependency>
  <groupId>com.alibaba.cloud</groupId>
  <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
Nacos中心yaml文件命名规则

官网公式:${spring.application.name}-{spring.profiles.active}-file-extension:properties
即为:服务名+环境+文件后缀名

Nacos命名空间分组

在这里插入图片描述
yml 相关配置

group: TEST_GROUP    指定哪个分组
namespace: 24199322121 通过ID指定 命名空间

Nacos集群与持久化配置

默认Nacos适用适用嵌入式数据库来实现数据存储(derby)。所以,如果启动多个默认配置下的Nacos结点,数据存储是存在一定性问题的,为了解决这个问题,Nacos采用了集中式存储方式来支持集群化部署,目前只支持MySQL的存储。

三种部署模式
模式应用场景
单机模式用于测试和单机调用
集群模式用于生产环境,确保高可用
多集群模式用于多数据中心场景

搭建Nacos集群架构图
在这里插入图片描述

Nacos数据库切换

执行sql脚本(例子为windos版本)
找到该目录执行sql脚本,创建数据库。数据库版本需要5.6.5+
在这里插入图片描述
修改配置文件
找到该目录修改 application.properties
在这里插入图片描述
添加如下配置后重启即可

spring.datasource.platform=mysql

db.num=1
db.url.0=jdbc:mysql://127.0.0.1:3306/nacostest1?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user=root
db.password=123456
Nacos集群配置
  1. Linux上配置mysql 并配置sql文件。
  2. 配置文件设置为mysql数据库
  3. Linux服务器上nacos的集群配置cluster.conf
    3.1在这里插入图片描述
    3.2在这里插入图片描述
  4. 修改startup.sh
    在这里插入图片描述
    在这里插入图片描述
    修改后启动 ./startup -p 3304
    查看启动个数 ps -ef|grep nacos|grep -v grep |wc -l
    5.nginx配置
    修改/usr/local/nginx/conf,进行如下修改。
    在这里插入图片描述
    启动 ./nginx -c /usr/local/nginx/conf/nginx.conf
    自此 :Nacos简单集群配置完毕

Sentinel

Sentinel简介

官网链接.
Sentinel是轻量级的流量控制、熔断降级的 Java库。

Sentinel功能配置

启动Sentinel

启动cmd java -jar Sentinel jar包 --server.port=8887 (8887为指定端口)
访问 ip+端口 账户密码 均为 sentinel 登录即可。
在这里插入图片描述

引用pom

使用Sentinel 您可能会用到如下依赖

        <!--nacos发现-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!-- 持久化 -->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>

        <!-- sentinel -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>

        <!-- openfeign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
yml配置
spring:
  application:
    name: cloudalibaba-sentinel-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    sentinel:
      transport:
        dashboard: localhost:8887 #配置Sentinel dashboard 地址
        port: 8719  #假如被占用会递增依次查找。sentinel的http server 需要占用的端口。

management:
  endpoints:
    web:
      exposure:
        include: '*'
启动类配置
@EnableDiscoveryClient
@SpringBootApplication
打卡与监控

打卡 \即:访问该服务一个接口,访问后Sentinel将会发现该服务,并进行实时监控。
监控界面如下图
在这里插入图片描述
查看请求链路表
在这里插入图片描述

流控规则

直接模式(默认)
在这里插入图片描述
QPS: 多余阈值的请求会被屏蔽掉
线程数: 服务同时处理服务个数
关联模式
当关联资源达到阈值后,限流自己(当A关联的资源B达到阈值后,就限流A自己)
例:当支付接口压力很大时,要限流下单接口。
链路模式
当存在接口行为链时,若以某个接口为入口资源,则该接口下的子链,均会受到阈值限制。

流控效果

快速失败(默认)
顾名思义:即达到阈值,直接失败
预热
根据codeFactor(冷加载因子,默认值为3)的值,从阈值/codeFactor,经过预热时间,达到阈值
预热公式:阈值除以coldFactor(默认值3),经过多长时间达到阈值。
例:
目标QPS为10、启始值:10/3=3,设置为5秒缓冲,阈值由3升到10。
在这里插入图片描述
排队等待
该效果,只适用于QPS 让请求匀速通过,超过阈值的请求会在超时时间内等待处理。超时后未被处理的会被裆下。适用于间隔性的大量请求。

熔断降级

Sentinel的熔断机制是没有半开状态。
降级规则
在这里插入图片描述
RT(平均响应时间,秒级)
平均响应时间,超出阈值,且在时间窗口内通过请求>=5,两个条件同时满足后触发降级。窗口期过后关闭断路器。
注:RT最大4900(更大的需要通过-Dcsp.sentinel.statistic.max.rt=xxxx才能生效)
RT时间内处理完成,搞不定会在时间窗口过后进行降级。
异常比例
QPS>=5,且异常统计(秒级计算)超过阈值时触发降级,时间窗口结束后,关闭降级。
异常数(分钟级)
异常数(分钟统计)超过阈值时,触发降级;时间窗口结束后,关闭降级。

热点规则

热点
何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据。
热点限流:为对某一带有参数的请求进行精准的限流操作。代码中为BlockException进行封装。
在这里插入图片描述
接口变动
注:该接口有两个参数,与数组相同,第一个参数的下表为0,第二个参数下表为1…

				//制定该接口唯一名字,并且指定备选方法
@SentinelResource(value ="testHotkey",
blockHandler = "deal_testHotkey",
//熔断方法
fallback = "fallbackhandler"//忽略某种异常
exceptionsToIgnore = {IllegalArgumentException.class})                

添加该注解的备选方案,只管sentinel异常,不管运行异常。
参数例外项
是指在上图配置了(第几个参数进行限流)在第N个下的参数下不进行限流的例外项。
在这里插入图片描述

Sentinel系统规则

Sentinel 系统自适应限流从整体维度对应用入口流量进行控制,结合应用的 Load、CPU 使用率、总体平均 RT、入口 QPS 和并发线程数等几个维度的监控指标,通过自适应的流控策略,让系统的入口流量和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
在这里插入图片描述
LOAD
仅对 Linux/Unix-like 机器生效):系统的 load1 作为启发指标,进行自适应系统保护。当系统 load1 超过设定的启发值,且系统当前的并发线程数超过估算的系统容量时才会触发系统保护(BBR 阶段)。设定参考值一般是 CPU 核数 * 2.5。
CPU usage
CPU使用率达到阈值,就无法访问该系统。
平均 RT
当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。
并发线程数
当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
入口 QPS
当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。

Sentinel+OpenFeign

该结构类似于Hystrix与OpenFeign.该结构具有低耦合的特性。可以看出Sentinel的思想结构也是借鉴Hystrix的。
yml配置

#激活Sentinel对feign的支持。
feign:
  sentinel:
    enabled: true

启动类配置

@EnableFeignClients

创建接口
创建接口通过注解分别指定服务名、与熔断

						//通讯微服务                    ,熔断方法
@Component
@FeignClient(value = "nacos-provider-chuang",fallback = PaymentFallbackServer.class)

接口里的抽象方法实则为,指定该服务下的某接口方法

//指定调用该服务提供者某方法
 @GetMapping(value = "/payment/nacos/{id}")
 public String getPayment(@PathVariable("id") Integer id);

最后该接口的实现类,为熔断fallback方法。

public class PaymentFallbackServer implements PaymentServer
Sentinel 配置持久化

背景
由于应用重启,Sentinel的限流规则将不复存在,因此我们需要对其进行配置持久化配置。
解决
将我们的Sentinel限流配置,保存在我们的Nacos里。(数据库等持久化的也可以)。
导入pom

<dependency>
 <groupId>com.alibaba.csp</groupId>
 <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>

yml配置

    sentinel:
      transport:
        dashboard: localhost:8887 #配置Sentinel dashboard 地址
        port: 8719  #假如被占用会递增依次查找。sentinel的http server 需要占用的端口。
#        以下配置为Sentinel持久化,将数据保存到Nacos
      datasource:
        ds:
          nacos:
            ### nacos连接地址
            server-addr: localhost:8848
            ## nacos连接的分组
            group-id: DEFAULT_GROUP
            ###路由存储规则
            rule-type: flow
            ### 读取配置文件的 data-id
            data-id: meitemayikt-order-sentinel
            ###  读取培训文件类型为json
            data-type: json

登录Nacos并创建配置文件
在这里插入图片描述

配置解析
resoure资源名
limitApp来源应用
grade阈值类型,0表线程、1表QPS
count单机阈值
strategy流控模式 , 0表直接、1表关联、2表链路
controlBehavior流控效果,0表快速失败,1表Warm Up ,2表排队等待
clusterMode是否集群

seata 处理分布式事务

背景

由于分布式的推广,会变成多服务,多库 ,会牵扯到多数据库,多数据源,夸数据库,夸数据源等等问题,因此分布式的事务处理技术孕育诞生。
例:用户购买商品的业务逻辑。整个业务逻辑由3个微服务提供支持
1.仓储服务:对给定的商品和陈仓储数量。
2.订单服务:根据采购需求创建订单。
3.账户服务:从用户账户中扣除余额
在这里插入图片描述
如上图一次业务操作需要跨多个数据源或需要跨多个系统进行远程调用,因此会产生分布式事务问题。

Seata简介

Seata是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务处理框架。起源于2019年1月由蚂蚁金服和阿里巴巴共同开源的分布式事务解决方案。
官方网站.由于官网很详细,不做过多赘述。
三大组件
TC (Transaction Coordinator) - 事务协调者
维护全局和分支事务的状态,驱动全局事务提交或回滚。
TM (Transaction Manager) - 事务管理器
定义全局事务的范围:开始全局事务、提交或回滚全局事务。
RM (Resource Manager) - 资源管理器
管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。
三大组件流程图
在这里插入图片描述
1.TM向TC申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的XID:
2.XID在微服务调用链路的上下文中传播。
3.RM向TC注册分支事务,将其纳入XID对应全局事务的管辖。
4.TM向TC发起针对XID的全局提交或回滚决议
5.TC调用XID下管辖的全部分支事务完成提交或回滚请求。

Seata安装

下载地址.
下载后修改file.conf在这里插入图片描述
修改添加的注册组件在这里插入图片描述

Seata配置

pom依赖
<dependency>
   <groupId>com.alibaba.cloud</groupId>
   <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
   <version>2.2.1.RELEASE</version>
</dependency>
seata版本冲突解决如下
		<dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
            <exclusions>
                <exclusion>
                    <artifactId>seata-all</artifactId>
                    <groupId>io.seata</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- 添加指定版本seata -->
        <dependency>
            <groupId>io.seata</groupId>
            <artifactId>seata-all</artifactId>
            <version>0.9.0</version>
        </dependency>
yml
  cloud:
    alibaba:
      seata:
        tx-service-group: sc_tx_group #需要与配置文件中server相同
其他文件

创建file.conf与registry.conf
将该目录下的配置文件粘贴进来。
在这里插入图片描述

主启动类
//取消自带数据源
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
Service层重构

在操作dao层的service层入口方法上添加seata的注解事务管理。

										  //有任何异常进行回滚
@GlobalTransactional(name = "自定义名字",rollbackFor = Exception.class
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

闯孙闯孙

觉得有用就赞一个呗

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值