OpenResty-实战

原创地址:http://jinnianshilongnian.iteye.com/blog/2190344

常用命令

# vi /usr/local/nginx/conf/nginx.conf

# vi /usr/server/example/example.conf

# /usr/local/nginx/sbin/nginx  -s reload &  tail -f /usr/local/nginx/logs/error.log

目录:

1.OpenRestry(Nginx+Lua)开发环境

2.OpenRestry开发入门

3.Lua模块开发

3.1 常用Lua开发库1-redis、mysql、http客户端 附-Redis/SSDB+Twemproxy安装与使用

3.2 常用Lua开发库2-JSON库、编码转换、字符串处理

3.3 常用Lua开发库3-模板渲染

4.实战

4.1 Web开发实战1——HTTP服务

4.2 Web开发实战2——商品详情页

4.3 流量复制/AB测试/协程

4.1 Web开发实战1——HTTP服务

标注:

这里的HTTP服务不是指的web页面,而是页面中异步加载的相关数据,比如热门搜索、用户登录、实时价格、实时库存、服务支持、广告语等。

这些服务的特点:访问量巨大、逻辑比较单一;但是如实时库存逻辑其实是非常复杂的。

1.良好的架构

京东这些服务每天有几亿十几亿的访问量,比如实时库存服务曾经在没有任何IP限流、DDos防御的情况被刷到600多万/分钟的访问量,而且能轻松应对。

支撑如此大的访问量就需要考虑设计良好的架构,并很容易实现水平扩展。

2.几种架构形式

3.实践架构:Nginx+Lua+ Redis集群+Mysql集群架构 

3.1 图

>>>>>需求:

假设京东有10亿商品,那么广告词极限情况是10亿;所以在设计时就要考虑:

1、数据量,数据更新是否频繁且更新量是否很大;

2、是K-V还是关系,是否需要批量获取,是否需要按照规则查询。

而对于本例,

(1)广告词更新量不会很大,每分钟可能在几万左右;

(2)而且是K-V的,其实适合使用关系存储;

(3)因为广告词是商家维护,因此后台查询需要知道这些商品是哪个商家的;而对于前台是不关心商家的,是KV存储,所以前台显示的可以放进如Redis中。 

即存在两种设计:

1、所有数据存储到Mysql,然后热点数据加载到Redis;

2、关系存储到Mysql,而数据存储到如SSDB这种持久化KV存储中。

基本数据结构:商品ID、广告词、所属商家、开始时间、结束时间、是否有效

>>>>>>>>后台逻辑

1、商家登录后台;

2、按照商家分页查询商家数据,此处要按照商品关键词或商品类目查询的话,需要走商品系统的搜索子系统,如通过Solr或elasticsearch实现搜索子系统;

3、进行广告词的增删改查;

4、增删改时可以直接更新Redis缓存或者只删除Redis缓存(第一次前台查询时写入缓存);

>>>>>>>>前台逻辑

1、首先Nginx通过Lua查询Redis缓存;

2、查询不到的话回源到Tomcat,Tomcat读取数据库查询到数据,然后把最新的数据异步写入Redis(一般设置过期时间,如5分钟);此处设计时要考虑假设Tomcat读取Mysql的极限值是多少,然后设计降级开关,如假设每秒回源达到100,则直接不查询Mysql而返回空的广告词来防止Tomcat应用雪崩。

为了简单,我们不进行后台的设计实现,只做前端的设计实现,此时数据结构我们简化为[商品ID、广告词]。另外有朋友可能看到了,可以直接把Tomcat部分干掉,通过Lua直接读取Mysql进行回源实现。为了完整性此处我们还是做回源到Tomcat的设计,因为如果逻辑比较复杂的话或一些限制(比如使用Java特有协议的RPC)还是通过Java去实现更方便一些。

>>>>>>>>项目搭建

/usr/chapter6  

  redis_6660.conf  

  redis_6661.conf  

  nginx_chapter6.conf  

  nutcracker.yml  

  nutcracker.init  

  webapp  

WEB-INF  

   lib  

   classes  

   web.xml  

>>>>>>>>>>Redis+Twemproxy配置

此处根据实际情况来决定Redis大小,此处我们已两个Redis实例(6660、6661),在Twemproxy上通过一致性Hash做分片逻辑。

安装:之前已经介绍过Redis和Twemproxy的安装了。

1.Redis配置redis_6660.conf和redis_6661.conf    

#分别为6660 6661  

port 6660  

#进程ID 分别改为redis_6660.pid redis_6661.pid  

pidfile "/var/run/redis_6660.pid"  

#设置内存大小,根据实际情况设置,此处测试仅设置20mb  

maxmemory 20mb  

#内存不足时,按照过期时间进行LRU删除  

maxmemory-policy volatile-lru  

#Redis的过期算法不是精确的而是通过采样来算的,默认采样为3个,此处我们改成10  

