微服务笔记03

01. Nacos

1. 简介

image-20220124173050419


据说 Nacos 在阿里巴巴内部有超过 10 万的实例运行,已经过了类似双十一等各种大型流量的考验

image-20220124173127532

C:Consistency(强一致性):所有节点在同一时间看到的数据是一致的

A:Availability(可用性):所有的请求都会收到响应

P:Partition tolerance(分区容错性):

CPA理论关注粒度是数据,而不是整体系统设计的策略。


最多只能同时较好的满足两个
CAP理论的核心是:一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需求
因此,根据 CAP 原理将 NoSQL 数据库分成了满足 CA 原则、满足 CP 原则和满足 AP 原则三 大类:
CA - 单点集群,满足一致性,可用性的系统,通常在可扩展性上不太强大
CP - 满足一致性,分区容忍性的系统,通常性能不是特别高
AP - 满足可用性,分区容忍性的系统,通常可能对一致性要求低一些。

知乎文章解析


2. 安装

安装

docker pull nacos/nacos-server:1.1.4

运行

docker run -d -p 8848:8848 -e MODE=standalone -e PREFER_HOST_MODE=hostname --restart always --name nacos nacos/nacos-server:1.1.4

3. 整合nacos

1. 创建项目

版本限制

<dependencyManagement>
    <!--spring cloud alibaba 2.1.0.RELEASE-->
    <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>
</dependencyManagement>

2. 引依赖

<!--SpringCloud alibaba nacos -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

3. 写yml

server:
  port: 9001

spring:
  application:
    name: nacos-payment-provider
  cloud:
    nacos:
      discovery:
        server-addr: 47.98.251.199:8848 #配置Nacos地址

management:
  endpoints:
    web:
      exposure:
        include: '*'

4. 主启动

@EnableDiscoveryClient //nacos发现
@SpringBootApplication
public class PaymentMain9001 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentMain9001.class,args);
    }
}

一个项目多个实例

image-20220125180119597


4. 负载均衡

为什么Nacos可以负载均衡,看包结构就能看到这个netflix.ribbon

5. nacos和CPA

image-20220125182403640


image-20220125182425397


nacos支持AP和CP模式切换

C是所有节点在同一时间看到的数据是一致的;而A的定义是所有的请求都会收到响应。

何时选择使用何种模式?
一般来说,
如果不需要存储服务级别的信息且服务实例是通过nacos-client注册,并能够保持心跳上报,那么就可以选择AP模式。当前主流的服务如 Spring cloud 和 Dubbo 服务,都适用于AP模式,AP模式为了服务的可能性而减弱了一致性,因此AP模式下只支持注册临时实例。

如果需要在服务级别编辑或者存储配置信息,那么 CP 是必须,K8S服务和DNS服务则适用于CP模式。
CP模式下则支持注册持久化实例,此时则是以 Raft 协议为集群运行模式,该模式下注册实例之前必须先注册服务,如果服务不存在,则会返回错误。

curl -X PUT ‘$NACOS_SERVER:8848/nacos/v1/ns/operator/switches?entry=serverMode&value=CP’


6. 配置中心-基础配置

Nacos同springcloud-config一样,在项目初始化时,要保证先从配置中心进行配置拉取,
拉取配置之后,才能保证项目的正常启动。

springboot中配置文件的加载是存在优先级顺序的,bootstrap优先级高于application


Nacos会记录配置文件的历史版本默认保留30天,此外还有一键回滚功能,回滚操作将会触发配置更新


image-20220125194040960


自带动态刷新

修改下Nacos中的yaml配置文件,再次调用查看配置的接口,就会发现配置已经刷新


springcloud的要发送post请求到那个…才能刷新,麻烦,这个就不一样了。还不用担心乱码问题了。

7. 配置中心-分类配置

问题1:
实际开发中,通常一个系统会准备
dev开发环境
test测试环境
prod生产环境。
如何保证指定环境启动时服务能正确读取到Nacos上相应环境的配置文件呢?

