(79)java Spring Cloud+Spring boot+mybatis企业快速开发架构之防止缓存雪崩方案

Posted JIAN2

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了(79)java Spring Cloud+Spring boot+mybatis企业快速开发架构之防止缓存雪崩方案相关的知识,希望对你有一定的参考价值。

缓存雪崩就是在某一时刻,大量缓存同时失效导致所有请求都去查询数据库,导致数据库压力过大,然后挂掉的情况。缓存穿透比较严重的时候也会导致缓存雪崩的发生。推荐分布式架构源码

 缓存雪崩的危害
缓存雪崩最乐观的情况是存储层能抗住,但是用户体验会受到影响,数据返回慢,当压力过大时会导致存储层直接挂掉,整个系统都受影响。对于要做到 99.99% 高可用的产品,是绝对不允许缓存雪崩的发生。

解决方案
这里总结了几种解决方案:

1)缓存存储高可用。比如 Redis 集群,这样就能防止某台 Redis 挂掉之后所有缓存丢失导致的雪崩问题。

2)缓存失效时间要设计好。不同的数据有不同的有效期,尽量保证不要在同一时间失效,统一去规划有效期,让失效时间分布均匀即可。

3)对于一些热门数据的持续读取,这种缓存数据也可以采取定时更新的方式来刷新缓存,避免自动失效。

4)服务限流和接口限流。如果服务和接口都有限流机制,就算缓存全部失效了,但是请求的总量是有限制的,可以在承受范围之内,这样短时间内系统响应慢点,但不至于挂掉,影响整个系统。

5)从数据库获取缓存需要的数据时加锁控制,本地锁或者分布式锁都可以。当所有请求都不能命中缓存,这就是我们之前讲的缓存穿透,这时候要去数据库中查询,如果同时并发的量大,也是会导致雪崩的发生,我们可以在对数据库查询的地方进行加锁控制,不要让所有请求都过去,这样可以保证存储服务不挂掉。

代码示例
这里对加锁的方式进行代码讲解,代码如下所示。

1
2
3
4
5
6
7
8
9
10
11
public Person get(String id) {
    Person person = repo.findOne(id);
    if (person != null) {
        return person;
    }
    synchronized (this) {
        person = dao.findById(id);
        repo.save(person);
    }
    return person;
}[object Object]

上面是最简单的方式,直接用代码同步块来加锁,当然我们也可以用 Lock 来加锁,加锁的本质还是控制并发量,不要让所有请求瞬时压到数据库上面去,加了锁就意味着性能要丢失一部分。其实我们可以用信号量来做,就是限制并发而已,信号量可以让多个线程同时操作,只要在数据库能够抗住的范围内即可。

分布式锁方式
加锁除了用 Jvm 提供的锁,我们还可以用分布式锁来解决缓存雪崩的问题,分布式锁常用的有两种,基于 Redis 和 Zookeeper 的实现。当你从网上搜分布式锁的时候,出来一大堆实现的文章,我个人不建议自己去实现这种功能,用开源的会好点,在这里我给大家推荐一个基于 Redis 实现的分布式锁。

Redisson 是一个在 Redis 的基础上实现的 Java 驻内存数据网格(In-Memory Data Grid)。

Redisson 不仅提供了一系列的分布式的 Java 常用对象,还提供了许多分布式服务(包括 BitSet、Set、Multimap、SortedSet、Map、List、Queue、BlockingQueue、Deque、BlockingDeque、Semaphore、Lock、AtomicLong、CountDownLatch、Publish/Subscribe、Bloom filter、Remote service、Spring cache、Executor service、Live Object service、Scheduler service)。

Redisson 提供了使用 Redis 最简单和便捷的方法。Redisson 的宗旨是促进使用者对 Redis 的关注分离(Separation of Concern),从而让使用者能够将精力更集中地放在处理业务逻辑上。

Redisson 跟 Jedis 差不多,都是用来操作 Redis 的框架,Redisson 中提供了很多封装,有信号量、布隆过滤器等。分布式锁只是其中一个,感兴趣的读者可以自行深入研究。

