RedisでNotification(通知)を実装する
弊社サービスTraBerryのWebアプリケーションで通知を実装しました。その実装方法の話です。
Notificationで使ったRedis keyは2つ。Arrayで全体の通知の管理。Hashで中身の格納です。
- list:notification:user_id:<user_id>
- hash:notification:user_id:<user_id>
- 上記hashのキーは通知タイプによって違う。コンテンツは通知用のメッセージ。
list:notification:usr_id:<user_id>で0を新しい要素。数が多くなる程古い通知メッセージです。
具体例で見ていきます。
ユーザ1がPhoto1にlikeする。このとき、like:photo_id:1:<user_id>:has_read:0 というkeyを作る。 このkeyをlist:notification:user_id:<user_id>に追加。そして、hash:notification:user_id:<user_id>にそれをkey, valueを通知メッセージとして、hashに格納。
my $key = "notification:user_id:" . $notified_user_id; my $field = "like:photo_id:" . $photo_id . ':user_id:' . $user_id . ':has_read:0' ; # Check notification length and then, delete and add my $notification_length = $self->cache->llen("list:$key"); $self->cache->rpop("list:$key") if ( $notification_length > 30); $self->cache->lpush("list:$key", $field); $self->cache->hset("hash:$key", $field => encode_json($message) }
新しい通知メッセージがあるかどうかは、配列の中のhas_read:0を探せば良い。 そして、通知が読まれたときには、has_read:1のkeyの入れ替えを行う。
sub get_notification { my ($self, $user_id) = @_; my @notification; my $index = 0; for my $field ( $self->cache->lrange("list:notification:user_id:${user_id}", 0, 30) ){ my $message = $self->cache->hget("hash:notification:user_id:${user_id}", $field); my $has_read = 1; if( $field =~ /has_read\:0/){ # change has_read:0 to has_read:1 $self->cache->hdel("hash:notification:user_id:${user_id}", $field); $field =~ s/has_read\:0/has_read\:1/; $self->cache->lset("list:notification:user_id:${user_id}", $index, $field); $self->cache->hset("hash:notification:user_id:${user_id}", $field, $message); $has_read = 0; } if (defined $message){ my $m = decode_json( $message ); $m->{has_read} = $has_read; push @notification, $m; } $index++; } return \@notification; }
通知を削除するときは、その要素を削除するだけ。