java脚手架系列10-统一缓存、分布式锁

server/2024/10/22 21:34:22/

之所以想写这一系列,是因为之前工作过程中有几次项目是从零开始搭建的,而且项目涉及的内容还不少。在这过程中,遇到了很多棘手的非业务问题,在不断实践过程中慢慢积累出一些基本的实践经验,认为这些与业务无关的基本的实践经验其实可以复刻到其它项目上,在行业内可能称为脚手架,因此决定将此java基础脚手架的搭建总结下来,分享给大家使用。

注意由于框架不同版本改造会有些使用的不同,因此本次系列中主要使用基本框架是 spring-boo-2.3.12.RELEASE和spring-cloud.-Hoxton.SR12,所有代码都在commonFramework项目上:https://github.com/forever1986/commonFramework/tree/master

目录

1 缓存

我们知道,无论是数据库或者是文件系统,大部分都存在于磁盘之上,而磁盘往往是整个访问链路中速度最慢之一,因此如何快速避开磁盘访问,往往是业务需要解决性能问题之一,因此缓存就是为了这里而生。在数据库或者对象存储中,其实它们本身就是说过了缓存,而这里要将的缓存是基于业务层面的。在业务中,某些热点数据由于其访问量巨大,因此可以放入缓存中实现。下面就以redis为例,做一个redis集成到项目的脚手架:

参考common-redis子模块和manage-biz子模块

1)在common子模块下面新建common-redis子模块,该子模块的作用就是配置redis基本配置,以spring.factories方式发布
2)创建RedisConfig配置类,里面默认配置redisTemplate和stringRedisTemplate(同时使用@ConditionalOnMissingBean({RedisTemplate.class})注解,使得引用该子模块也可以自定义自己的Template)

注意:redis有2种不同的template(2种的key不能共享)
1.StringRedisTemplate:以String作为存储方式:默认使用StringRedisTemplate,其value都是以String方式存储
2.RedisTemplate:
1)使用默认RedisTemplate时,其value都是根据jdk序列化的方式存储
2)自定义Jackson2JsonRedisSerializer序列化,以json格式存储,其key与StringRedisTemplate共享,返回值是LinkedHashMap(本案例中使用该种方式)
3)自定义GenericJackson2JsonRedisSerializer序列化,以json格式存储,其key与StringRedisTemplate共享,返回值是原先对象(因为保存了classname)

java">package com.demo.redis;import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;@Configuration
public class RedisConfig {/*** 主要做redis配置。redis有2种不同的template(2种的key不能共享)* 1.StringRedisTemplate:以String作为存储方式:默认使用StringRedisTemplate,其value都是以String方式存储* 2.RedisTemplate:*    1)使用默认RedisTemplate时,其value都是根据jdk序列化的方式存储*    2)自定义Jackson2JsonRedisSerializer序列化,以json格式存储,其key与StringRedisTemplate共享,返回值是LinkedHashMap*    3)自定义GenericJackson2JsonRedisSerializer序列化,以json格式存储,其key与StringRedisTemplate共享,返回值是原先对象(因为保存了classname)*/@Bean@ConditionalOnMissingBean({RedisTemplate.class})public RedisTemplate redisTemplate(RedisConnectionFactory factory) {RedisTemplate<String, Object> template = new RedisTemplate();template.setConnectionFactory(factory);//本实例采用Jackson2JsonRedisSerializerJackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);jackson2JsonRedisSerializer.setObjectMapper(om);StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();template.setKeySerializer(stringRedisSerializer);template.setHashKeySerializer(stringRedisSerializer);template.setValueSerializer(jackson2JsonRedisSerializer);template.setHashValueSerializer(jackson2JsonRedisSerializer);template.afterPropertiesSet();return template;}@Bean@ConditionalOnMissingBean({StringRedisTemplate.class})public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory factory) {StringRedisTemplate template = new StringRedisTemplate();template.setConnectionFactory(factory);return template;}
}

3)在spring.factories配置RedisConfig类
4)在manage-biz子模块中引入common-redis

<dependency><groupId>org.example</groupId><artifactId>common-redis</artifactId><version>${project.version}</version>
</dependency>

5)在yaml配置文件中redis访问(由于manage-biz的yaml配置在nacos上面,因此需要nacos修改cloud-manage-biz-service文件配置)

spring:# redis配置redis:database: 1host: 127.0.0.1port: 6379password:timeout: 30000client-type: jedisjedis:pool:max-active: 1000max-idle: 100min-idle: 0maxWait: 1000

6)编写TestRedisController和TestRedisService演示redis存储对象和byte字节示例

2 分布式

2.1 分布式锁的作用

在数据库,我们对一个资源进行操作,比如更新一行数据,那么数据库根据你设置的事务级别,一般都会对其加锁,加锁的原因其实就是怕并发操作时,避免脏数据。
而在不同服务之间,其锁的概念也是有的。比如我们为了避免前端重复点击,一般会给前端返回一个key,然后前端提交数据时,将key返回给后端,后端验证是否同时有同一个key多个请求,如果存在则返回重复操作。

2.2 分布式锁的实现方式

