The nginx + Lua implementation calls the. So file

Time:2021-10-21

Recently, we are working with the intelligent hardware department to provide an indoor positioning service. The service calculates the specific shopping mall according to the beacon device list transmitted from the mobile phone, takes the shopping mall ID and beacon device list as parameters, calls the calculation method in the. So file, obtains the location data (coordinates: X, y, z), and returns it to the mobile phone.

Because the service has high requirements for QPS and is pure query operation, it is decided to use the architecture of nginx + Lua + redis (this architecture has become the mainstream and mature within the company). Next, I will introduce how Lua calls the. So file.

Lua calls the. So file in two ways:

1. Lua directly calls the dynamic link library. For details, please refer to technology sharing.
2. Write a wrapper using C.

Since the first method requires the introduction of a third-party toolkit and is not efficient, we adopt the second method. The specific implementation process is as follows:

1. Write business code, including the wrapper of business methods.

?
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
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;    //Push the mylib table onto the stack, so you need to return 1
}

There is a naming rule for the function name of the wapper function. The prefix is luaopen, followed by the required string in Lua. Otherwise, an exception similar to the following will be reported:

?
1
2
3
4
5
6
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...

In addition, if it is a. Cpp file, it is in luaopen_ Mylib must add extern “C”, otherwise the exported function will be renamed. Remember! For external “C”, please refer to the usage analysis of external “C”.

2. Specify the folder where the so package is located in the nginx configuration file.

lua_package_cpath '/var/wdd/wrs/webroot/intelligent_lua/?.so;;';

Note that there are many configuration methods here, as long as Lua can find the so file. For example, you can import it through Lua code package.cpath (see the code snippet below) or directly copy the so file to the directory specified by Lua environment variable.
package.cpath = '/usr/local/lib/lua/5.1/?.so;'        — Search so module

If cpath is not specified or so file cannot be found in cpath, the system will report the following exception:

?
1
2
3
4
5
6
7
8
9
10
11
12
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. Introduce so package into Lua code and execute the call.

?
1
2
3
4
5
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)

The above is the whole process of lua calling the. So package.

During the actual pressure measurement, several problems were found:

1. So packages need to be compiled in the running environment. So packages compiled in different environments are not necessarily universal. For example, the so package I compiled on the MAC cannot be used if it is directly copied to the production environment (CentOS system). It needs to be recompiled in the production environment.

2. The single process of compiling so package can execute normally, but the multi process access is abnormal. The error message is as follows (this problem has not been solved yet).

2017/05/03 16:52:41 [notice] 14355#0: signal 17 (SIGCHLD) received
2017/05/03 16:52:41 [alert] 14355#0: worker process 14361 exited on signal 11
2017/05/03 16:52:41 [notice] 14355#0: start worker process 14427
2017/05/03 16:52:41 [notice] 14355#0: signal 29 (SIGIO) received
2017/05/03 16:52:41 [notice] 14427#0: sched_setaffinity(0x00000008)

On May 4, 2017, the reason was finally found:

Cause of the problem: when accessing the. So file, the process needs to call an initialization method in. So. This method is responsible for initial memory and other related operations. Each process needs to be called (initialized) separately, but all my processes only call (initialize) once, resulting in exceptions when some uninitialized processes execute code.

Solution: once the cause is found, the problem can be solved. It’s good to start each process once.