其实之前在ElasticSearch之Java API就有提到过批量处理器,它可以把多个请求放在bulk缓存集中处理,大大减少了ES的网络连接,实在是优化ES性能的神兵利器!
如何使用
首先创建一个BulkProcessor:
import org.elasticsearch.action.bulk.BackoffPolicy;
import org.elasticsearch.action.bulk.BulkProcessor;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.unit.ByteSizeUnit;
import org.elasticsearch.common.unit.ByteSizeValue;
import org.elasticsearch.common.unit.TimeValue;
public class EsBulkDatasource {
/**
* bulkProcessor
*/
private BulkProcessor bulkProcessor = null;
/**
* @return BulkProcessor
*/
public BulkProcessor getInstance() {
return bulkProcessor;
}
/**
* @param client
* @param batchNum
* @param flushInterval
*/
public EsBulkDatasource(TransportClient client, int batchNum, int flushInterval) {
bulkProcessor = BulkProcessor.builder(
client, // 添加你的客户端
new BulkProcessor.Listener() {
// 在批量执行之前调用此方法,可以查看numberOfActions
public void beforeBulk(long executionId, BulkRequest request) {
System.out.println(request.numberOfActions());
}
// 当批量执行引发异常时,调用此方法
public void afterBulk(long executionId, BulkRequest request, Throwable failure) {
}
// 批量执行之后调用此方法,可以检查是否存在一些失败的请求
public void afterBulk(long executionId, BulkRequest request, BulkResponse response) {
System.out.println(response.hasFailures());
}
})
.setBulkActions(batchNum) // 每10000个请求执行批量处理。默认值:1000
.setBulkSize(new ByteSizeValue(5,ByteSizeUnit.MB)) // 每5M冲洗一次数据。默认值:5mb
.setFlushInterval(TimeValue.timeValueSeconds(flushInterval)) // 无论请求数量多少,我们都希望每5秒刷新一次。默认值:无
.setConcurrentRequests(10) // 设置并发请求数。0 只允许执行单个请求,1 允许执行1个请求,同事累积新的批量请求。默认值:1(这意味着异步执行刷新操作)
// 设置自定义退避策略,该策略最初等待100毫秒,以指数方式增加并重试最多三次。
//每当一个或多个批量项目请求失败时,尝试重试,EsRejectedExecutionException这表示可用于处理请求的计算资源太少。
//要禁用退避策略,设置BackoffPolicy.noBackoff()
// 默认值:重试次数8次,启动延迟50ms。总等待5.1秒。
.setBackoffPolicy(
BackoffPolicy.exponentialBackoff(TimeValue.timeValueMillis(100), 3))
.build();
}
}
批量处理器工厂类:
public class BaseBulkRepository<T> {
/**
* bulkDatasource
*/
private EsBulkDatasource bulkDatasource;
/**
* 构造参数
* @param bulkDatasource
*/
public BaseBulkRepository(EsBulkDatasource bulkDatasource) {
this.bulkDatasource = bulkDatasource;
}
public void save(String index, String type, T entity, String id) {
String source = JsonUtil.toJson(entity);
bulkDatasource.getInstance().add(new IndexRequest(index, type, id).source(source));
}
public void remove(String index, String type, String id) {
bulkDatasource.getInstance().add(new DeleteRequest(index, type, id));
}
}
创建具体业务模块的批量处理器:
public class HistoryIndexBulkRepository extends BaseBulkRepository<Entity> {
public HistoryIndexBulkRepository(EsBulkDatasource bulkDatasource) {
super(bulkDatasource);
}
}
配置bean声明在xml中:
<elasticsearch:transport-client id="esClient" cluster-nodes="你的es节点" cluster-name="你的es节点名"/>
<bean name="bulkDatasource" class="cn.my.EsBulkDatasource" scope="singleton">
<constructor-arg name="client" ref="esClient"/>
<constructor-arg name="batchNum" value="20000" />
<constructor-arg name="flushInterval" value="5" />
</bean>
<bean name="historyIndexBulkRepository" class="cn.my.HistoryIndexBulkRepository">
<constructor-arg name="bulkDatasource" ref="bulkDatasource"/>
</bean>
最后调用批量处理器:
@Autowired
private HistoryIndexBulkRepository historyIndexBulkRepository;
historyIndexBulkRepository.save(ES_INDEX, ES_TYPE, entity, entity.getId());
小结
批量处理器有利有弊,好处很明显,能提高性能。弊端就是数据不是实时存储在ES库中的,如果对于实时性要求比较敏感,则建议根据自身业务酌情选择。