常见的分布式锁实现有以下几种方式:
1)基于数据库实现分布式
2)基于zookeeper实现分布式
3)基于redis实现分布式

从理解的难易程度角度(从低到高) :数据库 > 缓存 > Zookeeper
从实现的复杂性角度(从低到高):Zookeeper >= 缓存 > 数据库
从性能角度(从高到低):缓存 > Zookeeper >= 数据库
从可靠性角度(从高到低):Zookeeper > 缓存 > 数据库

2.3 代码实践

参考子模块:common-redis和distributed-lock-service

本案例中使用redis来实现分布式锁,同时引入redisson框架(该框架封装了基于redis分布式锁,让我们非常方便使用。另外如zookeeper也有Curator框架)
1)新建distributed-lock-service子模块,引入以下依赖:

<dependency><groupId>org.example</groupId><artifactId>common-redis</artifactId><version>${project.version}</version>
</dependency>
<!--redisson中已经引入spring-starter-web,因此无需在引入 -->
<dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId><version>3.12.5</version>
</dependency>
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId>
</dependency>

2)编写RedissonConfig,配置RedissonClient

java">package com.demo.redis.config;import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class RedissonConfig {@Value("${spring.redis.host}")private String host;@Value("${spring.redis.port}")private String port;@Value("${spring.redis.password}")private String redisPassword;@Beanpublic RedissonClient getRedisson(){Config config = new Config();//单机模式  依次设置redis地址和密码config.useSingleServer().setAddress("redis://" + host + ":" + port);
//                setPassword(redisPassword);return Redisson.create(config);}
}

3)编写RedisLockController,实现一个扣取库存的分布式锁模拟场景
4)通过启动2台服务器(记得修改接口),然后分别访问2台服务器的/redisLock/exportInventory接口,查看扣取库存日志是否正确


http://www.ppmy.cn/server/134000.html

相关文章

「UCD」ComfyUI设计提效工具

ComfyUI 是一个为 Stable Diffusion 专门设计的基于节点的图形用户界面(GUI),它使用户能够通过链接不同的块(称为节点)来构建复杂的图像生成工作流程。这些节点可以包括各种任务,如加载检查点模型、输入提示、指定采样器等。它实际上就是一个比较专业的 Stable Diffusion…

第二十九篇:图解TCP三次握手,看过不会忘,从底层说清楚,TCP系列四

⼀开始&#xff0c;客户端和服务端都处于 CLOSED 状态。先是服务端主动监听某个端⼝&#xff0c;处于 LISTEN 状态。 接下来这部分内容的介绍将影响你能不能彻底理解了TCP的三次握手。 一、划重点&#xff1a;只有服务端启动了端口监听&#xff0c;客户端TCP握手才能建立连接&…

pdf文件太大如何变小?

pdf文件太大如何变小&#xff1f;在处理PDF文件的过程中&#xff0c;文件大小往往成为了一个不可忽视的关键因素。庞大的文件不仅占据了宝贵的存储空间&#xff0c;还在文件传输过程中造成了时间上的延误&#xff0c;这无疑给工作效率带来了不小的挑战。特别是在电子邮件附件的…

QT QML 练习8-Simple Transformations

简单的转换&#xff08;Simple Transformations&#xff09; 转换操作改变了一个对象的几何状态。QML元素对象通常能够被平移&#xff0c;旋转&#xff0c;缩放。下面我们将讲解这些简单的操作和一些更高级的用法。 我们先从一个简单的转换开始。用下面的场景作为我们学习的开始…

pod相关面试题总结(持续更新)

1:当一个Pod有多个容器时&#xff0c;如果连接到指定的容器&#xff1f; #查看当前空间下的pod [rootmaster210 pods]# kubectl get pods NAME READY STATUS RESTARTS AGE linux85-nginx-tomcat 2/2 Running 0 63s [rootmaster210 …

前端面试经验总结1(简历篇)

本文分为3部分&#xff0c;分别为第一部分简历篇&#xff0c;第二部分经典问题篇以及第三部分知识体系篇&#xff0c;都是个人面试经验及同行面试经验总结和整理。 我对于简历的理解是这样的&#xff0c;简历的作用是让看简历的人能够快速、准确地捕捉到有用信息&#xff1a; 你…

[创业之路-154] :图解:结构需求分析、结构设计、加工、生产的整个流程与常见问题

目录 前言&#xff1a; 一、结构生产的流程中涉及的人员 二、结构设计人的主要职责 三、常见问题 1、需求分析 2、结构设计 3、加工 4、组装 5、测试 前言&#xff1a; 一、结构生产的流程中涉及的人员 在结构生产的流程中&#xff0c;涉及的人员及其职责通常如下&am…

开源的 FOC(Field-Oriented Control) 项目

开源的 FOC&#xff08;Field-Oriented Control&#xff09; 项目通常用于控制无刷直流电机&#xff08;BLDC&#xff09;和永磁同步电机&#xff08;PMSM&#xff09;。这些项目可以实现高效的电机控制&#xff0c;广泛应用于机器人、无人机、电动车等领域。以下是一些著名的开…