SpringCloud Alibaba微服务原理与实战-读书笔记

概述

主要包含以下组件

Sentinel 流量控制和服务降级
Nacos 服务注册与发现
Nacos 分布式配置中心
RocketMQ 消息驱动
Seate 分布式事务
Dubbo RPC通信
OSS 阿里云对象存储

springboot自动装配原理

eg:将redis整合到springboot中:
1.添加starter依赖:spring-boot-starter-data-redis
2.在application.properties中配置redis数据源
3.使用@Autowried引入redisTemplate
4.使用
当使用@Autowried注入redisTemplate实例时.说明IOC容器中已经存在RedisTemplate,这是Springboot自动装配而来的
所以,存在一个机制,这个机制的实现基于某种约定或者规范,只要Starter组件符合SpringBoot中自动装配的规范,就能实现自动装配

自动装配的实现

自动装配在SpringBoot中是通过@EnableAutoConfiguration注解开启的,这个注解声明在@SpringBootApplication中

@Enable注解:主要作用是把相关组件的Bean转配到IOC容器中
@Enable注解对JavaConfig进一步完善,常见的@Enable注解有
@EnableWebMvc和@EnableScheduling等

每个@Enable类的注解都包含@Import

@ImportSelector的作用


自动装配的核心是扫描约定目录下的文件进行解析,解析完成后把得到的Configuration配置类通过ImportSelector进行导入,从而完成Bean的自动装配过程

SpringFactoriesLoaderss是Spring内部提供的一种预定俗成的加载方式,类似于Java中的SPI,它会扫描classpath下的METE-INF/spring.factories文件,这个文件的数据由key=value方式存储



Conditional条件注解



  • springboot中的@Conditional做了一些扩展


手工实现一个starter

1.命名规范:
官方: spring-boot-starter-模块名称
自定义: 模块名称-spring-boot-starter
官方命名模块名称放最后,自定义模块名称放前面
2.创建一个工程,命名为redis-spring-boot-starter
3.添加jar包依赖

<dependency>
      <groupId>org.redisson</groupId>
      <artifactId>redisson</artifactId>
      <version>3.11.1</version>
</dependency>

4.定义属性

@ConfigurationProperties(prefix = "gp.redisson")
public class RedissonProperties {

    private String host = "localhost";
    private String password;
    private int port = 6379;
    private int timeout;
    private boolean ssl;

    public String getHost() {return host;}
    public void setHost(String host) {this.host = host;}
    public String getPassword() {return password;}
    public void setPassword(String password) {this.password = password;}
    public int getPort() {return port;}
    public void setPort(int port) {this.port = port;}
    public boolean isSsl() {return ssl;}
    public void setSsl(boolean ssl) {this.ssl = ssl;}
    public int getTimeout() {return timeout;}
    public void setTimeout(int timeout) {this.timeout = timeout;}
}

5.定义需要自动装配的配置类,主要是把RedissonClient装配到IOC容器
@ConditionalOnClass(Redisson.class)表示只有classpath下存在Redisson这个类,RedissonAutoConfiguration 才会实现自动装配

@Configuration
@ConditionalOnClass(Redisson.class)
@EnableConfigurationProperties(RedissonProperties.class)
public class RedissonAutoConfiguration {
    @Autowired
    RedissonProperties redissonProperties;
    @Bean
    RedissonClient redissonClient(){
       Config config=new Config();
       String prefix="redis://";
       if(redissonProperties.isSsl()){
           prefix="rediss://";
       }
       SingleServerConfig singleServerConfig=config.useSingleServer()
               .setAddress(prefix+redissonProperties.getHost()+":"+redissonProperties.getPort())
               .setConnectTimeout(redissonProperties.getTimeout());
       if(!StringUtils.isEmpty(redissonProperties.getPassword())){
           singleServerConfig.setPassword(redissonProperties.getPassword());
       }
       return Redisson.create(config);
    }
}

6.在resource下创建METE-INF/spring.factories文件,使得SpringBoot程序可以扫描到该文件完成自动装配

org.springframework.boot.autoconfigure.EnableAutoConfiguration=
  com.gupaoedu.book.RedissonAutoConfiguration

7.使用的时候,直接添加依赖

  <groupId>com.gupaoedu.book</groupId>
  <artifactId>redis-spring-boot-starter</artifactId>
  <version>1.0-SNAPSHOT</version>

