分类 编程分享 下的文章

【nginx】负载均衡和proxy的配置

简介 使用upstream模块实现nginx负载均衡 使用nginx_upstream_check_module模块实现后端服务器的健康检查 使用nginx-sticky-module扩展模块实现Cookie会话黏贴(session-sticky效果) 使用proxy模块实现静态文件缓存 使用ngx_cache_purge实现更强大的缓存清除功能

1. nginx-sticky-module


  这个模块的作用是通过cookie的方式将来自同一个客户端(浏览器)的请求发送到同一个后端服务器上处理,这样一定程度上可以解决多个backend servers的session同步的问题 —— 因为不再需要同步,而RR轮询模式必须要运维人员自己考虑session同步的实现。 另外内置的 ip_hash 也可以实现根据客户端IP来分发请求,但它很容易造成负载不均衡的情况,而如果nginx前面有CDN网络或者来自同一局域网的访问,它接收的客户端IP是一样的,容易造成负载不均衡现象。淘宝Tengine的 ngx_http_upstream_session_sticky_module 也是类似的功能。nginx-sticky-module的cookie过期时间,默认浏览器关闭就过期,也就是会话方式。 这个模块并不合适不支持 Cookie 或手动禁用了cookie的浏览器,此时默认sticky就会切换成RR。它不能与ip_hash同时使用。 原理其实很简单,当一个客户端请求过来时,会 set-cookie 一个 cookie 来标注本次请求的服务器(第一次是随机).然后下次请求都会包含这个 cookie .然后就能很好的区分原本请求的服务器了.我测试过,当默认请求的后端服务器死掉后,会还是会自动切换的.另外,这个模块并不合适对不支持 Cookie 的浏览器

Sticky module is based on a "best effort" algorithm. Its aim is not to handle security somehow. It's been made to ensure that normal users are always redirected to the same backend server: that's all!

sticky安装




 

sticky配置

1
2
3
./configure ... --add-module=/absolute/path/to/nginx-sticky-module-ng
make
make install
        



  • name: the name of the cookies used to track the persistant upstream srv; default: route
  • domain: the domain in which the cookie will be valid default: nothing. Let the browser handle this.
  • path: the path in which the cookie will be valid default: /
  • expires: the validity duration of the cookie default: nothing. It's a session cookie. restriction: must be a duration greater than one second
  • hash: the hash mechanism to encode upstream server. It cant' be used with hmac. default: md5hmac: the HMAC hash mechanism to encode upstream server It's like the hash mechanism but it uses hmac_key to secure the hashing. It can't be used with hash. md5|sha1: well known hash default: none. see hash.
    • md5|sha1: well known hash
    • index: it's not hashed, an in-memory index is used instead, it's quicker and the overhead is shorter Warning: the matching against upstream servers list is inconsistent. So, at reload, if upstreams servers has changed, index values are not guaranted to correspond to the same server as before! USE IT WITH CAUTION and only if you need to!
  •  
  • hmac_key: the key to use with hmac. It's mandatory when hmac is set default: nothing.
  • no_fallback: when this flag is set, nginx will return a 502 (Bad Gateway or Proxy Error) if a request comes with a cookie and the corresponding backend is unavailable.
  • secure enable secure cookies; transferred only via https
  • httponly enable cookies not to be leaked via js

官方文档也有个 sticky 指令,作用几乎是一样的,但这是nginx商业版本里才有的特性。包括后面的check指令,在nginx的商业版本里也有对应的health_check(配在 location )实现几乎一样的监控检查功能。

2、nginx负载均衡

  • 轮询(默认) : 每个请求按时间顺序逐一分配到不同的后端服务器,如果后端某台服务器宕机,故障系统被自动剔除,使用户访问不受影响。Weight 指定轮询权值,Weight值越大,分配到的访问机率越高,主要用于后端每个服务器性能不均的情况下。
  • ip_hash : 每个请求按访问IP的hash结果分配,这样来自同一个IP的访客固定访问一个后端服务器,有效解决了动态网页存在的session共享问题。当然如果这个节点不可用了,会发到下个节点,而此时没有session同步的话就注销掉了。
  • least_conn : 请求被发送到当前活跃连接最少的realserver上。会考虑weight的值。
  • url_hash : 此方法按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,可以进一步提高后端缓存服务器的效率。Nginx本身是不支持url_hash的,如果需要使用这种调度算法,必须安装nginx 的hash软件包 nginx_upstream_hash 。
  • fair : 这是比上面两个更加智能的负载均衡算法。此种算法可以依据页面大小和加载时间长短智能地进行负载均衡,也就是根据后端服务器的响应时间来分配请求,响应时间短的优先分配。Nginx本身是不支持fair的,如果需要使用这种调度算法,必须下载Nginx的 upstream_fair 模块。

