dot 快速的未来即将在您所在的城市举行活动。

加入我们参加 Redis 发布会

如何使用 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

这是一个 SpringBoot 应用程序,它由 API 和前端组成。 

  • 一切的基础是事务生成器,它生成所有银行事务。请注意,这只是一个用于生成测试数据的组件。在现实世界场景中,我们期望数据来自其他地方(应用程序外部)。
  • 对于进行的每笔银行交易,数据都会放在 RedisStreams 上。然后,流会被事务转发器接收。 
  • 事务转发器从 RedisStreams 获取事务信息,并将信息放在 Websocket 连接上,该连接可以被 Web UI 使用。这些事务也被用于使用 RedisSearch 填充辅助索引,并且该索引会公开搜索 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. 访问应用程序 

导航到 https://#:8080 并使用以下凭据登录

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

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

以下是登录页面应显示的屏幕截图。 

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

从这里,您将能够通过许多不同的门户功能更牢固地控制您的财务状况。这些功能包括

  • 实时交易更新:Redis 的低延迟确保所有交易都实时更新,为您提供有关财务状况的最新信息。 
  • 银行余额的图形说明:使用户能够清楚地了解自己的财务状况。 
  • 购买历史记录:使用户能够详细监控消费历史记录。
  • 搜索亮点:使用户能够从数据库中选出特定的搜索词。 

6. 工作原理

Redis 流是追加式日志。数据生成应用程序将把交易放在流上,应用程序将订阅该流,并将使用 Websocket 连接使用任何收入交易并将它们发送到前端。

  • 向应用程序添加一个名为 BankTransactionForwader 的新类,该类实现 InitializingBean、DisposableBeanStreamListener<String, MapRecord<String, String, String>> 接口。确保为其提供 @Component 注释,以便 Spring 能够获取并自动装配依赖项。

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

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

  • BankTransactionForwarder 的构造函数中,向方法签名(将被自动装配)添加三个参数:a RedisTemplateSimpMessageSendingOperationsConfig 对象(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 流。每当流上有新消息到达时,此类的 onMessage(…) 方法将被调用。您仍然需要执行此方法,因此让我们看看它是如何完成的。 

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

到目前为止,我们已经创建了一个 MessageListener 并订阅了 BankTransactions 的 Redis 流,并且对于每个传入的 BankTransaction,都将 BankTransaction 转发到 Stomp/Websocket 主题。

现在,让我们看看它在应用程序中的工作原理。首先,在终端窗口中运行数据生成应用程序。在那之后运行之后,在另一个终端窗口中构建并运行应用程序

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

此时两个应用程序都应该在运行,因此请访问 https://#:8080 并使用 用户名/密码 登录。您应该能够访问交易概览以及应用程序中一些未优化的区域(我们很快会处理这些区域)。 

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

添加 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。第三条语句执行搜索并返回结果。 

  • 现在您需要再次运行数据生成应用程序,然后构建并运行银行应用程序。之后,导航到 https://#:8080 并尝试搜索功能。
  • 在搜索框中输入字母 onli 并查看结果。您会注意到它会选择所有类型为 在线支付 的交易,并突出显示单词 在线
  • 现在在搜索框中输入字母 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;

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

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

  • 启动数据生成应用程序
  • 创建并运行应用程序。您会看到随时间推移的余额已填充。注意它如何随着每笔传入交易进行更新。 

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

向应用程序添加“最大支出者”功能

向应用程序添加此功能将显示从您的银行账户中扣款最多的支出者。因此,数据生成应用程序正在填充一个有序集合。对于每笔交易,它将交易的值添加到有序集合的成员中,该成员的键是对方账户的名称以及该账户的总交易金额。

  • 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 检索有序集合的成员。然后,您将结果放入 BiggestSpenders 对象中。

  • 运行数据生成应用程序。
  • 构建并运行应用程序,惊叹地观看最大支出者现在如何在应用程序中显示!

您现在应该在运行,因此请访问 https://#:8080 并使用 用户名/密码 登录。此时,您现在将看到交易概览。

Redis 中发生了什么?

Redis 流

数据生成应用程序每 10 秒左右生成一次银行交易,并将其放入流中。您可以使用以下命令查询流

xread count 10000 streams transactions_user 0-0

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

有序集合

数据生成应用程序还会将每笔交易添加到一个有序集合中。这会将交易金额添加到分数中,从而将每个账户名称的总交易金额累加起来,并将其存储在一个有序集合中。然后,您可以通过以下方式简单地查询有序集合来识别最大的支出者

zrangebyscore bigspenders_username 0 10000 withscores

时间序列

每次生成新的交易时,数据生成应用程序还会将银行账户余额添加到 TimeSeries 中。这允许您使用以下查询查看随时间推移的余额

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

哈希

每笔交易都作为哈希存储在 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. 使用 az spring-cloud app create -n acrebankapp -s acrebank -g rdsLroACRE --assign-endpoint true --runtime-version Java_11 在新创建的 Azure Spring Cloud 实例中创建一个应用程序
  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 页面,随时了解他的所有项目。