8.添加属性

gp.redisson.host=192.168.56.111
gp.redisson.port=6379

springboot集成dubbo

  • 生产者
    1.启动方法要加上@DubboComponentScan
    2.服务加上@Service注解,该注解是dubbo包下的,不是spring的
    3.添加dubbo依赖
<dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>2.7.5</version>
</dependency>

4.加上配置文件

dubbo.application.name=springboot-provider
dubbo.protocol.name=dubbo
dubbo.protocol.port=20880
dubbo.registry.address=N/A
  • 消费者
声明服务时使用@Reference注解获取一个远程代理对象
@Reference(url = "dubbo://127.0.0.1:20880/com.gupaoedu.book.dubbo.IHelloService")
private IHelloService helloService;

  • dubbo使用nacos作为注册中心
    1.依赖nacos组件
<dependency>
            <groupId>com.alibaba.boot</groupId>
            <artifactId>nacos-discovery-spring-boot-starter</artifactId>
            <version>0.2.4</version>
        </dependency>

2.dubbo注册地址加上nacos协议
dubbo.registry.address=nacos://192.168.56.111:8848
3.启动类加上Dubbo注解
@DubboComponentScan

  • dubbo使用nacos作为注册中心
    1.依赖nacos组件
<dependency>
            <groupId>com.alibaba.boot</groupId>
            <artifactId>nacos-discovery-spring-boot-starter</artifactId>
            <version>0.2.4</version>
        </dependency>

2.dubbo注册地址加上nacos协议
dubbo.registry.address=nacos://192.168.56.111:8848
3.启动类加上Dubbo注解
@DubboComponentScan

  • springcloud使用nacos作为注册中心
    1.引入服务发现依赖
<dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-nacos-discovery</artifactId>
</dependency>

2.配置
spring.cloud.nacos.discovery.server-addr=192.168.56.111:8848

Nacos服务注册与发现实现原理

nacos源码分析

  • 服务注册
  • 服务地址获取
  • 服务地址变化感知
    spring-cloud-common中有个ServiceRegistry接口,是服务注册的标准
package org.springframework.cloud.client.serviceregistry;

public interface ServiceRegistry<R extends Registration> {
    void register(R registration);

    void deregister(R registration);

    void close();

    void setStatus(R registration, String status);

    <T> T getStatus(R registration);
}

其中一个实现类是NacosServiceRegistry
在spring-cloud-commons包的META-INF/spring.factories中包含自动装配信息

org.springframework.boot.autoconfigure.EnableAutoConfiguration=
  com.alibaba.cloud.nacos.NacosDiscoveryAutoConfiguration,
  com.alibaba.cloud.nacos.ribbon.RibbonNacosAutoConfiguration,
  com.alibaba.cloud.nacos.endpoint.NacosDiscoveryEndpointAutoConfiguration,
  com.alibaba.cloud.nacos.discovery.NacosDiscoveryClientAutoConfiguration,
  com.alibaba.cloud.nacos.discovery.configclient.NacosConfigServerAutoConfiguration
org.springframework.cloud.bootstrap.BootstrapConfiguration=
  com.alibaba.cloud.nacos.discovery.configclient.NacosDiscoveryClientConfigServiceBootstrapConfiguration

sentinel微服务熔断和限流

1.限流规则配置

public class FlowRuleInitFunc implements InitFunc{
    @Override
    public void init() throws Exception {
        List<FlowRule> rules=new ArrayList<>();
        FlowRule rule=new FlowRule();
        rule.setCount(1);
        rule.setResource("hello");
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        rule.setLimitApp("default");
        rules.add(rule);
        FlowRuleManager.loadRules(rules);
    }
}

2.请求方法设置限流注解,增加限流后返回的方法

@RestController
public class HelloController {

    @SentinelResource(value = "hello",blockHandler = "blockHandlerHello")
    @GetMapping("/say")
    public String hello(){
        return "hello ,Mic";
    }
    public String blockHandlerHello(BlockException e){
        return "被限流了";
    }
}

3.在META-INF.services增加限流类,设置扩展点

com.gupaoedu.book.springcloud.sentinel.springcloudsentinelsample.FlowRuleInitFunc

4.添加限流依赖

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

5.将一类url归类为一个

