dot Redis 8 来了,而且它是开源的

了解更多

如何使用 Redis 创建实时手机银行应用

RedisBank 快速、准确且实时更新,只需在智能手机上轻扫几下,即可立即访问您的财务信息。随着手机银行从金融边缘进入主流,我们都已经习惯了随时随地将整个银行信息放在口袋里的想法。 

为了响应这种向数字化的转变,Lars Rosenquist 构建了一个应用程序,让您可以创建自己的手机银行应用。通过使用 Redis,您的所有财务信息都将实时可用,使您能够准确监控、记录和规划财务。 

让我们来看看 Lars 如何构建这个应用程序。但在深入了解这个应用的细节之前,我们想指出,您可以在 Redis Launchpad 上查看一系列令人兴奋的应用程序。 

https://www.youtube.com/embed/JEruQaQ9XpU

所以在这篇文章之后一定要去看看! 

如何使用 RedisBank 创建手机银行应用

  1. 您将构建什么?
  2. 您需要什么?
  3. 架构
  4. 入门
  5. 访问应用程序
  6. 工作原理
  7. 如何在 Azure Spring Cloud 上运行应用程序
  8. Azure Spring Cloud 故障排除技巧
  9. 已知问题

1. 您将构建什么?

您将构建一个在线银行应用程序,它将为您提供银行账户的实时更新。使用 RedisBank,您将能够密切关注您的支出,并与金钱建立更健康的关系。 

下面我们将逐步指导您完成构建过程,并重点介绍使此应用程序运行所需的所有组件。 

准备好开始了吗?好的,让我们直接开始吧。 

2. 您需要什么?

  • Java: 用作一种高级、面向对象的编程语言。 
  • Spring Boot: 用作一个开源的 Javascript 框架。
  • Bootstrap: 用作一个开源的前端开发框架,用于构建网站和应用程序。 
  • CSS: 用作一种样式表语言,用于描述用标记语言编写的文档的呈现。 
  • Vue: 用作一个渐进式框架,用于创建用户界面。 

此应用程序还使用了一系列 Redis 核心数据结构和模块。其中包括

3. 架构

RedisBank Architecture For Launchpad Post

这是一个包含 API 和前端的 SpringBoot 应用程序。 

  • 一切的基础是交易生成器,它生成所有的银行交易。请注意,这只是一个生成测试数据的组件。在现实世界的场景中,我们期望数据从其他地方(应用程序之外)填充。
  • 对于每一笔银行交易,数据都被放入 RedisStreams。然后由交易转发器接收该流。 
  • 交易转发器从 RedisStreams 获取交易信息,并将其放在 WebSocket 连接上,供 Web UI 使用。这些交易也用于使用 RediSearch 填充辅助索引,从而暴露搜索 API。 
  • 银行交易产生的银行余额也被放入 RedisTimeSeries 数据结构中,该数据结构由余额 API 暴露。 
  • 所有银行交易的累积值都放在一个有序集合中,以便可以返回最大消费者 API。 

4. 入门

 先决条件

  1. JDK 11 或更高版本
  2. Docker Desktop
  3. Azure CLI
  4. Azure CLI 的 Azure Spring Cloud 扩展

步骤 1:克隆存储库 

git clone https://github.com/redis-developer/redisbank/

步骤 2:在本地运行 Redis Stack Docker 容器

docker run -d --name redis-stack -p 6379:6379  redis/redis-stack-server

步骤 3:执行以下 CLI 命令以使用 Maven 运行 Spring Boot 应用程序

./mvnw clean package spring-boot:run

结果

[INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ redisbank ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 2 source files to /Users/ajeetraina/projects/redisbank/target/test-classes
[INFO] 
[INFO] <<< spring-boot-maven-plugin:2.4.5:run (default-cli) < test-compile @ redisbank <<<
[INFO] 
[INFO] 
[INFO] --- spring-boot-maven-plugin:2.4.5:run (default-cli) @ redisbank ---
[INFO] Attaching agents: []

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.4.5)

5. 访问应用程序 

导航到 http://localhost:8080 并使用以下凭据登录

  • 用户名: <输入您的用户名>
  • 密码: <输入您的密码>

请参考 文件获取凭据,因为这些值是为演示目的而硬编码的。

下面是登录页面的截图。 

登录后,您将进入主门户页面(见下图)。

