第四节:千万级流量下商品详情页的全套方案和压测分析

一.   整体业务再次分析

流程如下:

管理员-详细分析:

 1. 管理员在后台,增加和修改商品,利用Thymeleaf 来生成一个静态页面并将该页面上传到文件服务器上。

    PS:这个地方生成的是一个通用的静态页,敏感数据比如 价格,商品名称等,通过占位符来替换。

   2. 将敏感数据同步到redis中,如下:

    (1).  先创建一个布隆过滤器 【BF.RESERVE bf_taxi 0.01 10000 NONSCALING】 , 仅此一次

    (2).  将商品的主键编号(比如:1001),添加到该布隆过滤器中  【BF.ADD bf_taxi 1001

    (3).  利用redis的hash结构,存储商品的敏感信息

       A.  hashId :  hkey_商品主键编号, 比如  hey_1001

       B.  key-value键值对:   url 对应需要抓取的页面的地址  http://8.130.72.40/index.html; amount 对应该商品的价格  4999

    【hset hkey_1001 url http://8.130.72.40/index.html amount 4999

   (4). 后续如果只修改价格,那么只需要修改redis中的敏感数据即可。

用户请求-详细分析:

   1.  从请求的url (如:http://test2.hi-whales.com:8888/template?id=1002)中获取商品主键编号 1002

   2.  检验该reqId是否为空,如果为空,直接返回。

   3.  校验布隆过滤器中是否存在该reqId,如果不存在,则是非法请求,直接返回。

   4.  拼接得到hash结构中 hashId,即 hkey_1001 

   5.  根据该hashId,获取对应的所有key-value集合,判断是否存在key-value集合,不存在,直接返回; 再判断是否存在amount数据,不存在直接返回

   6.  根据reqId先在本地缓存中获取模板内容,如果本地缓存中没有,则根据redis中的url去远程抓取,抓取失败,则直接返回;如果抓取成功,则往本地缓存中存一份。

   7. 根据模板引擎,渲染模板中的敏感数据,返回页面。

 

二.  实操

1. 封装 requestTemplateRendering.lua 文件

  用来抓取网页文件,放到openresty安装目录下的script文件夹中。(其中:openresty/lualib/resty/目录下新增的库有:http.lua,http_headers.lua  、template.lua、html.lua、microbenchmark.lua、rediscluster.lua,xmodem.lua)前一节详细讲过了

查看代码
local template = require("resty.template")
local lrredis = require("redisUtils")
local lgethtml = require("requestHtml")
local lreqparm = require("requestUtils")
--获取请求参数
local reqParams = lreqparm.getRequestParam()
-- 定义本地缓存
local html_template_cache = ngx.shared.html_template_cache

-- 获取请求ID的参数
local reqId = reqParams["id"];

ngx.log(ngx.INFO, "requestID:", reqId);
-- 校验参数
if reqId==nil then
    ngx.say("缺少ID参数");
    return
end

-- 布隆过滤器检查id是否存在
local bfexist = lrredis.bfexists("bf_taxi",reqId)
ngx.log(ngx.INFO, "布隆过滤器检验:", bfexist)

-- 校验key不存在直接返回
if bfexist==0 then
    ngx.say("布隆过滤器校验key不存在...")
    return
end

-- 拼接hget的key
local hgetkey = "hkey_".. reqId

-- 通过hget获取map的所有数据
local templateData = lrredis.hgetall(hgetkey);
if next(templateData) == nil then
    ngx.say("redis没有存储数据...")
    return
end


--获取模板价格数据
local amount = templateData["amount"]
ngx.log(ngx.INFO, "amount:", amount)
if amount == nil then
    ngx.say("价格数据未配置");
    return
end


-- 获取本地缓存对象
ngx.log(ngx.INFO, "开始从缓存中获取模板数据----")
local html_template = html_template_cache:get(reqId)
-- 判断本地缓存是否存在
if html_template == nil then
    -- 获取模板url中的数据
        ngx.log(ngx.INFO, "缓存中不存在数据开始远程获取模板")
    local url = templateData["url"]
        ngx.log(ngx.INFO, "从缓存中获取的url地址:", url)
    if url == nil then
        ngx.say("URL路径未配置");
        return
    end
        -- 抓取远程url的html
        ngx.log(ngx.INFO, "开始抓取模板数据:", url)
    local returnResult = lgethtml.gethtml(url);
    -- 判断抓取模板是否正常
    if returnResult==nil then
        ngx.say("抓取URL失败...");
        return
    end
        -- 判断html状态
    if returnResult.status==200 then
        html_template = returnResult.body
                --设置模板缓存为一小时
                ngx.log(ngx.INFO, "将模板数据加入到本地缓存")
        html_template_cache:set(reqId,html_template,60 * 60)
    end
end
ngx.log(ngx.INFO, "缓存中获取模板数据结束----")

-- 模板渲染
--编译得到一个lua函数

local func = template.compile(html_template)
local context = {amount=amount}
ngx.log(ngx.INFO, "开始渲染模板数据")
--执行函数,得到渲染之后的内容
local content = func(context)

--通过ngx API输出
ngx.say(content)

 

2. 配置nginx

(1). 配置本地缓存

# 配置模板缓存
lua_shared_dict html_template_cache 10m;

 

(2). 配置server

# 6. 千万流量的全套方案
    server {
        listen 8888;
        charset utf-8;
        server_name test2.hi-whales.com;
        
        # 配置路径重写
        location ~* \.(gif|jpg|jpeg|png|css|js|ico)$ {
           rewrite ^/(.*) http://test1.hi-whales.com/$1 permanent;
        }
    
        # 配置获取网页
        location /template {
            default_type text/html;
            content_by_lua_file /root/mydevelop/openresty/script/requestTemplateRendering.lua;     
            # content_by_lua '    ngx.say("<p>hhhhh")     ';                        
        }
    
        #删除本地缓存
        location /delete {
             default_type text/html;
             content_by_lua '
              local lreqparm = require("requestUtils")
              --获取请求参数
              local reqParams = lreqparm.getRequestParam()
               -- 定义本地缓存
              local html_template_cache = ngx.shared.html_template_cache

                -- 获取请求ID的参数
              local reqId = reqParams["id"];

              ngx.log(ngx.INFO, "requestID:", reqId);
              -- 校验参数
              if reqId==nil then
                  ngx.say("缺少ID参数");
                  return
               end
               -- 获取本地缓存对象
               html_template_cache:delete(reqId);
               ngx.say("清除缓存成功");
            ';
        }


   }

 

3.初始化数据

 (ps: 8.130.72.40 为nginx所在服务ip的地址,用来代理两个静态资源服务器,这里不要写域名,写ip地址,否则抓取的时候,会报错无法解析域名)

#连接redis
redis-cli -c -h 127.0.0.1 -p 6380 -a 123456
# 在redis中添加一个布隆过滤器 错误率是0.01 数量是1万个
BF.RESERVE bf_taxi 0.01 10000 NONSCALING
# 在bf_test 的布隆过滤器添加一个key
BF.ADD bf_taxi 1001
#检查数据是否存在
BF.EXISTS bf_taxi 1001
# 添加URL以及价格
hset hkey_1001 url http://8.130.72.40/index.html amount 4999

 

4. 访问

http://test2.hi-whales.com:8888/template?id=1001   ,如下图,大功告成

 

访问:http://test2.hi-whales.com:8888/template?id=1002

 

 

三. 压测

 

改造后 和 改造前对比

 

 

 

 

 

 

 

 

 

!

  • 作       者 : Yaopengfei(姚鹏飞)
  • 博客地址 : http://www.cnblogs.com/yaopengfei/
  • 声     明1 : 如有错误,欢迎讨论,请勿谩骂^_^。
  • 声     明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
 
posted @ 2023-08-02 10:11  Yaopengfei  阅读(92)  评论(0编辑  收藏  举报