异想天开

What's the true meaning of light, Could you tell me why

nginx+lua+php学习笔记

日期:2015-01-23 21:29:20
  
最后更新日期:2015-10-25 16:55:33
【工作】
年底了,接到一个为安卓开发抽奖需求服务端开发的需求。完成这个需求总共花了3周时间,包括周末的时间,实际的工作日2周多。这个项目感觉获益良多。一则是首次把nginx+lua+redis+mysql放到项目中实践,二是编辑页面涉及到前端的知识,这次也首次项目中运用php,并且使用了thinkphp框架,临时学的。在此总结项目过程中,查找资料学来的通用知识点。
一.前端 thinkphp的路由url:
http://host/index.php/appname/Action/ActionFunc/

1.提交可变的二维数组表单
安卓涉及交互,需要知道第一步做什么,第二步做什么,这个步骤数量不是固定的。需要向php后端提交如下数据的步骤信息:
[code lang="cpp"]
array(
0 => array(
"step" => 1
"info" => "第一步"
),
1 => array(
"step" => 2
"info" => "第二步"
)
...
)
[/code]
实现的方法就是:web页面使用jquery动态增加table的行数,步骤信息放input控件里面,表单form提交时,在onsubmit函数里修改每一行的name值。动态增加行数时,使用固定的名字,例如process[INDEX]['step'],process[INDEX]['info'],然后遍历input控件,为对应的input空间赋上真实的index值,比如record[0]['step'],record[0]['info']表示第一条记录。动态增加table的js函数:
[code lang="cpp"]
<script lange="javascript">
function addrow() {
//jquery来操作dom对象
$("#process").append('<td><input type="text" name="process[INDEX]['step']"/></td><td><input type="text" name="process[INDEX]['info']"/></td><td><input type="button" onclick="delrow(this)"/></td>');
}
function delrow(inputobj){
var p1 = inputobj.Parent().Parent()
var p2 = p1.Parent()
p2.remove(p1)
}
</script>
<table >
<thead>
<tr>
<td>#</td><td>step</td><td>info</td><td><input type="button" onclick="addrow()"/></td>
</tr>
</thead>
<tbody id="process">
</tbody>
</table>
[/code]
而php后端只需要
[code lang="cpp"]
$process = $_POST["process"]
[/code]
就可以访问到该二维数组。

2.js对象
代码例子如下:
[code lang="cpp"]
function Map(){
this.container = {};
this.size=0;
var len;
}
Map.prototype.put = function(key, value){
this.container[key] = value;
this.size++;
};
Map.prototype.containsKey=function(key){
try{
for(var p in this.container)
{
if(p==key){
return true;
}
}
return false;
}catch(e){
alert(e);
return e;
}
};
Map.prototype.get = function(key){
try{
return this.container[key];
}catch(e){
alert(e);
return e;
}
};
function beforesubmit(){
var index = new Map();
index.put("process[INDEX][step]",0);
index.put("process[INDEX][info]",0);
index.put("award[INDEX][rank]",0);
index.put("award[INDEX][award]",0);
index.put("award[INDEX][num]",0);
var inputList = document.getElementsByTagName("input");
for(i=0;i<inputList.length;i++){
var tmpname=inputList[i].name ;
if( (inputList[i].type=="text") && (index.containsKey(tmpname) )){
var value = index.get(tmpname);
inputList[i].name = tmpname.replace("INDEX",value.toString());
index.put(tmpname,value+1 );
}
}
//检查不能为空
var id = $("#id").val();
if( ""==id || typeof(id) == 'undefined' ){
alert("id不能为空");
return false;
}
return true;
}
[/code]
js代码是不用以分号结束,不过为了压缩js代码,最好都补上分号。size变量表示Map对象里面的属性,而len变量则是Map内部的局部变量。这里的作用域size在外部函数可见,例如put函数。而len则只能由定义在内部的函数访问到。js对象可以利用prototype,所谓原型方法来扩展类和对象。当时是想在js里面找一个类似关联数组的数据结构,其实Object对象就有这种属性。然后在form的onsubmit=“return beforesubmit”这样调用验证。

二.nginx+lua
1.jmp操作
有时候在处理连接的时候,出错处理时,希望提供类似c语言的jmp,可以方便jmp到出错处理点,这样来让代码简洁。nginx嵌入的lua模块,jmp时,需要注意是:
[code lang="cpp"]
jmp EXIT_0
//注意jmp到EXIT_0之间,不能定义局部变量,即local var_xxx之类的
EXIT_0:
//语句
[/code]

2.nginx的ini.lua文件
希望将mysql的user,passwd,db这些相关信息,放到一个ini.lua文件里面,在这个文件定义为全局变量,所有location可见,那么可以:
[code lang="cpp"]
#nginx.conf里面这样定义:
init_by_lua_file "/usr/local/sandai/lottery/ini.lua";
#ini.lua这样定义:
g_mysql_host='127.0.0.1'
g_mysql_port=3306
g_mysql_db='foo'
g_mysql_user='foo'
g_mysql_passwd='bar'
[/code]
这样其它地方就可以访问到g_mysql_host这些变量。