从这里,您将能够通过多种不同的门户功能更好地掌握您的财务状况。其中包括

  • 实时交易更新: Redis 的低延迟确保所有交易都实时更新,为您提供最新的财务信息。 
  • 银行余额图示: 允许用户清晰地了解他们的财务状况。 
  • 购买历史: 允许用户详细监控消费历史。
  • 搜索高亮: 允许用户从数据库中提取特定的搜索词。 

6. 工作原理

Redis Streams 是一个只追加日志。数据生成应用会将交易放入流中,而应用程序将订阅此流并消费任何传入的交易,并通过 Websocket 连接将它们发送到前端。

  • 向应用程序添加一个名为 BankTransactionForwader 的新类,该类实现了 InitializingBean, DisposableBeanStreamListener<String, MapRecord<String, String, String>> 接口。确保为其添加 @Component 注解,以便 Spring 可以识别它并自动注入依赖项。

前两个接口允许您实现 afterPropertiesSet() 方法和 destroy() 方法,您将使用它们来设置和清理对 Redis Stream 的订阅。 

第三个接口要求您实现 nMessage(MapRecord<String,String, String> message),这是每当通过 Redis Stream 接收到新的银行交易时将执行的回调。 

  • BankTransactionForwarder 的构造函数中,向方法签名添加三个参数(它们将被自动注入):一个 RedisTemplate一个 SimpMessageSendingOperations一个 Config 对象(Config 对象已在应用程序中提供,并从 RestController 向前端提供 Stomp/Websocket 配置信息)。将它们存储在实例变量中。
  • afterPropertiesSet() 方法中,创建一个新的 StreamMessageListenerContainer<String, MapRecord<String, String, String>> 对象,并将其存储在一个名为 container 的实例变量中。 
  • 要创建 StreamMessageListenerContainer,请使用以下代码:StreamMessageListenerContainer.create(redis.getConnectionFactory(), StreamMessageListenerContainerOptions.builder().pollTimeout(Duration.ofMillis(1000)).build())
  • 在同一方法中,使用 container.start() 启动新创建的容器。 
  • 在同一方法中,使用 container.receive(StreamOffset.latest("transactions_user"), this) 创建对流的新订阅,并将其存储在一个名为 subscription 的实例变量中。 
  • 在同一方法中,调用 subscription.await(Duration.ofSeconds(10)) 以等待订阅激活。

此时,您已订阅 Redis Stream。每当有新消息到达流时,将调用此类中的 onMessage(…) 方法。您仍然需要执行此方法,所以让我们看看如何完成。 

  • onMessage(...) 方法中,对您在构造函数中存储的 SimpMessageSendingOperations 调用 convertAndSend(config.getStomp().getTransactionsTopic(), message.getValue())
  • destroy(...) 方法中,使用以下代码取消 Subscription 并停止 Container
if (subscription != null) {
    subscription.cancel();
}
 
if (container != null) {
    container.stop();
}

到目前为止,我们已经创建了一个 MessageListener 并订阅了银行交易的 Redis Streams,对于每个传入的银行交易,将其转发到 Stomp/Websocket 主题。

现在让我们看看这在我们的应用程序中是如何工作的。首先,在一个终端窗口中运行数据生成应用。该应用运行后,在另一个终端窗口中构建并运行该应用

./mvnw clean package
./mvnw spring-boot-run

此时两者都应该正在运行,因此导航到 http://localhost:8080 并使用 username/password 登录。您应该能够查看交易概览以及应用程序中一些尚未优化的区域(我们稍后会详细介绍)。 

您应该会看到大约每十秒出现一次新交易。如果没有,请检查您的源代码,看看是否遗漏了任何注解。现在让我们看看如何添加搜索功能。 

添加 RediSearch 功能

  • 首先停止数据生成应用和银行应用。 
  • TransactionOverviewController 类中,我们已经为您提供了大部分框架,以便您可以专注于构建 Redis 和 Spring Data 部分。
  • searchTransactions(@RequestParam("term") String term) 方法中,使用 RediSearch 搜索数据生成应用生成的交易,并将它们呈现在用户界面上。您可以通过添加以下代码来实现:
RediSearchCommands<String, String> commands = srsc.sync();
    SearchOptions options = SearchOptions
            .builder().highlight(Highlight.builder().field("description").field("fromAccountName")
                    .field("transactionType").tag(Tag.builder().open("<mark>").close("</mark>").build()).build())
            .build();
 
    SearchResults<String, String> results = commands.search(SEARCH_INDEX, term, options);

注意: 添加导入时,选择 com.redislabs 导入。 

第一条语句创建一个使用 RediSearch 客户端库的 RediSearch 连接。第二条语句根据您的偏好创建 SearchOptions。第三条语句执行搜索并返回结果。 

  • 现在您需要再次运行数据生成应用,然后构建并运行银行应用。之后,导航到 http://localhost:8080 并尝试搜索功能。
  • 在搜索框中输入字母 onli,看看会发生什么。您会注意到它会选择所有类型为 Online payment 的交易,并高亮显示单词 Online
  • 现在在搜索框中输入字母 paym,看看会发生什么。您会注意到它会选择名称字段中包含单词 BMW 的所有交易。 

添加余额随时间变化的视图

数据生成应用还会为每个账户的银行余额填充一个 TimeSeries。每当有新交易发生时,它都会更新。您可以查询此 TimeSeries 并将结果传递给用户界面,以便它可以在可视化图表中显示余额随时间的变化。现在让我们开始吧

  • 停止数据生成应用和银行应用。
  • TransactionOverviewController 类中的 balance(Principal principal) 方法中,添加以下代码:
String balance_ts_key = BALANCE_TS + principal.getName();
Map<String, String> tsValues = tsc.range(balance_ts_key, System.currentTimeMillis() - (1000 * 60 * 60 * 24 * 7),
        System.currentTimeMillis());
Balance[] balanceTs = new Balance[tsValues.size()];
int i = 0;
 
for (Entry<String, String> entry : tsValues.entrySet()) {
    Object keyString = entry.getKey();
    Object valueString = entry.getValue();
    balanceTs[i] = new Balance(keyString, valueString);
    i++;
}
return balanceTs;

您在这里做的实质上是请求存储在 BALANCE_TS 键下的、从现在到(现在 - 1 周)之间的时间序列值。然后,您需要将结果复制到 Balance 数组中并返回。 

您可能会注意到,您在这里使用 Principal 名称作为 TimeSeries 键的后缀。这意味着如果您使用不同的用户登录,它将是一个不同的键。 

  • 启动数据生成应用
  • 创建并运行该应用。您会看到余额随时间变化的视图被填充。注意它是如何随着每个传入的交易而更新的。 

注意:在此示例中,我们没有使用额外的客户端库是有原因的。相反,我们扩展了 Lettuce 库,并提供了一个我们注入的接口。扩展 Lettuce 是将 Redis Module 功能添加到您的应用中,同时限制依赖项数量的绝佳方法。

向应用添加“最大消费者”功能

将此功能添加到应用中将显示您的银行账户中最大的扣款方。因此,数据生成应用正在填充一个 Sorted Set。对于每笔交易,它会将交易值添加到 Sorted Set 的一个成员中,其中键是对手账户的名称以及该账户的总交易金额。

  • searchTransactions(@RequestParam("term") String term) 方法中,使用 RediSearch 发现数据生成应用生成的交易,并将它们呈现在用户界面上。您可以通过添加以下代码来实现:
String biggestSpendersKey = SORTED_SET_KEY + principal.getName();
Set<TypedTuple<String>> range = redis.opsForZSet().rangeByScoreWithScores(biggestSpendersKey, 0,
        Double.MAX_VALUE);
if (range.size() > 0) {
    BiggestSpenders biggestSpenders = new BiggestSpenders(range.size());
    int i = 0;
    for (TypedTuple<String> typedTuple : range) {
        biggestSpenders.getSeries()[i] = Math.floor(typedTuple.getScore() * 100) / 100;
        biggestSpenders.getLabels()[i] = typedTuple.getValue();
        i++;
    }
    return biggestSpenders;
} else {
    return new BiggestSpenders(0);
}

这将根据成员的分数(从 0 到 Double.MAX_VALUE)检索 Sorted Set 的成员。然后,您将把结果放入 BiggestSpenders 对象中。

  • 运行数据生成应用。
  • 构建并运行该应用,然后惊叹地看着最大消费者现在显示在应用中!

此时您应该正在运行,因此导航到 http://localhost:8080 并使用 username/password 登录。此时您将看到交易概览。

Redis 中发生了什么?

Redis Streams

数据生成应用大约每 10 秒生成一笔银行交易并将其放入流中。您可以使用以下命令查询该流:

xread count 10000 streams transactions_user 0-0

这将提供 transactions_username Stream 上的所有条目。请记住,您的应用通过 Spring Data 订阅了此流,因此每当向流中添加新元素时,它都会被应用处理并发送到用户界面。 

