问题描述

当使用SDK连接到Azure Event Hub时,最常规的方式为使用连接字符串。这种做法参考官网文档就可成功完成代码:https://docs.azure.cn/zh-cn/event-hubs/event-hubs-java-get-started-send

只是,如果使用Azure AD认证方式进行访问,代码需要如何修改呢? 如何来使用AAD的 TokenCredential呢?

分析问题

在使用Connection String的时候,EventProcessorClientBuilder使用connectionString方法配置连接字符串。

如果使用Azure AD认证,则需要先根据AAD中注册应用获取到Client ID, Tenant ID, Client Secret,然后把这些内容设置为系统环境变量

  1. AZURE_TENANT_ID :对应AAD 注册应用页面的 Tenant ID
  2. AZURE_CLIENT_ID :对应AAD 注册应用页面的 Application (Client) ID
  3. AZURE_CLIENT_SECRET :对应AAD 注册应用的 Certificates & secrets 中创建的Client Secrets

然后使用 credential 初始化 EventProcessorClientBuilder 对象

注意点:

1) DefaultAzureCredentialBuilder 需要指定 Authority Host为 Azure China

2) EventProcessorClientBuilder . Credential 方法需要指定Event Hub Namespce 的域名

操作实现

第一步:为AAD注册应用赋予操作Event Hub Data的权限

Azure 提供了以下 Azure 内置角色,用于通过 Azure AD 和 OAuth 授予对事件中心数据的访问权限:

  1. Azure 事件中心数据所有者 (Azure Event Hubs Data Owner): 使用此角色可以授予对事件中心资源的完全访问权限。
  2. Azure 事件中心数据发送者 (Azure Event Hubs Data Sender) : 使用此角色可以授予对事件中心资源的发送访问权限。
  3. Azure 事件中心数据接收者 (Azure Event Hubs Data Receiver): 使用此角色可以授予对事件中心资源的使用/接收访问权限。

本实例中,只需要接收数据,所以只赋予了 Azure Event Hubs Data Receiver权限。

第二步:在Java 项目中添加SDK依赖

添加在pom.xml文件中 dependencies 部分的内容为:azure-identity , azure-messaging-eventhubs , azure-messaging-eventhubs-checkpointstore-blob,最好都是用最新版本,避免出现运行时出现类型冲突或找不到

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId>
<artifactId>spdemo</artifactId>
<version>1.0-SNAPSHOT</version> <name>spdemo</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties> <dependencies>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-messaging-eventhubs</artifactId>
<version>5.12.2</version>
</dependency>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-messaging-eventhubs-checkpointstore-blob</artifactId>
<version>1.14.0</version>
</dependency>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-identity</artifactId>
<version>1.5.3</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies> <build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.5.5</version>
<configuration>
<archive>
<manifest>
<mainClass>com.example.App</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
</plugins>
</build>
</project>

第三步:添加完整代码

package com.example;