代码示例如下所示。

1
2
3
4
5
6
7
8
9
10
RLock lock = redisson.getLock("anyLock");
// 最常见的使用方法 lock.lock();
// 支持过期解锁功能
// 10 秒钟以后自动解锁
// 无须调用 unlock 方法手动解锁
lock.lock(10, TimeUnit.SECONDS);
// 尝试加锁, 最多等待 100 秒, 上锁以后 10 秒自动解锁
boolean res = lock.tryLock(10010, TimeUnit.SECONDS);
...
lock.unlock();

 

Java之 Spring Cloud 微服务搭建(第一个阶段)SpringBoot项目实现商品服务器端是调用

一、 Spring cloud微服务概述

1、微服务中的相关概念

(1)服务注册与发现

服务注册:服务实例将自身服务信息注册到注册中心。
这部分服务信息包括服务所在主机IP和提供服务的Port,以及暴露服务自身状态以及访问协议等信息。

服务发现:服务实例请求注册中心获取所依赖服务信息。
服务实例通过注册中心,获取到注册到其中的服务实例的信息,通过这些信息去请求它们提供的服务。

(2)负载均衡

负载均衡是高可用网络基础架构的关键组件,通常用于将工作负载分布到多个服务器来提高网站、应用、数据库或其他服务的性能和可靠性。

(3) 熔断

熔断这一概念来源于电子工程中的断路器(Circuit Breaker)。

在互联网系统中,当下游服务因访问压力过大而响应变慢或失败,上游服务为了保护系统整体的可用性,可以暂时切断对下游服务的调用。

这种牺牲局部,保全整体的措施就叫做熔断。

(4) 链路追踪

随着微服务架构的流行,服务按照不同的维度进行拆分,一次请求往往需要涉及到多个服务。

互联网应用构建在不同的软件模块集上,这些软件模块,有可能是由不同的团队开发、可能使用不同的编程语言来实现、有可能布在了几千台服务器,横跨多个不同的数据中心。

因此,就需要对一次请求涉及的多个服务链路进行日志记录,性能监控即链路追踪

(5)API网关

随着微服务的不断增多,不同的微服务一般会有不同的网络地址,而外部客户端可能需要调用多个服务的接口才能完成一个业务需求,如果让客户端直接与各个微服务通信可能出现:

  • 客户端需要调用不同的url地址,增加难度
  • 再一定的场景下,存在跨域请求的问题
  • 每个微服务都需要进行单独的身份认证

针对这些问题,API网关顺势而生。

API网关直面意思是将所有API调用统一接入到API网关层,由网关层统一接入和输出。一个网关的基本功能有:统一接入、安全防护、协议适配、流量管控、长短链接支持、容错能力。
有了网关之后,各个API服务提供团队可以专注于自己的的业务逻辑处理,而API网关更专注于安全、流量、路由等问题。

2、SpringCloud的介绍


Spring Cloud是一系列框架的有序集合。

它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。

Spring Cloud并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。

3、SpringCloud的架构

(1) SpringCloud中的核心组件

Spring Cloud的本质是在 Spring Boot 的基础上,增加了一堆微服务相关的规范,并对应用上下文 (Application Context)进行了功能增强。

既然 Spring Cloud 是规范,那么就需要去实现,目前Spring Cloud 规范已有 Spring官方,Spring Cloud Netflix,Spring Cloud Alibaba等实现。

通过组件化的方式,Spring Cloud将这些实现整合到一起构成全家桶式的微服务技术栈。
Spring Cloud Netflix组件

Spring Cloud Alibaba组件

Spring Cloud原生及其他组件

(2) SpringCloud的体系结构


从上图可以看出Spring Cloud各个组件相互配合,合作支持了一套完整的微服务架构。

  • 注册中心负责服务的注册与发现,很好将各服务连接起来
  • 断路器负责监控服务之间的调用情况,连续多次失败进行熔断保护。
  • API网关负责转发所有对外的请求和服务
  • 配置中心提供了统一的配置信息管理服务,可以实时的通知各个服务获取最新的配置信息
  • 链路追踪技术可以将所有的请求数据记录下来,方便我们进行后续分析
  • 各个组件又提供了功能完善的dashboard监控平台,可以方便的监控各组件的运行状况

