SpringBoot 프로젝트 에 서 는 Jedis 와 일반적인 설정 을 사용 합 니 다.

우아 한 사용 Jedis
블 로그 주소:https://www.cnblogs.com/keatsCoder/p/12609109.html 전재 출처 를 밝 혀 주 십시오.감사합니다.
Redis 의 자바 클 라 이언 트 는 매우 많은 데 Jedis 는 그 중에서 사용 이 비교적 광범 위 하고 성능 이 비교적 안정 적 인 것 이다.또한 API 와 RedisAPI 이름 스타일 이 유사 하 므 로 사용 을 추천 합 니 다.
프로젝트 에 Jedis 도입
Maven 을 통 해 직접 도입 할 수 있 으 며,현재 최신 버 전 은 3.2.0 이다.

    redis.clients
    jedis
    3.2.0


직접 연결 및 사용 연결 탱크
제 디 스 직 련
Jedis 를 도입 하면 프로젝트 는 new 방식 으로 Jedis 를 가 져 올 수 있 습 니 다.
  • 먼저 yml 에 redis 의 주소 와 포트 를 설정 합 니 다
  • @SpringBootTest(classes = RedisCliApplication.class)
    @RunWith(SpringRunner.class)
    public class JedisConnectionDemo {
        @Value("${redis.host}")
        private String host;
        @Value("${redis.port}")
        private int port;
    
    
        @Test
        public void testConnection(){
            //     
            Jedis jedis = new Jedis(host, port);
            //    key-value。       OK
            String setResult = jedis.set("name", "keats");
            Assert.assertEquals("OK", setResult);
            //    key    value 
            String value = jedis.get("name");
            Assert.assertEquals("keats", value);
            //     
            jedis.close();
        }
    }
    

    연결 풀 사용
    연결 하면 매번 TCP 연결 을 새로 만 들 고 TCP 연결 을 끊 는 다.이 과정 은 시간 이 많이 걸 리 기 때문에 Redis 와 같은 빈번 한 접근 과 효율 적 인 접근 이 필요 한 소프트웨어 는 분명 적합 하지 않다.연결 관리 도 불편 하 다.데이터베이스 연결 풀 사상 과 유사 하 게 제 디 스 도 제 디 스 풀 연결 풀 을 제공 해 연결 풀 관 리 를 한다.모든 Jedis 대상 은 JedisPool 에 미리 놓 여 있 고 클 라 이언 트 가 사용 해 야 할 때 연못 에서 빌려 쓰 고 다 쓴 후에 연못 에 돌려 줍 니 다.이렇게 하면 TCP 연결 을 자주 구축 하고 끊 는 네트워크 비용 을 피 할 수 있 고 속도 가 매우 빠르다.또한 합 리 적 인 배 치 를 통 해 합 리 적 인 관리 연결 을 실현 하고 연결 을 분배 할 수 있다.
    @Test
    public void testConnectionWithPool(){
        //      
        JedisPool jedisPool = new JedisPool(host, port);
    
        Jedis jedis = jedisPool.getResource();
    
        // doSomething
    
        //     
        jedis.close();
    }
    

    마지막 으로 사용 하 는 close()방법 은 연결 을 닫 는 것 처럼 보이 지만 실제로 누 르 면 dataSource(연결 풀)가 비어 있 지 않 으 면 연결 을 되 돌려 주 는 방법 을 실행 할 수 있 습 니 다.
    @Override
    public void close() {
        if (dataSource != null) {
            if (client.isBroken()) {
                this.dataSource.returnBrokenResource(this);
            } else {
                this.dataSource.returnResource(this);
            }
        } else {
            client.close();
        }
    }
    

    연결 탱크 에서 흔히 볼 수 있 는 문제
    위 에서 연결 을 돌려 주 는 방법 에 문제 가 있 습 니까?임 무 를 수행 할 때 이상 신 고 를 하면 close()방법 을 수행 할 수 없 을 것 이 고 오 랜 시간 동안 연못 에 있 는 Jedis 연결 이 소 진 되 어 전체 서 비 스 를 사용 할 수 없 을 것 이 라 고 생각해 보 세 요.이 문 제 는 개발 과 테스트 환경 에서 쉽게 발견 되 지 않 지만 생산 환경 은 사용량 이 늘 어 나 면 드러난다.
    JedisPool 의 기본 최대 연결 수 는 8 개 입 니 다.기본 으로 풀 에서 연결 을 가 져 오 는 시간 초과 시간 은-1 입 니 다.
    연결 을 돌려 주지 않 는 오 류 를 보 여주 기 위해 다음 코드 를 썼 습 니 다.
    @Test
    public void testConnectionNotClose(){
        //      
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setMaxWaitMillis(5000L); //   Jedis      
        JedisPool jedisPool = new JedisPool(poolConfig, host, port);
    
        try {
            for (int i = 1; i <= 10; i++) {
                Jedis jedis = jedisPool.getResource();
                System.out.println(i);
                // doSomething
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    

    8 회 전 까지 풀 에서 연결 을 가 져 와 사용 하고 돌려 주지 않 습 니 다.9 번 째 때 연결 을 가 져 오 려 면 이미 없습니다.기본 적 인 상황 에서 기다 릴 겁 니 다.그리고 제 가 설정 을 변경 한 것 은 5S 입 니 다.5S 를 기다 리 면 오류 가 발생 합 니 다.오류 정 보 는 다음 과 같 습 니 다.
    1
    2
    3
    4
    5
    6
    7
    8
    redis.clients.jedis.exceptions.JedisException: Could not get a resource from the pool
    	at redis.clients.util.Pool.getResource(Pool.java:51)
    	at redis.clients.jedis.JedisPool.getResource(JedisPool.java:99)
    	at cn.keats.rediscli.jedis.JedisConnectionDemo.testConnectionNotClose(JedisConnectionDemo.java:64)
    	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    	at java.lang.reflect.Method.invoke(Method.java:498)
    	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    	at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:74)
    	at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84)
    	at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
    	at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
    	at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
    	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
    	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
    	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    	at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    	at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
    	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    	at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    	at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
    Caused by: java.util.NoSuchElementException: Timeout waiting for idle object
    	at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:439)
    	at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:349)
    	at redis.clients.util.Pool.getResource(Pool.java:49)
    	... 32 more
    

    잘못 을 보고 하 든 기다 리 든 생산 환경 에서 지연 되 는 것 과 다름없다.그 러 니까 이 조작 은 피해 야 돼.그럼 제 가 실행 코드 의 마지막 문장 에 close()를 쓰 면 안심 이 되 지 않 을까요?진지 하 게 앞에서 다 오 는 애 들 은 아니 라 고 할 거 야.코드 가 이상 하 게 나 오 면close()방법 을 실행 할 수 없습니다.
    @Test
    public void testConnectionWithException() {
        //      
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setMaxWaitMillis(5000L); //   Jedis      
        JedisPool jedisPool = new JedisPool(poolConfig, host, port);
    
        for (int i = 1; i <= 8; i++) {
            System.out.println(i);
            try {
                new Thread(() -> {
                    Jedis jedis = jedisPool.getResource();
                    // doSomething
                    //       
                    int j = 1 / 0;
    
                    jedis.close();
                }).run();
            } catch (Exception e) {
                //            8   ,     close  
            }
    
        }
        //  9       
        Jedis jedis = jedisPool.getResource();
    }
    

    이렇게 하면 위 와 같은 잘못 도 보고 할 수 있다.자바 7 이후 의 try with resources 쓰기 로 연결 반환 을 완료 하 는 것 을 추천 합 니 다.
    try (Jedis jedis = jedisPool.getResource()) {
        new Thread(() -> {
            // doSomething
            //       
            int j = 1 / 0;
    
            jedis.close();
        }).run();
    } catch (Exception e) {
        //     
    }
    

    이렇게 하면 finally 를 쓴 셈 이다.정상적으로 실행/오류 가 발생 했 을 때 close()방법 으로 연결 을 닫 습 니 다.코드 에 순환 이 없 는 한
    이렇게 쓰 면 또 하나의 단점 은 일부 동료 들 이 반환 을 잊 어 버 릴 수도 있다 는 것 이다.작 가 는 오래된 돈 으로 강제 반환 하 는 연결 탱크 관리 방법 을 소개 했다.
    특수 한 사용자 정의 RedisPool 대상 을 통 해 JedisPool 대상 을 숨 기 고 프로그래머 가 getResource 방법 을 직접 사용 해서 반환 하 는 것 을 잊 지 않도록 합 니 다.프로그래머 가 RedisPool 대상 을 사용 할 때 리 셋 클래스 를 제공 해 야 Jedis 대상 을 사용 할 수 있 습 니 다.자바 8 의 Lambda 표현 식 을 결합 합 니 다.사용 해도 괜 찮 습 니 다.그러나 이 로 인해 패 킷 을 닫 는 문제 가 발생 했 고 람 다 의 익명 내부 클래스 는 외부 변수 에 접근 할 수 없습니다.그 는 또 방문 목적 을 달성 하기 위해 Hodler 를 사용 했다.사내 의 방법 은 매우 대단 하 다.그러나 개인 적 으로 이런 코드 의 복잡 도가 많이 높 아 졌 다.리 소스 를 사용 하고 돌려 주지 않 은 프로그래머 에 게 는 쓰기 가 복잡 할 수 있 습 니 다.그래서 블 로그 에 안 붙 였 어 요.관심 있 는 파트너 는 옛날 돈의 책 을 읽 거나 나의 GITHUB 에서 옛날 돈의 코드 를 찾 아 볼 수 있다.우아 한 Jedis-옛날 돈
    연결 탱크 설정 상세 설명
    연결 풀 을 기본 구조 로 초기 화 하 는 것 외 에 Jedis 는 설정 클래스 를 제공 하여 초기 화 합 니 다.
    JedisPoolConfig poolConfig = new JedisPoolConfig();
    poolConfig.setMaxWaitMillis(5000L); //   Jedis      
    JedisPool jedisPool = new JedisPool(poolConfig, host, port);
    

    설정 클래스 에서 자주 사용 하 는 매개 변 수 는 다음 과 같 습 니 다.
    매개 변수 이름
    속뜻
    기본 값
    maxActive
    연결 풀 의 최대 연결 수
    8
    maxIdle(minIdle)
    연결 풀 의 최대(작은)남 은 연결 수
    8(0)
    maxWaitMillis
    링크 풀 이 연결 되 지 않 았 을 때 호출 자의 최대 대기 시간,단 위 는 밀리초 입 니 다.기본 값 사용 을 권장 하지 않 습 니 다.
    -1 기다리다
    jmxEnabled
    jmx 모니터링 시작 여부
    minEvictableIdleTimeMillis
    연결 의 최소 남 은 시간,이 값 에 도달 하면 남 은 연결 이 삭 제 됩 니 다.
    1800000 L 30 분
    numTestsPerEvictionRun
    남 은 연결 검 사 를 할 때,매번 샘플링 수
    3
    testOnBorrow
    연결 풀 에 연결 을 빌 릴 때 연결 유효성 검사(Ping)를 할 지 여부 가 잘못 되면 연결 이 삭 제 됩 니 다.
    false
    testOnReturn
    주기 적 인 남 은 검사 할 지 여부
    false
    testWhileIdle
    연결 풀 에 연결 을 빌 릴 때 남 은 검 사 를 할 지 여부 입 니 다.남 은 시간 이 초과 되면 삭 제 됩 니 다.
    false
    timeBetweenEvictionRunsMillis
    남 은 연결 검사 주기,단 위 는 밀리초 입 니 다.
    -1 검 사 를 안 해 요.
    blockWhenExhausted
    연결 탱크 자원 이 다 떨 어 졌 을 때 호출 자 는 기 다 려 야 합 니까?maxWaitMillis 와 대응 합 니 다.true 일 때 maxWaitMillis 가 적 용 됩 니 다.
    true
    PipeLine 은 한 번 에 여러 명령 을 수행 합 니 다.
    Redis 는 mset,mget 등 방법 을 제 공 했 지만하지만 mdel 방법 은 제공 되 지 않 았 다.저 희 는 업무 중 에 mget 을 한 번 만난 후에 삭제 해 야 할 key 가 여러 개 있 으 면 PipeLine 을 통 해 mdel 을 모 의 할 수 있 습 니 다.비록 조작 은 원자 성 이 아니 지만 대부분 상황 에서 도 요 구 를 만족 시 킬 수 있다.
    @Test
    public void testPipeline() {
        //      
        JedisPool jedisPool = new JedisPool(host, port);
        try (Jedis jedis = jedisPool.getResource()){
            Pipeline pipelined = jedis.pipelined();
            // doSomething    keys
            List keys = new ArrayList<>();
    
            // pipelined     
            for (String key : keys) {
                pipelined.del(key);
            }
            //     
            pipelined.sync();
        }
    }
    

    프로젝트 코드
    레 디 스 를 공부 하 는 과정 에서 저 는 블 로그 의 코드 를 모두 Github 에 올 려 서 아이들 이 확인 할 수 있 도록 했 습 니 다.항목 주소:https://github.com/keatsCoder/redis-cli
    참고 문헌
    《Redis 개발 과 운 비》---부 레이 장 익 군
    《Redis 심도 있 는 모험:핵심 원리 와 응용 실천》---돈 과 물품

    좋은 웹페이지 즐겨찾기