- 浏览: 38930 次
- 来自: 深圳
文章分类
最新评论
-
ZacMa:
哈哈,突然感觉里面没怎么介绍,全是贴代码了
<8>redis及erl-redis阅读 -
惊涛翻案:
马博士,给我开课吧
<8>redis及erl-redis阅读
1 redis的功能相当的强大,里面的发布订阅pub/sub和设计模式中的观察者模式很相似
pub/sub不仅仅解决发布者和订阅者直接代码级别耦合也解决两者在物理部署上的耦合。
并且发布和订阅功能都是多对多的
见发布订阅的例子 http://bbs.chinaunix.net/thread-3755278-1-1.html
erl-redis是litaocheng实现的一个精炼有简单易懂的redis客户端
里面有很多巧妙的地方, 着也是一个标准的erlang客户端实现,如果想实现客户端用这种方法还是很不错的
其实整个实现流程是很清晰易懂的:
从redis_conn_sup:start_link()开始启动监督进程,然后通过start_child启动子进程,
start_link() ->
?INFO2("start the supervisor", []),
supervisor:start_link({local, ?CONN_SUP}, ?MODULE, []).
%% @doc the connection supervisor callback
init([]) ->
?DEBUG2("init supervisor", []),
Stragegy = {simple_one_for_one, 100000, 60},
Client = {undefined, {redis_client, start_link, []},
permanent, 1000, worker, [redis_client]},
{ok, {Stragegy, [Client]}}.
redis_client.erl中实现start_link和基本的tcp逻辑
start_link(Host, Port, Passwd) ->
?DEBUG2("start_link redis_client ~p:~p", [Host, Port]),
gen_server:start_link(?MODULE, {{Host, Port}, Passwd}, []).
-spec start_link(inet_host(), inet_port(), passwd(), atom()) ->
{'ok', any()} | 'ignore' | {'error', any()}.
start_link(Host, Port, Passwd, Name) ->
?DEBUG2("start_link redis_client ~p", [Name]),
gen_server:start_link({local, Name}, ?MODULE, {{Host, Port}, Passwd}, []).
%%
%% gen_server callbacks
%%
init({Server = {Host, Port}, Passwd}) ->
process_flag(trap_exit, true),
case gen_tcp:connect(Host, Port, ?TCP_OPTS, ?CONN_TIMEOUT) of
{ok, Sock} ->
case do_auth(Sock, Passwd) of
ok ->
Tid = do_create_table(),
{ok, #state{server = Server, sock = Sock, pubsub_tid = Tid}};
{tcp_error, Reason} ->
{stop, Reason};
_ ->
?ERROR2("auth failed", []),
{stop, auth_failed}
end;
{error, Reason} ->
{stop, Reason}
end.
其中do_create_table()是创建一个ets表,用来存储订阅相关的数据存储;
do_create_table() ->
ets:new(dummy, [set, private, {keypos, #pubsub.id},
{read_concurrency, true}]).
关于ets的read_concurrency选项就是并发读取数据,这个选项默认是false
看这个tcp选项
生成连接redis server的tcp连接
这里的CONN_TIMEOUT 是5000 5秒钟
tcp_option是
-define(TCP_OPTS, [inet, binary, {active, once},
{packet, line},
{nodelay, false},
{recbuf, 16#1000},
{sndbuf, 16#10000},
{send_timeout, 5000}, {send_timeout_close, true}]).
在调用redis的命令时候,执行:
%% call the command
call(Cmd) ->
call(Cmd, ?NONE).
call(Cmd, Fun) ->
case Pipeline of
false ->
% normal model
R = redis_client:command(Client, Cmd),
?IF(Fun =/= ?NONE, Fun(R), R);
true ->
% pipeline model
add_pipeline_cmd(Cmd, Fun)
end.
if的定义
-define(IF(C, T, F), (case (C) of true -> (T); false -> (F) end)).
继续看command的调用
%% @doc send the command to redis server
-spec command(client(), iolist()) -> any().
command(Client, Data) ->
call(Client, {command, {Data, ?COMMAND_TIMEOUT}}).
call(Client, Req) ->
gen_server:call(Client, Req, infinity).
调用和接收处理
命令的调用是同步的,
handle_call({command, {Data, Timeout}}, _From,
State = #state{sock = Sock, server = _Server, ctx = normal}) ->
?DEBUG2("command:~n~p~n\t=> ~p", [Data, _Server]),
Reply = do_send_recv(Data, Sock, Timeout),
{reply, Reply, State};
do_recv(Sock, PState, Timeout) ->
receive
{tcp, Sock, Packet} ->
%?DEBUG2("receive packet :~p", [Packet]),
do_handle_packet(Sock, Packet, PState, Timeout);
{tcp_closed, _Socket} ->
?ERROR2("socket closed by remote peer", []),
exit({error, tcp_closed});
{tcp_error, _Socket, Reason} ->
?ERROR2("recv message error:~p", [Reason]),
exit({error, {tcp_error, Reason}})
after
Timeout ->
?ERROR2("recv message timeout", []),
exit({error, recv_timeout})
end.
总结下其中写的比较巧妙的地方:
(1) 在redis_cient.erl 中gen_server:start_link时候可以传进去name参数
Name的生成 :
关于这个巧妙生成name的生成方法:
name(Host, Port, UserData) ->
to_name(Host, Port, UserData, true).
existing_name(Host, Port, UserData) ->
to_name(Host, Port, UserData, false).
to_name(Host, Port, UserData, First) when is_list(UserData);
is_atom(UserData);
is_integer(UserData) ->
L = lists:concat(["redis_client_", Host, "_", Port, "_", UserData]),
to_atom(L, First).
to_atom(String, true) ->
list_to_atom(String);
to_atom(String, false) ->
list_to_existing_atom(String).
只要输入自己的标识符UserData就可以生成名称
(2) 使代码更加简洁,把
关于redis.erl的模块,使用了一种比较少用的方法,
目的是为了使用起来更方便,没有其它的特殊用途,
刚好ligaoren对这种语法写了详细的博客说明,还提到本项目
-spec handler(client()) -> redis_handler().
-spec handler(client()) -> redis_handler().
handler(Client) ->
redis:new(Client, false).
handler(Client) ->
redis:new(Client, false).
http://www.cnblogs.com/me-sa/archive/2012/02/16/2354499.html
(3) 启动流程简单
README.md写的太好了,启动方法很简单,
其中有一种启动方法巧妙的启动了一个reids pool,这里默认是启动了5个redis进程
% in main supervisor:
{redis_conn_sup, {redis_conn_sup, start_link, []},
permanent, 1000, supervisor, [redis_client]}
% start client pool
[begin
Name = redis_client:name(Host, Port, I),
{ok, _} = redis_conn_sup:connect(Host, Port, Pass, Name)
end || I <- lists:seq(1, 5)],
% random select a client
Selected = redis_client:existing_name(Host, Port, random:uniform(5)),
Redis = redis_client:handler(Selected),
Redis:set("k1", "v1"),
Redis:get("k1").
还有第五种使用方法中,随机查找一个进程的方法
Redis = redis_conn_sup:sup_rand_client(Host, Port, Pass, Pool),
sup_rand_client() ->
Children = supervisor:which_children(?CONN_SUP),
Len = length(Children),
{_Id, Child, worker, _Modules} = lists:nth(random:uniform(Len), Children),
redis_client:handler(Child).
(4)这是一个标准的erlang客户端实现
erl-redis项目地址
https://github.com/litaocheng/erl-redis
pub/sub不仅仅解决发布者和订阅者直接代码级别耦合也解决两者在物理部署上的耦合。
并且发布和订阅功能都是多对多的
见发布订阅的例子 http://bbs.chinaunix.net/thread-3755278-1-1.html
erl-redis是litaocheng实现的一个精炼有简单易懂的redis客户端
里面有很多巧妙的地方, 着也是一个标准的erlang客户端实现,如果想实现客户端用这种方法还是很不错的
其实整个实现流程是很清晰易懂的:
从redis_conn_sup:start_link()开始启动监督进程,然后通过start_child启动子进程,
start_link() ->
?INFO2("start the supervisor", []),
supervisor:start_link({local, ?CONN_SUP}, ?MODULE, []).
%% @doc the connection supervisor callback
init([]) ->
?DEBUG2("init supervisor", []),
Stragegy = {simple_one_for_one, 100000, 60},
Client = {undefined, {redis_client, start_link, []},
permanent, 1000, worker, [redis_client]},
{ok, {Stragegy, [Client]}}.
redis_client.erl中实现start_link和基本的tcp逻辑
start_link(Host, Port, Passwd) ->
?DEBUG2("start_link redis_client ~p:~p", [Host, Port]),
gen_server:start_link(?MODULE, {{Host, Port}, Passwd}, []).
-spec start_link(inet_host(), inet_port(), passwd(), atom()) ->
{'ok', any()} | 'ignore' | {'error', any()}.
start_link(Host, Port, Passwd, Name) ->
?DEBUG2("start_link redis_client ~p", [Name]),
gen_server:start_link({local, Name}, ?MODULE, {{Host, Port}, Passwd}, []).
%%
%% gen_server callbacks
%%
init({Server = {Host, Port}, Passwd}) ->
process_flag(trap_exit, true),
case gen_tcp:connect(Host, Port, ?TCP_OPTS, ?CONN_TIMEOUT) of
{ok, Sock} ->
case do_auth(Sock, Passwd) of
ok ->
Tid = do_create_table(),
{ok, #state{server = Server, sock = Sock, pubsub_tid = Tid}};
{tcp_error, Reason} ->
{stop, Reason};
_ ->
?ERROR2("auth failed", []),
{stop, auth_failed}
end;
{error, Reason} ->
{stop, Reason}
end.
其中do_create_table()是创建一个ets表,用来存储订阅相关的数据存储;
do_create_table() ->
ets:new(dummy, [set, private, {keypos, #pubsub.id},
{read_concurrency, true}]).
关于ets的read_concurrency选项就是并发读取数据,这个选项默认是false
看这个tcp选项
生成连接redis server的tcp连接
这里的CONN_TIMEOUT 是5000 5秒钟
tcp_option是
-define(TCP_OPTS, [inet, binary, {active, once},
{packet, line},
{nodelay, false},
{recbuf, 16#1000},
{sndbuf, 16#10000},
{send_timeout, 5000}, {send_timeout_close, true}]).
在调用redis的命令时候,执行:
%% call the command
call(Cmd) ->
call(Cmd, ?NONE).
call(Cmd, Fun) ->
case Pipeline of
false ->
% normal model
R = redis_client:command(Client, Cmd),
?IF(Fun =/= ?NONE, Fun(R), R);
true ->
% pipeline model
add_pipeline_cmd(Cmd, Fun)
end.
if的定义
-define(IF(C, T, F), (case (C) of true -> (T); false -> (F) end)).
继续看command的调用
%% @doc send the command to redis server
-spec command(client(), iolist()) -> any().
command(Client, Data) ->
call(Client, {command, {Data, ?COMMAND_TIMEOUT}}).
call(Client, Req) ->
gen_server:call(Client, Req, infinity).
调用和接收处理
命令的调用是同步的,
handle_call({command, {Data, Timeout}}, _From,
State = #state{sock = Sock, server = _Server, ctx = normal}) ->
?DEBUG2("command:~n~p~n\t=> ~p", [Data, _Server]),
Reply = do_send_recv(Data, Sock, Timeout),
{reply, Reply, State};
do_recv(Sock, PState, Timeout) ->
receive
{tcp, Sock, Packet} ->
%?DEBUG2("receive packet :~p", [Packet]),
do_handle_packet(Sock, Packet, PState, Timeout);
{tcp_closed, _Socket} ->
?ERROR2("socket closed by remote peer", []),
exit({error, tcp_closed});
{tcp_error, _Socket, Reason} ->
?ERROR2("recv message error:~p", [Reason]),
exit({error, {tcp_error, Reason}})
after
Timeout ->
?ERROR2("recv message timeout", []),
exit({error, recv_timeout})
end.
总结下其中写的比较巧妙的地方:
(1) 在redis_cient.erl 中gen_server:start_link时候可以传进去name参数
Name的生成 :
关于这个巧妙生成name的生成方法:
name(Host, Port, UserData) ->
to_name(Host, Port, UserData, true).
existing_name(Host, Port, UserData) ->
to_name(Host, Port, UserData, false).
to_name(Host, Port, UserData, First) when is_list(UserData);
is_atom(UserData);
is_integer(UserData) ->
L = lists:concat(["redis_client_", Host, "_", Port, "_", UserData]),
to_atom(L, First).
to_atom(String, true) ->
list_to_atom(String);
to_atom(String, false) ->
list_to_existing_atom(String).
只要输入自己的标识符UserData就可以生成名称
(2) 使代码更加简洁,把
关于redis.erl的模块,使用了一种比较少用的方法,
目的是为了使用起来更方便,没有其它的特殊用途,
刚好ligaoren对这种语法写了详细的博客说明,还提到本项目
-spec handler(client()) -> redis_handler().
-spec handler(client()) -> redis_handler().
handler(Client) ->
redis:new(Client, false).
handler(Client) ->
redis:new(Client, false).
http://www.cnblogs.com/me-sa/archive/2012/02/16/2354499.html
(3) 启动流程简单
README.md写的太好了,启动方法很简单,
其中有一种启动方法巧妙的启动了一个reids pool,这里默认是启动了5个redis进程
% in main supervisor:
{redis_conn_sup, {redis_conn_sup, start_link, []},
permanent, 1000, supervisor, [redis_client]}
% start client pool
[begin
Name = redis_client:name(Host, Port, I),
{ok, _} = redis_conn_sup:connect(Host, Port, Pass, Name)
end || I <- lists:seq(1, 5)],
% random select a client
Selected = redis_client:existing_name(Host, Port, random:uniform(5)),
Redis = redis_client:handler(Selected),
Redis:set("k1", "v1"),
Redis:get("k1").
还有第五种使用方法中,随机查找一个进程的方法
Redis = redis_conn_sup:sup_rand_client(Host, Port, Pass, Pool),
sup_rand_client() ->
Children = supervisor:which_children(?CONN_SUP),
Len = length(Children),
{_Id, Child, worker, _Modules} = lists:nth(random:uniform(Len), Children),
redis_client:handler(Child).
(4)这是一个标准的erlang客户端实现
erl-redis项目地址
https://github.com/litaocheng/erl-redis
发表评论
-
erlang版本安装相关问题 <32>
2014-05-10 15:54 624<1> erlang R1603安装后,crytp ... -
关于iolist<30>
2014-01-15 10:42 625iolist是比较常用的数据结构. iolist的 ... -
erlang 字符编码 <29>
2014-01-14 16:31 1261用mochiweb通过网页发送中文到服务器,结果服务器显示乱码 ... -
<27>erlang record
2013-11-19 11:19 774平时总是忘记record的某些使用方法,每次使用都要翻文档, ... -
<26>io:format io_lib:format
2013-11-14 11:07 1316使用io_lib时候要注意参数,尤其是封装json串的时候,否 ... -
<24>用error_logger间隔记录日志
2013-10-22 16:09 651执行下面的代码 test:start(). test.erl ... -
<23>erlang 数据存储
2013-10-15 22:15 1660做为后端开发者,经常 ... -
<22> erlang中的数学计算函数相关
2013-10-10 10:34 16331. 幂函数 match:pow(m,n) 表示m的n次幂 ... -
<20>erlang中的类型和函数说明
2013-09-15 11:25 978erlang是一种动态类型的语言(运行时才决定数据类型),可以 ... -
<19>erlang中的时间,日期
2013-09-06 11:21 1195时间函数涉及的数据类型: DATA TYPES datetim ... -
<18>Efficient guide 之List handling
2013-08-31 18:45 6771 Deep and flat lists lists:fl ... -
<17>Efficiency Guide之Function
2013-08-27 22:30 5801. 函数模式匹配 模式匹配,在函数头,case和receiv ... -
<16>Efficiency Guide之Common Caveats
2013-08-11 11:07 809(1) ++ 如果做一个list的反转,不要这样, naiv ... -
<15> lists模块补充
2013-08-05 20:12 828%% 对list模块经常用到的进行补充 %% 1 对所有元素进 ... -
<15> lists模块解析和补充
2013-07-24 17:57 12%% 对list模块经常用到的进行补充 %% 1 对所有元素 ... -
<12>简述erlang的几种错误
2013-04-14 23:31 11821) badarg Bad argument. The ar ... -
<11>erlang中方便使用的模块和命令(2)
2013-04-06 22:33 798(1) 进程字典到底用不用,很多人推荐使用 http:// ... -
<9>rabbitmq网络层
2013-01-31 00:20 792抽离出了网络层, 逻辑层待以后研究 https://gith ... -
<7>pg2 分析
2012-12-08 13:42 1258网上看到erlang的pg2模块似乎没人推荐使用,但是还是有不 ... -
<6>error_logger 使用
2012-12-02 16:24 1452erlang中日志管理主要有error_loggger 模块, ...
相关推荐
redis-stack-server-7.2.0-v9.arm64.snap redis-stack-server-7.2.0-v9.bionic.arm64.tar.gz redis-stack-server-7.2.0-v9.bionic.x86_64.tar.gz ...redis-stack-server-7.2.0-v9.rhel7/8/9.x86_64.tar.gz
Another-Redis-Desktop-Manager是更快、更好、更稳定的Redis桌面(GUI)管理客户端,这里提供的是Windows-1.4.8的版本。 它还有Mac、Linux的版本,可在《说明.txt》中获取其他版本的下载链接。
tomcat-redis-session-manager-1.2-tomcat-7-java-7tomcat-redis-session-manager-1.2-tomcat-7-java-7tomcat-redis-session-manager-1.2-tomcat-7-java-7tomcat-redis-session-manager-1.2-tomcat-7-java-7tomcat-...
redis-5.0.14-1.el7.remi.x86_64.rpm安装包(含有部署手册) redis-5.0.14-1.el7.remi.x86_64.rpm安装包(含有部署手册) redis-5.0.14-1.el7.remi.x86_64.rpm安装包(含有部署手册) redis-5.0.14-1.el7.remi.x86_64.rpm...
**Redis 全面检查工具:redis-full-check** Redis 是一款高性能的键值存储系统,广泛应用于缓存、数据库和消息中间件等场景。在实际应用中,为了确保 Redis 的稳定性和数据一致性,需要定期对 Redis 实例进行健康...
【标题】"tomcat-redis-session-manager包集合下载(tomcat8)"涉及的主要知识点是将Redis集成到Tomcat中管理会话(session),以提高Web应用的性能和可扩展性。 【描述】中提到的"所需的tomcat-redis-session-...
< artifactId > tomcat-redis-session </ artifactId > < version > 8.5.5.0 </ version > </ dependency > 用它: redisManager redisManager = new RedisManager(); redisManager 。setDisableListeners...
《深入理解Tomcat-Redis-Session-Manager:在Tomcat7和Tomcat8中的应用》 在现代Web应用程序开发中,session管理是一个至关重要的环节,它涉及到用户会话的持久化和跨请求的数据共享。传统的session管理方式在高...
在Windows环境下安装Redis,可以借助于提供的压缩包"redis-windows-7.2.5.zip"进行。以下是关于Redis及其在Windows上的安装和使用的详细知识: 1. **Redis特性** - **键值对存储**:Redis的核心是键值对模型,其中...
1、redis_4.0.10-1_arm64.deb 银河麒麟v4+飞腾 安装包 2、自带服务启动 3、目录树 /opt/redis-4.0.10/ ├── bin ...│ ├── redis-sentinel -> redis-server │ └── redis-server └── redis.conf
赠送jar包:flink-connector-redis_2.10-1.1.5.jar; 赠送原API文档:flink-connector-redis_2.10-1.1.5-javadoc.jar; 赠送源代码:flink-connector-redis_2.10-1.1.5-sources.jar; 赠送Maven依赖信息文件:flink-...
"Redis++使用说明,windows下编译Redis-Plus-Plus" 在这篇文章中,我们将详细介绍如何在Windows平台下编译Redis++,包括编译hiredis.lib和Win32_Interop.lib静态库文件的过程,然后安装Cmake并编译Redis++,最后...
<groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>3.x.x</version> <!-- 使用最新版本 --> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</...
tomcat-redis-session-manager-2.0.0.jar jedis-2.5.2.jar commons-pool2-2.2.jar 2.修改 conf 目录下的 context.xml 文件 <Valve className=...
《Redis-Spring-Boot-Starter深度解析》 在现代Java Web开发中,Spring Boot框架以其高效、便捷的特点深受开发者喜爱。而Redis作为一种高性能的键值数据存储系统,常被用作缓存、消息队列等多种场景,与Spring Boot...
1. 下载源码包:`redis-2.8.13.tar.gz` 是Redis的源码包,解压后进行编译和安装。 2. 解压:`tar -zxvf redis-2.8.13.tar.gz` 3. 编译:`cd redis-2.8.13`,然后`make` 4. 安装:`sudo make install` 5. 启动Redis...
接下来,关于“redis-desktop-manager-0.8.8.384.exe”文件,这是一个 Redis 客户端工具,名为 Redis Desktop Manager。它提供了一个图形用户界面(GUI),使得用户可以方便地管理 Redis 服务器,包括查看键值、执行...
redis可视化工具redis-desktop-manager-0.8.8.384。。。。
关于标签"redis-desktop-ma",这可能是简写的“redis-desktop-manager”,暗示了这个软件的主要功能是作为Redis的桌面管理工具。 至于“新建文件夹”,这可能是压缩包内的一个文件夹名,但没有具体的文件列表,所以...
<section name="RedisConfig" type="Redis.Helper.RedisConfigInfo,Redis.Helper"/> </configSections> <!--Redis配置--> <RedisConfig WriteServerList="密码@192.168.1.232:6379" ReadServerList="密码@192....