Redis 연결 이 제대로 풀 리 지 않 는 문제 해결

오류 메시지:
IllegalStateException: Invalidated object not currently part of this pool
문제 설명
며칠 전에 다 중 스 레 드 로 작업 테스트 를 실시 하여 vanyar-redis 연결 탱크 를 검 증 했 습 니 다.응용 은 방금 재 개 된 상태 입 니 다.실행 작업 은 10 개의 스 레 드 를 열 고 10000 번 의 작업 을 동시에 수행 하 는 것 입 니 다.
다음 과 같다.

작업 이 끝 난 후 콘 솔 에서 9 개의 아래 오류 정 보 를 출력 하 는 것 을 발견 하 였 습 니 다:

이 오 류 는 대체로 redis 연결 을 풀 에 넣 을 수 없고 연결 풀 에 넣 을 대상 은 잘못된 대상 이라는 뜻 이다.인터넷 에서 같은 종류의 오 류 를 많이 찾 았 습 니 다.모두 return Resource 가 연결 자원 을 두 번 이나 방출 해서 생 긴 것 이 라 고 합 니 다.첫 번 째 return 이 성공 한 후에 두 번 째 return 은 이 오 류 를 보고 할 것 입 니 다.그러나 분명히 나 는 코드 를 두 번 이나 리 소스 를 호출 하지 않 았 다.
redis 서버 의 연결 수 에 대한 상세 한 정 보 는 다음 과 같 습 니 다.앞의 9 개의 연결,idle=453,453 초 동안 남 았 지만 풀 리 지 않 았 습 니 다.연결 탱크 는 60 초 동안 남 아 있 으 면 풀 리 고 이상 이 뚜렷하게 발생 합 니 다.

다 중 스 레 드 가 redis 작업 을 수행 하고 redis 연결 탱크 를 초기 화 하 는 데 문제 가 있 는 것 으로 의심 합 니 다.그래서 응용 을 다시 시작 합 니 다.먼저 단일 스 레 드 redis 작업 을 실행 한 다음 에 다 중 스 레 드 redis 작업 을 실행 합 니 다.위의 문제 가 발생 하지 않 았 습 니 다.redis 서버 연결 이 정상적으로 풀 릴 수 있 습 니 다.이 를 통 해 스 레 드 탱크 가 초기 화 되 지 않 았 을 때 다 중 스 레 드 가 redis 연결 탱크 초기 화 작업 을 동시에 수행 하여 발생 한 문제 라 는 결론 을 얻 었 다.
코드 보기(RedisJedisPool 이 최적화 되 기 전):10 개의 스 레 드 가 redis 연결 자원 을 동시에 요청 할 때 10 개의 스 레 드 가 비어 있 는 것 을 발견 합 니 다(연결 풀 을 만 드 는 것 보다 스 레 드 를 만 드 는 데 시간 이 걸 리 기 때 문 입 니 다).이때 10 개의 스 레 드 는 각각 연결 풀 을 초기 화하 고 redis 연결 을 얻 으 며 redis 작업 을 실 행 했 습 니 다.실행 이 끝 났 습 니 다.returnResource 를 실 행 했 을 때 pool 변수의 인용 은 마지막 스 레 드 가 초기 화 된 연결 풀 입 니 다.앞의 9 개의 스 레 드 에서 얻 은 redis 연결 은 마지막 연결 풀 의 자원 에 속 하지 않 기 때문에 잘못 던 졌 습 니 다:IllegalState Exception:Invalidated object not currently part of this pool

2.잘못된 원인 분석
스 레 드 1:redis 연결 풀 만 들 기 1:redis 연결 획득 1
스 레 드 2:redis 연결 풀 만 들 기 2:redis 연결 2 획득
스 레 드 3:redis 연결 풀 만 들 기 3:redis 연결 3 획득
……
스 레 드 8:redis 연결 탱크 8 만 들 기:redis 연결 8 획득
스 레 드 9:redis 연결 풀 만 들 기 9:redis 연결 9 획득
스 레 드 10:redis 연결 풀 만 들 기 10:redis 연결 10 획득
전역 변수 pool 참조 지향 redis 연결 풀 10
스 레 드 1-9 에 redis 연결 1-9 를 pool-redis 연결 풀 10 에 돌려 줍 니 다.
reds 연결 탱크 10 은 자 연 스 럽 게 오 류 를 보고 합 니 다.
IllegalStateException: Invalidated object not currently part of this pool
해결 방법
스 레 드 탱크 를 만 들 고 연결 탱크 등 작업 은 상대 적 으로 시간 이 걸 리 기 때문에 우 리 는 보통 응용 이 시 작 될 때 초기 화 되 고 연결 탱크 의 초기 화 작업 을 Spring 용기 관리 에 맡 기 는 동시에 연결 탱크 초기 화 와 연결 을 가 져 오 는 두 가지 작업 실현 방법 을 분리 시 키 며 연결 탱크 를 초기 화 하 는 방법 에 동기 화 잠 금 체 제 를 추가 하고 두 번 째 로 비어 있 는 지 판단 합 니 다.다 중 스 레 드 상황 에서 도 두 번 째 로 비어 있 는 지 여 부 를 판단 할 때 pool 은 비어 있 지 않 고 바로 돌아 갑 니 다.현재 다 중 스 레 드 안전 문제 가 해결 된다.
첨부,해결 전후 대비 도:

