HM-SpringCloud微服务系列11.3.3【实现多级缓存(2)】

4. 查询Tomcat[案例]

拿到商品ID后,本应去缓存中查询商品信息,不过目前我们还未建立nginx、redis缓存。因此,这里我们先根据商品id去tomcat查询商品信息。我们实现如图红框部分:
image
需要注意的是,我们的OpenResty是在虚拟机,Tomcat是在Windows电脑上。两者IP一定不要搞错了。
这里有一个结论如下:(前提是win防火墙关闭,方法参考https://jingyan.baidu.com/article/624e74590c1c4e34e8ba5a06.html
image
image

4.1 案例需求

image

4.2 发送http请求的API

nginx提供了内部API用以发送http请求:

local resp = ngx.location.capture("/path",{
    method = ngx.HTTP_GET,   -- 请求方式
    args = {a=1,b=2},  -- get方式传参数
    body = "c=3&d=4" -- post方式传参数
})

返回的响应内容包括:

  • resp.status:响应状态码
  • resp.header:响应头,是一个table
  • resp.body:响应体,就是响应数据

注意:这里的path是路径,并不包含IP和端口。这个请求会被nginx内部的server监听并处理。
但是我们希望这个请求发送到Tomcat服务器,所以还需要编写一个server来对这个路径做反向代理:

location /path {
	# 这里是windows电脑的ip和Java服务端口,需要确保windows防火墙处于关闭状态
	proxy_pass http://10.193.193.1:8081;
}

原理如图:
image

4.3 封装http工具

下面,我们封装一个发送Http请求的工具,基于ngx.location.capture来实现查询tomcat。

4.3.1 添加反向代理到windows的Java服务

因为item-service中的接口都是/item开头,所以我们监听/item路径,代理到windows上的tomcat服务。

修改 /usr/local/openresty/nginx/conf/nginx.conf文件,添加一个location:
image
以后,只要我们调用ngx.location.capture("/item"),就一定能发送请求到windows的tomcat服务。

4.3.2 封装工具类

之前我们说过,OpenResty启动时会加载以下两个目录中的工具文件:

image-20220503214820115

所以,自定义的http工具也需要放到这个目录下。

/usr/local/openresty/lualib目录下,新建一个common.lua文件:

vi /usr/local/openresty/lualib/common.lua

编写内容如下:

-- 封装函数,发送http请求,并解析响应
local function read_http(path, params)
    local resp = ngx.location.capture(path,{
        method = ngx.HTTP_GET,
        args = params,
    })
    if not resp then
        -- 记录错误信息,返回404
        ngx.log(ngx.ERR, "http请求查询失败, path: ", path , ", args: ", args)
        ngx.exit(404)
    end
    return resp.body
end
-- 将方法导出
local _M = {  
    read_http = read_http
}  
return _M

这个工具将read_http函数封装到_M这个table类型的变量中,并且返回,这类似于导出。

使用的时候,可以利用require('common')来导入该函数库,这里的common是函数库的文件名。


image-20220503214924526

image-20220503214942374

image-20220503215239291

4.3.3 实现商品查询(使用http函数查询数据)

刚才已经把http查询的请求封装为一个函数,放到OpenResty函数库中,接下来就可以使用这个库了

image-20220504103921293

我们修改/usr/local/openresty/lua/item.lua文件,利用刚刚封装的函数库实现对tomcat的查询:

-- 引入自定义common工具模块,返回值是common中返回的 _M
local common = require("common")
-- 从 common中获取read_http这个函数
local read_http = common.read_http
-- 获取路径参数
local id = ngx.var[1]
-- 根据id查询商品
local itemJSON = read_http("/item/".. id, nil)
-- 根据id查询商品库存
local itemStockJSON = read_http("/item/stock/".. id, nil)

image-20220504105540465

先返回一个JSON结果看看此前操作连通了没

image-20220504110147629

image-20220504114359297

image-20220504114330049

这里查询到的结果是json字符串,并且包含商品、库存两个json字符串,页面最终需要的是把两个json拼接为一个json:

image-20220504103608650

这就需要我们先把JSON变为lua的table,完成数据整合后,再转为JSON。

将两部分数据组装,需要用到JSON处理函数库,下一小结讲。

商品管理项目启动,参考<HM-SpringCloud微服务系列11.1.2【案例导入】 - yub4by - 博客园 (cnblogs.com)>

image-20220504104925371

image-20220504105013556

image-20220504105108006

image-20220504105129663

注意:要是此前根据虚拟机ip推算找到本地主机ip失败的话,可以直接将其改为本地主机真实的ip

image-20220504114120672

image-20220504114244312

4.4 JSON结果处理:CJSON工具类

OpenResty提供(内置)了一个cjson的模块用来处理JSON的序列化和反序列化。

官方地址: https://github.com/openresty/lua-cjson/

image-20220504123341347

4.4.1 引入cjson模块

local cjson = require "cjson"

4.4.2 序列化

local obj = {
    name = 'jack',
    age = 21
}
-- 把 table 序列化为 json
local json = cjson.encode(obj)

4.4.3 反序列化

local json = '{"name": "jack", "age": 21}'
-- 反序列化 json为 table
local obj = cjson.decode(json);
print(obj.name)

image-20220504125854084

-- 导入common函数库
local common = require('common')
local read_http = common.read_http
-- 导入cjson库
local cjson = require('cjson')

-- 获取路径参数
local id = ngx.var[1]
-- 根据id查询商品
local itemJSON = read_http("/item/".. id, nil)
-- 根据id查询商品库存
local itemStockJSON = 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))

