http://no-fucking-idea.com/blog/2012/03/23/using-eredis-in-erlang/

Using Eredis, Redis With Erlang

MAR 23RD, 2012 | COMMENTS

Recently i decided to move my blog from tumblr.com to octopress engine because it is just easier for me to maintain it and it looks nicer. The old blog is under http://no-fucking-idea.tumblr.com. My first post on new blog is dedicated to using redis with erlang.

Eredis

Wooga created a really nice (performance driven) redis driver for erlang. You can get it here https://github.com/wooga/eredis. It is really easy and nice.

Initial sample

On project page you can find simple examples how to use eredis. Examples there are all you need (but i need something for front page of my new blog so i will rewrite them and explain them :) ).

First you need to start your eredis application

initialization

1
{ok, Client} = eredis:start_link().

Client is the “connection / state” we will be using with rest of queries.

To query things with redis we will use q method from eredis module which takes “Connection / Client” state and list with params. This api is very simple here are two most basic examples of get and set. GET:

get

1
{ok, <<"OK">>} = eredis:q(Client, ["SET", "name", "kuba"]).

and SET:

set

1
{ok, <<"kuba">>} = eredis:q(Client, ["GET", "name"]).

From my point of view this is ideal candidate to be turned into gen_server behavior. We will pass “Connection / Client” as state and also we will build some “key” serialization methods around it to make it more durable and make our life easy if we will decide to refactor it later on.

Free Api wrapper

First thing i saw during development using Erlang is that you get free api if you follow simple patterns and encapsulate things into gen_server’s and applications.

example_db.erl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
-module(example_db).
-behaviour(gen_server). -author("jakub.oboza@gmail.com").
-define(Prefix, "example"). -export([start_link/0]).
-export([init/1, handle_call/3, handle_cast/2, terminate/2, handle_info/2, code_change/3, stop/1]).
-export([get_script/2, save_script/3]). % public api start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). init([]) ->
{ok, Redis} = eredis:start_link(),
{ok, Redis}. stop(_Pid) ->
stop(). stop() ->
gen_server:cast(?MODULE, stop). %% public client api get_script(Api, Method) ->
gen_server:call(?MODULE, {get_script, Api, Method}). save_script(Api, Method, Script) ->
gen_server:call(?MODULE, {save_script, Api, Method, Script}). %% genserver handles handle_call({get_script, Api, Method}, _From, Redis) ->
Response = eredis:q(Redis, [ "GET", get_key(Api, Method) ]),
{reply, Response, Redis}; handle_call({save_script, Api, Method, Script}, _From, Redis) ->
Response = eredis:q(Redis, ["SET", get_key(Api, Method), Script]),
{reply, Response, Redis}; handle_call(_Message, _From, Redis) ->
{reply, error, Redis}. handle_cast(_Message, Redis) -> {noreply, Redis}.
handle_info(_Message, Redis) -> {noreply, Redis}.
terminate(_Reason, _Redis) -> ok.
code_change(_OldVersion, Redis, _Extra) -> {ok, Redis}. %% helper methods get_key(Api, Method) ->
generate_key([Api, Method]). generate_key(KeysList) ->
lists:foldl(fun(Key, Acc) -> Acc ++ ":" ++ Key end, ?Prefix, KeysList). % tests -ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-endif. -ifdef(TEST). generate_key_test() ->
Key = generate_key(["one", "two", "three"]),
?assertEqual("example:one:two:three", Key). server_test_() ->
{setup, fun() -> example_db:start_link() end,
fun(_Pid) -> example_db:stop(_Pid) end,
fun generate_example_db_tests/1}.
generate_example_db_tests(_Pid) ->
[
?_assertEqual({ok,<<"OK">>}, example_db:save_script("jakub", "oboza", <<"yo dwang">>) ),
?_assertEqual({ok,<<"yo dwang">>}, example_db:get_script("jakub", "oboza") )
].
-endif

Public Api

This code listing has two important parts first at top it starts at line 26. This is the public API which will be used by developer. This is this free api. Later on i will explain how to change redis to mongodb and probably to other db engines without doing changes in rest of our app. From my perspective this is awesome feature. In most cases when i had to make app scale problem of having code that was glues to one db engine was heavy.

eunit tests

At line 60. starts the declaration of tests, using rebar and eunit is very easy and it is always good to have test coverage in case of refactoring. I’m a fan of test driven development so i like to cover in tests first things that i will use or i think they might be error prone. Here is used “test generators” to just to show how to write tests for gen_server.

Rebar

