Archive for February 2015

Redis 可以用來幹嘛?

Data Types

string
list
set
sorted set
hash (dict)
publish / subscribe

Redis 数据结构使用场景
http://get.jobdeer.com/523.get

谈 Redis 应用场景
http://www.qixing318.com/article/talking-about-redis-application-scenario.html

List 就是链表,相信略有数据结构知识的人都应该能理解其结构。使用Lists结构,我们可以轻松地实现最新消息排行等功能。Lists的另一个应用就是消息队列,可以利用Lists的PUSH操作,将任务存在Lists中,然后工作线程再用POP操作将任务取出进行执行。

Set 集合的概念就是一堆不重复值的组合。利用Redis提供的Sets数据结构,可以存储一些集合性的数据,比如在微博应用中,可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。Redis还为集合提供了求交集、并集、差集等操作,可以非常方便的实现如共同关注、共同喜好、二度好友等功能

Sorted Set 可以通过用户额外提供一个优先级(score)的参数来为成员排序,并且是插入有序的,即自动排序。当你需要一个有序的并且不重复的集合列表,那么可以选择sorted set数据结构,比如twitter 的public timeline可以以发表时间作为score来存储,这样获取时就是自动按时间排好序的。

Pub/Sub 从字面上理解就是发布(Publish)与订阅(Subscribe),在Redis中,你可以设定对某一个key值进行消息发布及消息订阅,当一个key值上进行了消息发布后,所有订阅它的客户端都会收到相应的消息。这一功能最明显的用法就是用作实时消息系统,比如普通的即时聊天,群聊等功能。

Redis 可以用來幹嘛?

大部分情況並不是用 redis 來「取代」資料庫
資料庫其實還是建議用 mysql
只是拿 redis 儲存相對不重要的資料
通常會把統計、計數的部份獨立出來用 redis 處理

How to take advantage of Redis just adding it to your stack?
http://oldblog.antirez.com/post/take-advantage-of-redis-adding-it-to-your-stack.html

Redis 在电商中的实际应用场景
http://kenny7.com/2012/09/redis-usage-scenario.html

列出最新的 20 則評論

SQL 可能是 SELECT * FROM foo WHERE ... ORDER BY created_at DESC LIMIT 20

可以改用 redis 的 list
假設我們有一個叫 latest.comments 的 redis list
可以 trim 這個 list 最多只記錄 20 個 item
每當一個 comment 被新增到資料庫之後
就 push comment id 到 redis list(LPUSH)
這樣每次我只要去撈 latest.comments 取出 comment ids
然後去資料庫把這些 comments 一次撈出來就好了

如果是要列出不同目錄下的最新 30 則文章
其實可以每個目錄都建立一個 redis list

當 sql 的條件很複雜的時候
就可以考慮改用 redis list 了

按 score 排行榜

可以使用 redis sorted set
sorted set 是當你把資料放進去的時候就排序了(用 score 來排序)

ZADD leaderboard <score> <username>

# get the top 100 users by score
ZREVRANGE leaderboard 0 99

# tell the user its global rank
ZRANK leaderboard <username>

Counter

INCR page_view:song:123

統計特定時間內的瀏覽人數

统计 10 分钟内在线用户数
http://lztian.com/blog/3170.html

daily unique user using bitmap
http://wbj0110.iteye.com/blog/2040189

Notification

redis> HSET user:<userId>:message:ur system 1//1条未读系统消息
(integer) 1
redis> HINCRBY user:<userId>:message:ur system 1 //未读系统消息+1
(integer) 2
redis> HINCRBY user:<userId>:message:ur comment 1 //未读评论消息+1
(integer) 1
redis> HSET user:<userId>:message:ur system 0//设为系统消息已读
(integer) 1
redis> HGETALL user:<userId>:message:ur //获取这key hashkey 和value
1) "system"
2) "0"
3) "comment"
4) "1"
SUBSCRIBE key

Timeline / Feed

redis> ZADD user:100000:feed:topic  61307510400000 <feedId> //score 为timestamp
(integer) 1
redis> EXPIRE user:100000:feed:topic 24*60*60 //set timeout to one day
(integer) 1
redis> ZADD user:100000:feed:friend 61307510400000 <feedId> //不同类型feed
(integer) 1
redis> EXPIRE user:100000:feed:friend 24*60*60 //set timeout
(integer) 1

HAProxy as Load Balancer for Web and Databases

HAProxy 可以用來當各種服務(db 或 web)的 load balancer
其實就是幫你把 requests 分散到後端不同的機器
也會自動偵測機器掛掉的話就不會把請求送給它
即所謂的 Reverse Proxy

還有一種是 Caching Reverse Proxy
例如 Varnish, Squid
通常會直接把 response 緩存下來
這樣就不需要把 requests 打到後端去了

Install

$ sudo apt-get install software-properties-common
$ sudo add-apt-repository ppa:vbernat/haproxy-1.5
$ sudo apt-get update
$ sudo apt-get install haproxy

Web Load Balancer: nginx

Configuration

in /etc/haproxy/haproxy.cfg

