Top Banner
닷넷에서 Redis 사용하기 티쓰리엔터테인먼트 모바일 1팀 공통 기술 개발팀 최흥배 과장
37

닷넷프레임워크에서 Redis 사용하기

Jun 18, 2015

Download

Technology

Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: 닷넷프레임워크에서 Redis 사용하기

닷넷에서 Redis 사용하기

티쓰리엔터테인먼트 모바일 1팀 공통 기술 개발팀 최흥배 과장

Page 2: 닷넷프레임워크에서 Redis 사용하기

Redis ?????

Page 3: 닷넷프레임워크에서 Redis 사용하기

Key-Value, Memory DB

Page 5: 닷넷프레임워크에서 Redis 사용하기

REDIS 연구노트 http://kerocat.tistory.com/1

Page 6: 닷넷프레임워크에서 Redis 사용하기

REDIS 공부 http://blog.naver.com/forioso/10173379225

Page 7: 닷넷프레임워크에서 Redis 사용하기

Redis 설치

http://cs-arthur.tistory.com/113

http://misoin.tistory.com/1

http://blog.outsider.ne.kr/763

Page 8: 닷넷프레임워크에서 Redis 사용하기

.NET과 Redis

Page 9: 닷넷프레임워크에서 Redis 사용하기

Redis의 .NET 라이브러리로 'ServiceStack.Redis'와 'BookSleeve'가 있다. 둘 다 Redis 공식 라이브러리이다

Page 10: 닷넷프레임워크에서 Redis 사용하기

추천!!! BookSleeve

Stack Overflow에서 근무하고 protobuf-net을 만든 Marc Gravell씨가 만들었다

Page 11: 닷넷프레임워크에서 Redis 사용하기
Page 12: 닷넷프레임워크에서 Redis 사용하기

"모든 것이 비동기로 파이프라인으로 동작한다"

Page 13: 닷넷프레임워크에서 Redis 사용하기

파이프닝

Client: INCR X Server: 1 Client: INCR X Server: 2 Client: INCR X Server: 3 Client: INCR X Server: 4

Client: INCR X Client: INCR X Client: INCR X Client: INCR X Server: 1 Server: 2 Server: 3 Server: 4

Page 14: 닷넷프레임워크에서 Redis 사용하기

파이프닝을 사용하면 클라이언트와 서버간 통신이 1번으로 끝난다. 단순히 GET, SET 뿐만이 아닌 다양한 조작을 파이프닝 할 수 있다

Page 16: 닷넷프레임워크에서 Redis 사용하기

var connection = new RedisConnection("127.0.0.1"); await connection.Open(); var x1 = connection.Strings.Increment(db: 0, key: "X"); var x2 = connection.Strings.Increment(db: 0, key: "X"); var x3 = connection.Strings.Increment(db: 0, key: "X"); var x4 = connection.Strings.Increment(db: 0, key: "X"); await Task.WhenAll(x1, x2, x3, x4); // 모든 완료를 기다린다. // 결과 표시 Console.WriteLine("{0}, {1}, {2}, {3}", x1.Result, x2.Result, x3.Result, x4.Result);

Page 17: 닷넷프레임워크에서 Redis 사용하기

BookSleeve는 모든 조작이 비동기이기 때문에 반환 값은 Task 형이 된다. 또 C# 5.0 에서 생긴 async/await 메소드를 사용하면 비동기 조작을 활용하기 쉬워진다

Page 18: 닷넷프레임워크에서 Redis 사용하기

암묵적 파이프닝

[예제 - 1]은 명시적으로 파이프라인을 사용하지 않고 있다.

BookSleeves는 내부에서 블럭킹 큐를 사용하여 명령어를 축척하고 있다.

또 큐가 비었는지 감시하고, 명령어를 보내는(네트워크로) 워커가 동작하고 있다.

워커가 동작할 때 큐에 복수의 명령어가 축척되면 이것들이 모두 일괄적으로 파이프닝으로 보낸다.

즉 같은 타이밍에 발행된 명령어는 자동적으로 파이프닝화 된다.

또 네트워크 접근은 비동기 I/O로 소켓 통신을 하므로 파이프닝 송신 동안의 대기 시간은 최소화 시킨다

Page 19: 닷넷프레임워크에서 Redis 사용하기

연결 관리 RedisConnection 오브젝트(=Redis 서버로의 접속)는 단독으로 열지 않고 공유 하고 있다. 접속을 관리하도록 아래와 같은 코드를 준비한다