보충 지식:자바 spring 프레임 워 크 에서 방법 급 redis 의 연결 자동 획득 및 방출 실현
자바 에서 redis 를 사용 하려 면 redis 연결 의 획득,방출 등 작업 을 처리 해 야 합 니 다.사용 할 때마다 코드 를 매우 추 하 게 만 듭 니 다.spring 에서 op 의 실현 을 모방 하여 동적 에이전트 로 연결 자동 으로 가 져 오고 방출 하 는 도 구 를 작성 합 니 다.
주요 사고 방향
Jedis ManageSupport 추상 류 는 op 과 유사 한 접근 점 입 니 다.이 클래스(일반적으로 service 층)를 계승 한 모든 클래스 는 redis 를 얻 는 방법 으로 redis 를 얻 을 수 있 으 며 방출 할 필요 가 없습니다.
JedisBeanPostProcessor 는 BeanPostProcessor 를 계승 하여 bean 초기 화 시 자신 이 정의 한 논 리 를 수행 합 니 다.
A 류 가 JedisManageSupport 를 계승 하면 redis 연결 을 가 져 오고 JedisManageSupport 의 구성원 변수 에 넣 습 니 다.A 류 의 인 스 턴 스(사실은 cglib 동적 에이전트 에서 생 성 된 것 입 니 다.
A 클래스 의 하위 클래스 인 스 턴 스)이 redis 연결 을 사용 하여 관련 작업 을 할 수 있 습 니 다.
프 록 시 클래스 의 인 스 턴 스 원본 코드 참조
원본 코드 는 아래 와 같다.

public class JedisBeanPostProcessor implements BeanPostProcessor {

@Autowired
ShardedJedisPool shardedJedisPool;

static final Logger logger = Logger.getLogger(JedisBeanPostProcessor.class);

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof JedisManageSupport) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(bean.getClass());
enhancer.setCallback(new JedisInterceptor(shardedJedisPool, bean));
Object targetBean = enhancer.create();
return targetBean;
}
else {
return bean;
}
}

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}

class JedisInterceptor implements MethodInterceptor {

static final Logger logger = Logger.getLogger(JedisInterceptor.class);

ShardedJedisPool pool;

Object src;

public JedisInterceptor(ShardedJedisPool pool, Object src) {
this.pool = pool;
this.src = src;
}

@Override
public Object intercept(Object target, Method method, Object[] arguments, MethodProxy methodProxy) throws Throwable {
Object result = null;
if (target instanceof JedisManageSupport) {
if (this.isDeclaredMethod(target, method)) {
ShardedJedis jedis = null;
try {
JedisManageSupport support = (JedisManageSupport) src;
jedis = pool.getResource();
support.setShardedJedis(jedis);
// logger.debug("      jedis  ,method:" + method);
/**
*          method.invoke(src,arguments)。     
* methodProxy.invokeSuper(target,arguments);
*   A   Autowired     ,       B ,    B     。              ,
*   methodProxy.invokeSuper()   ,       .
*/
result = methodProxy.invoke(src, arguments);
support.setShardedJedis(null);
}
catch (Exception e) {
pool.returnBrokenResource(jedis);
e.printStackTrace();
}
finally {
if (jedis != null) {
pool.returnResource(jedis);
}
// logger.debug("      jedis  ,method:" + method);
}
}
else {
result = methodProxy.invoke(src, arguments);
}
}
else {
throw new Exception("         JedisManageSupport");
}
return result;
}

/**
*    target            ,       
* @return true target            ,
*/
private boolean isDeclaredMethod(Object target, Method arg1) {
Method temp = null;
try {
temp = target.getClass().getDeclaredMethod(arg1.getName(), arg1.getParameterTypes());
}
catch (SecurityException e) {
e.printStackTrace();
}
catch (NoSuchMethodException e) {
e.printStackTrace();
}
/**
*   null,       ,  true
*/
if (temp != null) {

return true;
}
else {
return false;
}
}
}

public abstract class JedisManageSupport {
ThreadLocal<ShardedJedis> jedisHolder = new ThreadLocal<ShardedJedis>();

public final ShardedJedis getShardedJedis() {
return jedisHolder.get();
}

public final void setShardedJedis(ShardedJedis jedis) {
jedisHolder.set(jedis);
}

/**
*                 ,          redis  
*/
public final byte[] assemKey(String baseKey) {
Assert.isTrue(StringUtils.isNotBlank(baseKey), "      ");
return baseKey.getBytes();
}

/**
*   tableName+prefix     key assemKey(String baseKey, String tableName)
*     
*/
public final byte[] assemKeyByPrefix(String tableName, String baseKey) {
Assert.isTrue(StringUtils.isNotBlank(baseKey), "      ");
Assert.isTrue(StringUtils.isNotBlank(tableName), "      ");
UnitInfo unit = WebService.getUnitInfo();
Assert.isTrue(unit != null, "        ");
return (tableName + "-" + unit.getPrefix() + "-" + baseKey).getBytes();
}

/**
*
*               ,             baseKey ,     redis key          
* username  ,             username,mooc_t_userinfo
*          ,    redis key ,       mooc_school   redis 
*/
public final byte[] assemKeyByFid(String tableName, String baseKey) {
UnitInfo unit = WebService.getUnitInfo();
Assert.isTrue(unit != null, "        ");
return (tableName + "-" + unit.getMoocSchool() + "-" + baseKey).getBytes();
}

}
이상 레 디 스 연결 이 제대로 풀 리 지 않 는 문 제 를 해결 하 는 것 은 바로 편집장 이 여러분 에 게 공유 한 모든 내용 입 니 다.여러분 께 참고 가 되 고 많은 응원 부 탁 드 리 겠 습 니 다.

좋은 웹페이지 즐겨찾기