OpenResty | nginx内部发送请求



一. http请求的API

关于OpenResty如何安装和编写业务逻辑可以参考这篇文章:点击跳转
nginx提供了内部API用以发送http请求:

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

注意:args相当本质上还是利用查询字符串的方式进行参数传递,而body则是将要传递的参数存放在请求体中,两种传参方式最好不要一起使用。
返回的响应内容包括:

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

注意:这里的path是路径,并不包含IP和端口。这个请求会被nginx内部的server监听并处理。

但是我们希望这个请求发送到目标服务器,所以还需要在nginx的配置文件中编写一个server来对这个路径做反向代理:

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

原理如图:
请添加图片描述

二. 封装http工具函数

之前我们说过,OpenResty启动时会加载以下两个目录中的工具文件:
请添加图片描述
所以,自定义的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是函数库的文件名。
使用演示:
件,利用刚刚封装的函数库实现对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发送请求![请添加图片描述](https://img-blog.csdnimg.cn/75a041eed69d4a40946d052e9be545e2.png)
查询商品库存
local itemStockJSON = read_http("/item/stock/".. id, nil)

这里查询到的结果是json字符串,并且包含商品、库存两个json字符串,页面最终需要的是把两个json拼接为一个json:
请添加图片描述
这就需要我们先把JSON变为lua的table,完成数据整合后,再转为JSON。

扫描二维码关注公众号,回复: 14779925 查看本文章

三. CJSON工具类

OpenResty提供了一个cjson的模块用来处理JSON的序列化和反序列化。
官方地址: 点击跳转
1.引入cjson模块:

local cjson = require "cjson"

2.序列化:

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

3.反序列化:

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

四. hash均衡

而实际开发中,tomcat一定是集群模式:
请添加图片描述
因此,OpenResty需要对tomcat集群做负载均衡。

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

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

你看,因为轮询的原因,第一次查询8081形成的JVM缓存并未生效,直到下一次再次访问到8081时才可以生效,缓存命中率太低了。
怎么办?
如果能让同一个商品,每次查询时都访问同一个tomcat服务,那么JVM缓存就一定能生效了。
也就是说,我们需要根据商品id做负载均衡,而不是轮询。

(1) 原理

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

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

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

(2) 实现

修改/usr/local/openresty/nginx/conf/nginx.conf文件,实现基于ID做负载均衡。
首先,定义tomcat集群,并设置基于路径做负载均衡:

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

注意:$request_uri就是请求路径,hash对请求路径进行哈希运算
然后,修改对tomcat服务的反向代理,目标指向tomcat集群:

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

重新加载OpenResty

nginx -s reload

猜你喜欢

转载自blog.csdn.net/Siebert_Angers/article/details/129061676