Erlang pool management -- Emysql pool optimize
在上一篇关于Emysql pool (http://www.cnblogs.com/--00/p/4281938.html)的分析的最后提到
现在的emysql_conn_mgr gen_server 进程属于单点,也就是所有的pool 的管理调度都是由一个进程来完成.
如果在同一个Erlang node 中管理为数众多的pool,就会存在瓶颈. 对于热点进程而言,提高其process priority 是一个optimize 的方向,但是并不能彻底解决因单点带来的问题. 因此, 应该尝试将单个emysql_conn_mgr gen_server 进程拆分为多个, 而拆分的依据, 就是将pool 的管理optimize 为每个pool 交由一个emysql_conn_mgr 进程管理, 而不是现在的所有的pool 都是由同一个emysql_conn_mgr 管理.
pool 添加操作
对于pool 的数据结构,保持之前的结构不变.使用一个emysql_pool_mgr gen_server 进程,并在其中维护一个ets table(用以保存poolid 和 emysql_conn_mgr 进程ID).在每一次添加 pool 操作时start_child 一个emysql_conn_mgr 匿名进程,并与pool 的ID进行关联, 写入emysql_pool_mgr 进程维护的ets table 中.
add_pool(#pool{pool_id=PoolId,size=Size,user=User,password=Password,host=Host,port=Port,
database=Database,encoding=Encoding,start_cmds=StartCmds,
connect_timeout=ConnectTimeout,warnings=Warnings}=PoolSettings)->
config_ok(PoolSettings),
case emysql_pool_mgr:has_pool(PoolId) of
true ->
{error,pool_already_exists};
false ->
Pool = #pool{
pool_id = PoolId,
size = Size,
user = User,
password = Password,
host = Host,
port = Port,
database = Database,
encoding = Encoding,
start_cmds = StartCmds,
connect_timeout = ConnectTimeout,
warnings = Warnings
},
Pool2 = case emysql_conn:open_connections(Pool) of
{ok, Pool1} -> Pool1;
{error, Reason} -> throw(Reason)
end,
{ok, PoolServer} = emysql_pool_mgr:add_pool(PoolId, Pool2),
[gen_tcp:controlling_process(Conn#emysql_connection.socket, PoolServer)
|| Conn <- queue:to_list(Pool2#pool.available)],
ok
end.
start_child emysql_conn_mgr 匿名进程, 并将poolid 与 emysql_conn_mgr 进程ID关联的操作都在emysql_pool_mgr:add_pool/2函数中实现(L26).
emysql_pool_mgr module
emysql_pool_mgr module 是一个gen_server 进程, 其中维护了一个ets table, 用于存放{PoolID, EmysqlConnMgrProcessID} 信息. 因为每次execute SQL语句时, 都需要对pool 操作,也就是需要获取与之对应的EmysqlConnMgrProcessID, 如果将关联信息保存在emysql_pool_mgr 进程中, emysql_pool_mgr 进程同样会成为单点, 因此使用ets table 来分担emysql_pool_mgr 进程的压力负担.
emysql_pool_mgr module 提供了一下几个API:
1, has_pool/1
用于判断当前系统中是否存在该pool,输入参数为PoolID
2, add_pool/2
用以start_child emysql_conn_mgr 进程, 并写入{PoolID, EmysqlConnMgrProcessID}信息到ets table,输入参数为PoolID 和 Pool结构
3, remove_pool/1
删除Pool, 并stop emysql_conn_mgr 进程, 输入参数为PoolID
4, get_pool_server/1
根据PoolID 获取对应的EmysqlConnMgrProcessID, 输入参数为PoolID
5, pools/0
当前系统中所有的PoolID以及其对应的EmysqlConnMgrProcessID
6, conns_for_pool/1
根据PoolID 获取对应的pool 中的所有链接, 输入参数为PoolID
add_pool/2
add_pool/2 函数主要调用supervisor:start_child/2 函数在emysql_conn_pool_sup 监控树下添加emysql_conn_mgr gen_server 进程.
emysql_conn_pool_sup module 的代码片段:
start_link(Name, Module) ->
supervisor:start_link({local, Name}, ?MODULE, Module). init(Module) ->
{ok,
{ {simple_one_for_one, 10, 1},
[{undefined, {Module, start_link, []}, temporary,
brutal_kill, worker, [Module]}]} }.
相当常见的使用方式.
emyslq_conn_mgr module
optimize 之后,之前的emysql_conn_mgr module 的代码就必须做一些简单调整, 在调用start_link 函数 init初始化时, 就需要将pool 信息添加到进程 state 信息中.
init([Pool]) ->
erlang:process_flag(priority, high),
{ok, #state{pools = [Pool]}}.
此处提升了emysql_conn_mgr 进程的 priority (L2).
而对于其他函数的调用, 需要添加一个EmysqlConnMgrProcessID 参数,使其在gen_server:call/2 时, 将 "?MODULE" 改为 EmysqlConnMgrProcessID.
pool 使用
在调用execute 函数 执行某SQL 语句时, 代码同样需要做一些调整:
execute(PoolId, Query, Args, Timeout) when (is_list(Query) orelse is_binary(Query)) andalso is_list(Args) andalso (is_integer(Timeout) orelse Timeout == infinity) ->
PoolServer = emysql_pool_mgr:get_pool_server(PoolId),
Connection = emysql_conn_mgr:wait_for_connection(PoolServer, PoolId),
monitor_work(PoolServer, Connection, Timeout, [Connection, Query, Args]);
也就是在调用emysql_conn_mgr module 的函数之前,需要先根据PoolID 获取EmysqlConnMgrProcessID(L2), 然后将EmysqlConnMgrProcessID 作为emysql_conn_mgr module 函数的第一个参数进行调用(L3).
initialize_pools
在现今的Emysql 项目中, 有一个初始化pools 的功能. 也就是可以在app start 的时候, 自动加载config 文件中配置好的pool .现在的做法是在emysql_conn_mgr 进程 init 的时候, 调用相关函数, 将结果添加到 进程 state 中.
optimize 之后的结构, emysql_conn_mgr 只有在手动添加一个pool 的时候才会被start_link, 也就不能执行相关函数执行initialize_pools 的操作.
现在调整为在emysql_pool_mgr init 之后, emysql_pool_mgr 发送{initialize_pools} message 给self, 在handle_info callback 函数中进行处理:
handle_info({initialize_pools}, State) ->
%% if the emysql application values are not present in the config
%% file we will initialize and empty set of pools. Otherwise, the
%% values defined in the config are used to initialize the state.
InitializesPools =
[
{PoolId, #pool{
pool_id = PoolId,
size = proplists:get_value(size, Props, 1),
user = proplists:get_value(user, Props),
password = proplists:get_value(password, Props),
host = proplists:get_value(host, Props),
port = proplists:get_value(port, Props),
database = proplists:get_value(database, Props),
encoding = proplists:get_value(encoding, Props),
start_cmds = proplists:get_value(start_cmds, Props, [])
}} || {PoolId, Props} <- emysql_app:pools()
],
[begin
case emysql_conn:open_connections(Pool) of
{ok, Pool1} ->
emysql_pool_mgr:add_pool(PoolId, Pool1);
{error, Reason} ->
erlang:throw(Reason)
end
end || {PoolId, Pool} <- InitializesPools],
{noreply, State, ?HIBERNATE_TIMEOUT};
总不能optimize 之后, 直接remove 掉一些函数功能啊. :)
总结
整体上,对每一个pool 使用与之对应的一个emysql_conn_mgr 进程作为管理.能够尽可能的避免多pool 的管理工作给emysql_conn_mgr 进程带来的压力.
而新引入的emysql_pool_mgr gen_server 进程, 用来维护PoolID 和 EmysqlConnMgrProcessID 关联信息. 同时为了避免emysql_pool_mgr 的单点问题, 使用ets table 来分担emysql_pool_mgr gen_server 进程的负担.
optimize 的branch 为add_pool_mgr, 还未PR(等待小伙伴的测试反馈,欢迎 code review).
Erlang pool management -- Emysql pool optimize的更多相关文章
- Erlang pool management -- Emysql pool
从这篇开始,这一系列主要分析在开源社区中,Erlang 相关pool 的管理和使用. 在开源社区,Emysql 是Erlang 较为受欢迎的一个MySQL 驱动. Emysql 对pool 的管理和使 ...
- Erlang pool management -- RabbitMQ worker_pool
在RabbitMQ中,pool 是以worker_pool 的形式存在的, 其主要用途之一是对Mnesia transaction 的操作. 而在RabbitMQ 中, pool 中的worker 数 ...
- Erlang pool management -- RabbitMQ worker_pool 2
上一篇已经分析了rpool 的三个module , 以及简单的物理关系. 这次主要分析用户进程和 worker_pool 进程还有worker_pool_worker 进程之间的调用关系. 在开始之前 ...
- 使用boost实现线程池thread pool | boost thread pool example
本文首发于个人博客https://kezunlin.me/post/f241bd30/,欢迎阅读! boost thread pool example Guide boost thread pool ...
- The Run-Time Constant Pool The Constant Pool
http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.4 http://docs.oracle.com/javase ...
- [转载][概念]Storage Pool, Private RAID Group, Private LUN
Storage Pool的起源 ========================== Some time ago, EMC introduced the concept of Virtual Prov ...
- Oracle-buffer cache、shared pool
http://blog.csdn.net/panfelix/article/details/38347059 buffer pool 和shared pool 详解 http://blog.csd ...
- Why should I avoid blocking the Event Loop and the Worker Pool?
Don't Block the Event Loop (or the Worker Pool) | Node.js https://nodejs.org/en/docs/guides/dont-blo ...
- [转载】——故障排除:Shared Pool优化和Library Cache Latch冲突优化 (文档 ID 1523934.1)
原文链接:https://support.oracle.com/epmos/faces/DocumentDisplay?_adf.ctrlstate=23w4l35u5_4&id=152393 ...
随机推荐
- 基于SSM的单点登陆02
pom文件 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http: ...
- 大话设计模式之PHP篇 - 适配器模式
定义将一个类的接口转换成客户希望的另外一个接口,使用原本不兼容的而不能在一起工作的那些类可以在一起工作. 角色Target适配目标,该角色定义把其他类转换为何种接口,也就是我们的期望接口.Adapte ...
- 查看git安装目录
有时候需要知道Git在电脑中的安装位置,这里简单介绍下: Mac平台:在命令行中输入which git, 就会显示git的安装位置了; Windows平台:打开cmd,输入where git就会显示g ...
- Elasticsearch6.0简介入门介绍
Elasticsearch简单介绍 Elasticsearch (ES)是一个基于Lucene构建的开源.分布式.RESTful 接口全文搜索引擎.Elasticsearch 还是一个分布式文档数据库 ...
- java深入探究12-框架之Spring
1.引入Spring 我们在搭建框架时常常会解决问题:对象创建,对象之间依赖关系如何处理,Spring就是来解决这类问题的:控制反转依赖注入 2.环境搭建 1)下载源码:其中3.0以下版本源码中有Sp ...
- spring security在spring mvc的action中获取登录人信息
@RequestMapping("/index") public ModelAndView login( @RequestParam(value = "error&quo ...
- waitpid使用的一点问题
使用waipid的时候遇到了一个奇怪的问题,将情况简化后描述一下. 有关waitpid的基本介绍参见这里一下:http://www.cnblogs.com/mickole/p/3187770.html ...
- JavaScript文件下载 兼容所有浏览器 不可跨域
前端文件下载 兼容所有浏览器 download.js文件下载,几乎支持所有类型下载,详细内容参考官网 http://danml.com/download.html 引入文件 <script sr ...
- 使用struts2的iterator标签出现的错误
错误如下所示: 代码如下所示: <body> <s:debug></s:debug> 获取list的值第一种方式 <!-- 3 获取值栈list集合数据 -- ...
- css页面缩放
如果原来的宽度是1200, 缩放之后宽度可能就变成了1560, 然后你本来的图片1200可能就开始显示不全了. 如果你的图片按100%显示的话,这个时候又正常了.