global
    log /dev/log    local0
    log /dev/log    local1 notice
    chroot /var/lib/haproxy
    stats socket /run/haproxy/admin.sock mode 660 level admin
    stats timeout 30s
    user haproxy
    group haproxy
    daemon

    # Default SSL material locations
    ca-base /etc/ssl/certs
    crt-base /etc/ssl/private

    # Default ciphers to use on SSL-enabled listening sockets.
    # For more information, see ciphers(1SSL).
    ssl-default-bind-ciphers A_SECRET_STRING
    ssl-default-bind-options no-sslv3

defaults
    log     global
    mode    http
    option  httplog
    option  dontlognull
    timeout connect 5000
    timeout client  50000
    timeout server  50000
    errorfile 400 /etc/haproxy/errors/400.http
    errorfile 403 /etc/haproxy/errors/403.http
    errorfile 408 /etc/haproxy/errors/408.http
    errorfile 500 /etc/haproxy/errors/500.http
    errorfile 502 /etc/haproxy/errors/502.http
    errorfile 503 /etc/haproxy/errors/503.http
    errorfile 504 /etc/haproxy/errors/504.http

frontend web
    bind *:80
    mode http
    # acl static path_beg /asset
    # use_backend static_nodes if static
    default_backend web_nodes

backend web_nodes
    mode http
    balance roundrobin
    option forwardfor
    http-request set-header X-Forwarded-Port %[dst_port]
    http-request add-header X-Forwarded-Proto https if { ssl_fc }
    # option httpclose
    # option http-server-close
    # appsession session len 32 timeout 12h
    cookie haproxyserverid insert nocache maxidle 1h
    option httpchk HEAD /health/
    server web1 100.100.100.1:80 check cookie web1
    server web2 100.100.100.2:80 check cookie web2
    server web3 100.100.100.3:80 check cookie web3
    server worker1 100.100.100.11:80 check cookie worker1
    server worker2 100.100.100.12:80 check cookie worker2

listen stats *:1936
    stats enable
    stats uri /
    stats hide-version
    stats auth YOUR_USERNAME:YOUR_PASSWORD

balance
http://cbonte.github.io/haproxy-dconv/configuration-1.5.html#4.2-balance

cookie
http://cbonte.github.io/haproxy-dconv/configuration-1.5.html#4.2-cookie

appsession
http://cbonte.github.io/haproxy-dconv/configuration-1.5.html#4.2-appsession

option forwardfor
http://cbonte.github.io/haproxy-dconv/configuration-1.5.html#4-option%20forwardfor

ref:
https://serversforhackers.com/haproxy/
https://www.digitalocean.com/community/tutorials/how-to-use-haproxy-as-a-layer-7-load-balancer-for-wordpress-and-nginx-on-ubuntu-14-04

Database Load Balancer: MySQL

Create User for HAProxy in managed Databases

必須在要管理的資料庫新增一個 haproxy 的 user
讓 haproxy 檢測連線

# 100.100.100.78 是跑 haproxy 的那台機器
CREATE USER haproxy@100.100.100.78;

# 你可以看看 user 有沒有被建立
SELECT user, host, password FROM mysql.user;

Configuration

in /etc/haproxy/haproxy.cfg

global
    log 127.0.0.1 local0 notice
    maxconn 2000
    user haproxy
    group haproxy

defaults
    log     global
    retries 5
    timeout connect  10000
    timeout client  100000
    timeout server  100000

listen mariadb-cluster
    bind 0.0.0.0:3306
    mode tcp
    option mysql-check user haproxy
    balance source
    server svtw-db1 100.100.100.79:3306 check weight 4
    server svtw-db2 100.100.100.80:3306 check weight 4
    server svtw-db3 100.100.100.88:3306 check weight 2

listen webinterface
    bind 0.0.0.0:8080
    mode http
    stats enable
    stats uri /
$ sudo service haproxy reload
$ sudo service haproxy restart

ref:
https://www.digitalocean.com/community/tutorials/how-to-use-haproxy-to-set-up-http-load-balancing-on-an-ubuntu-vps

Python @property: setter, getter

class UserPreference(object):
    DEFAULT_VALUES = {
        'allow_fb_publish': True,
    }

    def __init__(self, user_id):
        """
        pref:2876321
        {
            'allow_fb_publish': 1,
        }
        """

        self.user_id = user_id
        self.key = 'pref:%s' % (self.user_id)

    @property
    def allow_fb_publish(self):
        value = rdb.hget(self.key, 'allow_fb_publish')
        if value is None:
            value = self.allow_fb_publish = self.DEFAULT_VALUES['allow_fb_publish']

        return value

    @allow_fb_publish.setter
    def allow_fb_publish(self, new_value):
        if not isinstance(new_value, bool):
            raise TypeError('Must be bool, not %s' % (type(new_value).__name__))

        new_value_int = int(new_value)
        rdb.hset(self.key, 'allow_fb_publish', new_value_int)

        return new_value_int

ref:
http://www.programiz.com/python-programming/property
http://openhome.cc/Gossip/Python/Property.html