当前位置: 首页 > news >正文

产品营销网站建设商标注册网app

产品营销网站建设,商标注册网app,网页版微信二维码传送助手,专业的扬州网站建设文章目录 一、多级缓存概念二、准备工作【导入案例#xff0c;并搭建Nginx反向代理】2.1 导入商品案例2.1.1 安装MySQL2.1.2 导入SQL2.1.3 导入Demo工程2.1.4 启动2.1.5 导入商品查询页面 三、JVM进程缓存【第三级缓存】3.1 本地进程缓存与分布式缓存的区别3.2 本地进程缓存并搭建Nginx反向代理】2.1 导入商品案例2.1.1 安装MySQL2.1.2 导入SQL2.1.3 导入Demo工程2.1.4 启动2.1.5 导入商品查询页面 三、JVM进程缓存【第三级缓存】3.1 本地进程缓存与分布式缓存的区别3.2 本地进程缓存Caffeine3.3 案例 四、Nginx编程Lua语法4.1 初识Lua4.2 变量4.3 循环4.4 条件控制和函数 五、多级缓存5.1 安装OpenResty5.1.1 安装5.1.2 启动和运行 5.2 OpenResty快速入门上述的总结流程 5.3 请求参数处理5.4 查询Tomcat5.4.1 nginx发送http请求5.4.2 nginx发出请求后反向代理给tomcat5.4.3 编写item.lua业务获取从本地tomcat响应请求结果 5.5 Tomcat集群的负载均衡5.6 Redis的冷启动与缓存预热5.7 查询Rdeis缓存【第二级缓存】5.8 Nginx本地缓存【第一级缓存】 六、缓存同步策略6.1 常见缓存策略6.2 安装Canal6.2.1 开启MySQL主从6.2.2 安装Canal 6.3 监听Canal 八、多级缓存总结九、额外说明cpolar内网穿透将私网暴露成公网供外部使用 一、多级缓存概念 多级缓存就是充分利用请求处理的每个环节分别添加缓存减轻Tomcat压力提升服务性能。 用作缓存的Nginx是业务Nginx需要部署为集群再有专门的Nginx用来做反向代理。 二、准备工作【导入案例并搭建Nginx反向代理】 本章实现橙色部分 2.1 导入商品案例 2.1.1 安装MySQL 后期做数据同步需要用到MySQL的主从功能所以需要大家在虚拟机中利用Docker来运行一个MySQL容器。 为了方便后期配置MySQL我们先准备两个目录用于挂载容器的数据和配置文件目录 # 进入/tmp目录 cd /tmp # 创建文件夹 mkdir mysql # 进入mysql目录 cd mysql进入mysql目录后执行下面的Docker命令 docker run \-p 3306:3306 \--name mysql \-v $PWD/conf:/etc/mysql/conf.d \-v $PWD/logs:/logs \-v $PWD/data:/var/lib/mysql \-e MYSQL_ROOT_PASSWORD123456 \--privileged \-d \mysql:5.7.25在/tmp/mysql/conf目录添加一个my.cnf文件作为mysql的配置文件 # 创建文件 touch /tmp/mysql/conf/my.cnf文件的内容如下 [mysqld] skip-name-resolve character_set_serverutf8 datadir/var/lib/mysql server-id1000配置修改后必须重启容器 docker restart mysql2.1.2 导入SQL 接下来利用Navicat客户端连接MySQL然后导入课前资料提供的sql文件item.sql 其中包含两张表 tb_item商品表包含商品的基本信息tb_item_stock商品库存表包含商品的库存信息 之所以将库存分离出来是因为库存是更新比较频繁的信息写操作较多。而其他信息修改的频率非常低。 2.1.3 导入Demo工程 下面导入课前资料提供的工程item-service 项目结构如图所示 其中的业务包括 分页查询商品新增商品修改商品修改库存删除商品根据id查询商品根据id查询库存 业务全部使用mybatis-plus来实现如有需要请自行修改业务逻辑。 分页查询商品 在com.heima.item.web包的ItemController中可以看到接口定义 新增商品 在com.heima.item.web包的ItemController中可以看到接口定义 修改商品 在com.heima.item.web包的ItemController中可以看到接口定义 修改库存 在com.heima.item.web包的ItemController中可以看到接口定义 删除商品 在com.heima.item.web包的ItemController中可以看到接口定义 这里是采用了逻辑删除将商品状态修改为3 根据id查询商品 在com.heima.item.web包的ItemController中可以看到接口定义 这里只返回了商品信息不包含库存 根据id查询库存 在com.heima.item.web包的ItemController中可以看到接口定义 2.1.4 启动 注意修改application.yml文件中配置的mysql地址信息 需要修改为自己的虚拟机地址信息、还有账号和密码。 修改后启动服务访问http://localhost:8081/item/10001即可查询数据 2.1.5 导入商品查询页面 商品查询是购物页面与商品管理的页面是分离的。 部署方式如图 我们需要准备一个反向代理的nginx服务器如上图红框所示将静态的商品页面放到nginx目录中。 页面需要的数据通过ajax向服务端nginx业务集群查询。 运行nginx服务 这里我已经给大家准备好了nginx反向代理服务器和静态资源。 我们找到课前资料的nginx目录nginx-1.18.0 将其拷贝到一个非中文目录下运行这个nginx服务。 运行命令 start nginx.exe然后访问 http://localhost/item.html?id10001即可 反向代理 现在页面是假数据展示的。我们需要向服务器发送ajax请求查询商品数据。 打开控制台可以看到页面有发起ajax查询数据 而这个请求地址同样是80端口所以被当前的nginx反向代理了。 查看nginx的conf目录下的nginx.conf文件 其中的关键配置如下 完整内容如下 #user nobody; worker_processes 1;events {worker_connections 1024; }http {include mime.types;default_type application/octet-stream;sendfile on;#tcp_nopush on;keepalive_timeout 65;upstream nginx-cluster{server 192.168.150.101:8081;}server {listen 80;server_name localhost;location /api {proxy_pass http://nginx-cluster;}location / {root html;index index.html index.htm;}error_page 500 502 503 504 /50x.html;location /50x.html {root html;}} }三、JVM进程缓存【第三级缓存】 本章实现红色框部分 3.1 本地进程缓存与分布式缓存的区别 3.2 本地进程缓存Caffeine Caffeine是一个基于|ava8开发的提供了近乎最佳命中率的高性能的本地缓存库。目前Spring内部的缓存使用的就是Caffeine。 GitHub地址:https://github.com/ben-manes/caffeine 第一步引入依赖 dependencygroupIdcom.github.ben-manes.caffeine/groupIdartifactIdcaffeine/artifactId/dependency第二步基本用法存/取数据 /**基本用法测试*/Testvoid testBasicOps() {// 1.创建缓存对象CacheString, String cache Caffeine.newBuilder().build();// 2.存数据cache.put(gf, 迪丽热巴);// 3.取数据不存在则返回nullString gf cache.getIfPresent(gf);System.out.println(gf gf);// 4.取数据不存在则去数据库查询String defaultGF cache.get(defaultGF, key - {// 这里可以去数据库根据 key查询valuereturn 柳岩;});System.out.println(defaultGF defaultGF);}第三步缓存驱逐策略 /**基于大小设置驱逐策略*/Testvoid testEvictByNum() throws InterruptedException {// 创建缓存对象CacheString, String cache Caffeine.newBuilder()// 设置缓存大小上限为 1.maximumSize(1).build();// 存数据cache.put(gf1, 柳岩);cache.put(gf2, 范冰冰);cache.put(gf3, 迪丽热巴);// 延迟10ms给清理线程一点时间Thread.sleep(10L);// 获取数据System.out.println(gf1: cache.getIfPresent(gf1));System.out.println(gf2: cache.getIfPresent(gf2));System.out.println(gf3: cache.getIfPresent(gf3));}结果 gf1: null gf2: null gf3: 迪丽热巴/**基于时间设置驱逐策略*/Testvoid testEvictByTime() throws InterruptedException {// 创建缓存对象CacheString, String cache Caffeine.newBuilder().expireAfterWrite(Duration.ofSeconds(1)) // 设置缓存有效期为 1 秒.build();// 存数据cache.put(gf, 柳岩);// 获取数据System.out.println(gf: cache.getIfPresent(gf));// 休眠一会儿Thread.sleep(1200L);System.out.println(gf: cache.getIfPresent(gf));} } 结果 gf: 柳岩 gf: null3.3 案例 第一步新建一个Config类 /*** 初始化本地缓存Caffeine*/ Configuration public class CaffeineConfig {/*** item商品的缓存* 缓存初始大小100* 缓存上限10000*/Beanpublic CacheLong, Item itemCache(){return Caffeine.newBuilder().initialCapacity(100).maximumSize(10_000).build();}/*** stock库存的缓存* 缓存初始大小100* 缓存上限10000*/Beanpublic CacheLong, ItemStock stockCache(){return Caffeine.newBuilder().initialCapacity(100).maximumSize(10_000).build();} }第二步编写业务代码 RestController RequestMapping(item) public class ItemController {Autowiredprivate IItemService itemService;Autowiredprivate IItemStockService stockService;Autowiredprivate CacheLong, Item itemCache;Autowiredprivate CacheLong, ItemStock stockCache;GetMapping(/{id})public Item findById(PathVariable(id) Long id) {// 优先根据item缓存的id查没有再去去数据库查return itemCache.get(id, key - itemService.query().ne(status, 3).eq(id, key).one());}GetMapping(/stock/{id})public ItemStock findStockById(PathVariable(id) Long id) {// 优先根据stock缓存的id查没有再去去数据库查return stockCache.get(id, key - stockService.getById(id));} }第三步启动服务第一次查询 http://localhost:8081/item/10001 控制台会出现查询语句日志再次查询并没有查询语句日志说明数据已经到缓存中了。 四、Nginx编程Lua语法 4.1 初识Lua CenOS自带Loa因此不用安装 可以使用lua命令直接打开编辑 4.2 变量 上面的local表示局部变量 字符串拼接是用..例如local str hello .. world! 4.3 循环 4.4 条件控制和函数 local arr {java,lua} local arr1local function printArr(arr)if (not arr) thenprint(数组不能为空)return nilendfor i,val in ipairs(arr)doprint(val)end endprintArr(arr) printArr(arr1)输出 [rootiZ2ze1r1nnqykr8zfme6cjZ tmp]# vi hello.lua [rootiZ2ze1r1nnqykr8zfme6cjZ tmp]# lua hello.lua java lua 数组不能为空五、多级缓存 本章实现红色框部分 5.1 安装OpenResty 5.1.1 安装 首先你的Linux虚拟机必须联网 安装开发库 首先要安装OpenResty的依赖开发库执行命令 yum install -y pcre-devel openssl-devel gcc --skip-broken安装OpenResty仓库 你可以在你的 CentOS 系统中添加 openresty 仓库这样就可以便于未来安装或更新我们的软件包通过 yum check-update 命令。运行下面的命令就可以添加我们的仓库 yum-config-manager --add-repo https://openresty.org/package/centos/openresty.repo如果提示说命令不存在则运行 yum install -y yum-utils 然后再重复上面的命令 安装OpenResty 然后就可以像下面这样安装软件包比如 openresty yum install -y openresty安装opm工具 opm是OpenResty的一个管理工具可以帮助我们安装一个第三方的Lua模块。 如果你想安装命令行工具 opm那么可以像下面这样安装 openresty-opm 包 yum install -y openresty-opm目录结构 默认情况下OpenResty安装的目录是/usr/local/openresty 看到里面的nginx目录了吗OpenResty就是在Nginx基础上集成了一些Lua模块。 配置nginx的环境变量 打开配置文件 vi /etc/profile在最下面加入两行 export NGINX_HOME/usr/local/openresty/nginx export PATH${NGINX_HOME}/sbin:$PATHNGINX_HOME后面是OpenResty安装目录下的nginx的目录 然后让配置生效 source /etc/profile5.1.2 启动和运行 OpenResty底层是基于Nginx的查看OpenResty目录的nginx目录结构与windows中安装的nginx基本一致 所以运行方式与nginx基本一致 # 启动nginx nginx # 重新加载配置 nginx -s reload # 停止 nginx -s stopnginx的默认配置文件注释太多影响后续我们的编辑这里将nginx.conf中的注释部分删除保留有效部分。 修改/usr/local/openresty/nginx/conf/nginx.conf文件内容如下 #user nobody; worker_processes 1; error_log logs/error.log;events {worker_connections 1024; }http {include mime.types;default_type application/octet-stream;sendfile on;keepalive_timeout 65;server {listen 8081;server_name localhost;location / {root html;index index.html index.htm;}error_page 500 502 503 504 /50x.html;location /50x.html {root html;}} }在Linux的控制台输入命令以启动nginx nginx然后通过ps -ef | grep nginx查看 然后访问页面http://192.168.150.101:8081注意ip地址替换为你自己的虚拟机IP 5.2 OpenResty快速入门 第一步修改nginx.conf文件 加载OpenResty的lua模块 #lua 模块 lua_package_path /usr/local/openresty/lualib/?.lua;;; #c模块 lua_package_cpath /usr/local/openresty/lualib/?.so;;; 在nginx.conf的server下面添加对/api/item这个路径的监听 location /api/item{#响应类型这里返回jsondefault_type application/json;#响应数据由 lua/item.lua这个文件来决定content_by_lua_file lua/item.lua; }nginx.conf #user nobody; worker_processes 1; error_log logs/error.log;events {worker_connections 1024; }http {include mime.types;default_type application/octet-stream;sendfile on;keepalive_timeout 65;# lua 模块lua_package_path /usr/local/openresty/lualib/?.lua;;;# c模块 lua_package_cpath /usr/local/openresty/lualib/?.so;;;server {listen 8081;server_name localhost;# 监听反向代理来的请求/api/itemlocation /api/item{# 响应类型这里返回jsondefault_type application/json;# 响应数据由 lua/item.lua这个文件来决定content_by_lua_file lua/item.lua;}location / {root html;index index.html index.htm;}error_page 500 502 503 504 /50x.html;location /50x.html {root html;}} }第二步编写item.lua的代码 在nginx目录下创建一个lua/item.lua文件 [rootiZ2ze1r1nnqykr8zfme6cjZ openresty]# cd /usr/local/openresty/nginx [rootiZ2ze1r1nnqykr8zfme6cjZ nginx]# mkdir lua [rootiZ2ze1r1nnqykr8zfme6cjZ nginx]# touch lua/item.lua编写业务内容 -- 返回假数据这里的ngx.say()函数就是写数据到Response中 ngx.say({id:10001,name:SALSA AIR,title:RIMOWA 2666寸托运箱拉杆箱 SALSA AIR系列果绿色 820.70.36.4,price:17900,image:https://m.360buyimg.com/mobilecms/s720x720_jfs/t6934/364/1195375010/84676/e9f2c55f/597ece38N0ddcbc77.jpg!q70.jpg.webp,category:拉杆箱,brand:RIMOWA,spec:,status:1,createTime:2019-04-30T16:00:00.00000:00,updateTime:2019-04-30T16:00:00.00000:00,stock:2999,sold:31290})重新加载配置 nginx -s reload刷新http://localhost/item.html?id10001查看页面数据已经修改如下 如果不能成功检查本机和虚拟机的配置nginx配置文件然后重启启动nginx命令为start nginx【windows】或者nginx【CentOS】 上述的总结流程 5.3 请求参数处理 修改nginx.conf注意location ~ /api/item/(\d) #user nobody; worker_processes 1; error_log logs/error.log;events {worker_connections 1024; }http {include mime.types;default_type application/octet-stream;sendfile on;keepalive_timeout 65;# lua 模块lua_package_path /usr/local/openresty/lualib/?.lua;;;# c模块 lua_package_cpath /usr/local/openresty/lualib/?.so;;;server {listen 8081;server_name localhost;# 监听反向代理来的请求/api/itemlocation ~ /api/item/(\d){# 响应类型这里返回jsondefault_type application/json;# 响应数据由 lua/item.lua这个文件来决定content_by_lua_file lua/item.lua;}location / {root html;index index.html index.htm;}error_page 500 502 503 504 /50x.html;location /50x.html {root html;}} }修改item.lua解释id ngx.var[1]获取http://localhost/item.html?id10003的参数id: .. id ..将10003使用..拼接并返回给页面 -- 获取路径参数 local id ngx.var[1] -- 返回结果 ngx.say({id: .. id ..,name:SALSA AIR,title:RIMOWA 2666寸托运箱拉杆箱 SALSA AIR系列果绿色 820.70.36.4,price:17900,image:https://m.360buyimg.com/mobilecms/s720x720_jfs/t6934/364/1195375010/84676/e9f2c55f/597ece38N0ddcbc77.jpg!q70.jpg.webp,category:拉杆箱,brand:RIMOWA,spec:,status:1,createTime:2019-04-30T16:00:00.00000:00,updateTime:2019-04-30T16:00:00.00000:00,stock:2999,sold:31290})5.4 查询Tomcat 本节实现红色部分 5.4.1 nginx发送http请求 我们可以把http查询的请求封装为一个函数放到0penResty函数库中方便后期使用 在/usr/local/openresty/lualib目录下创建common.lua文件: vi /usr/local/openresty/lualib/common.lua 在common.lua中封装http查询的函数发起http请求 common.lua -- 封装函数发送http请求并解析响应 local function read_http(path, params)-- 发送http请求local resp ngx.location.capture(path,{method ngx.HTTP_GET,args params,})if not resp then-- 记录错误信息返回404ngx.log(ngx.ERR, http查询失败路径为 , path , , args: , args)ngx.exit(404)endreturn resp.body end -- 将方法导出 local _M { read_http read_http } return _M5.4.2 nginx发出请求后反向代理给tomcat 在nginx.conf的server下增加记得此IP地址要是你电脑主机的IP地址 # 反向代理给tomcatlocation /item {proxy_pass http://192.168.150.1:8081;}一定要注意如果你的主机IP与服务器IP不属于同一个局域网那么nginx无法访问你的地址因为你的地址是内网地址。因此要做cpolar内网穿透并将上述IP地址改成经内网穿透够的外网地址 5.4.3 编写item.lua业务获取从本地tomcat响应请求结果 将从tomcat查询到的数据进行拼接然后序列化返回给前端页面 -- 案例3 -- 导入common函数库【自己编写的】,common.lua在/usr/local/openresty/lualib目录下 local common require(common) local read_http common.read_http -- 导入cjson解析库,也是在/usr/local/openresty/lualib目录下,默认就有此文件 local cjson require(cjson)-- 获取路径参数 local id ngx.var[1] -- 查询商品信息 local itemJSON read_http(/item/ .. id, nil) -- 查询库存信息 local stockJSON read_http(/item/stock/ .. id, nil)-- JSON转化成lua的table local item cjson.decode(itemJSON) local stock cjson.decode(stockJSON) -- 组合数据 item.stock stock.stock item.sold stock.sold-- 把item序列化为json 返回结果 ngx.say(cjson.encode(item))输入http://localhost/item.html?id10001可以看到从本地tomcat查到数据并显示了 5.5 Tomcat集群的负载均衡 修改nginx.conf添加tomcat集群并使用hash $request_uri哈希运算保证每次查询同一个值到同一个tomcat中访问。 #user nobody; worker_processes 1; error_log logs/error.log;events {worker_connections 1024; }http {include mime.types;default_type application/octet-stream;sendfile on;keepalive_timeout 65;# lua 模块lua_package_path /usr/local/openresty/lualib/?.lua;;;# c模块 lua_package_cpath /usr/local/openresty/lualib/?.so;;;# 定义tomcat集群upstream tomcat-cluster {hash $request_uri;server 198.168.101.1:8081;server 198.168.101.1:8082;}server {listen 8081;server_name localhost;# 反向代理给tomcat集群tomcat-cluster在上面定义location /item {proxy_pass http://tomcat-cluster;}# 监听反向代理来的请求/api/itemlocation ~ /api/item/(\d){# 响应类型这里返回jsondefault_type application/json;# 响应数据由 lua/item.lua这个文件来决定content_by_lua_file lua/item.lua;}location / {root html;index index.html index.htm;}error_page 500 502 503 504 /50x.html;location /50x.html {root html;}} }5.6 Redis的冷启动与缓存预热 冷启动服务刚刚启动时Redis中并没有缓存如果所有商品数据都在第一次查询时添加缓存可能会给数据库带来较大压力。 缓存预热在实际开发中我们可以利用大数据统计用户访问的热点数据在项目启动时将这些热点数据提前查询并保存到Redis中。 利用Docker安装Redis docker run --name redis -p 6379:6379 -d redis redis-server --appendonly yes在item-service服务中引入Redis依赖 dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId/dependency配置Redis地址 spring:redis:host: 192.168.150.101编写初始化类 Component public class RedisHandler implements InitializingBean {Autowiredprivate StringRedisTemplate redisTemplate;Autowiredprivate IItemService itemService;Autowiredprivate IItemStockService stockService;// jason处理工具private static final ObjectMapper MAPPER new ObjectMapper();Overridepublic void afterPropertiesSet() throws Exception {// 初始化缓存// 1.查询商品信息ListItem itemList itemService.list();// 2.放入缓存for (Item item : itemList) {// 2.1.item序列化为JSONString json MAPPER.writeValueAsString(item);// 2.2.存入redisredisTemplate.opsForValue().set(item:id: item.getId(), json);}// 3.查询商品库存信息ListItemStock stockList stockService.list();// 4.放入缓存for (ItemStock stock : stockList) {// 2.1.item序列化为JSONString json MAPPER.writeValueAsString(stock);// 2.2.存入redisredisTemplate.opsForValue().set(item:stock:id: stock.getId(), json);}} } 查看redis数据已经放入缓存中 5.7 查询Rdeis缓存【第二级缓存】 本节实现红色部分 OpenResty提供了操作Redis的模块我们只需要引入该模块即可在/usr/local/openresty/lualib/common.lua中 引入redis模块并初始化redis对象封装函数用来释放redis连接其实是放入连接池封装函数从redis读数据并返回 演示关闭本地server服务因为上面redis缓存预热已经将数据放入到了redis中因此访问http://localhost/item.html?id10005可以查到数据【从redis缓存中查的】。 common.lua -- 1.引入redis模块/usr/local/openresty/lualib/resty/redis.lua local redis require(resty.redis) -- 初始化redis对象 local red redis:new() -- 设置redis超时时间:建立请求 发送请求 响应请求的超时时间 red:set_timeouts(1000,1000,1000)-- 2.关闭redis连接的工具方法其实是放入连接池 local function close_redis(red)local pool_max_idle_time 10000 -- 连接的空闲时间单位是毫秒local pool_size 100 --连接池大小local ok, err red:set_keepalive(pool_max_idle_time, pool_size)if not ok thenngx.log(ngx.ERR, 放入redis连接池失败: , err)end end-- 3.建立redis连接读数据 -- 查询redis的方法 ip和port是redis地址key是查询的key local function read_redis(ip, port, key)-- 获取一个连接local ok, err red:connect(ip, port)if not ok thenngx.log(ngx.ERR, 连接redis失败 : , err)return nilend-- 查询redislocal resp, err red:get(key)-- 查询失败处理if not resp thenngx.log(ngx.ERR, 查询Redis失败: , err, , key , key)end--得到的数据为空处理if resp ngx.null thenresp nilngx.log(ngx.ERR, 查询Redis数据为空, key , key)end-- 释放连接放入连接池close_redis(red)return resp end-- 4.封装函数发送http请求并解析响应 local function read_http(path, params)local resp ngx.location.capture(path,{method ngx.HTTP_GET,args params,})if not resp then-- 记录错误信息返回404ngx.log(ngx.ERR, http not found, path: , path , , args: , args)ngx.exit(404)endreturn resp.body end -- 5.将方法导出 local _M { read_http read_http, -- 记得加逗号read_redis read_redis } return _Mitem.lua -- 案例4:封装一个read_data实现先查询redis未命中再查tomact-- 1.导入common函数库【自己编写的】,common.lua在/usr/local/openresty/lualib目录下 local common require(common) local read_http common.read_http local read_redis common.read_redis -- 2.导入cjson解析库,也是在/usr/local/openresty/lualib目录下,默认就有此文件 local cjson require(cjson)-- 3.封装查询函数 -- reids请求参数tomcat的http请求路径参数 function read_data(key, path, params)-- 查询redislocal resp read_redis(127.0.0.1, 6379, key)-- 判断查询结果if not resp thenngx.log(redis查询失败尝试查询httpkey, key)-- redis查询失败去查询httpresp read_http(path, params)endreturn resp end-- 获取路径参数 local id ngx.var[1] -- 查询商品信息 local itemJSON read_data(item:id: .. id, /item/ .. id, nil) -- 查询库存信息 local stockJSON read_data(item:stock:id: .. id, /item/stock/ .. id, nil)-- JSON转化成lua的table local item cjson.decode(itemJSON) local stock cjson.decode(stockJSON) -- 组合数据 item.stock stock.stock item.sold stock.sold-- 把item序列化为json 返回结果 ngx.say(cjson.encode(item))5.8 Nginx本地缓存【第一级缓存】 0penResty为Nginx提供了shard dict的功能可以在nginx的多个worker之间共享数据实现缓存功能。 开启共享字典在nginx.conf的http下添加配置: # 共享字典也就是本地缓存名称叫做item_cache大小150m lua_shared_dict item_cache 150m; 操作共享字典 -- 获取本地缓存对象 local item_cache ngx.shared.item_cache -- 存储指定key、value、过期时间单位s默认为0表示永不过期 item_cache:set(key,value,1000) -- 读取 local val item_cache:get(key)实战 在nginx.conf中加入nginx本地缓存 http {# 共享字典也就是本地缓存名称叫做item_cache大小150mlua_shared_dict item_cache 150m; }编写item.lua -- 案例5: -- 1实现先查询nginx本地缓存未命中再查redis未命中再查tomact -- 2查询redis或tomcat成功后将数据写入本地缓存并设置有效期 -- 3商品的基本信息有效期30分钟库存信息有效期1分钟-- 1.导入common函数库【自己编写的】,common.lua在/usr/local/openresty/lualib目录下 local common require(common) local read_http common.read_http local read_redis common.read_redis -- 导入共享词典nginx本地缓存 -- 获取本地缓存对象 local item_cache ngx.shared.item_cache-- 2.导入cjson解析库,也是在/usr/local/openresty/lualib目录下,默认就有此文件 local cjson require(cjson)-- 3.封装查询函数 -- reids请求参数tomcat的http请求路径参数 function read_data(key, expire, path, params)-- 1查询nginx本地缓存local val item_cache:get(key)if not val thenngx.log(ngx.ERR, 本地缓存查询失败尝试查询rediskey, key)-- 2查询redisval read_redis(127.0.0.1, 6379, key)-- 判断查询结果if not val thenngx.log(ngx.ERR, redis查询失败尝试查询httpkey, key)-- 3redis查询失败去查询httpval read_http(path, params)endend-- 查询成功把数据写入本地缓存item_cache:set(key, val, expire)-- 返回数据return val end-- 获取路径参数 local id ngx.var[1] -- 查询商品信息 local itemJSON read_data(item:id: .. id, 1800, /item/ .. id, nil) -- 查询库存信息 local stockJSON read_data(item:stock:id: .. id, 60, /item/stock/ .. id, nil)-- JSON转化成lua的table local item cjson.decode(itemJSON) local stock cjson.decode(stockJSON) -- 组合数据 item.stock stock.stock item.sold stock.sold-- 把item序列化为json 返回结果 ngx.say(cjson.encode(item))查看日志第一次会将redis的数据放到nginx本地缓存当再次查询时直接从本地缓存中查询 六、缓存同步策略 当数据库进行修改时缓存的内容也要进行相应的修改因此需要完成数据同步。 6.1 常见缓存策略 6.2 安装Canal 下面我们就开启mysql的主从同步机制让Canal来模拟salve 6.2.1 开启MySQL主从 Canal是基于MySQL的主从同步功能因此必须先开启MySQL的主从功能才可以。 这里以之前用Docker运行的mysql为例 开启binlog 打开mysql容器挂载的日志文件我的在/tmp/mysql/conf目录: 修改文件 vi /tmp/mysql/conf/my.cnf添加内容 log-bin/var/lib/mysql/mysql-bin binlog-do-dbheima配置解读 log-bin/var/lib/mysql/mysql-bin设置binary log文件的存放地址和文件名叫做mysql-binbinlog-do-dbheima指定对哪个database记录binary log events这里记录heima这个库 最终效果/tmp/mysql/conf/my.cnf [mysqld] skip-name-resolve character_set_serverutf8 datadir/var/lib/mysql server-id1000 log-bin/var/lib/mysql/mysql-bin binlog-do-dbheima重启mysql容器可以看到多了一个mysql-bin.000001 设置用户权限 接下来添加一个仅用于数据同步的账户出于安全考虑这里仅提供对heima这个库的操作权限。 create user canal% IDENTIFIED by canal; GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT,SUPER ON *.* TO canal% identified by canal; FLUSH PRIVILEGES;可以看到创建了一个canal用户 重启mysql容器即可 docker restart mysql测试设置是否成功在mysql控制台或者Navicat中输入命令 show master status;6.2.2 安装Canal 创建网络 我们需要创建一个网络将MySQL、Canal、MQ放到同一个Docker网络中 docker network create heima让mysql加入这个网络 docker network connect heima mysql安装Canal 课前资料中提供了canal的镜像压缩包: 大家可以上传到虚拟机然后通过命令导入 docker load -i canal.tar然后运行命令创建Canal容器 docker run -p 11111:11111 --name canal \ -e canal.destinationsheima \ -e canal.instance.master.addressmysql:3306 \ -e canal.instance.dbUsernamecanal \ -e canal.instance.dbPasswordcanal \ -e canal.instance.connectionCharsetUTF-8 \ -e canal.instance.tsdb.enabletrue \ -e canal.instance.gtidonfalse \ -e canal.instance.filter.regexheima\\..* \ --network heima \ -d canal/canal-server:v1.1.5说明: -p 11111:11111这是canal的默认监听端口-e canal.destinationsheima所属集群名称-e canal.instance.master.addressmysql:3306数据库地址和端口因为mysql与canal同属一个网络因此可以用mysql代替IP地址。如果不知道mysql容器地址可以通过docker inspect 容器id来查看-e canal.instance.dbUsernamecanal数据库用户名-e canal.instance.dbPasswordcanal 数据库密码-e canal.instance.filter.regex要监听的表名称--network heima \将canal放入heima这个网络中 表名称监听支持的语法 mysql 数据解析关注的表Perl正则表达式. 多个正则之间以逗号(,)分隔转义符需要双斜杠(\\) 常见例子 1. 所有表.* or .*\\..* 2. canal schema下所有表 canal\\..* 3. canal下的以canal打头的表canal\\.canal.* 4. canal schema下的一张表canal.test1 5. 多个规则组合使用然后以逗号隔开canal\\..*,mysql.test1,mysql.test2 通过docker logs -f canal查看日志是否启动成功 Canal与mysql是否建立连接 通过docker exec -it canal bash进入canal容器内部 通过tail -f canal-server/logs/canal/canal.log查看canal运行日志 通过tail -f canal-server/logs/heima/heima.log查看其他运行日志 最后通过exit退出容器 6.3 监听Canal Canal提供了各种语言的客户端当Canal监听到binlog变化时会通知Canal的客户端。不过这里我们会使用GitHub上的第三方开源的canal-starter。地址:https://github.com/NormanGyllenhaal/canal-client 引入依赖 dependencygroupIdtop.javatool/groupIdartifactIdcanal-spring-boot-starter/artifactIdversion1.2.1-RELEASE/version /dependency编写配置 canal:destination: heima # canal实例名称要跟虚拟机上设置的destination一致server: 39.107.236.163:11111 # canal地址编写监听器监听canal消息 CanalTable(tb_item) Component public class ItemHandler implements EntryHandlerItem {Autowiredprivate RedisHandler redisHandler;Autowiredprivate CacheLong, Item itemCache;Overridepublic void insert(Item item) {// 写数据到JVM进程缓存itemCache.put(item.getId(), item);// 写数据到redisredisHandler.saveItem(item);}Overridepublic void update(Item before, Item after) {// 写数据到JVM进程缓存itemCache.put(after.getId(), after);// 写数据到redisredisHandler.saveItem(after);}Overridepublic void delete(Item item) {// 删除数据到JVM进程缓存itemCache.invalidate(item.getId());// 删除数据到redisredisHandler.deleteItemById(item.getId());} }RedisHandler.java Component public class RedisHandler implements InitializingBean {Autowiredprivate StringRedisTemplate redisTemplate;Autowiredprivate IItemService itemService;Autowiredprivate IItemStockService stockService;// jason处理工具private static final ObjectMapper MAPPER new ObjectMapper();Overridepublic void afterPropertiesSet() throws Exception {// 初始化缓存// 1.查询商品信息ListItem itemList itemService.list();// 2.放入缓存for (Item item : itemList) {// 2.1.item序列化为JSONString json MAPPER.writeValueAsString(item);// 2.2.存入redisredisTemplate.opsForValue().set(item:id: item.getId(), json);}// 3.查询商品库存信息ListItemStock stockList stockService.list();// 4.放入缓存for (ItemStock stock : stockList) {// 2.1.item序列化为JSONString json MAPPER.writeValueAsString(stock);// 2.2.存入redisredisTemplate.opsForValue().set(item:stock:id: stock.getId(), json);}}public void saveItem(Item item) {try {String json MAPPER.writeValueAsString(item);redisTemplate.opsForValue().set(item:id: item.getId(), json);} catch (JsonProcessingException e) {throw new RuntimeException(e);}}public void deleteItemById(Long id) {redisTemplate.delete(item:id: id);} }Canal推送给canal-client的是被修改的这一行数据(row)而我们引入的canal-client则会帮我们把行数据封装到ltem实体类中。这个过程中需要知道数据库与实体的映射关系要用到PA的几个注解: Data TableName(tb_item) public class Item {TableId(type IdType.AUTO)Idprivate Long id;//商品idColumn(name name)private String name;//商品名称private String title;//商品标题private Long price;//价格分private String image;//商品图片private String category;//分类名称private String brand;//品牌名称private String spec;//规格private Integer status;//商品状态 1-正常2-下架private Date createTime;//创建时间private Date updateTime;//更新时间TableField(exist false)Transientprivate Integer stock;TableField(exist false)Transientprivate Integer sold; }测试修改10001的价格发现本机控台日志消息变化并且访问http://localhost/item.html?id10001也发生变化 八、多级缓存总结 九、额外说明cpolar内网穿透将私网暴露成公网供外部使用 第一步下载并注册账号cpolar官方https://www.cpolar.com/ 第二步配置隧道 第二步查看公网地址
http://www.zqtcl.cn/news/634733/

