关于mysql的数据库驱动,网上有开源的git代码,https://github.com/dizzyd/erlang-mysql-driver
erlang先跟mysql建立tcp连接登录,然后在上面执行SQL命令。首先看mysql.erl模块的start和start_link函数,最终进入start1中启动gen_server,所以mysql驱动的分发中心是一个gen_server。在init初始化中,启动了一个连接进程mysql_conn,并加入gen_server的连接池里。进入mysql_conn:start,发现它首先创建了mysql_recv进程接收mysql返回的信息,然后从mysql_auth进行登录验证。关于mysql模块与mysql_conn的关系,我们可以从mysql:fetch函数看起。从fetch追踪,进入到get_next_conn,然后从连接池中取出空闲中的mysql_conn进程,如果所有mysql_conn进程都在繁忙,则重置列表,该次请求会进行对应的mysql_conn进程中队列中等待。但是我们注意fetch函数使用了gen_server:call的同步操作,这样线程池的作用就发挥不出来了?当然不会。
我们再进入fetch_queries函数(fetch最终会执行到此处),看到执行完mysql_conn:fetch后,进程直接进入{noreply, State1}挂起状态,而我们调用该次fecth操作的进程依然在等着,而mysql_conn进程跟mysql通信结束后,会把结果通过gen_server:reply方法返回给调用者。这样最终阻塞的地方就是我们的mysql_conn进程。而mysql进程作为分发进程,并不会被阻塞。
因此我们使用mysql驱动时,可以开一个mysql的分发进程,多个mysql_conn的通信进程。
mysql:start(...).
lists:foreach(fun(_) ->
mysql:connect(...)
end, lists:seq(10)).
上面一段代码中,就启动了11个mysql_conn通信进程(mysql:start启动时已经有一个)。