SpringBoot 는 Redis 를 통합 하여 분포 식 잠 금 을 정확하게 실현 하 는 예제 코드 입 니 다.
9621 단어 SpringBootRedis분산 식 자물쇠
최근 블록 업 로드 를 하 는 업 무 는 업로드 과정 에서 블록 번 호 를 유지 하기 위해 레 디 스 를 사용 했다.
업로드 가 완 료 될 때마다 파일 의 블록 집합 을 가 져 오고 새로 업로드 한 번 호 를 추가 합 니 다.수 동 인터페이스 테스트 에서 문제 가 없습니다.전단 은 동시 업로드 호출 을 통 해 문제 가 발생 합 니 다.동시 다발 get 재 set 는 덮어 쓰기 현상 이 존재 하여 마지막 블록 데이터 가 잘못 되 어 블록 합병 요청 을 촉발 할 수 없습니다.
동시 다발 에 부 딪 히 면 먼저 자 물 쇠 를 잠 그 고 실행 코드 블록 에 JVM 자 물 쇠 를 추가 한 후에 문 제 는 해결 되 었 다.
곰 곰 이 생각해 보 니 그렇지 않 습 니 다.프로젝트 는 분포 식 으로 배치 되 었 고 부하 균형 을 이 루 었 습 니 다.한 노드 의 코드 가 잠 겨 있 습 니 다.다른 노드 에 문의 하 시 면 덮어 쓸 수 있 습 니까?문 제 를 해결 하지 못 했 습 니 다.
어 쩔 수 없어,분포 식 자 물 쇠 를 써 야 지.이전에 분포 식 자물쇠 의 이론 에 대해 잘 알 고 있 었 다.비교적 좋 은 응용 장면 이 없 으 면 구체 적 인 코드 를 쓰 지 않 았 고 이 기 회 를 틈 타 분포 식 자 물 쇠 를 사용 하 는 것 을 배 웠 다.
이론.
분산 식 잠 금 은 분포 식 시스템 간 에 공유 자원 을 동기 화 하 는 방식 이다.분포 식 시스템 에서 서로 다른 시스템 이나 같은 시스템 의 서로 다른 호스트 가 같은 자원 을 공유 하 는 문 제 를 해결 하기 위해 서로 배척 하여 프로그램의 일치 성 을 확보한다.
일반적인 실현 방식 은 세 가지 가 있다.
코드
1.더 의지
Spring Boot 와 Redis 통합 의 빠 른 사용 의존 도입
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.설정 추가application.properties 에 Redis 연결 설정 추가
spring.redis.host=xxx
spring.redis.port=6379
spring.redis.database=0
spring.redis.password=xxx
spring.redis.timeout=10000
# jedis
spring.redis.jedis.pool.max-active=50
spring.redis.jedis.pool.min-idle=20
3.레 디 스 의 서열 화 규칙 다시 쓰기기본적으로 사용 하 는 JDK 의 직렬 화 는 Redis 의 데 이 터 를 스스로 설정 하지 않 으 면 알 아 볼 수 없습니다.
/**
* @author Chkl
* @create 2020/6/7
* @since 1.0.0
*/
@Component
public class RedisConfig {
/**
* RedisTemplate, ,
* @param connectionFactory
* @return
*/
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate redisTemplate = new RedisTemplate();
redisTemplate.setConnectionFactory(connectionFactory);
// key value
redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer(Object.class));
redisTemplate.setKeySerializer(new StringRedisSerializer());
return redisTemplate;
}
}
4.어떻게 자 물 쇠 를 정확하게 잠 그 는가직접 코드
@Component
public class RedisLock {
@Autowired
private StringRedisTemplate redisTemplate;
private long timeout = 3000;
/**
*
* @param key
* @param value
* @return
*/
public boolean lock(String key, String value) {
long start = System.currentTimeMillis();
while (true) {
//
if (System.currentTimeMillis() - start > timeout) {
return false;
}
// set
Boolean absent = redisTemplate.opsForValue().setIfAbsent(key, value, timeout, TimeUnit.MILLISECONDS);//1
//
if (absent) {
return true;
}
return false;
}
}
}
핵심 코드 는
Boolean absent = redisTemplate.opsForValue().setIfAbsent(key, value, timeout, TimeUnit.MILLISECONDS);
setIfAbsent 방법 은 명령 행 의 Setnx 방법 과 같 습 니 다.지정 한 key 가 존재 하지 않 을 때 key 에 지정 한 값 을 설정 합 니 다.매개 변 수 는 각각
key、value、
@Component
public class RedisLock {
@Autowired
private StringRedisTemplate redisTemplate;
@Autowired
private DefaultRedisScript<Long> redisScript;
private static final Long RELEASE_SUCCESS = 1L;
/**
*
* @param key
* @param value
* @return
*/
public boolean unlock(String key, String value) {
// Lua : ,
Long result = redisTemplate.execute(redisScript, Arrays.asList(key,value));
//
return RELEASE_SUCCESS.equals(result);
}
/**
* @return lua
*/
@Bean
public DefaultRedisScript<Long> defaultRedisScript() {
DefaultRedisScript<Long> defaultRedisScript = new DefaultRedisScript<>();
defaultRedisScript.setResultType(Long.class);
defaultRedisScript.setScriptText("if redis.call('get', KEYS[1]) == KEYS[2] then return redis.call('del', KEYS[1]) else return 0 end");
return defaultRedisScript;
}
}
잠 금 해제 과정 은 두 단계 의 조작 이 필요 하 다.1.조작 스 레 드 가 잠 금 스 레 드 인지 판단
2.잠 금 스 레 드 를 추가 하면 잠 금 해제 작업 을 수행 합 니 다.
이 두 단계 작업 도 원자의 조작 이 필요 하지만 Redis 는 이 두 단계 의 합병 작업 을 지원 하지 않 기 때문에 lua 스 크 립 트 를 사용 하여 원자 성 을 확보 해 야 합 니 다.
만약 에 자 물 쇠 를 넣 는 스 레 드 라 고 판단 한 후에 자 물 쇠 를 풀 기 전에 자물쇠 가 만 료 되 었 고 다른 스 레 드 에 의 해 자 물 쇠 를 얻 었 다.이때 자 물 쇠 를 풀 면 다른 스 레 드 의 자 물 쇠 를 풀 고 결자해지 에 만족 하지 못 하 게 할 것 이다.
6.실제 응용
분산 잠 금 을 사용 할 때 파일 블록 을 저장 하 는 코드 가 없습니다.
/**
* redis
* @param chunkNumber
* @param identifier
* @return
*/
@Override
public Integer saveChunk(Integer chunkNumber, String identifier) {
// Redis
Set<Integer> oldChunkNumber = (Set<Integer>) JSON.parseObject(redisOperator.get("chunkNumberList_"+identifier),Set.class);
// ,
if (Objects.isNull(oldChunkNumber)) {
Set<Integer> newChunkNumber = new HashSet<>();
newChunkNumber.add(chunkNumber);
redisOperator.set("chunkNumberList_"+identifier, JSON.toJSONString(newChunkNumber),36000);
return newChunkNumber.size();
// ,
} else {
oldChunkNumber.add(chunkNumber);
redisOperator.set("chunkNumberList_"+identifier, JSON.toJSONString(oldChunkNumber),36000);
return oldChunkNumber.size();
}
}
존재 하 는 문 제 는 동시 다발 요청 이 들 어 오 면 같은 상태의 집합 을 가 져 와 수정 할 수 있 습 니 다.수정 한 후에 직접 기록 하여 같은 상태 에서 얻 은 집합 작업 스 레 드 를 덮어 쓰 는 현상 을 초래 할 수 있 습 니 다.분포 식 잠 금 을 사용 하면 한 개의 스 레 드 만 집합 을 얻 고 수정 할 수 있 으 며 덮어 쓰기 현상 을 피 할 수 있 습 니 다.
분산 잠 금 코드 사용
/**
* redis
* @param chunkNumber
* @param identifier
* @return
*/
@Override
public Integer saveChunk(Integer chunkNumber, String identifier) {
// UUID value
String threadUUID = CoreUtil.getUUID();
// , : , key, UUID value
redisLock.lock(identifier,threadUUID);
// Redis
Set<Integer> oldChunkNumber = (Set<Integer>) JSON.parseObject(redisOperator.get("chunkNumberList_"+identifier),Set.class);
// ,
if (Objects.isNull(oldChunkNumber)) {
Set<Integer> newChunkNumber = new HashSet<>();
newChunkNumber.add(chunkNumber);
redisOperator.set("chunkNumberList_"+identifier, JSON.toJSONString(newChunkNumber),36000);
//
redisLock.unlock(identifier,threadUUID);
return newChunkNumber.size();
// ,
} else {
oldChunkNumber.add(chunkNumber);
redisOperator.set("chunkNumberList_"+identifier, JSON.toJSONString(oldChunkNumber),36000);
//
redisLock.unlock(identifier,threadUUID);
return oldChunkNumber.size();
}
}
코드 에서 사용 하 는 공유 자원 표 지 는 파일 의 유일한 번호 identifier 로 잠 금 코드 세그먼트 의 유일한 자원,즉 key"chunkNumberList_"+identifier
의 집합 을 표시 할 수 있 습 니 다.코드 에 사 용 된 스 레 드 의 유일한 표 지 는 UUID 로 잠 금 추가 와 잠 금 해제 시 얻 은 표지 가 중복 되 지 않도록 보장 합 니 다.
SpringBoot 통합 Redis 가 분포 식 잠 금 을 정확하게 실현 하 는 예제 코드 에 관 한 이 글 은 여기까지 소개 되 었 습 니 다.더 많은 SpringBoot 통합 Redis 분포 식 잠 금 내용 은 우리 의 이전 글 을 검색 하거나 아래 의 관련 글 을 계속 조회 하 시기 바 랍 니 다.앞으로 많이 지 켜 주세요!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
【Java・SpringBoot・Thymeleaf】 에러 메세지를 구현(SpringBoot 어플리케이션 실천편 3)로그인하여 사용자 목록을 표시하는 응용 프로그램을 만들고, Spring에서의 개발에 대해 공부하겠습니다 🌟 마지막 데이터 바인딩에 계속 바인딩 실패 시 오류 메시지를 구현합니다. 마지막 기사🌟 src/main/res...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.