ip_hash的配置

1
2
3
4
5
6
7
8
9
upstream {
  sticky;
  server 127.0.0.1:9000;
  server 127.0.0.1:9001;
  server 127.0.0.1:9002;
}
 
  sticky [name=route] [domain=.foo.bar] [path=/] [expires=1h]
       [hash=index|md5|sha1] [no_fallback] [secure] [httponly];
        



  • weight轮询权值也是可以用在ip_hash的,默认值为1
  • max_fails允许请求失败的次数,默认为1。当超过最大次数时,返回proxy_next_upstream 模块定义的错误
  • fail_timeout有两层含义,一是在 30s 时间内最多容许 2 次失败;二是在经历了 2 次失败以后,30s时间内不分配请求到这台服务器。
  • backup备份机器。当其他所有的非backup机器出现故障的时候,才会请求backup机器
  • max_conns限制同时连接到某台后端服务器的连接数,默认为0即无限制。因为queue指令是commercial,所以还是保持默认吧。
  • proxy_next_upstream这个指令属于 http_proxy 模块的,指定后端返回什么样的异常响应时,使用另一个realserver

项目地址: https://bitbucket.org/nginx-goodies/nginx-sticky-module-ng https://code.google.com/p/nginx-sticky-module/

3、 upstream_check

nginx自带是没有针对负载均衡后端节点的健康检查的,但是可以通过默认自带的 ngx_http_proxy_module 模块和 ngx_http_upstream_module 模块中的相关指令来完成当后端节点出现故障时,自动切换到下一个节点来提供访问。 nginx_upstream_check_module 是专门提供负载均衡器内节点的健康检查的外部模块,由淘宝工程师开发,通过它可以用来检测后端 realserver 的健康状态。如果后端 realserver 不可用,则后面的请求就不会转发到该节点上,并持续检查几点的状态。在淘宝自己的 tengine 上是自带了该模块。

1
2
3
4
5
6
7
8
9
10
11
12
upstream backend {
    ip_hash;
    server 192.168.1.225:8080 weight 2;
    server 192.168.1.226:8080 weight=1 max_fails=2 fail_timeout=30s ;
    server 192.168.1.227:8080 backup;
}
server {
    location / {
        proxy_pass http://backend;
        proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
    }
}
        




 
nginx_upstream_check_module 对name这个负载均衡条目中的所有节点,每个5秒检测一次,请求2次正常则标记 realserver状态为up,如果检测 3 次都失败,则标记 realserver的状态为down,超时时间为1秒。check指令只能出现在upstream中:
  • interval : 向后端发送的健康检查包的间隔。
  • fall : 如果连续失败次数达到fall_count,服务器就被认为是down。
  • rise : 如果连续成功次数达到rise_count,服务器就被认为是up。
  • timeout : 后端健康请求的超时时间。
  • default_down : 设定初始时服务器的状态,也就是一开始服务器认为是不可用,要等健康检查包达到一定成功次数以后才会被认为是健康的。
  • type:健康检查包的类型,现在支持以下多种类型
    • tcp:简单的tcp连接,如果连接成功,就说明后端正常。
    • http:发送HTTP请求,通过后端的回复包的状态来判断后端是否存活。
    • ajp:向后端发送AJP协议的Cping包,通过接收Cpong包来判断后端是否存活。
    • ssl_hello:发送一个初始的SSL hello包并接受服务器的SSL hello包。
    • mysql: 向mysql服务器连接,通过接收服务器的greeting包来判断后端是否存活。
    • fastcgi:发送一个fastcgi请求,通过接受解析fastcgi响应来判断后端是否存活
  • port: 指定后端服务器的检查端口。你可以指定不同于真实服务的后端服务器的端口,比如后端提供的是443端口的应用,你可以去检查80端口的状态来判断后端健康状况。默认是0,表示跟后端server提供真实服务的端口一样。该选项出现于Tengine-1.4.0。