public static class RedisConnectionManager { static RedisConnection connection; static object connectionLock = new object(); public static RedisConnection GetConnection() { if ((connection == null) || ( (connection.State != RedisConnectionBase.ConnectionState.Open) && (connection.State != RedisConnectionBase.ConnectionState.Opening) )) { lock (connectionLock) { if ((connection == null) || ( (connection.State != RedisConnectionBase.ConnectionState.Open) && (connection.State != RedisConnectionBase.ConnectionState.Opening) )) { connection = new RedisConnection("127.0.0.1"); // 접속 설정은 변경한다 connection.Wait(connection.Open()); } } } return connection; } }

Page 20: 닷넷프레임워크에서 Redis 사용하기

var redis = RedisConnectionManager.GetConnection(); await redis.Strings.Set(db: 0, key: "jacking", value: "흥배"); var value = await redis.Strings.Get(db: 0, key: "jacking");

RedisConnection은 모든 요청에서 공유된다. ASP.NET에서는 모든 독립된 리퀘스트, 관련 없는 모든 다른 명령어가 파이프닝화 되어 모아서 보내므로 큰 폭으로 Round Trip Time 이 줄어든다

Page 22: 닷넷프레임워크에서 Redis 사용하기

왜? BookSleeve 에서 제공하는 API는 원시 수준으로 대부분의 반환형이 byte[] 이다. 그래서 대부분 시리얼라이즈를 통해서 오브젝트로 변환해야 한다

Page 23: 닷넷프레임워크에서 Redis 사용하기

CloudStructures

https://github.com/neuecc/CloudStructures

Page 24: 닷넷프레임워크에서 Redis 사용하기
Page 25: 닷넷프레임워크에서 Redis 사용하기

직렬화, 접속 관리, 클라이언트 사이드에서 분산, .config 파일에서 설정 읽기

BookSleeve

Page 26: 닷넷프레임워크에서 Redis 사용하기

var settings = new RedisSettings("127.0.0.1"); var list = new RedisList<person>(settings, "Person-Key-0"); await list.AddLast(new Person { Name = "AAA", Age = 20 }); await list.AddLast(new Person { Name = "BBB", Age = 35 }); var persons = await list.Range(0, 2);

Page 27: 닷넷프레임워크에서 Redis 사용하기

RedisSettings

var settings = new RedisSettings("127.0.0.1"); // 연결할지 않았다면 연결 후 객체를 반환한다. var conn = settings.GetConnection();

데이터 직렬화 방법 new RedisSettings("127.0.0.1", converter: new JsonRedisValueConverter()); new RedisSettings("127.0.0.1", converter: new ProtoBufRedisValueConverter());

Page 28: 닷넷프레임워크에서 Redis 사용하기

RedisSettings

2개 이상의 redis 서버를 수평 분할로 사용하고 싶을 때 사용한다. // multi group of connections var group = new RedisGroup(groupName: "Cache", settings: new[] { new RedisSettings(host: "100.0.0.1", port: 6379, db: 0), new RedisSettings(host: "105.0.0.1", port: 6379, db: 0), }); // key hashing. key 값으로 어느쪽의 redis 서버를 사용할지 선택할 수 있다. var conn = group.GetSettings("hogehoge-100").GetConnection();

Page 29: 닷넷프레임워크에서 Redis 사용하기

public static RedisGroup redisGroup = null; //var addressList = new List<Tuple<string,int>>(); //addressList.Add(new Tuple<string, int>("172.20.60.208",6379)); //addressList.Add(new Tuple<string, int>("172.20.60.208", 6380)); public static void Init(List<Tuple<string, int>> addressList) { var redisSettings = new RedisSettings[addressList.Count]; for (int i = 0; i < addressList.Count; ++i) { redisSettings[i] = new RedisSettings(host: addressList[i].Item1, port: addressList[i].Item2, db: 0); } redisGroup = new RedisGroup(groupName: "GameServer", settings: redisSettings); }

사례: Redis 샤딩

Page 30: 닷넷프레임워크에서 Redis 사용하기

var list = new CloudStructures.Redis.RedisList<int>(GlobalSettings.Default, "listkey1"); // 모든 값을 지운다. await list.Clear(); // 제일 뒤에 추가 await list.AddLast(1); await list.AddLast(10); // 제일 앞에 추가 await list.AddFirst(100); await list.AddFirst(1000); // 총 갯수 await list.GetLength(); // redis 명령어 중 LRANGE 기능 // 리스트의 0번째부터 시작해서 3개 await list.Range(0, 2); // 1000, 100, 1

사례: list

Page 31: 닷넷프레임워크에서 Redis 사용하기

사례: hash public class UserAuthInfo { public string ID; public string PW; public string AuthToken; public Int64 UnqiueNumber; } static async Task<UserAuthInfo> GetAccountInfo(string id) { var redisObj = new RedisClass<UserAuthInfo>(MemoryDB.redisGroup, id); var userData = new UserAuthInfo(); userData = await redisObj.GetValue(); return userData; } static void SaveAccountInfo(UserAuthInfo userAuth) { var redisObj = new RedisClass<UserAuthInfo>(MemoryDB.redisGroup, userAuth.ID); redisObj.SetValue(userAuth); }

Page 32: 닷넷프레임워크에서 Redis 사용하기

Configuration <configSections> <section name="cloudStructures" type="CloudStructures.Redis.CloudStructuresConfigurationSection, CloudStructures" /> </configSections> <cloudStructures> <redis> <group name="cache"> <add host="127.0.0.1" /> <add host="127.0.0.2" port="1000" /> </group> <group name="session"> <add host="127.0.0.1" db="2" valueConverter="CloudStructures.Redis.ProtoBufRedisValueConverter, CloudStructures" /> </group> </redis> </cloudStructures> var groups = CloudStructuresConfigurationSection.GetSection().ToRedisGroups()

Page 33: 닷넷프레임워크에서 Redis 사용하기

Redis와 Lua

Page 34: 닷넷프레임워크에서 Redis 사용하기
Page 35: 닷넷프레임워크에서 Redis 사용하기

lua 사용 public async Task<double> IncrementLimitByMin(double value, double min, bool queueJump = false) { using (Monitor.Start(Settings.PerformanceMonitor, Key, CallType)) { var v = Connection.Scripting.Eval(Settings.Db, @" local inc = tonumber(ARGV[1]) local min = tonumber(ARGV[2]) local x = tonumber(redis.call('incrbyfloat', KEYS[1], inc)) if(x < min) then redis.call('set', KEYS[1], min) x = min end return tostring(x)", new[] { Key }, new object[] { value, min }, useCache: true, inferStrings: true, queueJump: queueJump); return double.Parse((string)(await v.ConfigureAwait(false))); } }

Page 37: 닷넷프레임워크에서 Redis 사용하기