Before i will explain more i need to say little about rebar. It is a command line tool that was developed by basho to help create app. it is by far the best thing i found learning erlang to help me do boring stuff and eliminate a lot of rage writing app.src files. To get rebar simply do (you can always go to https://github.com/basho/rebar to get most up to date informations about building it)

1
2
3
λ  git clone git://github.com/basho/rebar.git
λ cd rebar
λ ./bootstrap

I use my own set of zsh scripts so all i did to add it to my path was to edit .furby file in my home dir. I strongly suggest also adding it to $PATH just to make your life easier.

Back to example_db!

To create app using rebar you just need to

1
2
3
4
5
6
λ mkdir example_db
λ rebar create-app appid=example_db
==> example_db (create-app)
Writing src/example_db.app.src
Writing src/example_db_app.erl
Writing src/example_db_sup.erl

This command created src folder with scaffold of application OTP pattern and supervisorthats almost all we need :). Now you can compile it using rebar compile and run tests using rebar compile eunit in out app currently we will see

rebar compile eunit

1
2
3
4
5
6
7
8
λ rebar compile eunit
==> example_db (compile)
Compiled src/example_db_app.erl
Compiled src/example_db_sup.erl
==> example_db (eunit)
Compiled src/example_db_app.erl
Compiled src/example_db_sup.erl
There were no tests to run.

Nothing to do because its empty. Lets add our db module. But before this we need to add dependencies for eredis module. Lets create rebar.config file and add it.

1
2
3
4
5
6
7
8
9
10
11
12
λ emacs rebar.config
λ cat rebar.config
%%-*- mode: erlang -*- {erl_opts, []}.
{cover_enabled, true}. {deps,
[
{eredis, ".*", {git, "https://github.com/wooga/eredis.git", "HEAD"}}
]
}.

