学习

如何使用 Redis 构建实时排行榜应用

Ajeet Raina
作者
Ajeet Raina, Redis 前开发者增长经理

排行榜的概念——显示领先竞争者排名、当前分数(或其他数据点)的计分板——对于电脑游戏世界至关重要,但排行榜现在不仅仅关乎游戏。它们关乎游戏化,这是一种更广泛的实现方式,可以包含任何拥有共同目标的人群(同事、学生、销售团队、健身小组、志愿者等等)。

排行榜通过公开显示每个成员的当前排名来鼓励团队内的良性竞争。它们也提供了一种清晰的方式来查看成员向目标迈进过程中整个团队的持续成就。通过排行榜对任务和目标进行游戏化是激励人们的好方法,因为它提供了与组内其他成员相比的持续反馈。做得好,这可以促成健康的竞争,从而增强团队凝聚力。

Step 1. 安装以下软件#

Step 2. 克隆仓库#

git clone https://github.com/redis-developer/basic-redis-leaderboard-demo-java

Step 3. 运行 docker compose#

docker network create global
docker-compose up -d --build

Step 4. 验证容器是否正在运行#

 docker-compose ps
            Name                           Command               State             Ports
--------------------------------------------------------------------------------------------------
redis.redisleaderboard.docker   docker-entrypoint.sh redis ...   Up      127.0.0.1:55000->6379/tcp

Step 5. 复制 .env.example 创建 .env#

提供环境变量的值(如果需要)

- REDIS_URL: Redis database endpoint URL
- REDIS_HOST: Redis server host
- REDIS_PORT: Redis server port
- REDIS_DB: Redis server db index
- REDIS_PASSWORD: Redis server password

如果您使用 Redis Cloud,则必须提供数据库端点、密码、端口和数据库名称。如果是本地系统,条目如下所示:

REDIS_URL=
REDIS_HOST=redis://localhost
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DB=

Step 6. 运行后端#

  • 安装 gradle

请参照以下链接安装适用于您 MacOS 的 Gradle: https://gradle.org.cn/install/

brew install gradle
  • 安装 JDK

请参照以下链接安装适用于您 MacOS 的 JDK: https://docs.oracle.com/javase/10/install/installation-jdk-and-jre-macos.htm

export $(cat .env | xargs)

Step 7. 运行 wrapper 任务#

要使用 Wrapper,我们需要生成一些特定文件。我们将使用内置的 Gradle 任务 wrapper 来生成这些文件。请注意,这些文件只需生成一次。

现在,在我们的项目目录中运行 wrapper 任务

gradle wrapper

应该显示以下结果

Welcome to Gradle 6.8.3!

Here are the highlights of this release:
 - Faster Kotlin DSL script compilation
 - Vendor selection for Java toolchains
 - Convenient execution of tasks in composite builds
 - Consistent dependency resolution

For more details see https://docs.gradle.org.cn/6.8.3/release-notes.html

Starting a Gradle Daemon (subsequent builds will be faster)

BUILD SUCCESSFUL in 29s
1 actionable task: 1 executed

Step 8. 执行构建任务#

Gradle Wrapper 现在可用于构建您的项目。是时候运行 wrapper 脚本来执行构建任务了。

./gradlew build
% ./gradlew build
Downloading https://services.gradle.org/distributions/gradle-6.8.3-bin.zip
..........10%..........20%..........30%...........40%..........50%..........60%..........70%...........80%..........90%..........100%
Starting a Gradle Daemon, 1 incompatible Daemon could not be reused, use --status for details

> Task :test
2021-03-01 07:08:42.962  INFO 3624 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'

BUILD SUCCESSFUL in 1m 13s
12 actionable tasks: 12 executed

Step 9. 运行您的应用#

./gradlew run
> Task :run

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

