.soファイルを呼び出すためのNginx+lua
本サービスでは、携帯電話から渡されたビーコンデバイスリストをもとに、特定のショッピングモールをあるアルゴリズムに従って算出し、モールIDとビーコンデバイスリストをパラメータとして、.soファイル内の計算メソッドを呼び出して位置データ(座標:x、y、z)を導き出し、携帯電話に返送するものです。
このサービスはQPSの要求が高く、純粋なクエリ操作であるため、(社内で主流となり成熟している)Nginx+lua+Redisアーキテクチャを採用することにしました。luaが.soファイルを呼び出す方法について説明します。
luaが.soファイルを呼び出す方法は、大きく分けて2つあります。
1. Luaは、Tech Shareに記載されているように、ダイナミックリンクライブラリを直接呼び出します。
2. C 言語で Wrapper を作成します。
1番目のアプローチではサードパーティのツールキットを導入する必要があり、効率も悪いことから、2番目のアプローチで実装することにします。実装は以下の通りである。
1. ビジネスメソッドのラッパーを含むビジネスコードを記述する。
static int lua_Locate( lua_State* L )
{
long handle = lua_tonumber( L, 1 );
const char* beacon_rssi_json = lua_tostring( L, 2 );
vector<RSSI_INFO> rssi_info_vec;
FingerprintLocationServer* p = (FingerprintLocationServer*)handle;
ConvertJson2CppRSSI( beacon_rssi_json, rssi_info_vec );
double x;
double y;
float floor;
p->UpdateBeaconSignalGetResult( rssi_info_vec, x, y, floor );
lua_pushnumber(L,x);
lua_pushnumber(L,y);
lua_pushnumber(L,floor);
return 3;
}
static const struct luaL_Reg myLib[] =
{
{"lua_Locate", lua_Locate}
{NULL, NULL} // the last pair in the array must be {NULL, NULL}, to indicate the end
};
int luaopen_mLualib(lua_State *L)
{
luaL_register(L, "FPCalc", myLib);
return 1; // pressed the myLib table onto the stack, so it needs to return 1
}
wapper関数の名前には命名規則があり、luaopenというプレフィックスを付け、その後にluaで必要な文字列を付けると、次のような例外が報告されます。
lua entry thread aborted: runtime error: error loading module 'mLualib' from file '/var/wdd/wrs/webroot/intelligent_lua/mLualib.so':
/var/wdd/wrs/webroot/intelligent_lua/mLualib.so: undefined symbol: _Z13lua_tolstringP9lua_StateiPm
stack traceback:
coroutine 0:
[C]: in function 'require'
/var/wdd/wrs/webroot/intelligent_lua/location.lua:18: in function...
また、.cppファイルであれば、luaopen_mylibにextern "C"を必ず追加してください。そうしないと、エクスポートした関数がリネームされます。覚えておいてくださいね。extern "C"については、extern "C" Usage Explanationを参照してください。
2. Nginxの設定ファイルにsoパッケージがあるフォルダを指定します。
lua_package_cpath '/var/wdd/wrs/webroot/intelligent_lua/? .so;;';
なお、luaがsoファイルを見つけることができれば、例えば、luaのコードpackage.cpathを通じて導入したり(以下のスニペットを参照)、soファイルをlua環境変数で指定したディレクトリに直接コピーするなど、様々な方法で設定することが可能です。
package.cpath = '/usr/local/lib/lua/5.1/? .so;'
-- So モジュールの検索
cpathが指定されていない場合、またはcpathにsoファイルが見つからない場合は、以下のような例外が報告されます。
no file '. /mLualib.lua'
no file '/usr/local/openresty/luajit/share/luajit-2.1.0-beta1/mLualib.lua'
no file '/usr/local/share/lua/5.1/mLualib.lua'
no file '/usr/local/share/lua/5.1/mLualib/init.lua'
no file '/usr/local/openresty/luajit/share/lua/5.1/mLualib.lua'
no file '/usr/local/openresty/luajit/share/lua/5.1/mLualib/init.lua'
no file '/usr/local/openresty/lualib/mLualib.so'
no file '. /mLualib.so'
no file '/usr/local/lib/lua/5.1/mLualib.so'
no file '/usr/local/openresty/luajit/lib/lua/5.1/mLualib.so'
no file '/usr/local/lib/lua/5.1/loadall.so'
no file '/var/wdd/wrs/webroot/intelligent_lua/mLualib.so'
3. luaのコードでは、soパッケージを導入し、呼び出しを実行しています。
local FPCalc = require "mLualib"
local x, y, floor = FPCalc.lua_Locate(c_addr, umm_json)
ngx.log(ngx.ERR, "lua_Locate:end:return result:", "x=" . x, " y=" . y, " floor=" . floor)
これが、luaから.soパッケージを呼び出すまでの全体の流れです。
実際の圧力テストでは、さらにいくつかの問題が見つかりました。
1. soパッケージは実行環境でコンパイルする必要があり、異なる環境でコンパイルされたsoパッケージは必ずしも一般的ではありません。例えば、macでコンパイルしたsoパッケージをそのまま本番環境(centos)にコピーすると動作せず、本番環境で再コンパイルしないと動作しないことがありました。
2. コンパイルしたsoパッケージは、単一プロセスでは正常に実行されるが、複数プロセスでアクセスすると例外が発生し、以下のようなエラーメッセージが表示される(この問題はまだ解決されていない)。
2017/05/03 16:52:41 [notice] 14355#0: 信号17(SIGCHLD)を受信しました。
2017/05/03 16:52:41 [alert] 14355#0: ワーカープロセス14361がシグナル11で終了しました。
2017/05/03 16:52:41 [通知] 14355#0: ワーカープロセス14427を開始します。
2017/05/03 16:52:41 [notice] 14355#0: 信号29(SIGIO)を受信しました。
2017/05/03 16:52:41 [notice] 14427#0: sched_setaffinity(0x00000008)です。
2017-05-04 やっと原因がわかりました。
原因:あるプロセスが.soファイルにアクセスする際、.so内の初期化メソッドを呼び出す必要がある。このメソッドはメモリの初期化などを行うもので、各プロセスは個別に1回呼び出す(初期化する)必要がありますが、私のプロセスはすべて1回しか呼び出さない(初期化しない)ため、初期化しないプロセスがあると例外的にコードを実行することになります。
解決方法 原因がわかったら、各プロセスを一度初期化することで問題は解決します。
関連
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン