阿里云里面java对接datahub的语法和demo实在简陋, 新手刚使用的时候,真的有点惨不忍睹
下面是原sdk里面的demo
Maven Pom
<dependency>
<groupId>com.aliyun.datahub</groupId>
<artifactId>aliyun-sdk-datahub</artifactId>
<version>2.19.0-public</version>
</dependency>
初始化
用户可以使用阿里云认证账号访问DataHub,并需要提供云账号AccessId和AccessKey,同时需要提供访问DataHub的服务地址。以下代码用于使用DataHub域名新建DataHubClient:
// Endpoint以Region: 华东1为例,其他Region请按实际情况填写
String endpoint = "http://dh-cn-hangzhou.aliyuncs.com";
String accessId = "<YourAccessKeyId>";
String accessKey = "<YourAccessKeySecret>";
// 创建DataHubClient实例
DatahubClient datahubClient = DatahubClientBuilder.newBuilder()
.setDatahubConfig(
new DatahubConfig(endpoint,
// 是否开启二进制传输,服务端2.12版本开始支持
new AliyunAccount(accessId, accessKey), true))
//专有云使用出错尝试将参数设置为 false
// HttpConfig可不设置,不设置时采用默认值
.setHttpConfig(new HttpConfig()
.setCompressType(HttpConfig.CompressType.LZ4) // 读写数据推荐打开网络传输 LZ4压缩
.setConnTimeout(10000))
.build();
写入数据到DataHub
以Tuple类型Topic为例
// 写入Tuple型数据
public static void tupleExample(String project,String topic,int retryTimes) {
// 获取schema
RecordSchema recordSchema = datahubClient.getTopic(project,topic ).getRecordSchema();
// 生成十条数据
List<RecordEntry> recordEntries = new ArrayList<>();
for (int i = 0; i < 10; ++i) {
RecordEntry recordEntry = new RecordEntry();
// 对每条数据设置额外属性,例如ip 机器名等。可以不设置额外属性,不影响数据写入
recordEntry.addAttribute("key1", "value1");
TupleRecordData data = new TupleRecordData(recordSchema);
data.setField("field1", "HelloWorld");
data.setField("field2", 1234567);
recordEntry.setRecordData(data);
recordEntries.add(recordEntry);
}
try {
PutRecordsResult result = datahubClient.putRecords(project, topic, recordEntries);
int i = result.getFailedRecordCount();
if (i > 0) {
retry(datahubClient, result.getFailedRecords(), retryTimes, project, topic);
}
} catch (DatahubClientException e) {
System.out.println("requestId:" + e.getRequestId() + "\tmessage:" + e.getErrorMessage());
}
}
//重试机制
public static void retry(DatahubClient client, List<RecordEntry> records, int retryTimes, String project, String topic) {
boolean suc = false;
while (retryTimes != 0) {
retryTimes = retryTimes - 1;
PutRecordsResult recordsResult = client.putRecords(project, topic, records);
if (recordsResult.getFailedRecordCount() > 0) {
retry(client,recordsResult.getFailedRecords(),retryTimes,project,topic);
}
suc = true;
break;
}
if (!suc) {
System.out.println("retryFailure");
}
}
创建订阅消费DataHub数据
//点位消费示例,并在消费过程中进行点位的提交
public static void example() {
String shardId = "0";
List<String> shardIds = Arrays.asList("0", "1");
OpenSubscriptionSessionResult openSubscriptionSessionResult = datahubClient.openSubscriptionSession(Constant.projectName, Constant.topicName, subId, shardIds);
SubscriptionOffset subscriptionOffset = openSubscriptionSessionResult.getOffsets().get(shardId);
// 1、获取当前点位的cursor,如果当前点位已过期则获取生命周期内第一条record的cursor,未消费同样获取生命周期内第一条record的cursor
String cursor = null;
//sequence < 0说明未消费
if (subscriptionOffset.getSequence() < 0) {
// 获取生命周期内第一条record的cursor
cursor = datahubClient.getCursor(Constant.projectName, Constant.topicName, shardId, CursorType.OLDEST).getCursor();
} else {
// 获取下一条记录的Cursor
long nextSequence = subscriptionOffset.getSequence() + 1;
try {
//按照SEQUENCE getCursor可能报SeekOutOfRange错误,表示当前cursor的数据已过期
cursor = datahubClient.getCursor(Constant.projectName, Constant.topicName, shardId, CursorType.SEQUENCE, nextSequence).getCursor();
} catch (SeekOutOfRangeException e) {
// 获取生命周期内第一条record的cursor
cursor = datahubClient.getCursor(Constant.projectName, Constant.topicName, shardId, CursorType.OLDEST).getCursor();
}
}
// 2、读取并保存点位,这里以读取Tuple数据为例,并且每1000条记录保存一次点位
long recordCount = 0L;
// 每次读取10条record
int fetchNum = 10;
while (true) {
try {
GetRecordsResult getRecordsResult = datahubClient.getRecords(Constant.projectName, Constant.topicName, shardId, schema, cursor, fetchNum);
if (getRecordsResult.getRecordCount() <= 0) {
// 无数据,sleep后读取
Thread.sleep(1000);
continue;
}
for (RecordEntry recordEntry : getRecordsResult.getRecords()) {
//消费数据
TupleRecordData data = (TupleRecordData) recordEntry.getRecordData();
System.out.println("field1:" + data.getField("field1") + "\t"
+ "field2:" + data.getField("field2"));
// 处理数据完成后,设置点位
++recordCount;
subscriptionOffset.setSequence(recordEntry.getSequence());
subscriptionOffset.setTimestamp(recordEntry.getSystemTime());
if (recordCount % 1000 == 0) {
//提交点位点位
Map<String, SubscriptionOffset> offsetMap = new HashMap<>();
offsetMap.put(shardId, subscriptionOffset);
datahubClient.commitSubscriptionOffset(Constant.projectName, Constant.topicName, subId, offsetMap);
System.out.println("commit offset successful");
}
}
cursor = getRecordsResult.getNextCursor();
} catch (SubscriptionOfflineException | SubscriptionSessionInvalidException e) {
// 退出. Offline: 订阅下线; SubscriptionSessionInvalid: 表示订阅被其他客户端同时消费
break;
} catch (SubscriptionOffsetResetException e) {
// 表示点位被重置,重新获取SubscriptionOffset信息,这里以Sequence重置为例
// 如果以Timestamp重置,需要通过CursorType.SYSTEM_TIME获取cursor
subscriptionOffset = datahubClient.getSubscriptionOffset(Constant.projectName, Constant.topicName, subId, shardIds).getOffsets().get(shardId);
long nextSequence = subscriptionOffset.getSequence() + 1;
cursor = datahubClient.getCursor(Constant.projectName, Constant.topicName, shardId, CursorType.SEQUENCE, nextSequence).getCursor();
} catch (DatahubClientException e) {
// TODO: 针对不同异常决定是否退出
} catch (Exception e) {
break;
}
}
}
其实他这里面是有个逻辑的, 写这个sdk的哥们太粗心了, 好多细节没有说清楚, 关键网上关于datahub的资料少
首先这个field1字段是怎么来的
我们配置公钥私钥,项目名字,topic名字, 代码就可以自动联上阿里云datahub的仓库获取我们的配置信息, 就可以拿到我们配置的字段信息
原理就是这样, 但是因为放数据的时候设置的为kv结构就代表我们需要不停的set数据进去, 这样的写法是很丑陋的, 最好是直接放一个对象进去, 自动获取对象的属性值.
具体的实现是这样的:
@Service
public class PushDataHubService {
private final static Logger logger = LoggerFactory.getLogger(PushDataHubService.class);
@Value("${datahub.endpoint}")
private String endpoint;
@Value("${datahub.accessId}")
private String accessId;
@Value("${datahub.accessKey}")
private String accessKey;
@Value("${datahub.projectName}")
private String projectName;
@Value("${datahub.shardId}")
private String shardId;
@Async
public void writeToDataHub(String topicName, Object o) {
try {
DatahubClient client = PushDataHubUtil.getDataHubClient(endpoint, accessId, accessKey);
PushDataHubUtil.write(client, projectName, topicName, shardId, o);
logger.info("数据推送datahub成功");
} catch (Exception e) {
System.out.println("推送数据到DataHub异常:{}," + e.getMessage() +
"topicName:{}" + topicName);
}
}
}
PushDataUtil
@Component
@Configuration
public class PushDataHubUtil {
/**
* 获取DataHub客户端
*/
public static DatahubClient getDataHubClient(String endpoint, String accessId, String accessKey) {
return DatahubClientBuilder.newBuilder()
.setDatahubConfig(new DatahubConfig(endpoint,
// 是否开启二进制传输,服务端2.12版本开始支持
new AliyunAccount(accessId, accessKey), false))
// 专有云使用出错尝试将参数设置为 false
// HttpConfig可不设置,不设置时采用默认值
.setHttpConfig(new HttpConfig().setConnTimeout(60000).setReadTimeout(30000)).build();
}
public static void write(DatahubClient datahubClient, String projectName, String topicName, String shardId, Object o) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
List<RecordEntry> recordEntries = new ArrayList<>();
System.out.println("projectName: " + projectName + ",topicName: " + topicName);
RecordSchema recordSchema = datahubClient.getTopic(projectName, topicName).getRecordSchema();
RecordEntry recordEntry = new RecordEntry();
TupleRecordData data = new TupleRecordData(recordSchema);
List<Field> fields = recordSchema.getFields();
for (Field field : fields) {
String name = field.getName();
Object invoke = get(o.getClass(), name).invoke(o);
if (invoke != null && invoke.getClass().isInstance(LocalDateTime.class)) {
String format = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format((LocalDateTime) invoke);
data.setField(name, format);
} else if (field.getType().equals(BIGINT)) {
data.setField(name, invoke);
} else {
data.setField(name, invoke != null ? invoke.toString() : null);
}
}
recordEntry.setRecordData(data);
recordEntry.setShardId(shardId);
recordEntries.add(recordEntry);
datahubClient.putRecords(projectName, topicName, recordEntries);
recordEntries.clear();
}
public static Method get(Class<?> obj, String name) throws NoSuchMethodException {
StringBuilder methodName = new StringBuilder("get");
for (String s : name.split("_")) {
StringBuilder append = methodName.append(s.substring(0, 1).toUpperCase()).append(s.substring(1));
}
return obj.getDeclaredMethod(methodName.toString());
}
}
解析一下, 你也可以自己debug跑一下, 除了datahub本身的代码,剩下的主要是使用字符串拼接和反射,具体操作就是通过datahub本身的方法获取配置在远端datahub中schema里面的字段名,拼接上get,同时去掉"_" , 在进行首字母大写, 这样就是形成了我们的get方法名,通过返回获取值
如果你要抄上面的代码的话,需要注意几点, 不然无法使用
1: datahub里面schema里面的字段名必须以 xxx_xx 形式, 就是说必须有"_", 如果没有可以修改下下面get方法的代码,
2:第二你传入对象的实体类里面必须使用驼峰命名 比如msgInfo 这样
就是这两点, 剩下有问题的话粘贴出去自己跑跑
就这样, 爱你么么么么么哒 略