import com.azure.core.credential.TokenCredential;
import com.azure.identity.AzureAuthorityHosts;
import com.azure.identity.DefaultAzureCredentialBuilder;
import com.azure.messaging.eventhubs.*;
import com.azure.messaging.eventhubs.checkpointstore.blob.BlobCheckpointStore;
import com.azure.messaging.eventhubs.models.*;
import com.azure.storage.blob.*; import java.io.IOException;
import java.sql.Date;
import java.time.Instant;
import java.time.temporal.TemporalUnit;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer; /**
* Hello world!
*
*/
public class App {
// private static final String connectionString ="<connectionString>";
// private static final String eventHubName = "<eventHubName>";
// private static final String storageConnectionString = "<storageConnectionString>";
// private static final String storageContainerName = "<storageContainerName>"; public static void main(String[] args) throws IOException {
System.out.println("Hello World!"); // String connectionString ="<connectionString>";
// The fully qualified namespace for the Event Hubs instance. This is likely to
// be similar to:
// {your-namespace}.servicebus.windows.net
// String fullyQualifiedNamespace ="<your event hub namespace>.servicebus.chinacloudapi.cn";
// String eventHubName = "<eventHubName>"; String storageConnectionString = System.getenv("storageConnectionString");
String storageContainerName = System.getenv("storageContainerName");
String fullyQualifiedNamespace = System.getenv("fullyQualifiedNamespace");
String eventHubName = System.getenv("eventHubName"); TokenCredential credential = new DefaultAzureCredentialBuilder().authorityHost(AzureAuthorityHosts.AZURE_CHINA)
.build(); // Create a blob container client that you use later to build an event processor
// client to receive and process events
BlobContainerAsyncClient blobContainerAsyncClient = new BlobContainerClientBuilder()
.connectionString(storageConnectionString)
.containerName(storageContainerName)
.buildAsyncClient(); // EventHubProducerClient
// EventHubProducerClient client = new EventHubClientBuilder()
// .credential(fullyQualifiedNamespace, eventHubName, credential)
// .buildProducerClient(); Map<String, EventPosition> initialPartitionEventPosition = new HashMap<>();
initialPartitionEventPosition.put("0", EventPosition.fromSequenceNumber(3000)); // EventProcessorClientBuilder
// Create a builder object that you will use later to build an event processor
// client to receive and process events and errors.
EventProcessorClientBuilder eventProcessorClientBuilder = new EventProcessorClientBuilder()
// .connectionString(connectionString, eventHubName)
.credential(fullyQualifiedNamespace, eventHubName, credential)
.initialPartitionEventPosition(initialPartitionEventPosition)
.consumerGroup(EventHubClientBuilder.DEFAULT_CONSUMER_GROUP_NAME)
.processEvent(PARTITION_PROCESSOR)
.processError(ERROR_HANDLER)
.checkpointStore(new BlobCheckpointStore(blobContainerAsyncClient)); // EventPosition.f
// Use the builder object to create an event processor client
EventProcessorClient eventProcessorClient = eventProcessorClientBuilder.buildEventProcessorClient(); System.out.println("Starting event processor");
eventProcessorClient.start(); System.out.println("Press enter to stop.");
System.in.read(); System.out.println("Stopping event processor");
eventProcessorClient.stop();
System.out.println("Event processor stopped."); System.out.println("Exiting process"); } public static final Consumer<EventContext> PARTITION_PROCESSOR = eventContext -> {
PartitionContext partitionContext = eventContext.getPartitionContext();
EventData eventData = eventContext.getEventData(); System.out.printf("Processing event from partition %s with sequence number %d with body: %s%n",
partitionContext.getPartitionId(), eventData.getSequenceNumber(), eventData.getBodyAsString()); // Every 10 events received, it will update the checkpoint stored in Azure Blob
// Storage.
if (eventData.getSequenceNumber() % 10 == 0) {
eventContext.updateCheckpoint();
}
}; public static final Consumer<ErrorContext> ERROR_HANDLER = errorContext -> {
System.out.printf("Error occurred in partition processor for partition %s, %s.%n",
errorContext.getPartitionContext().getPartitionId(),
errorContext.getThrowable());
};
}

附录:自定义设置 Event Position,当程序运行时,指定从Event Hub中获取消息的 Sequence Number

使用EventPosition对象中的fromSequenceNumber方法,可以指定一个序列号,Consume端会根据这个号码获取之后的消息。其他的方法还有 fromOffset(指定游标) / fromEnqueuedTime(指定一个时间点,获取之后的消息) / earliest(从最早开始) / latest(从最后开始获取新的数据,旧数据不获取)

    Map<String, EventPosition> initialPartitionEventPosition = new HashMap<>();
initialPartitionEventPosition.put("0", EventPosition.fromSequenceNumber(3000));

注意:

Map<String, EventPosition> 中的String 对象为Event Hub的分区ID,如果Event Hub有2个分区,则它的值分别时0,1.

EventPosition 设置的值,只在Storage Account所保存在CheckPoint Store中的值没有,或者小于此处设定的值时,才会起效果。否则,Consume 会根据从Checkpoint中获取的SequenceNumber为准。

参考资料

授予对 Azure 事件中心的访问权限 :https://docs.azure.cn/zh-cn/event-hubs/authorize-access-event-hubs

对使用 Azure Active Directory 访问事件中心资源的应用程序进行身份验证 : https://docs.azure.cn/zh-cn/event-hubs/authenticate-application

使用 Java 向/从 Azure 事件中心 (azure-messaging-eventhubs) 发送/接收事件 : https://docs.azure.cn/zh-cn/event-hubs/event-hubs-java-get-started-send

 [END]

 

 