@Service
public class CustomerUrlCleaner implements UrlCleaner{
    @Override
    public String clean(String originUrl) {
        if(StringUtils.isEmpty(originUrl)){
            return originUrl;
        }
        if(originUrl.startsWith("/clean/")){
            return "/clean/*";
        }
        return originUrl;
    }
}
  • Sentinel集成Nacos动态流控规则
    1.nacos配置
[{
    "resource":"/dynamic",
    "limitApp":"default",
    "grade":1,
    "count":1,
    "strategy":0,
    "controlBehavior":0,
    "clusterMode":false
}]

2.项目配置文件中配置

spring:
  application:
    name: spring-cloud-sentinel-dynamic
  cloud:
    sentinel:
      transport:
        dashboard: 127.0.0.1:7777
      datasource:
        - nacos:
            server-addr: 192.168.56.111:8848
            data-id: ${spring.application.name}-sentinel-flow
            group-id: DEFAULT_GROUP
            data-type: json
            rule-type: flow

3.在sentinel控制台可以看到规则

现在不支持在控制台修改规则同步到nacos
可以通过修改源码,对控制台的curd操作的结果保存到nacos实现将控制台的规则持久化到nacos中

Sentinel工作原理

分布式事务

分布式事务指事务的参与者,支持事务的服务器分别位于不同的节点上
分布式事务常见解决方案:
1.两阶段提交协议

第一阶段是事务准备阶段
第二阶段是事务提交或者回滚阶段
准备阶段:事务管理器(TM)通知资源管理器(RM)准备分支事务,记录事务日志,并告知事务管理器准备结果
提交/回滚阶段:如果所有资源管理器(RM)在准备阶段都明确返回成功,则事务管理器向所有资源管理器发起事务提交指令完成数据变更,反之,如果任何一个资源管理器明确返回失败,则事务管理器向所有资源管理器发送事务回滚指令
缺点: 同步阻塞,过于保守(任意一个节点失败都会回滚)
事务协调者单点故障:如果协调者第二阶段出现故障,那么其他参与者会处于锁定状态
脑裂导致数据不一致: 在第二阶段中,事务协调者向所有参与者发送commit请求
发生局部网络异常导致只有一部分参与者收到commit请求,这部分参与者收到请求后会执行commit操作,但是未收到commit请求的节点由于事务无法提交
导致数据出现不一致问题

2.三阶段提交协议
比二阶段多一个询问阶段
事务协调者向参与者发送事务执行请求,询问是否可以执行指令

Base理论

通过牺牲数据的强一致性来获取高可用性
Base Avaliable(基本可用): 出现故障时,允许损失一部分功能可用性,保证核心功能可用
Soft State(软状态) : 允许系统中数据存在中间状态,这个状态不影响系统可用性,就是允许系统中不同节点的数据副本存在延时
Eventually Consistent(最终一致性):中间状态数据经过一段时间之后达到最终数据一致性

分布式事务常见解决方案

  1. TCC补偿型方案(属于两段式)(关键字:事先冻结):



2.基于可靠性消息的最终一致性方案(基于中间件实现)


支付服务的本地事务与发送消息这个操作的原子性问题:
1.先发送消息,再执行数据库事务
会出现消息发送成功但是本地事务更新失败的情况
2.先执行数据库事务,再发送消息
出现mq响应超时导致异常,从而将本地事务回滚,但消息可能已经发送成功,也存在数据不一致问题



最大努力通知型


分布式事务框架seata

AT模式

分为三大模块:TM(事务管理器) RM(资源管理器) TC(事务控制器),
TM: 负责向TC注册一个全局事务,并生成一个全局唯一的XID
在AT模式下,每个数据库资源被当做一个RM,在业务层面通过JDBC标准接口访问RM时,Seata会对所有的请求进行拦截,每个本地事务提交时,RM会向TC注册一个分支事务

Saga模式

把一个业务流程中的长事务分为多个本地短事务,业务流程中每个参与者都提交真实的提交给本地短事务,
当其中一个参与者事务执行失败,则通过补偿机制补偿前面已经成功的参与者


常见两种协调方式

  1. 事件/编排式
  2. 命令/协调式

Seata AT实现原理

AT模式是基于XA事务模型演进而来,所以它的整体机制也是一个改进版的两阶段提交协议
第一阶段: 业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源
第二阶段: 提交异步化,非常快速完成,回滚通过第一阶段的回滚日志进行反向补偿



原文地址:https://www.cnblogs.com/Baronboy/p/15384815.html