二、案例搭建

使用微服务架构的分布式系统,微服务之间通过网络通信。我们通过服务提供者与服务消费者来描述微服务间的调用关系。

服务提供者:服务的被调用方,提供调用接口的一方
服务消费者:服务的调用方,依赖于其他服务的一方

我们以电商系统中常见的用户下单为例,用户向订单微服务发起一个购买的请求。在进行保存订单之前需要调用商品微服务查询当前商品库存,单价等信息。

在这种场景下,订单微服务就是一个服务消费者,商品微服务就是一个服务提供者。

1、 项目搭建

(1)创建数据库

创建数据库表

SET FOREIGN_KEY_CHECKS=0;

DROP TABLE IF EXISTS `tb_order`;
CREATE TABLE `tb_order` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) DEFAULT NULL COMMENT '用户id',
  `product_id` int(11) DEFAULT NULL COMMENT '商品id',
  `number` int(11) DEFAULT NULL COMMENT '数量',
  `price` decimal(10,2) DEFAULT NULL COMMENT '单价',
  `amount` decimal(10,2) DEFAULT NULL COMMENT '总额',
  `product_name` varchar(40) DEFAULT NULL COMMENT '商品名',
  `username` varchar(40) DEFAULT NULL COMMENT '用户名',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

DROP TABLE IF EXISTS `tb_product`;
CREATE TABLE `tb_product` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `product_name` varchar(40) DEFAULT NULL COMMENT '名称',
  `status` int(2) DEFAULT NULL COMMENT '状态',
  `price` decimal(10,2) DEFAULT NULL COMMENT '单价',
  `product_desc` varchar(255) DEFAULT NULL COMMENT '描述',
  `caption` varchar(255) DEFAULT NULL COMMENT '标题',
  `inventory` int(11) DEFAULT NULL COMMENT '库存',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

DROP TABLE IF EXISTS `tb_user`;
CREATE TABLE `tb_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(40) DEFAULT NULL COMMENT '用户名',
  `password` varchar(40) DEFAULT NULL COMMENT '密码',
  `age` int(3) DEFAULT NULL COMMENT '年龄',
  `balance` decimal(10,2) DEFAULT NULL COMMENT '余额',
  `address` varchar(80) DEFAULT NULL COMMENT '地址',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

(2)创建父工程

1)工程搭建



2)引入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>cn.itbluebox</groupId>
    <artifactId>spring_cloud_demo</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>product_service</module>
        <module>order_service</module>
        <module>eureka_server</module>
    </modules>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.4</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>


    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Greenwich.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <repositories>
        <repository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>http://repo.spring.io/libs-snapshot-local</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>http://repo.spring.io/libs-milestone-local</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
        <repository>
            <id>spring-releases</id>
            <name>Spring Releases</name>
            <url>http://repo.spring.io/libs-release-local</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>http://repo.spring.io/libs-snapshot-local</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </pluginRepository>
        <pluginRepository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>http://repo.spring.io/libs-milestone-local</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
    </pluginRepositories>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

删除父工程的src文件

(3)创建子模块

1)创建product_service子模块

(一)工程搭建



(二)引入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>spring_cloud_demo</artifactId>
        <groupId>cn.itbluebox</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>product_service</artifactId>

    <dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.32</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
    </dependencies>
</project>
(三)创建DAO相关接口和子类
01)创建Product(商品实体类)


package cn.itbluebox.product.entity;
import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import java.math.BigDecimal;
/*
商品实体类
 */
面试官:说说Spring Cloud底层原理?

Spring Cloud(18)——gateway

Spring Cloud Config

快速构建Spring Cloud工程

微服务架构之spring cloud turbine

Spring Cloud-honghu Cloud分布式微服务云系统