2021-03-01 07:09:59.610  INFO 3672 --- [  restartedMain] BasicRedisLeaderLoardDemoJavaApplication : Starting BasicRedisLeaderLoardDemoJavaApplication using Java 13.0.2 on Ajeets-MacBook-Pro.local with PID 3672 (/Users/ajeetraina/projects/basic-redis-leaderboard-demo-java/build/classes/java/main started by ajeetraina in /Users/ajeetraina/projects/basic-redis-leaderboard-demo-java)
2021-03-01 07:09:59.614  INFO 3672 --- [  restartedMain] BasicRedisLeaderLoardDemoJavaApplication : No active profile set, falling back to default profiles: default
2021-03-01 07:09:59.661  INFO 3672 --- [  restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : Devtools property defaults active! Set 'spring.devtools.add-properties' to 'false' to disable
2021-03-01 07:09:59.661  INFO 3672 --- [  restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : For additional web related logging consider setting the 'logging.level.web' property to 'DEBUG'
2021-03-01 07:10:00.481  INFO 3672 --- [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 5000 (http)
2021-03-01 07:10:00.492  INFO 3672 --- [  restartedMain] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2021-03-01 07:10:00.492  INFO 3672 --- [  restartedMain] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.41]
2021-03-01 07:10:00.551  INFO 3672 --- [  restartedMain] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2021-03-01 07:10:00.551  INFO 3672 --- [  restartedMain] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 889 ms
2021-03-01 07:10:00.756  INFO 3672 --- [  restartedMain] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2021-03-01 07:10:00.845  INFO 3672 --- [  restartedMain] o.s.b.a.w.s.WelcomePageHandlerMapping    : Adding welcome page: URL [file:/Users/ajeetraina/projects/basic-redis-leaderboard-demo-java/assets/index.html]
2021-03-01 07:10:00.949  INFO 3672 --- [  restartedMain] .s.s.UserDetailsServiceAutoConfiguration :

Using generated security password: ea2d5326-b04c-4f93-b771-57bcb53f656e

2021-03-01 07:10:01.016  INFO 3672 --- [  restartedMain] o.s.s.web.DefaultSecurityFilterChain     : Will secure any request with [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@583fa06c, org.springframework.security.web.context.SecurityContextPersistenceFilter@524c0386, org.springframework.security.web.header.HeaderWriterFilter@c6e5d4e, org.springframework.security.web.authentication.logout.LogoutFilter@3e1f33e9, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@6790427f, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@40ddf86, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@1412ffa9, org.springframework.security.web.session.SessionManagementFilter@3eb6c20f, org.springframework.security.web.access.ExceptionTranslationFilter@21646e94, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@649e1b25]
2021-03-01 07:10:01.043  INFO 3672 --- [  restartedMain] o.s.b.d.a.OptionalLiveReloadServer       : LiveReload server is running on port 35729
2021-03-01 07:10:01.065  INFO 3672 --- [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 5000 (http) with context path ''
2021-03-01 07:10:01.093  INFO 3672 --- [  restartedMain] BasicRedisLeaderLoardDemoJavaApplication : Started BasicRedisLeaderLoardDemoJavaApplication in 1.937 seconds (JVM running for 2.327)
<=========----> 75% EXECUTING [17s]
> :run

Step 10. 访问排行榜应用#

工作原理#

数据存储方式:#

AAPL 的详细信息——2.6 万亿美元的市值和美国来源——存储在如下所示的 hash 中

 HSET "company:AAPL" symbol "AAPL" market_cap "2600000000000" country USA

AAPL 2.6 万亿美元的排名存储在 ZSET 中。

 ZADD  companyLeaderboard 2600000000000 company:AAPL

数据访问方式:#

排名前 10 的公司

 ZREVRANGE companyLeaderboard 0 9 WITHSCORES

所有公司

 ZREVRANGE companyLeaderboard 0 -1 WITHSCORES

排名后 10 的公司

 ZRANGE companyLeaderboard 0 9 WITHSCORES

排名在 10 到 15 之间

 ZREVRANGE companyLeaderboard 9 14 WITHSCORES

显示 AAPL、FB 和 TSLA 的排名

 ZREVRANGE  companyLeaderBoard company:AAPL company:FB company:TSLA

给 FB 公司市值增加 10 亿

 ZINCRBY companyLeaderBoard 1000000000 "company:FB"

给 FB 公司市值减少 10 亿

 ZINCRBY companyLeaderBoard -1000000000 "company:FB"

市值在 5000 亿到 1 万亿之间的公司

 ZCOUNT companyLeaderBoard 500000000000 1000000000000

市值超过 1 万亿的公司

 ZCOUNT companyLeaderBoard 1000000000000 +inf

参考资料#