如果 type 为 http ,你还可以使用check_http_send来配置http监控检查包发送的请求内容,为了减少传输数据量,推荐采用 HEAD 方法。当采用长连接进行健康检查时,需在该指令中添加keep-alive请求头,如: HEAD / HTTP/1.1\r\nConnection: keep-alive\r\n\r\n 。当采用 GET 方法的情况下,请求uri的size不宜过大,确保可以在1个interval内传输完成,否则会被健康检查模块视为后端服务器或网络异常。 check_http_expect_alive 指定HTTP回复的成功状态,默认认为 2XX 和 3XX 的状态是健康的。 项目地址:https://github.com/yaoweibin/nginx_upstream_check_module 。

4、 proxy缓存的使用

缓存配置如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
upstream backend {
    sticky;
    server 192.168.1.225:8080 weight=2;
    server 192.168.1.226:8081 weight=1 max_fails=2 fail_timeout=30s ;
    server 192.168.1.227:8080 weight=1 max_fails=2 fail_timeout=30s ;
    server 192.168.1.227:8081;
     
    check interval=5000 rise=2 fall=3 timeout=1000 type=http;
    check_http_send "HEAD / HTTP/1.0\r\n\r\n";
    check_http_expect_alive http_2xx http_3xx;
}
server {
    location / {
        proxy_pass http://backend;
    }
    location /status {
        check_status;
        access_log off;
        allow 192.168.1.0/24;
        deny all;
    }
}
        



清除缓存

上述配置的proxy_cache_purge指令用于方便的清除缓存,需要ngx_cache_purge 模块

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
http {
    proxy_temp_path /usr/local/nginx-1.6/proxy_temp;
    proxy_cache_path /usr/local/nginx-1.6/proxy_cache levels=1:2 keys_zone=cache_one:100m inactive=2d max_size=2g;
     
    server {
 
 
        location ~ .*\.(gif|jpg|png|html|css|js|ico|swf|pdf)(.*) {
            proxy_pass http://backend;
            proxy_redirect off;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
             
            proxy_cache cache_one;
            add_header Nginx-Cache $upstream_cache_status;
            proxy_cache_valid 200 304 301 302 8h;
            proxy_cache_valid 404 1m;
            proxy_cache_valid any 2d;
            proxy_cache_key $host$uri$is_args$args;
            expires 30d;
        }
 
        location ~ /purge(/.*) {
            allow 127.0.0.1;
            allow 192.168.1.0/24;
            deny all;
            proxy_cache_purge cache_one $host$1$is_args$args;
            error_page 405 =200 /purge$1;
        }
    }
}
        




项目地址:https://github.com/FRiCKLE/ngx_cache_purge/ 。

IIS事件ID5009错误 应用程序池自动停止解决办法

现象: 为应用程序池XXXX提供服务的进程意外终止。进程 ID 是1920。进程退出代码是0xff。 RDP 协议组件 X.224 在协议流中发现一个错误并且中断了客户端连接。 策略禁用了其他支持设备的重定向。 应用程序池XXXXX将被自动禁用,原因是为此应用程序池提供服务的进程中出
现象:

20151209021646
为应用程序池“XXXX”提供服务的进程意外终止。进程 ID 是“1920”。进程退出代码是“0xff”。
RDP 协议组件 X.224 在协议流中发现一个错误并且中断了客户端连接。
策略禁用了其他支持设备的重定向。
应用程序池“XXXXX”将被自动禁用,原因是为此应用程序池提供服务的进程中出现一系列错误。
为应用程序池“xxxx”提供服务的进程在与 Windows Process Activation Service 通信时出现严重错误。该进程 ID 为“2232”。数据字段包含错误号。

错误应用程序名称: w3wp.exe,版本: 7.5.7600.16385,时间戳: 0x4a5bd0eb
错误模块名称: ntdll.dll,版本: 6.1.7600.16385,时间戳: 0x4a5be02b
异常代码: 0xc0000374
错误偏移量: 0x00000000000c6cd2
错误进程 ID: 0x780
错误应用程序启动时间: 0x01cdfe245a46a28e
错误应用程序路径: c:\windows\system32\inetsrv\w3wp.exe
错误模块路径: C:\Windows\SYSTEM32\ntdll.dll
报告 ID: a1a059ae-6a17-11e2-b70b-81055d3a5c70