maxmemory-samples 10  

#不进行RDB持久化  

save “”  

#不进行AOF持久化  

appendonly no  

注:将如上配置放到redis_6660.conf和redis_6661.conf配置文件最后即可,后边的配置会覆盖前边的。  

2.Twemproxy配置nutcracker.yml 

server1:  

  listen: 127.0.0.1:1111  

  hash: fnv1a_64  

  distribution: ketama  

  redis: true  

  timeout: 1000  

  servers:  

   - 127.0.0.1:6660:1 server1  

   - 127.0.0.1:6661:1 server2  

复制nutcracker.init到/usr/chapter6下,并修改配置文件为/usr/chapter6/nutcracker.yml。

3.启动

nohup /usr/servers/redis-2.8.19/src/redis-server  /usr/chapter6/redis_6660.conf &  

nohup /usr/servers/redis-2.8.19/src/redis-server  /usr/chapter6/redis_6661.conf &  

/usr/chapter6/nutcracker.init start  注:1.需要赋予执行权限,比如755; 2.${prog} $OPTIONS--->/usr/servers/twemproxy-0.4.0/src/${prog} $OPTIONS

ps -aux | grep -e redis  -e nutcracker

>>>>>>>>>Mysql+Atlas配置

Atlas类似于Twemproxy,是Qihoo 360基于Mysql Proxy开发的一个Mysql中间件,据称每天承载读写请求数达几十亿,可以实现分表、分库(sharding版本)、读写分离、数据库连接池等功能,

缺点:没有实现跨库分表功能,需要在客户端使用分库逻辑,目前Atlas不活跃。

另一个选择是使用如阿里的TDDL,它是在客户端完成之前说的功能。到底选择是在客户端还是在中间件根据实际情况选择。

此处我们不做Mysql的主从复制(读写分离),只做分库分表实现。

Mysql初始化:为了测试我们此处分两个表(做分库分表)

===实践:在单机的mysql上运行===

CREATE DATABASE chapter6 DEFAULT CHARACTER SET utf8;

  

use chapter6;  

CREATE TABLE  chapter6.ad_0(  

      sku_id BIGINT,  

      content VARCHAR(4000)  

) ENGINE=InnoDB  DEFAULT CHARSET=utf8;  

CREATE TABLE  chapter6.ad_1(  

      sku_id BIGINT,  

      content VARCHAR(4000)  

) ENGINE=InnoDB  DEFAULT CHARSET=utf8;  

【Atlas安装与配置】

前提得配置好主从:http://www.cnblogs.com/super-d2/p/4802990.html

首先,先去下载Altas的rpm包,下载地址:https://github.com/Qihoo360/Atlas/releases

要看清楚版本,centos 5.x:Atlas-1.0.3.el5.x86_64.rpm    centos 6.x:

我的系统是centos 6.4 ,所以下载Atlas-1.0.3.el6.x86_64.rpm

千万别搞错了,否则最后启动会出错。大家也可以采取源码编译安装,不过rpm安装比较省时省力,而且简单。

下载rpm 

wget https://github.com/Qihoo360/Atlas/releases/download/1.0.3/Atlas-1.0.3.el6.x86_64.rpm

安装 

rpm -i Atlas-1.0.3.el6.x86_64.rpm

安装的目录是/usr/local/mysql-proxy

conf文件夹下有一个自带的配置文件test.cnf,我们可以直接修改,下面是我修改的

主数据库服务器:192.168.83.11

从数据库服务器:192.168.83.12

proxy服务器:192.168.83.13

复制代码

[mysql-proxy]

#Atlas加载的模块名称,不需要改

plugins = admin, proxy

#管理接口的用户名

admin-username = root

#管理接口的密码

admin-password = 123456

#实现管理接口的Lua脚本所在路径

admin-lua-script = /usr/local/mysql-proxy/lib/mysql-proxy/lua/admin.lua

#Atlas后端连接的MySQL主库的IP和端口,可设置多项,用逗号分隔

proxy-backend-addresses = 192.168.83.11:3306

#Atlas后端连接的MySQL从库的IP和端口,@后面的数字代表权重,用来作负载均衡,若省略则默认为1,可设置多项,用逗号分隔

proxy-read-only-backend-addresses = 192.168.83.12:3306@1

#设置Atlas的运行方式,设为true时为守护进程方式,设为false时为前台方式,一般开发调试时设为false,线上运行时设为true

daemon = false

#设置Atlas的运行方式,设为true时Atlas会启动两个进程,一个为monitor,一个为worker,monitor在worker意外退出后会自动将其重启,设为false时只有worker,没有monitor,一般开发调试时设为false,线上运行时设为true

keepalive = false

#工作线程数,推荐设置与系统的CPU核数相等

event-threads = 4

#日志级别,分为message、warning、critical、error、debug五个级别

log-level = message

#日志存放的路径

