工作中的点点滴滴:SpringBoot集成Elasticsearch索引多租户
背景:
供应商系统是一套典型的saas系统,所以我们在使用Elasticsearch的过程中,也就遇到了如何把Elasticsearch中的索引也通过不同的供应商code也就是租户信息来区分来。
技术实现:
方案一:每个租户都有一套自己的Elasticsearch:这是最难管理的,并且需要大量的devops自动化。 根据客户的不同,完全隔离客户可能是值得的,但这种情况很少发生
方案二:每个租户的索引–,并且不需要额外的编码(您只需在查询的URL中参数化“ index”参数)。
方案三:基于租户的路由–这意味着您将所有内容都放在一个群集中,但是将搜索路由配置为特定于租户,这使您可以在逻辑上隔离单个索引中的数据,代码的调整可能会比较大。
通过对比采取最优的方案二来实现,首先我们先要定义文档结构:
@Data @Document(indexName = "index_goods") @ToString @NoArgsConstructor @Accessors(chain = true) public class EsGoodsIndex implements Serializable { private static final long serialVersionUID = -6856471777036048874L; @Id @Schema(description = "商品skuId") private String id; /** * 商品id */ @Schema(description = "商品Id") @Field(type = FieldType.Text) private String goodsId; /** * 商品名称 */ @Field(type = FieldType.Text, analyzer = "ik_max_word") @Schema(description = "商品名称") private String goodsName; /** * 商品编号 */ @Field(type = FieldType.Keyword) @Schema(description = "商品编号") private String sn; /** * 卖家id */ @Field(type = FieldType.Text) @Schema(description = "卖家id") private String storeId; }
@Document就是文档对应的缩影的名称,这里我们是需要调整成动态参数的,因为不同的租户过来,在读取这个值的时候应该是跟着租户走的。如果你的saas是一个租户一个计算节点,你就可以把indexName写在配置文件里面,通过@Document(indexName = "#{@elasticsearchProperties.indexPrefix}")来读取。
@Data @Builder @Component("elasticsearchProperties") @NoArgsConstructor @AllArgsConstructor @ConfigurationProperties(prefix = "data.elasticsearch") public class ElasticsearchProperties { /** * 索引 */ private String indexPrefix; }
因为我的saas服务是公用一个计算节点,所以这里需要调整通过运行时来取值的。
@Component("indexNameGenerator") public class IndexNameGenerator { public String commonIndex() { //根据租户生成index String date = TenantContext.getTenantId(); return date + "_mall_" + EsSuffix.GOODS_INDEX_NAME; } }
在文档的@Document里面的赋值就ok了,这样在运行时就可以获取到对应的租户信息,和对应的索引关联。
@Document(indexName = "#{@indexNameGenerator.commonIndex()}")
这个@Document是支持Spel的,所以通过Spel这里就可以想怎么动态就怎么动态了,不熟悉Spel的,请移步这里 。