如何在JHipster中实现基于租户的ElasticSearch路由?

kuuvgm7e  于 2023-01-20  发布在  ElasticSearch
关注(0)|答案(1)|浏览(136)

我目前正在尝试在JHipster微服务中实现多租户,但是我找不到一种方法来实现基于租户的ElasticSearch路由。
到目前为止,我已经成功地实现了PostgreSQL DB的数据源路由,类似于以下文章:https://websparrow.org/spring/spring-boot-dynamic-datasource-routing-using-abstractroutingdatasource
当我开始寻找在ElasticSearch中实现多租户的方法时,我发现了下面的文章:https://techblog.bozho.net/elasticsearch-multitenancy-with-routing/
在那里我读到了关于基于租户的路由。首先我试着在互联网上查找它,但我找到的任何东西都是5年前的,或者与java无关,更不用说与Spring/Jhipster了。然后我试着查看ElasticSearchTemplate的方法,@Document和@Settings的注解变量以及. yml文件中的配置选项,但没有找到任何有用的东西。
我现在使用的是Jhipster 7.9.3版,它使用的是Spring-Boot 2.7.3版,所有的微服务都是用JDL创建的,其中一半我在配置中加入了elasticsearch,另一半无所谓。
编辑:我想补充的是,我的数据库中的多租户是通过数据库分离归档的(Tenant1使用DB1,Tenant2使用DB2等),租户变量是一个枚举,不包含在我的实体中。
Edit2:我实现了我自己的解决方案。我使用租户作为索引,并使用DataSource Routing中的ContextHolder路由到正确的租户索引。为此,我必须对包". www.example.com"的生成类中的elasticsearchTemplate做一些更改。 <my.package.name>. repository.search ".
这可能不是使用ElasticSearch实现多租户的最有效方法,但它不需要太多配置。
下面是代码:

public interface ProductSearchRepository extends ElasticsearchRepository<Product, Long>, ProductSearchRepositoryInternal {}

interface ProductSearchRepositoryInternal {
    Stream<Product> search(String query);

    Stream<Product> search(Query query);

    void index(Product entity);
}

class ProductSearchRepositoryInternalImpl implements ProductSearchRepositoryInternal {

    private final ElasticsearchRestTemplate elasticsearchTemplate;
    private final ProductRepository repository;

    ProductSearchRepositoryInternalImpl(ElasticsearchRestTemplate elasticsearchTemplate, ProductRepository repository) {
        this.elasticsearchTemplate = elasticsearchTemplate;
        this.repository = repository;
    }

    @Override
    public Stream<Product> search(String query) {
        NativeSearchQuery nativeSearchQuery = new NativeSearchQuery(queryStringQuery(query));
        return search(nativeSearchQuery);
    }

    @Override
    public Stream<Product> search(Query query) {
        return elasticsearchTemplate.search(query, Product.class, IndexCoordinates.of(TenantContextHolder.getTenantContext().getTenant())).map(SearchHit::getContent).stream();
    }

    @Override
    public void index(Product entity) {
        repository.findById(entity.getId()).ifPresent(t -> elasticsearchTemplate.save(t, IndexCoordinates.of(TenantContextHolder.getTenantContext().getTenant())));
    }
}

编辑3:由于人们可能不知道. getTenant()来自哪里,我将展示我的租户枚举:

public enum Tenant {
    TENANTA("tenant_a"),
    TENANTB("tenant_b");
    String tenant;
    
    Tenant(String name) {
        this.tenant=name;
    }
    public String getTenant() {
        return this.tenant;
    }
}

Edit4:我的解决方案没有按计划工作,一旦我找到更好更健壮的解决方案,我会给出更新。
Edit5:我已经找到了如何实现基于租户的路由。首先,你必须添加以下注解到你的实体:

@org.springframework.data.elasticsearch.annotations.Routing(value = "tenant")

在我的例子中,我必须将枚举"Tenant"与getter和setter一起包含到实体中:

@Transient
private Tenant tenant;

public Tenant getTenant() {
    return tenant;
}