Now just run rebar get-deps to get all dependencies downloaded. After adding our example_db.erl into src directory we can run rebar compile eunit to compile and run tests. We have added {cover_enabled, true} in rebar.conf so also test code coverage will be generated for us.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
λ rebar compile eunit
==> eredis (compile)
==> example_db (compile)
Compiled src/example_db.erl
==> eredis (eunit) =ERROR REPORT==== 28-Mar-2012::22:19:35 ===
** Generic server <0.263.0> terminating
** Last message in was {tcp,#Port<0.4516>,
<<"*3\r\n$7\r\nmessage\r\n$3\r\nfoo\r\n$2\r\n12\r\n">>}
** When Server state == {state,"127.0.0.1",6379,<<>>,100,#Port<0.4516>,
{pstate,undefined,undefined},
[<<"foo">>],
{#Ref<0.0.0.4058>,<0.180.0>},
{[{message,<<"foo">>,<<"11">>,<0.263.0>},
{message,<<"foo">>,<<"10">>,<0.263.0>},
{message,<<"foo">>,<<"9">>,<0.263.0>},
{message,<<"foo">>,<<"8">>,<0.263.0>},
{message,<<"foo">>,<<"7">>,<0.263.0>},
{message,<<"foo">>,<<"6">>,<0.263.0>},
{message,<<"foo">>,<<"5">>,<0.263.0>},
{message,<<"foo">>,<<"4">>,<0.263.0>},
{message,<<"foo">>,<<"3">>,<0.263.0>}],
[{message,<<"foo">>,<<"2">>,<0.263.0>}]},
10,exit,need_ack}
** Reason for termination ==
** max_queue_size
All 53 tests passed.
Cover analysis: /private/tmp/example_db/deps/eredis/.eunit/index.html
==> example_db (eunit)
Compiled src/example_db.erl
All 3 tests passed.
Cover analysis: /private/tmp/example_db/.eunit/index.html

All seems to be fine! lets create file called start.sh to test it out

1
2
λ cat start.sh
erl -pa ebin -pa deps/*/ebin

and make it executable with chmod +x start.sh

And lets rock ‘n’ roll

1
2
3
4
5
6
7
8
9
10
 λ ./start.sh
Erlang R15B (erts-5.9) [source] [64-bit] [smp:4:4] [async-threads:0] [hipe] [kernel-poll:false] Eshell V5.9 (abort with ^G)
1> example_db:start_link().
{ok,<0.33.0>}
2> example_db:save_script("example", "script", "puts '2+2'").
{ok,<<"OK">>}
3> example_db:get_script("example", "script").
{ok,<<"puts '2+2'">>}

Have fun :) Hope it was useful. You can download code for this blog post here https://github.com/JakubOboza/example_db-code

Huh ^___^

that was my first post on new blog ;)

Using Eredis, Redis With Erlang的更多相关文章

  1. Redis单台的安装部署及集群部署

    Redis是一种高级key-value数据库.它跟memcached类似,不过数据可以持久化,而且支持的数据类型很丰富.有字符串,链表,集 合和有序集合.支持在服务器端计算集合的并,交和补集(diff ...

  2. 玩转spring boot——结合redis

    一.准备工作 下载redis的windows版zip包:https://github.com/MSOpenTech/redis/releases 运行redis-server.exe程序 出现黑色窗口 ...

  3. 缓存、队列(Memcached、redis、RabbitMQ)

    本章内容: Memcached 简介.安装.使用 Python 操作 Memcached 天生支持集群 redis 简介.安装.使用.实例 Python 操作 Redis String.Hash.Li ...

  4. [Erlang 0122] Erlang Resources 2014年1月~6月资讯合集

    虽然忙,有些事还是要抽时间做; Erlang Resources 小站 2014年1月~6月资讯合集,方便检索.      小站地址: http://site.douban.com/204209/   ...

  5. Redis 与 数据库处理数据的两种模式

    Redis 是一个高性能的key-value数据库. redis的出现,很大程度补偿了memcached这类key-value存储的不足,在部 分场合可以对关系数据库起到很好的补充作用.它提供了Pyt ...

  6. windows下redis安装

    最近因公司项目原因,去了趟昆明出差,其中第一次接触安装redis,配置sentinel,学习到不少,但也都是皮毛而已,本随笔记下所学知识. 1.首先介绍下redis,来源自百度百科 redis是一个k ...

  7. mac下搭建redis环境

    一.redis简介 redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合)和zset(有 ...

  8. Redis初识、设计思想与一些学习资源推荐

    一.Redis简介 1.什么是Redis Redis 是一个开源的使用ANSI C 语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value 数据库,并提供多种语言的API.从2010 年 ...

  9. Python 【第六章】:Python操作 RabbitMQ、Redis、Memcache、SQLAlchemy

    Memcached Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态.数据库驱动网站的速度 ...

随机推荐

  1. pycharm快捷键、经常使用设置、配置管理

    http://blog.csdn.net/pipisorry/article/details/39909057 本博客一直在同步更新中! 内容包括:pycharm学习技巧 Learning tips. ...

  2. ORACLE使用WITH AS和HINT MATERIALIZE优化SQL解决FILTER效率低下

     在做项目的过程中,一个页面使用类似例如以下的SQL查询数据.为了保密和使用方便,我把项目中有关的表名和字段替换使用ORACLE数据库中的系统表和字段. 在我所做的项目中.类似ALL_TABLES ...

  3. Rick's RoTs -- Rules of Thumb for MySQL--转载

    原文地址:http://mysql.rjweb.org/doc.php/ricksrots Brought to you by Rick James Here are 160+ tips, trick ...

  4. HTTP网络协议(二)

    HTTP报文内的HTTP信息 HTTP协议交互的信息被称为HTTP报文,请求端的HTTP报文叫做请求报文,响应端的叫做响应报文.    HTTP为了提升传输速率,其在传输数据时,按照数据原样进行压缩传 ...

  5. 深度解析VC中的消息(转发)

    http://blog.csdn.net/chenlycly/article/details/7586067 这篇转发的文章总结的比较好,但是没有告诉我为什么ON_MESSAGE的返回值必须是LRES ...

  6. IOS获取手机设备所有应用

    //返回一个数组 1 NSMutableArray *applist = [[NSMutableArray alloc]init]; NSString *pathOfApplications = @& ...

  7. 表单提交数据格式form data

    前言: 最近遇到的最多的问题就是表单提交数据格式问题了. 常见的三种表单提交数据格式,分别举例说明:(项目是vue的框架) 1.application/x-www-form-urlencoded 提交 ...

  8. SQLite header and source version mismatch解决方案

    SQLite header and source version mismatch 最近需要用到sqlite,去官网下了一个编译安装后打开sqlite3出现SQLite header and sour ...

  9. vue项目中一些文件的作用

    原文 简书原文:https://www.jianshu.com/p/38749e5bec3c 大纲 1.vue项目结构 2.主要的配置文件 2.1.package.json 2.2.dev-serve ...

  10. 7、linux系统2440开发板域名解析问题

    如果在linux系统中ping某一台电脑的ip地址可以ping 通: ~ >: ping 192.168.1.3PING 192.168.1.3 (192.168.1.3): 56 data b ...