解决办法:

点击"开始"-"控制面板"-"管理工具"-"组件服务"-"计算机"-"我的电脑"-"DCOM"选项,
选择其下的"IIS ADMIN SERVICE",右健选择"属性",找到"安全",在"启动和激活权限"中编辑"自定义",添加帐号"NETWORK SERVICE ", 给该帐号赋予"本地启动"和"本地激活"的权限,重新启动IIS即可("开始"-"运行"-"CMD",点确定,然后运行IISRESET)。

。(系统中没有"IIS ADMIN SERVICES"服务和DCOM选项的解决方法:这一般是IIS系统角色未完全安装导致的,在服务器管理器中将IIS角色服务里的"管理工具"全部安装即可,最重要的是要安装"管理工具"下的"管理服务")。

 

 

如果为灰色不可选:

这个是win2008 R2 x64的安全特性.一些核心系统组件只能允许本地帐号,TrustedInstaller有完全控制权限,而不是本地管理员组

要在Windows Server 2008 R2 上修改 “IIS Admin Service” ,你需要授予本地管理员组有注册表权限:

1.在组件管理器中查到IIS的appid

2.使用regedit,定位到HKEY_CLASSES_ROOT\AppID\{刚查到的ID}”

3.右键点击{刚查到的ID},选权限

4.在权限设置窗口中点高级,选择所有者标签,选中administrators为所有者

5.授予administrators组有读/写等所有权 注意:务必不要修改TrustedInstaller的权限

 

现在,重新执行组件管理器,可以修改权限了

WordPress 全站HTTPS化视频播放支持

一些浏览器会禁止http内容展现在https页面中。比如我们在WordPress 里插入一个Youku的视频(Youku嵌入的视频不支持https),就会无法播放

解决方案

我们需要找一个免费的支持https的serve视频的地方。第一个选择就是七牛云,搞了半天发现qiniu的ssl证书是invalid的...
于是又想起了“可怜的”LeanCloud。虽然这个可怜没人,爱各种亲爹(官方说他的架构在各大云平台上QWQ,高端。。)的家伙也有云引擎,可比起如日中天的BAT差的太远啦。。。不过,他比SAE,BAE,骑牛云存储神马的最棒的就是原生支持HTTPS。所以,我们用到了他的存储功能。。

LeanCloud注册。。

LeanCloud创建应用。。

(打个广告,这货最好玩的地方就是。。他们说:LeanCloud 提供了一个移动 app 的完整后端解决方案,我们的目标是帮助开发团队完全消除写后端代码和维护服务器的负担。)

OK!用他的存储~

如图所示:

20151129141431

选择_file -添加行-上传-设置所有人可读-OK。

这样你就得到了一个视频地址

这个地址支持 http/https 的。 所以

20151129143858
你就都可以用了, HTTPS的连接有了,视频还不会用吗-0- !


接下来解决主要问题!就是引用外链视频!HTTPS下引用http连接会默认不加载,比如youku,iqiyi之类的用iframe引用的玩意儿都是普通http协议。而且国内的那些视频网站基本都不支持https的。好吧,这个用代理解决吧。那就写个HTML来解决它。这个HTML要能处理各种Video的请求的话就得读到url里的query。









>

[download id="2"]

20151129141611

用法:https://mbv.pw/video.html?video=xxx - -。

当然,WordPress直接插入HTTPS的视频就好了,我这地方是故意这么写的!显示我有个非常牛逼的 视频代理!

直接插入视频:
https://ac-56eop0wn.clouddn.com/fc416cc35330fc8c.mp4

连接:20151129174641

可以直接播放
非常牛逼的视频代理:

恩0 0萌萌哒绿锁会没。。没了! 但是,完美解决https站点加载http视频外链咯。

 

说个题外话,就是我之前用的视频转码,免费的!真的是免费的!

百度开放云的媒体服务bce.baidu.com

怎么用0 0 有文档。效率不错,高品质mp4转码1分钟。。毕竟是云任务,可以接受,支持批量转码。

