示例图:
1. OpenResty
官网地址:openresty.org/cn/
OpenResty® 是一款基于 NGINX 和 LuaJIT(lua plus) 的 Web 平台。
1.1 OpenResty简介
OpenResty 是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。
OpenResty® 通过汇聚各种设计精良的 Nginx 模块(主要由 OpenResty 团队自主开发),从而将 Nginx 有效地变成一个强大的通用 Web 应用平台。这样,Web 开发人员和系统工程师可以使用 Lua 脚本语言调动 Nginx 支持的各种 C 以及 Lua 模块,快速构造出足以胜任 10K 乃至 1000K 以上单机并发连接的高性能 Web 应用系统。
OpenResty® 的目标是让你的Web服务直接跑在 Nginx 服务内部,充分利用 Nginx 的非阻塞 I/O 模型,不仅仅对 HTTP 客户端请求,甚至于对远程后端诸如 MySQL、PostgreSQL、Memcached 以及 Redis 等都进行一致的高性能响应。
1.2 编译安装OpenResty
此处可以跳转至另一篇博客
2. lua
2.1 安装及简单语法
3. 多级缓存
mysql有缓存
redis有缓存
nginx有缓存,也可以利用lua直接调用redis缓存
tomcat支持的并发数并不多,所以要尽量减少访问,而且,你访问redis也要经过tomcat,tomcat死了,唇亡齿寒
3.1 静态页面动静分离
有关nginx的内容就不再写了
location / {
root 你的静态页面存放地址
}
3.2 注解缓存
@EnableCaching:
在项目启动类上加,表示允许使用注解的方式进行缓存操作。
@Cacheable:
(缓存的增加)
可用于类或方法上;在目标方法执行前,会根据key先去缓存中查询看是否有数据,
有就直接返回缓存中的key对应的value值,不再执行目标方法;
无则执行目标方法,并将方法的返回值作为value,并以键值对的形式存入缓存。
@CacheEvict:
(缓存的删除)
可用于类或方法上;在执行完目标方法后,清除缓存中对应key的数据(如果缓存中有对应key的数据缓存的话)。
@CachePut:
(缓存的更新)
(查到什么都加到缓存里面,不管缓存里有没有)
可用于类或方法上;在执行完目标方法后,并将方法的返回值作为value,并以键值对的形式存入缓存中。
@Caching:
此注解即可作为@Cacheable、@CacheEvict、@CachePut三种注解中的的任何一种或几种来使用。
@CacheConfig:
可以用于配置@Cacheable、@CacheEvict、@CachePut这三个注解的一些公共属性,例如cacheNames、keyGenerator。
注意这里是存到redis的,所以要配置一下redis的地址
例子:
@Cacheable这里第二次查的时候明显感觉快很多,也可以打印一些句子,发现根本没有走Spring,而是走的缓存
@CachePut会发现每次都是打的数据库,不会查缓存,用于修改
@CacheEvict就是删除的时候顺带删掉缓存
@CacheConfig作用如上图,在一个bean的头部加
3.3 Lua Redis缓存
ok,如上(tomcat支持的并发数并不多,所以要尽量减少访问,而且,你访问redis也要经过tomcat,tomcat死了,唇亡齿寒)所述,接下来记录如何通过lua+nginx跳过nginx实现调用redis
按照上面分析的架构,可以每次在Nginx的时候使用Lua脚本查询Redis,如果Redis有数据,则将数据存入到Nginx缓存,再将数据响应给用户,此时我们需要实现使用Lua将数据从Redis中加载出来。
我们在/usr/local/openresty/nginx/lua中创建文件sc.lua,脚本如下:
--数据响应类型JSON
ngx.header.content_type="application/json;charset=utf8"
--Redis库依赖
local redis = require("resty.redis");
local cjson = require("cjson");
--获取id参数(type)这里只是个例子,你可以写上面的注解缓存里面的key
local id = ngx.req.get_uri_args()["id"];
--key组装
local key = "ad-items-skus::"..id
--创建链接对象
local red = redis:new()
--设置超时时间
red:set_timeout(2000)
--设置服务器链接信息
red:connect("你的redis地址", 6379)
--查询指定key的数据
local result=red:get(key);
--关闭Redis链接
red:close()
if result==nil or result==null or result==ngx.null then
return true
else
--输出数据
ngx.say(result)
end
修改nginx.conf添加如下配置:(最后记得将content_by_lua_file改成rewrite_by_lua_file)
location /sku/aditems/type {
content_by_lua_file /usr/local/openresty/nginx/lua/aditem.lua;
}
3.4 Nginx缓存
1)开启代理缓存
修改nginx.conf,添加如下配置:
proxy_cache_path /usr/local/openresty/nginx/cache levels=1:2 keys_zone=proxy_cache:10m max_size=1g inactive=60m use_temp_path=off;
修改
nginx.conf,添加如下配置:
#门户发布
server {
listen 80;
server_name www.scscscscs.com;
#推广产品查询
location /sku/aditems/type {
#先找Nginx缓存
rewrite_by_lua_file /usr/local/openresty/nginx/lua/aditem.lua;
#启用缓存openresty_cache
proxy_cache proxy_cache;
#针对指定请求缓存
#proxy_cache_methods GET;
#设置指定请求会缓存
proxy_cache_valid 200 304 60s;
#最少请求1次才会缓存
proxy_cache_min_uses 1;
#如果并发请求,只有第1个请求会去服务器获取数据
#proxy_cache_lock on;
#唯一的key
proxy_cache_key $host$uri$is_args$args;
#动态代理
proxy_pass http://192.168.100.1:8081;
}
#其他所有请求
location / {
root /usr/local/web/static/frant;
}
}
重启nginx或者重新加载配置文件nginx -s reload,再次测试,可以发现下面个规律:
1:先查找Redis缓存
2:Redis缓存没数据,直接找Nginx缓存
3:Nginx缓存没数据,则找真实服务器
我们还可以发现cache目录下多了目录和一个文件,这就是Nginx缓存
参数说明:
【proxy_cache_path】指定缓存存储的路径,缓存存储在/usr/local/openresty/nginx/cache目录
【levels=1:2】设置一个两级目录层次结构存储缓存,在单个目录中包含大量文件会降低文件访问速度,因此我们建议对大多数部署使用两级目录层次结构。如果 levels 未包含该参数,Nginx 会将所有文件放在同一目录中。
【keys_zone=proxy_cache:10m】设置共享内存区域,用于存储缓存键和元数据,例如使用计时器。拥有内存中的密钥副本,Nginx 可以快速确定请求是否是一个 HIT 或 MISS 不必转到磁盘,从而大大加快了检查速度。1 MB 区域可以存储大约 8,000 个密钥的数据,因此示例中配置的 10 MB 区域可以存储大约 80,000 个密钥的数据。
【max_size=1g】设置缓存大小的上限。它是可选的; 不指定值允许缓存增长以使用所有可用磁盘空间。当缓存大小达到限制时,一个称为缓存管理器的进程将删除最近最少使用的缓存,将大小恢复到限制之下的文件。
【inactive=60m】指定项目在未被访问的情况下可以保留在缓存中的时间长度。在此示例中,缓存管理器进程会自动从缓存中删除 60 分钟未请求的文件,无论其是否已过期。默认值为 10 分钟(10m)。非活动内容与过期内容不同。Nginx 不会自动删除缓存 header 定义为已过期内容(例如 Cache-Control:max-age=120)。过期(陈旧)内容仅在指定时间内未被访问时被删除。访问过期内容时,Nginx 会从原始服务器刷新它并重置 inactive 计时器。
【use_temp_path=off】表示NGINX会将临时文件保存在缓存数据的同一目录中。这是为了避免在更新缓存时,磁盘之间互相复制响应数据,我们一般关闭该功能。
2)Proxy_Cache属性:
proxy_cache:设置是否开启对后端响应的缓存,如果开启的话,参数值就是zone的名称,比如:proxy_cache。
proxy_cache_valid:针对不同的response code设定不同的缓存时间,如果不设置code,默认为200,301,302,也可以用any指定所有code。
proxy_cache_min_uses:指定在多少次请求之后才缓存响应内容,这里表示将缓存内容写入到磁盘。
proxy_cache_lock:默认不开启,开启的话则每次只能有一个请求更新相同的缓存,其他请求要么等待缓存有数据要么限时等待锁释放;nginx 1.1.12才开始有。
配套着proxy_cache_lock_timeout一起使用。
proxy_cache_key:缓存文件的唯一key,可以根据它实现对缓存文件的清理操作。
(执行顺序也有点迷,等会了再写)
删除:
这个模块在上面编译安装的时候就已经安装了
在上面请求的网址路径前面加上/purge
#清理缓存
location ~ /purge(/.*) {
#清理缓存
proxy_cache_purge proxy_cache $host$1$is_args$args;
}
4. 数据一致性canal
早期阿里巴巴因为杭州和美国双机房部署,存在跨机房同步的业务需求,实现方式主要是基于业务 trigger 获取增量变更。从 2010 年开始,业务逐步尝试数据库日志解析获取增量变更进行同步,由此衍生出了大量的数据库增量订阅和消费业务。 基于日志增量订阅和消费的业务包括
- 数据库镜像
- 数据库实时备份
- 索引构建和实时维护(拆分异构索引、倒排索引等)
- 业务 cache 刷新
- 带业务逻辑的增量数据处理
当前的 canal 支持源端 MySQL 版本包括 5.1.x , 5.5.x , 5.6.x , 5.7.x , 8.0.x。
Canal 工作原理
- canal 模拟 MySQL slave 的交互协议,伪装自己为 MySQL slave ,向 MySQL master 发送dump 协议
- MySQL master 收到 dump 请求,开始推送 binary log 给 slave (即 canal )
- canal 解析 binary log 对象(原始为 byte 流)
4.1 MySQL开启binlog
对于MySQL , 需要先开启 Binlog 写入功能,配置 binlog-format 为 ROW 模式,my.cnf 中配置如下
docker exec -it mysql /bin/bash
cd /etc/mysql/mysql.conf.d
vi mysqld.cnf
在最文件尾部添加如下配置:
log-bin=mysql-bin # 开启 binlog
binlog-format=ROW # 选择 ROW 模式
server_id=1 # 配置 MySQL replaction 需要定义,不要和 canal 的 slaveId 重复
注意: 针对阿里云 RDS for MySQL , 默认打开了 binlog , 并且账号默认具有 binlog dump 权限 , 不需要任何权限或者 binlog 设置,可以直接跳过这一步。
授权 canal 链接 MySQL 账号具有作为 MySQL slave 的权限, 如果已有账户可直接 grant:
CREATE USER canal IDENTIFIED BY 'canal';
GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%';
-- GRANT ALL PRIVILEGES ON *.* TO 'canal'@'%' ;
FLUSH PRIVILEGES;
重启mysql容器
docker restart canal
查看是否开启binlog:
show variables like 'log_bin';
4.2 Canal安装
我们采用docker安装方式:
docker run -p 11111:11111 --name canal -d docker.io/canal/canal-server
进入容器,修改核心配置canal.properties 和instance.properties,canal.properties 是canal自身的配置,instance.properties是需要同步数据的数据库连接配置。
修改配置如下:
# position info
canal.instance.master.address=192.168.100.130:3306
另一处配置:
# table regex
#canal.instance.filter.regex=.*\..*
#监听配置
canal.instance.filter.regex=shop_goods.ad_items
配置完成后,重启canal容器
docker restart canal
4.3 Canal微服务搭建
这里我也没搞懂,等搞懂了再写
<!--springboot-canal快速构建依赖包-->
<dependency>
<groupId>top.javatool</groupId>
<artifactId>canal-spring-boot-starter</artifactId>
<version>1.2.1-RELEASE</version>
</dependency>
server:
port: 8083
......
#Canal配置
canal:
server: 地址+docker的端口号
destination: example
#日志配置
logging:
pattern:
console: "%msg%n"
level:
root: error
创建监听类:listener.AdItemsHandler
@CanalTable(value = "ad_items")
@Component
public class AdItemsHandler implements EntryHandler<AdItems> {
}
创建启动类:
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
(根据学习心得和gupao云课堂资料所写,侵删OvO)