套「多商户系统对接阿里云图像搜索(Image Search)做以图找商品」的落地方案(架构、数据设计、对接流程、Java 代码示例、风控与成本要点)。我直接按能开干的粒度写,照着做就行。
1. 选型与整体思路
- 能力:阿里云 Image Search 支持以图搜图,提供“入库/搜索/删除/更新”等 API 与官方 SDK(Java/Node 等)。推荐使用 2019-03-25 版本的 OpenAPI / V3 SDK。(阿里云)
- 关键接口:AddImage(入库)、SearchImage(搜索),以及对应的 SDK 方法(例如 Java V3:Add、SearchImageByPic 等命名)。(阿里云, 阿里云, 阿里云帮助中心)
- 接入方式:HTTP 公网或 VPC 端点,按 OpenAPI 规范签名/鉴权;生产建议走 VPC + 专线/云企业网。(阿里云)
- 性能配额:入库默认 QPS 有限(实例规格 10 万容量默认 1 QPS,其它规格默认 5 QPS),需要用异步队列与批处理。(阿里云)
2. 多商户架构(参考)
组件:
- OSS:商品主图、细节图原图存储。
- Image Search 实例:向量索引;入库/搜索都打到它。
- Ingestion Service(异步入库):消费队列,把每个商户商品图入库到 Image Search。
- Search API 网关:对前端/小程序/商家后台暴露统一“以图找货”接口。
- RDS/Redis:SKU 与图片索引映射、缓存热数据。
- MQ:如 RocketMQ/RabbitMQ,用于入库削峰。
商户隔离策略(3 选 1,可叠加):
- 属性过滤:入库时把 merchant_id 写入自定义属性(如 str_attr),搜索时带过滤条件,仅返回该商户结果。(阿里云文档)
- PicName/外部ID 命名规范:picName = {merchantId}-{skuId}-{imageNo},搜索命中后用 picName 反查 SKU。(阿里云)
- 分实例:大客单独买一个 Image Search 实例,小商户共享实例,通过属性过滤做软隔离。(阿里云)
3. 数据模型(简化)
- merchant(id, name, ... )
- product(id, merchant_id, sku, title, ... )
- product_image(id, product_id, url, status, md5, ... )
- imagesearch_index(id, merchant_id, sku, pic_name, int_attr, str_attr, status, last_sync_at)
- search_log(id, merchant_id, query_img_md5, topk, latency_ms, result_sku_json, created_at)
4. 流程
入库(商品上新/更新)
- 商户上传商品图 → 存 OSS,生成标准 800px 边长压缩图与 MD5。
- 发送 MQ 消息(merchantId/sku/picURL 等)。
- Ingestion Service 拉取消息: 组织 picName 与属性:str_attr = merchantId,必要时 int_attr 放品类/可筛选字段。 调 AddImage(或 SDK 的 Add)。 回写 imagesearch_index 状态。(阿里云, 阿里云帮助中心)
搜索(C 端或商家端)
- 前端上传拍照图(或粘贴图 URL)。
- Search API: 先做安全/大小校验,必要时压缩到 ≤ 2MB、短边 ≥ 300px。 调 SearchImage/SearchImageByPic,带 filter = str_attr=merchantId AND ...。 拿到命中列表(含 picName/score),解析出 sku,回库查价存货,组装结果返回。(阿里云, 阿里云文档)
5. Java 对接示例(V3 SDK 思路)
说明:阿里云会同时提供 OpenAPI 与多语言 SDK。以下示例基于官方文档给出的 Java V3 用法思路(命名可能因版本略有差异,实际以你引入的 SDK 文档示例为准)。(阿里云, 阿里云帮助中心, GitHub)
// 依赖(示意):com.aliyun:alibabacloud-imagesearch20200320 或最新 V3 SDK
// 初始化客户端
ImageSearchClient buildClient(String accessKeyId, String accessKeySecret, String region) {
Config config = new Config()
.setAccessKeyId(accessKeyId)
.setAccessKeySecret(accessKeySecret)
.setRegionId(region); // 如 cn-shanghai
return new ImageSearchClient(config);
}
// 入库:Add(等价于 AddImage)
void addImage(ImageSearchClient client, String instanceName, String picName, String picUrl,
String merchantId, Integer catId) throws Exception {
AddRequest req = new AddRequest()
.setInstanceName(instanceName)
.setPicName(picName)
.setPicUrl(picUrl)
.setCategoryId(catId) // 可选:商品类目
.setStrAttr(merchantId); // 关键:商户隔离
AddResponse resp = client.add(req);
// 检查 resp.getBody().getSuccess() / code 等,写入库状态
}
// 搜索:SearchImageByPic
List searchByPic(ImageSearchClient client, String instanceName, byte[] imageBytes,
String merchantId, Integer topK) throws Exception {
SearchImageByPicRequest req = new SearchImageByPicRequest()
.setInstanceName(instanceName)
.setPicContent(Base64.getEncoder().encodeToString(imageBytes))
.setFilter("str_attr='" + merchantId + "'")
.setNum(topK);
SearchImageByPicResponse resp = client.searchImageByPic(req);
// 解析 resp:拿到 items 列表(包含 picName/score),再映射 SKU
return resp.getBody().getAuctions(); // 命名以你引入的 SDK 为准
}
6. 工程细节
- 分库/分表/分缓存:merchant_id 作为所有商品与图片索引表的前缀键;Redis 用 imagesearch:{merchantId}:{sku}。
- 索引一致性:商品下架/图片替换要同步 DeleteImage 或 Update;入库失败重试+死信队列。