log-path = /usr/local/mysql-proxy/log

#实例名称,用于同一台机器上多个Atlas实例间的区分

instance = test

#Atlas监听的工作接口IP和端口

proxy-address = 0.0.0.0:1234

#Atlas监听的管理接口IP和端口

admin-address = 0.0.0.0:2345

#连接池的最小空闲连接数,应设为event-threads的整数倍,可根据业务请求量大小适当调大或调小

min-idle-connections = 8

#分表设置,此例中person为库名,mt为表名,id为分表字段,3为子表数量,可设置多项,以逗号分隔,若不分表则不需要设置该项

#tables = person.mt.id.3

#用户名与其对应的加密过的MySQL密码,密码使用PREFIX/bin目录下的加密程序encrypt加密,此设置项用于多个用户名同时访问同一个Atlas实例的情况,若只有一个用户名>则不需要设置该项

#pwds = user1:+jKsgB3YAG8=, user2:GS+tr4TPgqc=

#默认字符集,若不设置该项,则默认字符集为latin1

charset = utf8

#允许连接Atlas的客户端的IP,可以是精确IP,也可以是IP段,以逗号分隔,若不设置该项则允许所有IP连接,否则只允许列表中的IP连接

#client-ips = 127.0.0.1, 192.168.1

#Atlas前面挂接的LVS的物理网卡的IP(注意不是虚IP),若有LVS且设置了client-ips则此项必须设置,否则可以不设置

#lvs-ips = 192.168.1.1

复制代码

最后,进入到bin文件夹下,启动Altas

./mysql-proxy --defaults-file=../conf/test.cnf

最好配置文件中的 daemon设置为true,为后台守护运行。

文件目录:/usr/local/mysql-proxy

启动:

使用官网的

 /usr/local/mysql-proxy/bin/mysql-proxyd test start

查看Altas运行情况

 /usr/local/mysql-proxy/bin/mysql-proxyd test status

有两个进程的。

我后来使用NaviCat连接工作端口,用户名和密码就是上面配置文件的管理用户和密码,成功了。

经过测试,Altas的读写分离和事务支持很好,明天发布2.0版本,增加对JDBC的支持。

参考:

https://github.com/Qihoo360/Atlas

-- 测试

use chapter6;  

insert into ad values(1,'测试1');      

insert into ad values(2,'测试2');      

insert into ad values(3,'测试3');      

select * from ad where sku_id=1;  

select * from ad where sku_id=2; 

INSERT INTO ad VALUES(1,1);

./mysql-proxyd test start

[报错:启动报错,因为这个test需要和配置文件的INSTANCE一致]

 nginx配置

 vi /usr/chapter6/nginx_chapter6.conf 

我这里配置针对web服务器的地址

  upstream配置:http://nginx.org/cn/docs/http/ngx_http_upstream_module.html

  server:指定上游到的服务器,

  weight:权重,权重可以认为负载均衡的比例;  

  fail_timeout+max_fails:在指定时间内失败多少次认为服务器不可用,通过proxy_next_upstream来判断是否失败。

  check:ngx_http_upstream_check_module模块,上游服务器的健康检查,interval:发送心跳包的时间间隔,rise:连续成功rise次数则认为服务器up,fall:连续失败fall次则认为服务器down,timeout:上游服务器请求超时时间,type:心跳检测类型(比如此处使用tcp)更多配置请参考https://github.com/yaoweibin/nginx_upstream_check_modulehttp://tengine.taobao.org/document_cn/http_upstream_check_cn.html

  keepalive:用来支持upstream server http keepalive特性(需要上游服务器支持,比如tomcat)。默认的负载均衡算法是round-robin,还可以根据ip、url等做hash来做负载均衡。更多资料请参考官方文档。

tomcat keepalive配置: http://tomcat.apache.org/tomcat-7.0-doc/config/http.html

  maxKeepAliveRequests:默认100;

  keepAliveTimeout:默认等于connectionTimeout,默认60秒;

location proxy配置:http://nginx.org/cn/docs/http/ngx_http_proxy_module.html

  rewrite:将当前请求的url重写,如我们请求时是/backend/ad,则重写后是/ad。

  proxy_pass:将整个请求转发到上游服务器。

  proxy_next_upstream:什么情况认为当前upstream server失败,需要next upstream,默认是连接失败/超时,负载均衡参数。

  proxy_pass_request_headers:之前已经介绍过了,两个原因:1、假设上游服务器不需要请求头则没必要传输请求头;2、ngx.location.capture时防止gzip乱码(也可以使用more_clear_input_headers配置)。

  keepalive:keepalive_timeout:keepalive超时设置,keepalive_requests:长连接数量。此处的keepalive(别人访问该location时的长连接)和upstream keepalive(nginx与上游服务器的长连接)是不一样的;此处注意,如果您的服务是面向客户的,而且是单个动态内容就没必要使用长连接了。