본문으로 건너뛰기

redis 캐시 서비스

무료2017-04-30#Node#Solution#node redis缓存#redis nodejs#node内存缓存#nodejs redis配置

눈에 띄는 속도 향상 효과, 20s->5s

일.문제 배경

HTTPS 를 위해 국내 가상 호스트를 버리고, Node 로 이전 PHP 서비스를 재작성하여 비싼 VPS 에 배치했습니다. 이전 후 국내 RSS 가져오기가 자주 타임아웃되고, 타임아웃되지 않는 경우에도 loading 이20s 정도로 완전히 사용 불가능했습니다. 이전은 이미 결정한 일이므로 속도 향상 방법을 모색해야 합니다

이전 방안은 요청 시 실시간으로 가져와서 파싱 완료 후 요청에 응답하는 방식으로, 과정은 느려 보이지만 실제로는 빨라 일반적으로 loading 이 3s 를 초과하지 않아 자가 사용은 허용 범위였으므로 클라이언트 메모리 캐시와 오프라인 캐시만 구현했습니다

현재 20s 는 완전히 참을 수 없으므로, 먼저가장 빠르게 효과가 나타나는메모리 캐시를 도입합니다:

  • 정기 가져오기, 미리 redis 에 저장

  • redis 메모리 캐시, 간단한 만료 전략

2 시간마다 전체를 한 번 가져와 redis 에 저장하고, 요청은 먼저 캐시 검사를 통과하며, 캐시에 있으면 실시간으로 가져오지 않고, 서비스가 막 재시작된 경우에만 실시간으로 가져오면 됩니다. 정기 가져오기 시 정상 응답에 영향을 미치지 않습니다. 가져오기 시나리오는 더러운 데이터 문제를 고려할 필요가 없고, 새 것/오래된 것에 큰 관계가 없기 때문입니다 (하지만 극단적인 경우 데이터는정기 가져오기 간격 + 클라이언트 캐시 만료 시간이전의 것이 되어,这时 데이터는 매우 오래됩니다)

이.redis 설치 및 설정

설치

CentOS 환경, redis stable 컴파일 설치:

# 下载
wget http://download.redis.io/releases/redis-stable.tar.gz
# 解压
tar -axvf redis-stable.tar.gz
cd redis-stable
# 编译安装
make
make test
make install

기본 설치 경로는/usr/local/bin:

$ ls /usr/local/bin | grep 'redis'
redis-benchmark
redis-check-aof
redis-check-rdb
redis-cli
redis-sentinel
redis-server

설정

기본 설정 파일은 설치 패키지의 루트 디렉토리redis.conf:

mkdir -p /etc/redis/
cp redis.conf etc/redis
# 修改配置项
vi etc/redis/redis.conf
# 后台运行,默认不后台
#   把 GENERAL 节的 daemonize no 改为 daemonize yes
# 改密码,默认免密登录
#   把 SECURITY 节的 requirepass 去掉注释,改为 requirepass <mypassword>

기타 설정 항목 (포트 번호, 로그 디렉토리 등) 은 필요 시 수정한 후 시작하여 검증:

# 启动服务
redis-server /etc/redis/redis.conf
# 客户端连接
redis-cli
auth <mypassword>
# 操作
set 'key' 'value'
get 'key'

P.S.redis 의 더 많은 명령은 Command reference – Redis 를 참조하거나, 온라인으로 체험Try Redis

시스템 서비스에 추가

redis-server /etc/redis/redis.conf로 매번 시작하는 것은 보기 싫으므로, 시스템 서비스에 추가하여service redis <cmd>로 관리할 수 있게 합니다:

# 拷贝启动脚本
cp util/redis_init_script /etc/init.d/
# 改名
mv /etc/init.d/redis_init_script /etc/init.d/redis

그 후 설정 항목 수정:

vi /etc/init.d/redis
# 修改第二行的 chkconfig xxx 为 chkconfig 2345 80 90
# 确认端口号正确 REDISPORT=6379
# 确认 server 可执行文件路径正确 EXEC=/usr/local/bin/redis-server
# 确认 cli 路径正确 CLIEXEC=/usr/local/bin/redis-cli
# 确认 redis.conf 路径正确 CONF="/etc/redis/${REDISPORT}.conf"
# start 改为后台执行
#   把$EXEC $CONF 改为$EXEC $CONF &