问题2:
一个大型分布式微服务系统会有很多微服务子项目,
每个微服务项目又都会有相应的开发环境、测试环境、预发环境、正式环境…
那怎么对这些微服务配置进行管理呢?


1. 是什么
类似Java里面的package名和类名
最外层的namespace是可以用于区分部署环境的,Group和DataID逻辑上区分两个目标对象。
2. 三者情况

image-20220125195209273

NameSpace,Group,Service

3. 默认情况:
Namespace=public,Group=DEFAULT_GROUP, 默认Cluster是DEFAULT

Nacos默认的命名空间是public,Namespace主要用来实现隔离
比方说我们现在有三个环境:开发、测试、生产环境,我们就可以创建三个Namespace,不同的Namespace之间是隔离的。

Group默认是DEFAULT_GROUP,Group可以把不同的微服务划分到同一个分组里面去

Service就是微服务;一个Service可以包含多个Cluster(集群),Nacos默认Cluster是DEFAULT,Cluster是对指定微服务的一个虚拟划分。
比方说为了容灾,将Service微服务分别部署在了杭州机房和广州机房
这时就可以给杭州机房的Service微服务起一个集群名称(HZ),
给广州机房的Service微服务起一个集群名称(GZ),还可以尽量让同一个机房的微服务互相调用,以提升性能。

最后是Instance,就是微服务的实例

以后要是又忘记了,就回去看尚硅谷springcloud视频复习吧。

8. 集群搭建

没有进行学习,希望可以在以后,mysql和nacos这些一起玩集群搭建,然后就是里面涉及Nigix,这个我还没有进行学习,所有只能暂时放弃,以后回来学爆它。

02. Sentinel

1. 简介

自我陈述,和hystrix一样,做服务降级,服务熔断,服务限流等。

它与hystrix相比的优点是,该程序为我们提供了操作界面,它使用的技术栈是spring boot。

image-20220129181030380


2. 环境搭建

环境搭建:

这里我采用docker安装环境,

docker pull bladex/sentinel-dashboard:1.7.0
docker run --restart=always \
--name sentinel \
-d -p 8858:8858 -d bladex/sentinel-dashboard:1.7.0

非常快捷就可以安装成功了

访问方式:http://47.98.251.199:8858

上面会出现,实时监控页面为空白的情况

哪怕自己已经按照视频思路来也还是出现一样的情况,所有最后我妥协了,选择在本地运行jar包开启

这个时候,访问页面为:http:localhost:8080

3. 初始化演示工程

  • 依赖
<dependencies>
    <!--SpringCloud ailibaba nacos -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <!--SpringCloud ailibaba sentinel-datasource-nacos 后续做持久化用到-->
    <dependency>
        <groupId>com.alibaba.csp</groupId>
        <artifactId>sentinel-datasource-nacos</artifactId>
    </dependency>
    <!--SpringCloud ailibaba 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>
    <!-- SpringBoot整合Web组件+actuator -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <!--日常通用jar包配置-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>4.6.3</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

4. 流控规则

image-20220130155512367

1. 基本介绍

  • 资源名:唯一名称,默认请求路径
  • 针对来源:Sentinel可以针对调用者进行限流,填写微服务名,默认default
  • 阀值类型/单机阈值:
    • QPS(每秒钟的请求数量):当调用该api的QPS达到阈值的时候,进行限流。
    • 线程数:当调用该api的线程达到阈值的时候,进行限流
  • 是否集群:不需要集群
  • 流控模式:
    • 直接:api达到限流条件时,直接限流
    • 关联:当关联的资源达到阈值时,就限流自己
    • 链路:只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就进行限流[api级别的针对来源])
  • 流控效果:
    • 快速失败:直接失败,抛异常
    • Warm Up:根据codeFactor(冷加载因子,默认3)的值,从阈值/codeFactor,经过预热时长,才达到设置的QPS阈值。
    • 排队等待:匀速排队,让请求以匀速的速度通过,阈值类型必须设置为QPS,否则无效。