【Azure 事件中心】使用Azure AD认证方式创建Event Hub Consume Client + 自定义Event Position的更多相关文章

  1. 【Azure 事件中心】Azure Event Hub 新功能尝试 -- 异地灾难恢复 (Geo-Disaster Recovery)

    问题描述 关于Event Hub(事件中心)的灾备方案,大多数就是新建另外一个备用的Event Hub,当主Event Hub出现不可用的情况时,就需要切换到备Event Hub上. 而在切换的过程中 ...

  2. 【Azure 事件中心】在微软云中国区 (Mooncake) 上实验以Apache Kafka协议方式发送/接受Event Hubs消息 (Java版)

    问题描述 事件中心提供 Kafka 终结点,现有的基于 Kafka 的应用程序可将该终结点用作运行你自己的 Kafka 群集的替代方案. 事件中心可与许多现有 Kafka 应用程序配合使用.在Azur ...

  3. 【Azure 事件中心】EPH (EventProcessorHost) 消费端观察到多次Shutdown,LeaseLost的error信息,这是什么情况呢?

    问题详情 使用EPH获取Event Hub数据时,多次出现连接shutdown和LeaseLost的error  ,截取某一次的error log如: Time:2021-03-10 08:43:48 ...

  4. 【Azure 事件中心】为应用程序网关(Application Gateway with WAF) 配置诊断日志,发送到事件中心

    问题描述 在Application Gateway中,开启WAF(Web application firewall)后,现在需要把访问的日志输出到第三方分析代码中进行分析,如何来获取WAF的诊断日志呢 ...

  5. 【Azure 事件中心】Event Hub 无法连接,出现 Did not observe any item or terminal signal within 60000ms in 'flatMapMany' 的错误消息

    问题描述 使用Java SDK连接Azure Event Hub,一直出现 java.util.concurrent.TimeoutException 异常, 消息为:java.util.concur ...

  6. 【Azure 事件中心】 org.slf4j.Logger 收集 Event Hub SDK(Java) 输出日志并以文件形式保存

    问题描述 在使用Azure Event Hub的SDK时候,常规情况下,发现示例代码中并没有SDK内部的日志输出.因为在Java项目中,没有添加 SLF4J 依赖,已致于在启动时候有如下提示: SLF ...

  7. 微软公有云事件中心(Azure Event Hubs)在开放物联网大会(OIOT)啼声初试

     发布于 2014-12-29 作者 刘 天栋 2014年12月18日,InfoQ在京召开开放物联网大会(Open IOT Conference),微软开放技术(中国)资深项目经理陈岭在大会中针对 ...

  8. 【Azure 事件中心】azure-spring-cloud-stream-binder-eventhubs客户端组件问题, 实践消息非顺序可达

    问题描述 查阅了Azure的官方文档( 将事件发送到特定分区: https://docs.azure.cn/zh-cn/event-hubs/event-hubs-availability-and-c ...

  9. 【Azure 事件中心】在Service Bus Explorer工具种查看到EventHub数据在分区中的各种属性问题

    问题描述 通过Service Bus Explorer工具,查看到Event Hub的属性值,从而产生的问题及讨论: Size in Bytes:   这个是表示当前分区可以存储的最大字节数吗? La ...

随机推荐

  1. 女朋友面试回来抱怨说会redis,面试官问了一堆redis

    Redis 优缺点及特点 什么是Redis?简述它的优缺点? Redis本质上是一个Key-Value类型的内存数据库,类似MemoryCache,整个数据库统统加载在内存当中进行操作,定期通过异步操 ...

  2. 【算法】插入排序(Insertion Sort)(三)

    插入排序(Insertion Sort) 插入排序(Insertion-Sort)的算法描述是一种简单直观的排序算法.它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相 ...

  3. Tenseal库

    在此记录Tenseal的学习笔记 介绍 在张量上进行同态计算的库,是对Seal的python版实现,给开发者提供简单的python接口,无需深究底层密码实现. 当前最新版本:3.11 位置:A lib ...

  4. mysql查询关键字补充与多表查询

    目录 查询关键字补充 having过滤 distinct去重 order by排序 limit分页 regexp正则 多表查询 子查询 连表查询 查询关键字补充 having过滤 关键字having和 ...

  5. CabloyJS V3.2.0支持Socket IO

    CabloyJS v3.2.0引入了Socket IO,并且实现了统一的在线推送和离线推送机制 效果演示 1. IM 用户向系统发送一条消息,系统通过websocket在线通道向用户推送一条回复 2. ...

  6. Oracle常见问题解决方法

    1.设置数据库用户的密码有效期为 无限制 --查询proile文件名 SELECT username,PROFILE FROM dba_users; --查询文件 的密码保护策略 SELECT * F ...

  7. JAVA学习之第一个HelloWorld程序

    第一个HelloWorld程序 第一步,创建java类型的文件 第二步,在创建文件的目录中打开cmd窗口 第三步,使用javac 命令将java文件编译为.class类型的字节码文件 第四步,使用ja ...

  8. tf.data(二) —— 并行化 tf.data.Dataset 生成器

    在处理大规模数据时,数据无法全部载入内存,我们通常用两个选项 使用tfrecords 使用 tf.data.Dataset.from_generator() tfrecords的并行化使用前文已经有过 ...

  9. 浅析DispatchProxy动态代理AOP

    浅析DispatchProxy动态代理AOP(代码源码) 最近学习了一段时间Java,了解到Java实现动态代理AOP主要分为两种方式JDK.CGLIB,我之前使用NET实现AOP切面编程,会用Fil ...

  10. 【Redis】集群请求命令处理

    集群请求命令处理 在Redis的命令处理函数processCommand(server.c)中有对集群节点的处理,满足以下条件时进入集群节点处理逻辑中: 启用了集群模式,通过server.cluste ...