Sorted Set

数据生成应用还会将每笔交易添加到 Sorted Set 中。这会将交易金额添加到分数中,累加每个账户名称的总交易金额并将其存储在有序集合中。然后,您只需像这样查询 Sorted Set 就可以识别出最大消费者:

zrangebyscore bigspenders_username 0 10000 withscores

TimeSeries

数据生成应用还会将银行账户余额添加到 TimeSeries 中,每当有新交易生成时。这使您可以使用如下查询来查看余额随时间的变化:

ts.range balance_ts_username 1623000000000 1653230682038 (use appropriate timestamps or it will return an empty array)

Hashes

每笔交易都作为 Hash 存储在 Redis 中,以便 RediSearch 可以对其进行索引。您将能够像下面的示例一样搜索每笔交易:

ft.search transaction_description_idx Fuel
ft.search transaction_description_idx Fuel highlight
ft.search transaction_description_idx Fuel highlight tags <mytag> </mytag>

会话数据

会话数据也存储在 Redis 中。这完全不需要您编写代码——只需添加依赖项即可。 

7. 如何在 Azure Spring Cloud 上运行应用程序

  1. 按照“在本地运行”中的步骤操作
  2. 确保您已登录 Azure CLI
  3. 将 Azure Spring Cloud 扩展添加到 Azure CLI:az extension add –name spring-cloud。如果您已经安装了该扩展,请使用 az extension update --name spring-cloud 命令确保它是最新的。
  4. 使用 az spring-cloud create -n acrebank -g rdsLroACRE -l northeurope 命令创建 Azure Spring Cloud 实例(这可能需要几分钟)。
  5. 在新创建的 Azure Spring Cloud 实例中,使用 az spring-cloud app create -n acrebankapp -s acrebank -g rdsLroACRE --assign-endpoint true --runtime-version Java_11 命令创建一个应用。
  6. 修改应用程序属性,使其指向您新创建的 ACRE 实例,使用以下代码:
spring.redis.host=your ACRE hostname
spring.redis.port=your ACRE port (default: 10000)
spring.redis.password= your ACRE access key
  1. 修改应用程序属性,使 WebSocket 配置指向在步骤 3 中创建的 Azure Spring Cloud 应用实例端点。
stomp.host=your ASC app endpoint URL (Default: <appname>-<service-name>.azuremicroservices.io)
stomp.port=443
stomp.protocol=wss
  1. 使用 ./mvnw package 命令重新构建应用。
  2. 使用以下代码将应用部署到 Azure Spring Cloud:
az spring-cloud app deploy -n acrebankapp -s acrebank -g rdsLroAcre --jar-path target/redisbank-0.0.1-SNAPSHOT.jar

 

8. Azure Spring Cloud 故障排除技巧

使用以下代码获取应用程序登录信息:

az spring-cloud app logs -n acrebankapp -g rdsLroAcre -s acrebank

注意:该项目使用 JDK11 进行编译,因为它是 Azure Spring Cloud 支持的最高 LTS 版本。该项目也可以在本地或最高 JDK16 的其他平台上运行。 

9. 已知问题

下面是一些您可能会遇到的已知问题:

  1. 线程安全:数据是从单一交易流生成的,这意味着对所有用户来说都是相同的。这在当前迭代中不是问题。 
  2. 硬编码值。代码用户在整个代码中硬编码了值。这些需要替换为适当的变量。 

 结论:使用 Redis 为用户提供便捷、准确和实时的手机银行。 

手机银行是新常态,为了有效,它必须快速、便捷和准确。数字时代让我们都习惯了实时更新的无缝数字交互,未能满足这些期望将导致一个注定失败的次优应用程序。 

但通过其先进的功能和低延迟,Redis 能够保证实时数据传输,消除任何延迟的可能性。只需在移动设备上轻点几下,用户就可以访问他们的银行信息,并获得对其财务状况的全面、准确了解。 

要更直观地了解此应用程序是如何创建的,请务必查看 Lars 的 YouTube 视频。 

如果您喜欢这篇文章,请务必访问 Redis Launchpad,您可以在那里找到各种影响日常生活的不同应用程序。 

查看它。获得启发。加入 Redis 的乐趣!

谁构建了这个应用?

Lars Rosenquist

Author Image of RedisBank

Lars 在软件行业拥有丰富的经验,目前担任解决方案架构师经理,帮助他人发挥最大潜力。务必关注他的 GitHub 页面,以了解他的所有项目。