JD亿级流量电商平台商品模块业务与技术详解实战

前言:

各位老铁们好,今天2B哥给大伙来介绍下平常咱们逛京东时候打开商品详细页其中实现的技术。是不是很吊哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈

看2B哥的商品图是不是更吊,最近在家办公,除了写代码就疯狂用它了。


图片

大家不要笑,其实商品中心里技术难度有二:

一、商品搜索

二、商品详细页

今天和大家重点讲商品详细页(商品搜索也想学可以看文章末尾福利)。

商品中心业务:

一件商品从他发布到下架大致流程:

图片

商品分类+商品(1对多)

图片

t_catalog商品分类表

参数 名称 类型 备注
id 自增长 int 唯一
name 分类名称 String
code 编码,简码 String 唯一
pid 父ID Int ~~ ~~
order1 排序 Int
type 类型 String 类型,a:文章目录;p:产品目录
showInNav 是否显示在首页的导航条上 String y:显示,n:不显示;默认n。仅对type=p有效

t_product商品表

字段名 数据类型 默认值 描述
id int 唯一,商品ID
catalogID varchar 商品类别catalog表id
name varchar 商品名称
introduce text 商品简介
price DECIMAL(9,2) 定价
nowPrice DECIMAL(9,2) 现价
picture String 小图片地址
score Int 0 赠送积分
maxPicture String ~~ ~~ 大图片地址
isnew String n 是否新品。n:否,y:是
sale String n 是否特价。n:否,y:是
activityID String 绑定的活动ID
giftID String 绑定的礼品ID
hit int 0 浏览次数
unit String 商品单位。默认“item:件”
createAccount String 录入人账号
createtime datetime 录入时间
updateAccount String 最后修改人账号
updatetime String 最后修改时间
isTimePromotion String n 是否限时促销。n:否,y:是
status Int 0 商品状态。1:新增,2:已上架,3:已下架
productHTML LONGTEXT 商品介绍
images String 商品多张图片集合,逗号分割
sellcount Int 销售数量 默认:0
stock Int 剩余库存数 默认:0
searchKey String 搜索关键词
title String 页面标题
description String 页面描述
keywords String 页面关键词

分类+商品+品牌:

图片

我要查看苹果所有的产品(手机苹果、电脑苹果)需求

图片

分类+商品+品牌+属性:

图片

更加快速找到我们想购买的商品

图片

t_attribute商品属性(参数)表

参数 名称 类型 备注
id 自增长 int 唯一
name 属性/参数名称 String
catalogID 类别ID Int
pid 父ID Int 该字段具有双重含义。0表示属性大类,一般情况下产品只有两层attribute,一层为属性名称类别,一层为属性;-1:参数
order1 排序 Int

t_attribute_link商品属性(参数)中间表

参数 名称 类型 备注
id 自增长 int 唯一
attrID 属性(参数)ID Int
productID 商品ID Int
value 商品参数值 String 名称从属性表中取得

分类+商品+品牌+属性+规格:

t_spec商品规格表

字段名 数据类型 默认值 描述
id int 唯一
productID String 商品ID
specColor String 颜色
specSize String 尺寸
specStock Int 此规格的商品库存数
specPrice Double 此规格的商品价格
specStatus String y:显示规格;n:不显示规格

图片

SPU :

SPU(Standard Product Unit):标准化产品单元。是商品信息聚合的最小单位,是一组可复用、易检索的标准化信息的集合,该集合描述了一个产品的特性。通俗点讲,属性值、特性相同的商品就可以称为一个SPU。

举例:Iphone6

SKU:

SKU=Stock Keeping Unit(库存量单位)。即库存进出计量的基本单元,可以是以件,盒,托盘等为单位。

举例:Iphonex+土豪金+32G

商品中心技术实现(详细页):

图片


商品详细页:商品+图片+库存+店铺+商品相关的信息

特点:QPS高、相应速度高、变化少(商品名称和属性和信息一般不会修改)

总结:发现打开一个商品详细页对于后台要请求的数据还是挺多的,现在问题来了如果

京东这么大的流量,商品页面没有做相应的处理,肯定是要玩玩的。那优化方案是什么了?

业界有两种解决方案:

1、中小型电商平台:页面静态化

2、大型电商平台:动态渲染页面。

中小型电商平台:页面静态化:


常用模板技术:freemarker velocity

图片

@RequestMapping(value = "/item/static/{id}",method = RequestMethod.DELETE)
@ApiOperation(value = "静态化商品")
public Result<String> buildStatic(@PathVariable Long id){

    String path = itemService.toStatic(id);
    if(StringUtils.isEmpty(path)){
        return new ResultUtil<String>().setErrorMsg("静态化商品页面出现异常");
    }
    return new ResultUtil<String>().setData(path);
}

此方式的缺点大家知道是什么吗?
1、不利于分布式(静态化页面过多不利于同步)

2、模板文件改动需要重新生成(JD上亿商品要要死吧)

大型电商平台:动态渲染页面。:

这种技术的话需要用到一些脚本语音:OpenResty、Lua、Nginx

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 等都进行一致的高性能响应。

网址:http://openresty.org/

OpenResty安装:

下载:https://openresty.org/download/openresty-1.15.8.2-win64.zip

图片

实战:

主要解决两个点:

** 一、流量分发**

** 二、缓存数据**

nginx+lua开发流量分发:

流量分发的nginx,会发送http请求到后端的应用层nginx上去,所以要先引入lua http lib包

wget https://raw.githubusercontent.com/pintsized/lua‐resty‐http/master/lib/resty/http_headers.lua

wget https://raw.githubusercontent.com/pintsized/lua‐resty‐http/master/lib/resty/http.lua

最近网络不稳定,也可以从:https://github.com/bungle/lua-resty-template 去下载这两个lua脚本

图片

文件放在:openresty-1.15.8.2-win642\lualib\resty下

在nginx.conf文http模块中加入:

导入lua相关的包

lua_package_path '../lualib/?.lua;;';  

lua_package_cpath '../lualib/?.so;;'; 

包含lua.conf文件

include lua.conf; (实际演示文件名为:lua-demo.conf)

图片

lua.conf文件内容(实际演示文件名为:lua-demo.conf):

 新建lua.conf文件
server {
        listen 331;
        location /product {
	      default_type 'text/html;charset=UTF-8';
		  lua_code_cache on; 
	      content_by_lua_file D:/dis.lua; 流量分发逻辑写在dis.lua文件中
		  }
}

dis.lua代码:

local uri_args = ngx.req.get_uri_args() 
local productId = uri_args["productId"]
local host = {"127.0.0.1:332","127.0.0.1:333"} 
local hash = ngx.crc32_long(productId)
hash = (hash % 2) + 1
backend = "http://"..host[hash]
local method = uri_args["method"]
local requestBody = "/"..method.."?productId="..productId
local http = require("resty.http")
local httpc = http.new()
local resp, err = httpc:request_uri(backend,{
method = "GET",path = requestBody,	keepalive=false
})
if not resp then
ngx.say("request error :", err)
return
end
ngx.say(resp.body)
httpc:close()

端口号为:332的 lua-demo.conf

图片

端口号为:333的 lua-demo.conf

图片

访问:

http://localhost:331/product?method=product&productId=122

http://localhost:331/product?method=product&productId=123

图片

图片

nginx+lua开发页面缓存与渲染:

应用层还需要用到模板动态渲染技术,所以还需要引入lua的template包

wget https://raw.githubusercontent.com/bungle/lua‐resty‐template/master/lib/resty/template.lua

wget https://raw.githubusercontent.com/bungle/lua‐resty‐template/master/lib/resty/template/html.lua

最近网络不稳定,也可以从:https://github.com/bungle/lua-resty-template 去下载这两个lua脚本

图片

两个lua文件放在:openresty-1.15.8.2-win642\lualib\resty\html下

增加html静态模板

	<html>
	<head>
	<meta http‐equiv="Content‐Type" content="text/html; charset=utf‐8" />
	</head>
	<body>
	<h1>
	商品id: {* productId *}<br/>
	商品名称: {* productName *}<br/>
	商品原价: {* productPrice *}<br/>
	商品描述: {* productSpecification *}<br/>
	商品: {* productPictureList *}<br/>
	</h1>
</body>

增加product.lua

local uri_args = ngx.req.get_uri_args()
local productId = uri_args["productId"]
local cache_ngx = ngx.shared.my_cache
local productCacheKey = "product_info_"..productId
local productCache = cache_ngx:get(productCacheKey)
if productCache == "" or productCache == nil then
     local http = require("resty.http")
	 local httpc = http.new()
	 local resp, err = httpc:request_uri("http://127.0.0.1:7777",{
	 method = "GET",
	 path = "/goods/productDet?productId="..productId
	 })
	 productCache = resp.body
	 local expireTime = math.random(600,1200)
	 cache_ngx:set(productCacheKey, productCache, expireTime)
end
local cjson = require("cjson")
local productCacheJSON =cjson.decode(productCache) 
local context = {
	productId = productCacheJSON.productId,
	productName = productCacheJSON.productName,
	productPrice = productCacheJSON.salePrice,
	productPictureList = productCacheJSON.productImageSmall,
	productSpecification = productCacheJSON.detail
}
local template = require("resty.template")
template.render("product.html", context)

启动tomcat

图片

@RequestMapping(value = "/goods/productDet",method = RequestMethod.GET)
@ApiOperation(value = "商品详情")
public ProductDet getProductDet2(Long productId){
    ProductDet productDet=contentService.getProductDet(productId);
    return productDet;
}

图片

{
productId: 562379,
salePrice: 49,
productName: "坚果 Pro 软胶保护套",
subTitle: "TPU 环保材质、耐磨、耐油、耐久性强",
limitNum: 100,
productImageBig: "http://image.smartisanos.cn/resource/902befd5f32a8caf4ca79b55d39ee25a.jpg",
detail: "<img src="http://image.smartisanos.cn/resource/98521dbfe1dd1e67db3f7ca21e76c9ef.jpg" style="width:1220px;height:7000px;" alt="" />",
productImageSmall: 
[
"http://image.smartisanos.cn/resource/902befd5f32a8caf4ca79b55d39ee25a.jpg"
]
}

启动nginx测试:

http://localhost:331/product?method=product&productId=562379

图片

总结:

大致过程如上所述,其中细节问题还是挺多的,2b哥今天也弄了大半天,主要是这种nginx和lua的报错和语法不太熟悉,很容易出错的哦。不过终于还是把他弄好了。如果大家需要更详细的教程,可以关注2B哥的微信(微信搜:java2b)。喜欢文章的点个赞吧

商品还有其他的更细的业务哦,因为篇幅关系不能全部写完,如果有不足之处欢迎大家指出来哦。

猜你喜欢

转载自blog.csdn.net/HarderXin/article/details/104556978