public void setTenant(Tenant tenant) {
    this.tenant = tenant;
}

然后,我必须在处理REST请求期间设置租户,然后才能由elasticsearchtemplate索引:

entity.setTenant(TenantContextHolder.getTenantContext());

至于搜索功能,我必须添加一个术语查询作为过滤器来启用路由:

@Override
public Stream<Product> search(String query) {
    
    NativeSearchQuery nativeSearchQuery = new NativeSearchQuery(queryStringQuery(query)
            , QueryBuilders.termQuery("_routing", TenantContextHolder.getTenantContext()));
    return search(nativeSearchQuery);
}

"nativeSearchQuery"的方法"setRoute(String route)"要么在我的情况下不起作用,要么我不明白它是如何工作的。
我已经成功地用GET和POST请求测试了这个实现。目前我遇到了一个问题,如果来自一个租户的实体的id与来自另一个租户的实体的id相同,则elasticsearch覆盖数据。

daolsyd0

daolsyd01#

经过反复试验,我找到了覆盖问题的解决方案,成功地完成并测试了基于租户的路由实现。
Product.java

import java.io.Serializable;
import javax.persistence.*;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.springframework.data.elasticsearch.annotations.Field;

@Entity
@Table(name = "product")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)

@org.springframework.data.elasticsearch.annotations.Document(indexName = "product")
@SuppressWarnings("common-java:DuplicatedBlocks")
@org.springframework.data.elasticsearch.annotations.Routing(value = "tenant")
public class Product implements Serializable {

    private static final long serialVersionUID = 1L;
    
    @Transient
    private Tenant tenant;
    
    @Transient
    @Field(name = "elastic_id")
    @org.springframework.data.annotation.Id
    private String elasticsearchId;

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = 
"sequenceGenerator")
    @SequenceGenerator(name = "sequenceGenerator")
    @Column(name = "id")    
    @Field("postgres_id")
    private Long id;
//Getters, Setters and other variables
}

产品搜索存储库

public interface ProductSearchRepository extends ElasticsearchRepository<Product, Long>, ProductSearchRepositoryInternal {}

interface ProductSearchRepositoryInternal {
    Stream<Product> search(String query);

    Stream<Product> search(Query query);

    void index(Product entity);
    
    Product save(Product entity);
    
    void deleteById(Long id);
}
@Transactional
class ProductSearchRepositoryInternalImpl implements ProductSearchRepositoryInternal {

    private final ElasticsearchRestTemplate elasticsearchTemplate;
    private final ProductRepository repository;

    ProductSearchRepositoryInternalImpl(ElasticsearchRestTemplate elasticsearchTemplate, ProductRepository repository) {
        this.elasticsearchTemplate = elasticsearchTemplate;
        this.repository = repository;
    }

    @Override
    public Stream<Product> search(String query) {
            
        NativeSearchQuery nativeSearchQuery = new NativeSearchQuery(queryStringQuery(query)
                , QueryBuilders.termQuery("_routing", TenantContextHolder.getTenantContext()));
        nativeSearchQuery.setMaxResults(30);
        return search(nativeSearchQuery);
    }

    @Override
    public Stream<Product> search(Query query) {
        return elasticsearchTemplate.search(query, Product.class).map(SearchHit::getContent).stream();
    }

    @Override
    public void index(Product entity) {
        entity.setTenant(TenantContextHolder.getTenantContext());
        repository.findById(Long.valueOf(entity.getId())).ifPresent(t -> {
            entity.setElasticsearchId(entity.getTenant()+String.valueOf(entity.getId()));
            elasticsearchTemplate.save(t);
            });
    }
    
    
    @Override
    public Product save(Product entity) {
        entity.setTenant(TenantContextHolder.getTenantContext());
        entity.setElasticsearchId(entity.getTenant()+String.valueOf(entity.getId()));
        return elasticsearchTemplate.save(entity);
    }
    
    
    @Override
    public void deleteById(Long id) {
        elasticsearchTemplate.delete(TenantContextHolder.getTenantContext() + String.valueOf(id), Product.class);
    }
}

相关问题