20151129141017

 

好吧,我承认题外话又是广告!拍死我吧!QAQ!

全站HTTPS化

之前以为沃通证书不好用,虽然站点支持HTTPS但没有强制升级。

好不容易申请到了startssl ,还是用@小卢砸的资料申请,因为我脸黑,自己的资料死活申请不过去。

恩。用了一个高大上的免费SSL证书,所以更新了全站HTTPS化。

申请资料:http://www.freehao123.com/startssl-ssl/

其中对我帮助比较大的资料就是lwl12的HTTPS更新公告:https://blog.lwl12.com/read/enable-ssl.html

恩0 0 就是看了一句sql。免去了我去手动替换图片链接的。

 

比较烦人的就是更新HTTPS后萌萌哒小绿锁不出现的问题了。(>_

不过主要罪魁祸首是 多说评论。

首先介绍一下实现原理,写一个php,实时从多说服务器获取最新的js文件,然后实时将js中头像的URL部分修改为自己服务器上的地址,由于新浪的头像服务器并不支持HTTPS,我们必须在自己的服务器上建立一个图片代理php,来解决问题,最后再修改多说插件,让它调用我们的php即可

这可能会消耗一些服务器资源和流量,当然,由于头像图片一般都很小,几乎可以忽略不计

注:代码作者:https://geeku.net/1878.html

duoshuo-local.7z
[download id="1"](多说缓存代码)

 

下载到后解压代码,编辑embed.php,12和13行,修改为自己对应的网址,保存好完整上传即可,上传到哪里自己可以随意选择,注意要和php里面设置的对应
20151129021522

进入后台-插件-编辑,选择多说,再找到Wordpress.php,全部复制出来,寻找embed.js,大概在610行和591行,都修改为上面的embed.php所在的地址

保存覆盖后,若有WP Super Cache,请删除缓存,再进入首页,查看源代码,是否已经调用embed.php

20151129022354

 

若有调用,请检查多说评论插件是否工作正常

应该没有什么问题

好友删除和好友验证设置

user_deletefriend.php //好友删除接口

 

//参数
$hisUid = intval($_POST['hisUid']); //必选 $token = strtoupper($_POST["token"]);//必选 $sign = strtoupper($_POST["sign"]);//必选
//返回值
{ "ret": "888", "msg": "删除好友['.$hisName.'('.$hisUid.')]成功!" }
user_getfriendset.php//获取指定uid好友设置

//参数

$hisUid = intval($_POST['hisUid']); //必选 根据指定uid获取好友设置 $token = strtoupper($_POST["token"]);//必选 $sign = strtoupper($_POST["sign"]);//必选

//返回值

 

{
    "ret": "888",
    "msg": "ok",
    "uid": "10000",
    "addconfig": "1"  //0需要验证,1拒加所有人,2允许所有人加
}

user_friendset.php//设置我的好友设置

 

//参数

$addconfig = intval($_POST["addconfig"]); //int 必选  0需要验证,1拒加所有人,2允许所有人加
$token = strtoupper($_POST["token"]);
$sign = strtoupper($_POST["sign"]);

//返回值

 

 

{
    "ret""888",
    "msg"$addconfigname."修改成功'"
}

 

用户资料修改

user_ziliaoupdate.php

参数

$nick =htmlspecialchars($_POST["nick"]); //必选 不能与服务器中已有的重复
$sex = $_POST["sex"];//必选  值: 0or1
$birth = $_POST["birth"];//必选 1990-01-01
$phone = $_POST["phone"];//必选
$description = htmlspecialchars($_POST["description"]);//必选 长度小于等于200
$token = strtoupper($_POST["token"]);//必选
$sign = strtoupper($_POST["sign"]);//必选

返回值示例

{
"ret": "888",
"num": "修改成功"
}

 

收获、催熟、喂养、出售处理逻辑

shouhuo.php


参数

$id = intval($_POST['id']);//int 必选
$token = strtoupper($_POST["token"]);//必选
$sign = strtoupper($_POST["sign"]);//必选


返回值示例


{
    "ret": "888",
    "msg": "收获成功"
}


cuishu.php


参数

$id = intval($_POST['id']);//int 必选
$token = strtoupper($_POST["token"]);//必选
$sign = strtoupper($_POST["sign"]);//必选


返回值示例


{
    "ret": "888",
    "msg": "催熟成功"
}


cuishu_getMoney.php


参数

$id = intval($_POST['id']);//int 必选 土地ID
$token = strtoupper($_POST["token"]);//必选
$sign = strtoupper($_POST["sign"]);//必选


返回值示例


{
    "ret": "888",
    "num": 12
}

feed.php

参数

$id = intval($_POST['id']);//int 必选
$count = intval($_POST['count']);//int 必选
$token = strtoupper($_POST["token"]); 必选
$sign = strtoupper($_POST["sign"]);必选


返回值示例


{
    "ret": "888",
    "msg": "喂养成功"
}


chushou.php


参数

$id = intval($_POST['id']);//int 必选
$token = strtoupper($_POST["token"]);//必选
$sign = strtoupper($_POST["sign"]);//必选


返回值示例


{
    "ret": "888",
    "msg": "出售成功"
}


商店逻辑实现 - jQuery+Ajax+PHP+Mysql实现分页显示数据

商店逻辑实现与分析

shop_pages.php

批量获取shop列表


参数

$page = intval($_POST['pageNum']);//页数,0为第一页
$token = strtoupper($_POST["token"]);
$sign = strtoupper($_POST["sign"]);

返回值实例

{
    "total": 10,
    "pageSize": 6,
    "totalPage": 2,
    "list": [
        {
            "C_ID": "4",
            "C_TYPE": "1",
            "C_NAME": "苹果种子",
            "C_ICON": "image/zhongzi/001.png",
            "C_TITLE": "苹果种子,可购买种植。收获后可喂养鸡。",
            "C_DESCRIPTION": "苹果种子,可购买种植。收获后可喂养鸡。",
            "C_PRICE": "10",
            "C_DANWEI": "个",
            "C_COUNT_NOW": "10",
            "C_STORAGE": "95135266",
            "C_STATUS": "10"
        },
        {
            "C_ID": "5",
            "C_TYPE": "1",
            "C_NAME": "香蕉种子",
            "C_ICON": "image/zhongzi/002.png",
            "C_TITLE": "香蕉种子,可购买种植。收获后可喂养狗。",
            "C_DESCRIPTION": "香蕉种子,可购买种植。收获后可喂养狗。",
            "C_PRICE": "20",
            "C_DANWEI": "个",
            "C_COUNT_NOW": "10",
            "C_STORAGE": "99332847",
            "C_STATUS": "10"
        },
        {
            "C_ID": "6",
            "C_TYPE": "1",
            "C_NAME": "菠萝种子",
            "C_ICON": "image/zhongzi/003.png",
            "C_TITLE": "菠萝种子,可购买种植,收获后可喂养马。",
            "C_DESCRIPTION": "菠萝种子,可购买种植,收获后可喂养马。",
            "C_PRICE": "30",
            "C_DANWEI": "个",
            "C_COUNT_NOW": "10",
            "C_STORAGE": "98152033",
            "C_STATUS": "10"
        },
        {
            "C_ID": "8",
            "C_TYPE": "2",
            "C_NAME": "幼芦花鸡",
            "C_ICON": "image/icon/dw_ji.png",
            "C_TITLE": "幼芦花鸡,可购买喂养。",
            "C_DESCRIPTION": "幼芦花鸡,可购买喂养。",
            "C_PRICE": "500",
            "C_DANWEI": "只",
            "C_COUNT_NOW": "10",
            "C_STORAGE": "99994876",
            "C_STATUS": "10"
        },
        {
            "C_ID": "9",
            "C_TYPE": "2",
            "C_NAME": "幼秋田犬",
            "C_ICON": "image/icon/dw_gou.png",
            "C_TITLE": "幼秋田犬,可购买喂养。",
            "C_DESCRIPTION": "幼秋田犬,可购买喂养。",
            "C_PRICE": "550",
            "C_DANWEI": "只",
            "C_COUNT_NOW": "10",
            "C_STORAGE": "99998701",
            "C_STATUS": "10"
        },
        {
            "C_ID": "10",
            "C_TYPE": "2",
            "C_NAME": "幼河曲马",
            "C_ICON": "image/icon/dw_ma.png",
            "C_TITLE": "幼河曲马,可购买喂养。",
            "C_DESCRIPTION": "幼河曲马,可购买喂养。",
            "C_PRICE": "600",
            "C_DANWEI": "只",
            "C_COUNT_NOW": "10",
            "C_STORAGE": "99999473",
            "C_STATUS": "10"
        }
    ]
}


shop_getById.php

按照shopid获取单个商品信息

参数

$id = intval($_POST['id']);
$token = strtoupper($_POST["token"]);
$sign = strtoupper($_POST["sign"]);

返回时示例

{
    "C_ID": 8,
    "C_TYPE": "2",
    "C_NAME": "幼芦花鸡",
    "C_ICON": "image/icon/dw_ji.png",
    "C_TITLE": "幼芦花鸡,可购买喂养。",
    "C_DESCRIPTION": "幼芦花鸡,可购买喂养。",
    "C_PRICE": 500,
    "C_DANWEI": "只",
    "C_COUNT_NOW": "10",
    "C_STORAGE": "99994876",
    "C_STATUS": "10"
}


shop_buy.php

参数


$shopId=intval($_POST["shopId"]);//int
$count=intval($_POST["count"]);//int
$token = strtoupper($_POST["token"]);
$sign = strtoupper($_POST["sign"]);


其中 shopid、token、sign为必选参数。


返回值示例

{
    "ret": "888",
    "msg": "购买成功并成功养殖!"
}

jQuery+Ajax+PHP+Mysql实现分页显示数据


本文使用jQuery,结合PHP和Mysql,通过实例讲解如何实现Ajax数据加载效果。

HTML


    

    页面中,#list用来展示数据列表,包括本例要展示的商品图片和标题,#pagecount用来展示分页条,即本例中的上一页、下一页。

    当然,别忘了,在head中预先载入jquery库文件。

    CSS

    我们需要将商品图片进行排列,以及设置分页条样式,当然这些样式的设计可以根据读取成功后的数据进行设置,本例中我们先把css代码贴出来。

    #list{width:680px; height:530px; margin:2px auto; position:relative}
    #list ul li{float:left;width:220px; height:260px; margin:2px}
    #list ul li img{width:220px; height:220px}
    #list ul li p{line-height:22px}
    #pagecount{width:500px; margin:10px auto; text-align:center}
    #pagecount span{margin:4px; font-size:14px}
    #list ul li#loading{width:120px; height:32px; border:1px solid #d3d3d3; 
    position:absolute; top:35%; left:42%; text-align:center; background:#f7f7f7 
    url(loading.gif) no-repeat 8px 8px;-moz-box-shadow:1px 1px 2px rgba(0,0,0,.2); 
    -webkit-box-shadow:1px 1px 2px rgba(0,0,0,.2); box-shadow:1px 1px 2px rgba(0,0,0,.2);}

    jQuery

    我们先声明变量,后面的代码要用到以下变量。

    var curPage = 1; //当前页码
    var total,pageSize,totalPage; //总记录数,每页显示数,总页数

    接下来,我们自定义一个函数:getData(),用来获取当前页数据。函数中,我们利用$.ajax()向后台pages.php发送POST异步请求,将当前页码以JSON格式传递给后台。

    //获取数据
    function getData(page){ 
    	$.ajax({
    		type: 'POST',
    		url: 'pages.php',
    		data: {'pageNum':page-1},
    		dataType:'json',
    		beforeSend:function(){
    			$("#list ul").append("loading...");//显示加载动画
    		},
    		success:function(json){
    			$("#list ul").empty();//清空数据区
    			total = json.total; //总记录数
    			pageSize = json.pageSize; //每页显示条数
    			curPage = page; //当前页
    			totalPage = json.totalPage; //总页数
    			var li = "";
    			var list = json.list;
    			$.each(list,function(index,array){ //遍历json数据列
    				li += "
  • "+array['title']                 +"
  • "; }); $("#list ul").append(li); }, complete:function(){ //生成分页条 getPageBar(); }, error:function(){ alert("数据加载失败"); } }); }

    请求成功后并返回数据,将相应的数据附给变量,并将返回的商品数据列表循环展示到对应容器#list ul中。当数据完全加载完毕后,调用分页条函数getPageBar()生成分页条。

    //获取分页条
    function getPageBar(){
    	//页码大于最大页数
    	if(curPage>totalPage) curPage=totalPage;
    	//页码小于1
    	if(curPage<1) curPage=1;
    	pageStr = "共"+total+"条"+curPage
        +"/"+totalPage+"";
    	
    	//如果是第一页
    	if(curPage==1){
    		pageStr += "首页上一页";
    	}else{
    		pageStr += "首页
            上一页";
    	}
    	
    	//如果是最后页
    	if(curPage>=totalPage){
    		pageStr += "下一页尾页";
    	}else{
    		pageStr += "
            下一页尾页
            ";
    	}
    		
    	$("#pagecount").html(pageStr);
    }

    最后,当页面第一次加载时,我们加载第一页数据即getData(1),当点击分页条中的分页链接时,调用getData(page)加载对应页码的数据。我们通过getPageBar()函数已预先在翻页连接的属性rel中在埋入了数字页码。

    $(function(){
    	getData(1);
    	$("#pagecount span a").live('click',function(){
    		var rel = $(this).attr("rel");
    		if(rel){
    			getData(rel);
    		}
    	});
    });

    PHP

    pages.php接收每次前端页面的ajax请求,根据提交的页码pageNum值,从mysql数据库中获取数据,计算总记录数和总页数,读取对应页码下的数据列表,并将最终结果以JSON格式返回给前端页面。

    include_once('connect.php'); //连接数据库,略过,具体请下载源码查看
    
    $page = intval($_POST['pageNum']); //当前页
    
    $result = mysql_query("select id from food");
    $total = mysql_num_rows($result);//总记录数
    $pageSize = 6; //每页显示数
    $totalPage = ceil($total/$pageSize); //总页数
    
    $startPage = $page*$pageSize; //开始记录
    //构造数组
    $arr['total'] = $total;
    $arr['pageSize'] = $pageSize;
    $arr['totalPage'] = $totalPage;
    $query = mysql_query("select id,title,pic from food order by id asc limit 
    $startPage,$pageSize"); //查询分页数据
    while($row=mysql_fetch_array($query)){
    	 $arr['list'][] = array(
    	 	'id' => $row['id'],
    		'title' => $row['title'],
    		'pic' => $row['pic'],
    	 );
    }
    echo json_encode($arr); //输出JSON数据

    这时再回到前端页面,即看到数据已分号页,点击“下一页”看看是不是你要的效果,分页条的样式大家可以自己定制,我给的是最基本的样式。

    最后,附上Mysql表结构,下载源码包中带数据表哦,^-^都为您准备好了。

    http://pan.baidu.com/s/1nQuPg

    CREATE TABLE IF NOT EXISTS `food` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `title` varchar(100) NOT NULL,
      `pic` varchar(255) NOT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=MyISAM  DEFAULT CHARSET=utf8;

    验证sign和token实现

    参数

    $token = strtoupper($_POST["token"]);
    $sign = strtoupper($_POST["sign"]);

    页面调用

    $ret_json = verify_signandtoken($sign,$token);
    $ret_arr = json_decode($ret_json);
    if($ret_arr->ret != 888){
     echo $ret_json;
    }

    逻辑实现

    config_mysql.ini.php 的公用方法注册
    
    function verify_signandtoken($sign,$token){
        if($sign != strtoupper(md5($token.$keys))){
            $returnData=array('ret'=>1011,'msg'=>'sign错误');
            return json_encode($returnData);
        }
        $sql = "SELECT * FROM `AA_ANDROID_USER` WHERE `C_TOKEN` = '".$token."';";
        $result = mysql_query($sql);
        $num = mysql_num_rows($result);
        $row = mysql_fetch_array($result);
        if($num != 1){
             $returnData=array('ret'=>1010,'msg'=>'token过期或不存在');
             return json_encode($returnData);
         }
         $returnData=array('ret'=>888,'msg'=>'ok');
         return json_encode($returnData);
     }


    preView
    1
    2
    echo -e 'PURGE / HTTP/1.0\r\n' | nc http:/example.com/purge/path
    echo -e 'GET /purge/ HTTP/1.0\r\n' | nc http:/example.com/purge/path