LUA
简介
lua是一个小巧的脚本语言,LUA不适合作为独立开发应用程序的语言,其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能
一般数据库连接是层层递进的,但是这样做性能不高,但有了LUA之后,我们甚至不需要Java代码,直接从nginx连接数据库
- 先从nginx获取数据,如果没有(nginx本身是有缓存的)
- 再通过LUA连接到redis中获取数据,如果没有
- 再通过LUA连接到mysqsl,查询数据,压入redis
特性
- 支持面向过程编程,和函数式编程
- 自动内存管理
- 语言内置模式匹配,闭包,提供多线程(协同进程,并非操作系统所支持的线程)
- 通过闭包和table可以很方便的支持面向对象编程所需要的一些关键机制,比如数据抽象,虚拟数,继承和重载等
应用场景
- redis中嵌套调用实现类似事务的功能
- web容器中应用处理一些过滤,缓存等逻辑
OpenResty
介绍
OpenResty是一个强大的Web应用服务器,可以胜任10K以上的并发量
OpenResty就是一个基于nginx的高性能的web服务器,提供了很多的lua的脚本模块,直接使用即可,不需要开发模块了
使用LUA和OpenResty
分析:
- 页面发送一个请求,openResty/nginx接收到请求之后
- 将改请求发送到数据库中,数据库将数据查询出来放到redis中
开发步骤:
- 配置nginx的配置项,用于接收请求路径,转发请求给lua脚本处理
- 创建lua脚本,调用mysql的模块 调用redis的模块
ngx.header.content_type="application/json;charset=utf8"
local cjson = require("cjson")
local mysql = require("resty.mysql")
local uri_args = ngx.req.get_uri_args()
local id = uri_args["id"]
local db = mysql:new()
db:set_timeout(1000)
local props = {
host = "192.168.211.132",
port = 3306,
database = "changgou_content",
user = "root",
password = "123456"
}
local res = db:connect(props)
local select_sql = "select url,pic from tb_content where status ='1' and category_id="..id.." order by sort_order"
res = db:query(select_sql)
db:close()
local redis = require("resty.redis")
local red = redis:new()
red:set_timeout(2000)
local ip ="192.168.211.132"
local port = 6379
red:connect(ip,port)
red:set("content_"..id,cjson.encode(res))
red:close()
ngx.say("{flag:true}")
nginx限流
- 控制速率
- 控制并发连接数
控制速率
采用漏桶算法
#限流设置
limit_req_zone $binary_remote_addr zone=contentRateLimit:10m rate=10r/s;
#根据IP地址来限制,存储内存大小10M
limit_conn_zone $binary_remote_addr zone=addr:10m;
#个人IP显示
limit_conn_zone $binary_remote_addr zone=perip:10m;
#针对整个服务所有的并发量控制
limit_conn_zone $server_name zone=perserver:10m;
binary_remote_addr 是一种key,表示基于 remote_addr(客户端IP) 来做限流,binary_ 的目的是压缩内存占用量。
zone:定义共享内存区来存储访问信息, contentRateLimit:10m 表示一个大小为10M,名字为contentRateLimit的内存区域。1M能存储16000 IP地址的访问信息,10M可以存储16W IP地址访问信息。
rate 用于设置最大访问速率,rate=10r/s 表示每秒最多处理10个请求。Nginx 实际上以毫秒为粒度来跟踪请求信息,因此 10r/s 实际上是限制:每100毫秒处理一个请求。这意味着,自上一个请求处理完后,若后续100毫秒内又有请求到达,将拒绝处理该请求
处理突发流量
如果流量突发,那我们可以使用burst参数解决该问题
server {
listen 80;
server_name localhost;
location /update_content {
content_by_lua_file /root/lua/update_content.lua;
}
location /read_content {
limit_req zone=contentRateLimit burst=4;
content_by_lua_file /root/lua/read_content.lua;
}
}
此处burst=4 若同时有4个请求到达,Nginx会处理第一个请求,剩余3个放到队列,等到处理完了,再从队列中获取下一个请求进行处理,一般与上面漏桶算法配合使用,100毫秒处理一个请求,同时来4个请求只需要4毫秒
控制并发量
ngx_http_limit_conn_module 提供了限制连接数的能力。主要是利用limit_conn_zone和limit_conn两个指令。
利用连接数限制 某一个用户的ip连接的数量来控制流量。
注意:并非所有连接都被计算在内 只有当服务器正在处理请求并且已经读取了整个请求头时,才会计算有效连接。此处忽略测试。
配置语法:
Syntax: limit_conn zone number;
Default: —;
Context: http, server, location;
Canal
canal是阿里巴巴开源处理的一款数据库监听的软件
package com.changgou.listener;/*
@Author:李正铠
@Date:2020年05月09日23时40分
*/
import com.alibaba.otter.canal.protocol.CanalEntry;
import com.xpand.starter.canal.annotation.CanalEventListener;
import com.xpand.starter.canal.annotation.InsertListenPoint;
import com.xpand.starter.canal.annotation.ListenPoint;
import com.xpand.starter.canal.annotation.UpdateListenPoint;
@CanalEventListener
public class CanalDataEventListener {
/**
* 增加数据监听
* @param eventType
* @param rowData
*/
@InsertListenPoint
public void onEventInsert(CanalEntry.EventType eventType,CanalEntry.RowData rowData){
rowData.getAfterColumnsList().forEach((column ->
System.out.println("By--Annotation:"+column.getName()+"::"+column.getValue())));
}
/**
* 修改数据监听
* @param rowData
*/
@UpdateListenPoint
public void onEventUpdate(CanalEntry.RowData rowData){
System.out.println("UpdaeListenPoint");
rowData.getAfterColumnsList().forEach(column -> System.out.println("by--Annotation"+column.getName()+"::"+column.getValue()));
}
/**
* 自定义数据修改监听
* @param eventType
* @param rowData
*/
@ListenPoint(destination = "example",schema = "changgou_content",
table = {"tb_content_category","tb_content"},eventType = CanalEntry.EventType.UPDATE)
public void onEventCustomUpdate(CanalEntry.EventType eventType,CanalEntry.RowData rowData){
System.out.println("DeleteListenPoint");
rowData.getAfterColumnsList().forEach(column -> System.out.println("By--Annotation:"+column.getName()+"::"+column.getValue()));
}
}
起步引导类添加的注解
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@EnableCanalClient
#canal配置
canal:
client:
instances:
example:
host: 192.168.211.132
port: 11111