3.lua模块
将源代码组织成几个模块,可以让代码看起来简洁。
自定义的lua模块
[code lang="cpp"]
#common.lua里面
module("common",package.seeall)
function fail( info )
ngx.log(ngx.ERR, "common package fail: "..info )
return
end
[/code]
外部使用:
[code lang="cpp"]
local common = require("common")
common.fail("just test")
[/code]
怎么用c模块来扩展,openresty里面有个cjson的c封装的.so模块。稍微查看了cjson代码,里面利用了lua的c封装的api,处理任务。下次着重弄懂了,openresty怎么嵌入lua模块的机制。

4.lua语言
a.lua中提供如下几种基础类型:
[code lang="cpp"]
1.数值(number):内部以double表示.
2.字符串(string):总是以零结尾,但可以包含任意字符(包括零),因此并不等价于C字符串,而是其超集.
3.布尔(boolean):只有"true"和"false"两个值.
4.函数(function):Lua的关键概念之一.不简单等同于C的函数或函数指针.
5.表(table):异构的Hash表.Lua的关键概念之一.
6.userdata:用户(非脚本用户)定义的C数据结构.脚本用户只能使用它,不能定义.
7.线程(thread):Lua协作线程(coroutine),与一般操作系统的抢占式线程不一样.
8.nil:代表什么也没有,可以与C的NULL类比,但它不是空指针
[/code]
b.其它lua的概念元表(metatable)和原方法(metamethod),主要也是用于扩展语言。假设你想为table增加个函数,比如tostring函数,那么可以直接:
[code lang="cpp"]
function table.tostring(tbl)
local result={}
for k,v in pairs(tbl) do
if type(v)=='number' then
table.insert(result,"'"..tostring(k).."':"..tostring(v))
end
if type(v)=='string' then
table.insert(result,"'"..tostring(k).."':'"..v.."'")
end
if type(v)=='table' then
table.insert(result,"'"..tostring(k).."':"..table.tostring(v))
end
end
return "{"..table.concat(result,",").."}"
end
print(table.tostring(a))
[/code]
c.ipairs和pairs的区别
ipairs遍历,一般用于key为number类型,否则遇到第一个不是number类型的key停止遍历,pairs则返回所有的k,v键值对

三.mysql数据库 还是遭遇了字符乱码的问题,由于使用php后台向mysql插入数据,接口使用nginx+lua拉取到的mysql的数据和终端mysql命令拉取到的都是乱码。而mysql终端插入时,查询的结果没有乱码。当时也是拿这个问题网络上搜索。记录一下:
首先贴下当时使用的默认设置:
[code lang="cpp"]
mysql> show variables like "%char%";
+--------------------------+--------------------------------------------------------------+
| Variable_name | Value |
+--------------------------+--------------------------------------------------------------+
| character_set_client | latin1 |
| character_set_connection | latin1 |
| character_set_database | latin1 |
| character_set_filesystem | binary |
| character_set_results | latin1 |
| character_set_server | latin1 |
| character_set_system | utf8 |
| character_sets_dir | /usr/local/mysql-5.1.66-linux-x86_64-glibc23/share/charsets/ |
+--------------------------+--------------------------------------------------------------+
8 rows in set (0.02 sec)
mysql> show variables like '%server%' ;
+----------------------+-------------------+
| Variable_name | Value |
+----------------------+-------------------+
| character_set_server | latin1 |
| collation_server | latin1_swedish_ci |
| server_id | 0 |
+----------------------+-------------------+
3 rows in set (0.00 sec)
[/code]
a.mysql server接收请求后,会将character_set_client转化为character_set_connection
b.进行内部操作前,会进行内部操作字符集转换,转换方法:
1.使用字段的默认字符集;
2.若上诉值不存在,则使用表的默认字符集(非SQL标准,扩展);
3.若上诉值不存在,则使用库的默认字符集;
4.若上诉值不存在,则使用character_set_server定义的字符集。
c.内部操作后,会将结果用character_set_results字符集返回。
我的问题:
当时建表的时候,指定了编码为utf8,但数据库建库的时候,没有指定故使用默认值latin1。php页面提交的内容,在nginx+lua查询mysql是乱码。
解决及分析:
与mysql server连接过程中,重要的是传输层。需要协商的也是传输层。即client用什么编码发请求,服务器答复用什么编码。解决的办法就是无论是php还是nginx+lua连接mysql,前面都用SET NAMES utf8指定了编码,该操作也即指定了character_set_client,character_set_connection,character_set_results都为utf8。而内部操作的字符集不那么重要,无论是用gbk,还是utf8。不过,还是使用utf8好。另外的问题就是,当在/etc/my.conf配置文件里面,设置character-set-server = utf8 时,所有客户端没有SET NAMES ut8也没有乱码。是不是与mysql server连接的一个serssion会继承服务器的字符编码。