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