自我尝试例子:

  • 可以在簇点链路那里点击配置
  • 当然也可在流控规则那里配置,建议就是在簇点链路那里配置。

image-20220201210027808

测试结果:

image-20220201210214695


关于QPS与线程数的区别

  • 简单粗暴的说:QPS是挡在家门外,线程数是关门打狗

QPS会在请求进入时有一道门,给多少个进就多少个进,多了就报异常

线程数是,全部请求全部涌入,配了多少个线程就只有多少个线程。处理不过来了就直接报错。

我们可以试一试就是在请求那里休眠个几秒,多发几个请求就直接崩盘了。


2. 流控模式

关联

设置效果(认真细读,不要搞反主次了)
当关联资源/testB的qps阀值超过1时,就限流/testA的Rest访问地址,当关联资源到阈值后限制配置好的资源名

image-20220201214357814


image-20220201223613515

解: 当/testA因为只有一个线程数而处理不过来的时候,当访问/testC会直接报错。

(/testC被限流)

链路

只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就进行限流[api级别的针对来源])

image-20220202182930278

3. 流控效果

预热

公式:阈值除以coldFactor(默认值为3),经过预热时长后才会达到阈值


默认 coldFactor 为 3,即请求QPS从(threshold / 3) 开始,经多少预热时长才逐渐升至设定的 QPS 阈值。


案例,阀值为10+预热时长设置5秒。
系统初始化的阀值为10 / 3 约等于3,即阀值刚开始为3;然后过了5秒后阀值才慢慢升高恢复到10


image-20220203123847629


应用场景

如:秒杀系统在开启的瞬间,会有很多流量上来,很有可能把系统打死,预热方式就是把为了保护系统,可慢慢的把流量放进来,慢慢的把阀值增长到设置的阀值。


排队等待

匀速排队,让请求以均匀的速度通过,阀值类型必须设成QPS,否则无效。
设置含义:/testA每秒1次请求,超过的话就排队等待,等待的超时时间为20000毫秒。

一开始理解错了超时等待的作用,现在我感觉这个的作用就是,当出现需要等待20秒的就报错。

image-20220203130312893

5. 降级规则

基本介绍

image-20220203131613377

RT(平均响应时间,秒级)
平均响应时间 超出阈值 且 在时间窗口内通过的请求>=5,两个条件同时满足后触发降级
窗口期过后关闭断路器
RT最大4900(更大的需要通过-Dcsp.sentinel.statistic.max.rt=XXXX才能生效)

异常比列(秒级)
QPS >= 5 且异常比例(秒级统计)超过阈值时,触发降级;时间窗口结束后,关闭降级

异常数(分钟级)
异常数(分钟统计)超过阈值时,触发降级;时间窗口结束后,关闭降级


Sentinel的断路器是没有半开状态的

RT

image-20220203143108221

当五次平均访问时间炒锅阈值,就是这里设置的200ms,那么在时间窗口期内访问都会报错,当时间窗口期结束,又恢复正常。


image-20220203143343610


image-20220203143355135

异常比例

image-20220203165514522

image-20220203170646720


按照上述配置,
单独访问一次,必然来一次报错一次(int age = 10/0),调一次错一次;

开启jmeter后,直接高并发发送请求,多次调用达到我们的配置条件了。
断路器开启(保险丝跳闸),微服务不可用了,不再报错error而是服务降级了。

异常数

image-20220203171322778


时间窗口一定要大于等于60秒。


image-20220203171334282

6. 热点key限流

是什么

image-20220203181207559

建议使用第二种:

image-20220203181107981


配置与测试

@GetMapping("/testHotKey")
@SentinelResource(value = "testHotKey",blockHandler = "deal_testHotKey")
public String testHotKey(@RequestParam(value = "p1",required = false)String p1
        , @RequestParam(value = "p2",required = false)String p2) {
    return "testHotKey,"+"p1: "+p1+",p2: "+p2;
}

public String deal_testHotKey(String p1, String p2, BlockException blockException) {
    return "testHotKey,"+"p1: "+p1+",p2: "+p2+",error!!!!";
}

