上次写了一个Java整合Storm的一个最简单的例子https://blog.youkuaiyun.com/xxkalychen/article/details/117018310,一个数据源,一个处理中间环节,一个最终环节,一个任务拓扑。我们能看到的效果是控制台打印。根据需要,我们要把数据写入ElasticSearch。
我们在原有的项目中做一点小的调整,来实现这个需求。
当然,首先我们需要一个ElasticSearch的服务器。我已经开启了自己架设好的ES服务器,版本号7.12.0
一、pom中添加ElasticSearch和Gson依赖
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.12.0</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.6</version>
</dependency>
这是ElasticSearch7.x以上版本官方建议使用的包,大版本要和服务器一致。Gson用于将对象构建成json,因为ElasticSearch需要将数据构建为合法的json进行存储。
二、创建一个数据包装类WordPkg。由于我们处理的最终数据都是单词,是字符串。我们需要一个类,将单词包装为有字段识别的类。
package com.chris.storm.model;
import java.io.Serializable;
/**
* @author Chris Chan
* Create on 2021/5/19 10:37
* Use for:
* Explain:
*/
public class WordPkg implements Serializable {
private String word;
public WordPkg() {
}
public WordPkg(String word) {
this.word = word;
}
public String getWord() {
return word;
}
public void setWord(String word) {
this.word = word;
}
}
三、为使用方便,我写了一个ElasticSearchUtil工具类,来简化操作
package com.chris.storm.utils;
import com.chris.storm.model.WordPkg;
import com.google.gson.Gson;
import org.apache.http.HttpHost;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.xcontent.XContentType;
import java.io.IOException;
import java.util.UUID;
/**
* @author Chris Chan
* Create on 2021/5/19 7:37
* Use for:
* Explain:
*/
public class ElasticSearchUtil {
private static RestHighLevelClient client = null;
static {
ElasticSearchUtil.client = new RestHighLevelClient(RestClient.builder(new HttpHost("192.168.0.52", 9200, "http")));
}
public static RestHighLevelClient getClient() {
return client;
}
public static void close() {
try {
ElasticSearchUtil.client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static boolean isIndexExists(String indexName) {
try {
return ElasticSearchUtil.client.indices().exists(new GetIndexRequest(indexName), RequestOptions.DEFAULT);
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
public static void createIndex(String indexName) {
if (isIndexExists(indexName)) {
return;
}
try {
ElasticSearchUtil.client.indices().create(new CreateIndexRequest(indexName), RequestOptions.DEFAULT);
} catch (IOException e) {
e.printStackTrace();
}
}
public static IndexResponse add(WordPkg wordPkg, String indexName) {
IndexRequest indexRequest = new IndexRequest(indexName).id(UUID.randomUUID().toString());
indexRequest.source(new Gson().toJson(wordPkg), XContentType.JSON);
try {
return ElasticSearchUtil.client.index(indexRequest, RequestOptions.DEFAULT);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
四、修改PrintBolt。我们需要添加写入ElasticSearch的逻辑。
package com.chris.storm.bolt;
import com.chris.storm.model.WordPkg;
import com.chris.storm.utils.ElasticSearchUtil;
import org.apache.storm.task.OutputCollector;
import org.apache.storm.task.TopologyContext;
import org.apache.storm.topology.OutputFieldsDeclarer;
import org.apache.storm.topology.base.BaseRichBolt;
import org.apache.storm.tuple.Tuple;
import java.util.Map;
/**
* @author Chris Chan
* Create on 2021/5/19 9:44
* Use for:
* Explain:
*/
public class PrintBolt extends BaseRichBolt {
//在ElasticSearch中创建的索引名称
public static final String INDEX_NAME = "storm_word";
static {
ElasticSearchUtil.createIndex(INDEX_NAME);
}
@Override
public void prepare(Map<String, Object> map, TopologyContext topologyContext, OutputCollector outputCollector) {
}
@Override
public void execute(Tuple tuple) {
String word = tuple.getStringByField("word");
System.out.printf("%s: %s\n", "接收到单词", word);
//输出到ElasticSearch
ElasticSearchUtil.add(new WordPkg(word), INDEX_NAME);
}
/**
* 这是流水线上的终点,不需要在发给下一环,所以无须再定义元组字段
*
* @param outputFieldsDeclarer
*/
@Override
public void declareOutputFields(OutputFieldsDeclarer outputFieldsDeclarer) {
}
}
因为封装了工具类,所以使用看起来就特别简单。
除了这一处,其他的地方都不需要修改。直接运行测试即可。
五、运行测试
这个程序还是不能远程提交,我们还需要进行修改。