image-20220504125924700

image-20220504130051145

PS :可以从4.3.3访问测试结果图中看到,当时是没有销量和库存的,而只有item本身的一些数据;现在有了

4.5 Tomcat的负载均衡(基于ID)

image-20220504145347416

刚才的代码中,我们的tomcat是单机部署(如上图)。而实际开发中,tomcat一定是集群模式(如下图)

image-20220504145433613

因此,OpenResty需要对tomcat集群做负载均衡。

而默认的负载均衡规则是轮询模式,当我们查询/item/10001时:

  • 第一次会访问8081端口的tomcat服务,在该服务内部就形成了JVM进程缓存
  • 第二次会访问8082端口的tomcat服务,该服务内部没有JVM缓存(因为JVM缓存无法共享),会查询数据库
  • ...

你看,因为轮询的原因,第一次查询8081形成的JVM缓存并未生效,直到下一次再次访问到8081时才可以生效,缓存命中率太低了。

怎么办?

如果能让同一个商品,每次查询时都访问同一个tomcat服务,那么JVM缓存就一定能生效了。

也就是说,我们需要根据商品id做负载均衡,而不是轮询。

4.5.1 原理

nginx提供了基于请求路径做负载均衡的算法:

nginx根据请求路径做hash运算,把得到的数值对tomcat服务的数量取余,余数是几,就访问第几个服务,实现负载均衡。

例如:

  • 我们的请求路径是 /item/10001
  • tomcat总数为2台(8081、8082)
  • 对请求路径/item/1001做hash运算求余的结果为1
  • 则访问第一个tomcat服务,也就是8081

只要id不变,每次hash运算结果也不会变,那就可以保证同一个商品,一直访问同一个tomcat服务,确保JVM缓存生效。

4.5.2 实现

修改/usr/local/openresty/nginx/conf/nginx.conf文件,实现基于ID做负载均衡。

首先,定义tomcat集群,并设置基于路径做负载均衡:

upstream tomcat-cluster {
    hash $request_uri;
    server 192.168.71.67:8081;
    server 192.168.71.67:8082;
}

然后,修改对tomcat服务的反向代理,目标指向tomcat集群:

location /item {
    proxy_pass http://tomcat-cluster;
}

重新加载OpenResty

nginx -s reload

image-20220504151850627

image-20220504151909533

4.5.3 测试

idea启动两台tomcat服务:

image-20220504152018764

image-20220504152310487

同时启动:

image-20220504152352757

清空启动日志后,再次访问页面,可以看到不同id的商品,访问到了不同的tomcat服务:

  1. http://localhost/item.html?id=10002

image-20220504152602911

image-20220504152629868

image-20220504152657884

image-20220504152754693

image-20220504152835555

8082没有日志,说明还是走了8081(即不是通过轮询)

image-20220504152927060

可以看到8082也没有日志,因为8082有JVM进程缓存了,不需要再重新查了

  1. http://localhost/item.html?id=10003

    第一次访问

image-20220504153153302

image-20220504153232358

image-20220504153250374

换了商品id后再访问,走的时候8082

刷新页面,重新访问

image-20220504153431804

在此查看idea控制台日志情况,发现与第二次访问10002时一致

image-20220504153531738

image-20220504153541573

这次是没走8001,继续走8002,而8002中已有JVM进程缓存,不需要重新查询所以没有日志

posted @   yub4by  阅读(32)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示