image-20220203181344677

参数例外项

image-20220204143516299


7. 系统规则

是什么

image-20220204151946669

规则介绍

系统保护规则是从应用级别的入口流量进行控制,从单台机器的load、cpu使用率、平均RT、入口QPS和并发线程等几个维度监控应用指标,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。

系统保护规则是应用整体维度的,而不是资源维度的,并且仅对入口流量生效。入口流量指的是进入应用的流量,比如Web服务或Dubbo服务端接收的请求,都属于入口流量。

系统规则支持一下的模式:

  • Load自适应(仅对Linux/Unix-like机器生效):系统的load1作为启动指标,进行自适应系统保护。当系统load1超过设定的启动值,且系统当前的并发线程超过估算的系统容量时才会触发系统的maxQPS * minRT估算得出。设定参考值一般是:CPU cores * 2.5
  • CPU usage(1.5.0+版本):当系统CPU使用率超过阈值即触发系统保护(取值范围0.0-1.0),比较敏感
  • 平均RT:当单台机器上所有入口流量的平均RT达到阈值即触发系统保护,单位是毫秒。
  • 并发线程数:当单台机器上所有入口流量的并发线程数达到阈值触发系统保护。
  • 入口QPS:当单台机器上所有入口流量的QPS达到阈值即触发系统保护。

8. @SentinelResource

1 系统默认的,没有体现我们自己的业务要求

2 依照现有条件,我们自定义的处理方法又和业务代码耦合在一块,不直观。

3 每个业务方法都添加一个兜底的,那代码膨胀加剧。

4 全局统一的处理方法没有体现。

1. 客户自定义限流处理逻辑
  • 创建CustomerBlockHandler类用于自定义限流处理逻辑

  • 自定义限流处理类

  • import com.alibaba.csp.sentinel.slots.block.BlockException;
    import com.lin.entities.CommonResult;
    
    public class CustomerBlockHandler {
        public static CommonResult handleException(BlockException exception){
            return new CommonResult(2020,"自定义的限流处理信息......CustomerBlockHandler");
        }
    }
    
  • @GetMapping("/rateLimit/customerBlockHandler")
        @SentinelResource(value = "customerBlockHandler"
                ,blockHandlerClass = CustomerBlockHandler.class
                ,blockHandler = "handleException")
        public CommonResult customerBlockHandler() {
            return new CommonResult(200, "按客户自定义限流处理逻辑");
        }
    

9. 服务熔断功能

兜底方法:fallback

合上服务降级方法

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.lin.entities.CommonResult;
import com.lin.entities.Payment;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

@RestController
public class CircleBreakerController {
    public static final String SERVICE_URL = "http://nacos-payment-provider";

    @Resource
    private RestTemplate restTemplate;

    /**
     * 这里我们最先演示就是配fallback的情况
     * 前面我们配的是:blockHandler
     * */
    @GetMapping("/consumer/fallback/{id}")
    @SentinelResource(value = "fallback"
            , fallback = "handlerFallback"
            ,blockHandler = "blockHandler"
            ,exceptionsToIgnore = {IllegalArgumentException.class}) //fallback负责业务异常
    public CommonResult<Payment> fallback(@PathVariable Long id) {
        CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/" + id, CommonResult.class, id);

        if (id == 4) {
            throw new IllegalArgumentException("IllegalArgumentException,非法参数异常....");
        } else if (result.getData() == null) {
            throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常");
        }

        return result;
    }

    public CommonResult handlerFallback(@PathVariable Long id, Throwable e) {
        Payment payment = new Payment(id, "null");
        return new CommonResult<>(444, "兜底异常handlerFallback,exception内容  " + e.getMessage(), payment);
    }

    public CommonResult blockHandler(@PathVariable("id") Long id, BlockException blockException) {
        Payment payment = new Payment(id,"null");
        return new CommonResult(445,"blockHandler-sentinel限流,无此流水: blockException "+blockException.getMessage(),payment);
    }
}

对比,还没看

image-20220205200532891