相关文章:

  • 做职业资格考试的网站有哪些网页游戏排行榜2024前十名
  • 网站设计方案怎么写wordpress仿站软件
  • 汕头建站模板系统北京有哪些电商平台公司
  • 深圳网站建设zhaoseo小包工头接活的平台
  • 电商平面设计前景如何seo推广什么意思
  • 网站解析不了wordpress 密码失败
  • 临沂企业建站系统模板扮家家室内设计
  • 做简单网站用什么软件网站开发国外研究现状
  • 江苏seo推广网站建设湖南软件定制开发
  • 台州商务网站手机端seo
  • 网站的切换语言都是怎么做的有哪些开发网站公司
  • 上海人才中心网站湖州建设公司网站
  • 网站的前台后台网站建设公司新报
  • 菜鸟式网站建设图书深圳建站公司好坏
  • 品牌网站建设熊掌号一级消防工程师考试通过率多少
  • 网站建设淘宝客模板湖口网站建设
  • 拱墅区建设局网站做设计的搜素材上什么网站
  • 济南烨铭网站建设外贸建网站免费模板
  • 那些网站可以做反链浏览器网站大全
  • 泉州网站建设推广企业网页兼容性站点
  • 怎样做视频上网站赚钱推广计划怎么做推广是什么
  • 台州外贸网站建设做网站开发一般用什么语言
  • 咸阳做网站的公司漯河网做网站
  • 红酒网站模板下载做网站加推广
  • 免费网站服务器域名在线手机网站建设
  • 北京网站ui设计公司在线设计装修
  • 大学生网站作业北京网站优化技术
  • 静安区网站开发固原网络推广
  • WordPress网站修改志成网站设计制作
  • 做网站需要注意的昭通网站seo优化