P.S.# chkconfig 2345 80 90 에서, 2345 는 실행 레벨, 80 90 은 각각 시작/종료 우선순위를 나타내며, 숫자가 작을수록 우선순위가 높고 순서를 제어합니다. 더 많은 정보는 chkconfig 를 참조하세요

기본적으로/etc/redis/${REDISPORT}.conf, 즉/etc/redis/6379.conf를 읽어 여러 인스턴스 상황에 대응하므로, 설정 파일 이름을 변경:

mv /etc/redis/redis.conf /etc/redis/6379.conf

마지막으로 시스템 서비스 등록:

# 注册
chkconfig --add redis
# 设置自启动
chkconfig redis on

service 명령으로 관리 가능:

service redis start

삼.node 에서 redis 연결

기존 서드파티 모듈 node_redis 가 있습니다:

npm install redis --save

연결 시도:

const redis = require('redis');

const PORT = 6379;
const HOST = '127.0.0.1';
const PWD = 'mypassword';
const OPTS = {auth_pass: PWD};

// connect redis
let client = redis.createClient(PORT, HOST, OPTS);
client
    .on('error', (err) => {
        console.log('Error ' + err);
    })
    .on('ready', () => console.log('redis connected'));

connect 후 자유롭게 조작 가능하며, API 는 redis 명령과 일치:

// 写
client.set(key, val, callback);
// 读
client.get(key, (error, val) => {});
// 设置有效期
client.expire(key, seconds);
// 检查过期
client.expire(url, (error, ttl) => {
    if (ttl > 0) console.log('alive');
    else console.log('died');
});

특별 주의: 모든callback 은 Node 의 클래식 방식으로, 첫 번째 파라미터는err이며, data 가 아닙니다

간단한 캐시 레이어 작성, 구조는 다음과 같음:

cache
  - queue
  - clearQueue()
  - expire()
  - ttl()

  + set()
  + get()
  + checkFresh()

액세스 가져오기 및 정기 가져오기와 연동:

fetch
  - onerror(error) => {
        emitter.emit('error', error);
    };
  - onsuccess(data) => {
        data && cache.set(url, data);
    };
  - fetchNow()

  + fetch() => {
        if (noCache) {
            cache.checkFresh(url, (fresh) => {
                if (!fresh) console.log('schedule force fetch now'), fetchNow();
                else oncancel('cache is still fresh now');
            }
        }
        else {
            cache.get(url, (data) => {
                if (data) console.log('fetch from cache'), onsuccess(data);
                else fetchNow();
            });
        }
    }

액세스 가져오기는 캐시를 통해 직접 캐시에서 가져가고, 없으면 가져옴. 정기 가져오기는 강제로 캐시를 거치지 않지만, 만료 검사를 하여 데이터가 아직 새롭으면 가져오기 작업을 취소하고, 새롭지 않으면 실시간으로 가져와서 가져오기 성공 후 캐시 레이어에 기록

P.S.정기 가져오기 시 만료 검사는 불필요한 중복 가져오기를 피하기 위함입니다. 예를 들어 서비스가 다운되어 재시작된 경우, redis 의 데이터는 영향을 받지 않고 여전히 새롭다면,这时 다시 가져올 필요가 없습니다

사.정리

속도 향상 효과는 매우 명확하여, 이전에는 국내 리소스 액세스 시20s 의 loading 이5-6s로 단축되었고, 국외 리소스도1-2s 정도 빨라졌습니다. 이전 PHP 서비스와 비교하면5-6s는 여전히 꽤 느리지만, 다음 최적화 항목은 이렇게 단순하지 않습니다:

todo
1.接口拆分,有接口返回 128K 文本,考虑分页
2.redis 数据结构优化,目前是 url key 对应一个很大的 JSON 字符串,应该有更科学的方式
3.长连接,降低路途成本

댓글

아직 댓글이 없습니다

댓글 작성