10. 规则持久化

1. 是什么

一旦我们重启应用,sentinel规则将消失,生产环境需要将配置规则进行持久化


2. 怎么玩

将限流配置规则持久化进Nacos保存,只要刷新8401某个rest地址,sentinel控制台的流控规则就能看到,只要Nacos里面的配置不删除,针对8401上sentinel上的流控规则持续有效

3. 配置
spring:
  application:
    name: cloudalibaba-sentinel-service
  cloud:
    nacos:
      discovery:
        #Nacos服务注册中心地址
        server-addr: 47.98.251.199:8848
    sentinel:
      transport:
        #配置Sentinel dashboard地址
        dashboard: localhost:8080
        #默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口
        port: 8719
      datasource:
        ds1:
          nacos:
            server-addr: 47.98.251.199:8848
            dataId: cloudalibaba-sentinel-service
            groupId: DEFAULT_GROUP
            data-type: json
            rule-type: flow
  • 看那个datasource那里就行了。

Nacos配置一下:

image-20220205202851450

[
    {
        "resource": "/rateLimit/byUrl",
        "limitApp": "default",
        "grade": 1,
        "count": 1,
        "strategy": 0,
        "controlBehavior": 0,
        "clusterMode": false
    }
]
resource:资源名称;
limitApp:来源应用;
grade:阈值类型,0表示线程数,1表示QPS;
count:单机阈值;
strategy:流控模式,0表示直接,1表示关联,2表示链路;
controlBehavior:流控效果,0表示快速失败,1表示Warm Up,2表示排队等待;
clusterMode:是否集群。

03. Seata

是什么:

Seata是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务

官网

能干嘛:

一个典型的分布式事务过程

  • 分布式事务处理过程的一ID+三组件模型

Transaction ID XID:全局唯一的事务ID

三个组件概念

  • Transaction Coordinator(TC):事务协调器,维护全局事务的运行状态,负责协调并驱动全局的提交或回滚
  • Transaction Manager™:控制全局事务的边界,负责开启全局事务,并最终发起全局提交或全局回滚的协议。
  • Resource Manager(RM):控制分支事务,负责分支注册、状态汇报,并接收事务协调器的指令,驱动分支(本地)事务的提交和回滚

处理过程

  1. TM 向 TC 申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的 XID;
  2. XID 在微服务调用链路的上下文中传播;
  3. RM 向 TC 注册分支事务,将其纳入 XID 对应全局事务的管辖;
  4. TM 向 TC 发起针对 XID 的全局提交或回滚决议;
  5. TC 调度 XID

image-20220205212506408

小项目分析能力

分析数据库,理清思路。

  • 想象它们是在不同机器上面的,所有什么联表就不需要想了

image-20220207002733352


seata的@GlobalTransactional注解:

image-20220207012944265


image-20220207013002264


具体怎么玩,还不是很懂。

config下面的

import com.alibaba.druid.pool.DruidDataSource;
import io.seata.rm.datasource.DataSourceProxy;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.transaction.SpringManagedTransactionFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import javax.sql.DataSource;

@Configuration
public class DataSourceProxyConfig {
    @Value("${mybatis.mapperLocations}")
    private String mapperLocations;

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource druidDataSource() {
        return new DruidDataSource();
    }

    @Bean
    public DataSourceProxy dataSourceProxy(DataSource dataSource) {
        return new DataSourceProxy(dataSource);
    }

    @Bean
    public SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSourceProxy);
        sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));
        sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());
        return sqlSessionFactoryBean.getObject();
    }
}

cations}")
private String mapperLocations;

@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource druidDataSource() {
    return new DruidDataSource();
}

@Bean
public DataSourceProxy dataSourceProxy(DataSource dataSource) {
    return new DataSourceProxy(dataSource);
}

@Bean
public SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception {
    SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
    sqlSessionFactoryBean.setDataSource(dataSourceProxy);
    sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));
    sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());
    return sqlSessionFactoryBean.getObject();
}

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

with the wind(随风)

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值