微擎添加新模块

    科技2025-03-30  10

    微擎添加新模块

    Microservices are something that feels very natural when you have been working with monoliths in the past and are tired of the overheads and errors that arise in maintaining a monolith application.

    当您过去使用整体式服务时,微服务是一种非常自然的感觉,并且您厌倦了维护整体式应用程序时产生的开销和错误。

    My first microservice was a user service that would manage login/logout/auth for a user. We were adding more logins to our application because just one Google login wasn’t enough. I knew we’d be adding/removing more ways to log in users in the future. So I wanted to create a separate service to manage all this and not affect other parts of the application when we add/remove new login methods.

    我的第一个微服务是一项用户服务,它将为用户管理登录/注销/身份验证。 我们正在向应用程序中添加更多登录名,因为仅一个Google登录名是不够的。 我知道我们将来会添加/删除更多登录用户的方式。 因此,我想创建一个单独的服务来管理所有这些事务,并且在我们添加/删除新的登录方法时不影响应用程序的其他部分。

    We agreed that a user service can take care of all this. After login, it would provide a token to the client that could then be used by the client to validate the user’s identity when calling other parts of our application (other services).

    我们同意用户服务可以解决所有这些问题。 登录后,它将为客户端提供一个令牌,当调用我们应用程序的其他部分(其他服务)时,客户端可以使用该令牌来验证用户的身份。

    It was a success. We added more logins and configured the availability of different logins based on regions, and the microservice architecture helped a lot. We could independently deploy the user service without worrying about breaking something in some other part of our application. No matter what login method was used, the other services would always get an auth token to validate the user's identity.

    这是成功的。 我们添加了更多登录名,并根据区域配置了不同登录名的可用性,并且微服务架构起了很大的作用。 我们可以独立部署用户服务,而不必担心会破坏应用程序其他部分的内容。 无论使用哪种登录方法,其他服务都将始终获得身份验证令牌以验证用户的身份。

    It was all so elegant right from the beginning. All the operations related to managing a user can be handled by this service alone, and no part of my application needed to worry about it.

    从一开始,一切就如此优雅。 与管理用户有关的所有操作都可以单独通过此服务来处理,而我的应用程序的任何部分都不必为此担心。

    No doubt this is a very basic and simple use case for a microservice and might even be the status quo for an experienced microservice architect, but for me, it was a moment of joy. I had come up with a pattern to create a microservice on my own.

    毫无疑问,这是微服务的一个非常基本且简单的用例,甚至可能是经验丰富的微服务架构师的现状,但是对我而言,这是一个快乐的时刻。 我想出了一种模式,可以自行创建微服务。

    This decomposition of microservices (deciding if you need a microservice for something) can be so natural for such simple use cases that I was tricked into believing that microservices are easy and I somewhat had a handle on them. So naive!

    在如此简单的用例中,微服务的这种分解(决定您是否需要微服务)很自然,以至于我被骗了以为微服务很容易,并且我对它们有所了解。 太天真了!

    A couple of other microservices later…

    稍后还有其他一些微服务…

    排行榜 (The Leaderboards)

    Recently we were developing leaderboards for our applications. These leaderboards would rank users and communities, separately, across games. So it came to us that maybe this leaderboard thing should be extracted into a separate service of its own.

    最近,我们正在为我们的应用程序开发排行榜。 这些排行榜将在游戏中分别对用户和社区进行排名。 因此我们想到也许应该将排行榜中的内容提取到自己的单独服务中。

    We wanted a typical leaderboard that would maintain the scores for an entity and based on the score, rank that entity amongst others.

    我们想要一个典型的排行榜,该排行榜将维护实体的得分,并根据该得分对实体进行排名。

    What we wanted the leaderboards to be like 我们希望排行榜成为什么样

    Note: For simplicity, we’ll just talk in the context of a user leaderboard from now on.

    注意:为简单起见,从现在开始,我们仅在用户排行榜的背景下进行讨论。

    We started discussing, and we defined what the role of a leaderboard service would be:

    我们开始讨论,并定义了排行榜服务的作用是:

    Accept match-end payload. (Leaderboards were to be updated when a match between two users concluded.)

    接受匹配结束负载。 (当两个用户之间的比赛结束时,将更新排行榜。) Fetch current scores for users involved in that match.

    为参与该比赛的用户获取当前分数。 Apply “the algorithm” to update the scores, based on the match result. (You can assume the algorithm to be a black box which takes in the current scores and returns the updated scores based on the outcome of the match.)

    根据匹配结果,应用“算法”更新分数。 (您可以假设该算法是一个黑匣子,该黑匣子接受当前分数并根据比赛结果返回更新的分数。) Update the new scores on the leaderboard.

    在排行榜上更新新分数。 The leaderboard should reorder the ranks for users according to the new scores.

    排行榜应根据新分数重新排列用户的排名。

    We thought if we could create a service that could accept the match-end payload and do all this work in abstraction without other services knowing anything, that would be great.

    我们认为,如果我们可以创建一个可以接受匹配端有效负载的服务,并且抽象地完成所有这些工作,而其他服务却一无所知,那就太好了。

    I wanted the leaderboard service to be dumb and reusable by any other service that wanted to maintain a leaderboard. My idea was that each individual service that wanted a leaderboard should call the leaderboard service with a generic payload and expect the leaderboard service to take care of everything, from initialising a leaderboard to updating it.

    我希望排行榜服务能够被任何其他想要保持排行榜的服务所愚弄和重用。 我的想法是,每个需要排行榜的服务都应使用通用有效负载调用排行榜服务,并期望排行榜服务能够处理从初始化排行榜到更新排行榜的所有事务。

    But I was having a hard time generalising the logic to update the leaderboard into a single service without making a mess of the leaderboard service. For example, if a match for Fortnite just ended, we wanted to update the leaderboard for Fortnite with the latest scores of the users involved in the match. But after updating the Fortnite leaderboard (i.e., the game-specific leaderboard) we also wanted to update the overall global leaderboard.

    但是我很难推广将排行榜更新为单个服务而不弄乱排行榜服务的逻辑。 例如,如果与Fortnite的比赛刚刚结束,我们想用与比赛相关的用户的最新分数来更新Fortnite的页首横幅。 但是在更新了Fortnite排行榜(即特定于游戏的排行榜)之后,我们还希望更新整个全球排行榜。

    The global leaderboard would be a common scale to rank all the users, whereas the game-specific leaderboards would maintain the user’s rank for a single game. The logic to update the global leaderboard is not something I'm proud of, but this is how it is: The scores for the global leaderboard would be decided based on the max score of the user out of all the game-specific leaderboards.

    全球排行榜是对所有用户进行排名的通用标度,而游戏特定的排行榜将维护单个游戏的用户排名。 更新全球排行榜的逻辑不是我引以为豪的事情,而是这样的:全局排行榜的分数将根据用户在所有特定游戏排行榜中的最高分数来决定。

    So a regular scenario would go like this: A match ends → we update the scores for game-specific leaderboard → aggregate the new max score for the user across all game-specific leaderboards → update the score on the global leaderboard.

    因此,常规情况如下:比赛结束→我们更新游戏特定排行榜的分数→在所有游戏特定排行榜上为用户汇总新的最高分→在全局排行榜上更新分数。

    How leaderboards will update after a match 比赛后排行榜的更新方式

    To update the global leaderboard, I did not need to apply “the algorithm” that would calculate the new scores based on the previous scores and the outcome of the match. I just needed to get the max score for the user across all game-specific leaderboards and use that as the score for the global leaderboard.

    要更新全球排行榜,我不需要应用“算法”,该算法将根据以前的分数和比赛结果计算新分数。 我只需要在所有特定于游戏的排行榜上获得用户的最高分数,并将其用作全局排行榜的分数。

    有效参数 (The Valid Arguments)

    With the current mindset, it was clear that we needed to separate the score calculation logic from the storage of the leaderboard. We all agreed that we could combine the calculation logic into a library. For the storage of the leaderboard, some of my peers wanted to keep the storage of the leaderboard in a separate service. I, on the other hand, wanted to create another library that would manage the storage of the leaderboard for each service as well.

    以当前的心态来看,很明显,我们需要将分数计算逻辑与排行榜的存储区分开。 我们都同意我们可以将计算逻辑合并到一个库中。 对于排行榜的存储,我的一些同僚希望将排行榜的存储保留在单独的服务中。 另一方面,我想创建另一个库来管理每个服务的排行榜存储。

    I wanted to ship two libraries, one for calculation and another for storage of the leaderboard. Any service that wanted a leaderboard would just implement these leaderboard libraries and have the leaderboards.

    我想提供两个库,一个用于计算,另一个用于存储排行榜。 任何需要排行榜的服务都只需实现这些排行榜库并拥有排行榜。

    But my peers presented some valid arguments for creating a separate service for the storage of leaderboards and having the calculation algorithm of the scores reside in a library. Each service that needs a leaderboard would use the library to calculate the scores and then reach out to the leaderboard service to update/get the scores.

    但是我的同事们提出了一些有效的论据,以创建用于排行榜存储的单独服务,并将分数的计算算法存储在库中。 每个需要排行榜的服务都将使用库来计算分数,然后与排行榜服务联系以更新/获取分数。

    I’ve tried to list some of the arguments my peers presented in favor of having a separate service for leaderboard storage:

    我尝试列出了我的同事提出的一些论点,以支持为排行榜存储提供单独的服务:

    Changes in leaderboard storage logic will need to deploy the entire service or maybe all the services implementing the leaderboard storage lib. Instead, if storage was handled by a separate service, only one service will have to be changed. This one was the first indication that maybe I was wrong.

    排行榜存储逻辑的更改将需要部署整个服务,或者可能部署实现排行榜存储库的所有服务。 相反,如果存储是由单独的服务处理的,则只需更改一项服务。 这是我可能是错的第一个迹象。

    Leaderboard storage might not require the same resources as the service maintaining the leaderboard, and if the load for leaderboard requests increases, there is no point in spawning expensive instances of the particular service just to serve leaderboard requests. I was almost down.

    排行榜存储可能不需要与维护排行榜的服务相同的资源,并且如果排行榜请求的负载增加,则仅产生用于服务排行榜请求的特定服务的昂贵实例就没有意义。 我快要失望了。

    Lastly, the final arrow that took me down was the enforcement of single-threaded operations when updating a leaderboard. The updates to the leaderboard could not be concurrent and had to regulated/batched to be executed sequentially to avoid parallel updates. Ensuring this in each service through the library would have been an overhead for each service. We could hide that in the leaderboard service, and no other service would have to worry about it.

    最后,让我失望的最后一个箭头是在更新页首横幅时执行单线程操作 。 排行榜的更新不能是并发的,必须进行调整/批处理才能顺序执行,以避免并行更新。 通过库在每个服务中确保这样做将是每个服务的开销。 我们可以将其隐藏在排行榜服务中,而其他任何服务都不必担心。

    Well, I couldn’t reject these arguments so agreed to develop a service. But because of the different calculation logics that we had to manage, we thought it would be best if the leaderboard service only implemented the storage part of the leaderboard and had just the APIs to get score/rank and set score and eventually update the rank based on the new score.

    好吧,我不能拒绝这样的论点,因此同意开发服务。 但是由于我们必须管理不同的计算逻辑,我们认为最好是排行榜服务仅实现排行榜的存储部分,并且仅使用API​​来获取得分/排名和设置得分,并最终根据排名更新在新分数上。

    How the scores change would be managed by the respective service that wants to maintain a leaderboard. We would ship a library with the algorithms that we used to update the score.

    分数的变化方式将由希望维持排行榜的各个服务来管理。 我们将提供带有用于更新得分的算法的库。

    So the new flow would be something like this: match end → get the current score from leaderboard service → update the score based on the match result → set the new score at leaderboard service.

    因此,新流程将如下所示:比赛结束→从排行榜服务获取当前分数→根据匹配结果更新分数→在排行榜服务处设置新分数。

    启示录 (The Revelation)

    The above approach solved our use case, but I was convinced that there is still something better out there for us.

    上面的方法解决了我们的用例,但是我确信仍然有一些更好的选择。

    I felt that if we were going to implement the leaderboard service, it should abstract away all the complexities with managing a leaderboard, and no other service should have to worry about stuff related to leaderboards. The only thing that was holding us from making this abstraction possible was the inability to generalise the score calculation logics — the max score across all games for global score logic, to be precise.

    我认为,如果我们要实施排行榜服务,它应该通过排行榜抽象出所有复杂性,而其他任何服务都不必担心与排行榜有关的事情。 唯一使我们无法实现这种抽象的原因是无法概括分数计算逻辑-确切地说,是所有游戏中针对全局分数逻辑的最大分数。

    Any general algorithm that would update scores should be based on the match result — who won, who lost — not on some aggregation like max. This max logic was a temporary requirement; a correct algorithm for a leaderboard would always be based on some win/lose result.

    任何会更新分数的通用算法都应基于比赛结果-谁赢了,谁输了-而不是基于像max这样的汇总。 这个最大逻辑是一个临时需求; 排行榜的正确算法将始终基于某些获胜/失败结果。

    Ten microservices articles later…

    稍后发表十篇微服务文章……

    With some luck, I managed to create a decoupled architecture for the leaderboard service that I was satisfied with. It abstracted away the complexities of a leaderboard, was extendable to the different types of scenarios we had, and would support the different types of calculation logics that we wanted.

    幸运的是,我设法为自己满意的排行榜服务创建了一个分离的体系结构。 它抽象了排行榜的复杂性,可以扩展到我们所拥有的不同类型的场景,并且可以支持我们想要的不同类型的计算逻辑。

    The final design that served all our use cases 适用于我们所有用例的最终设计

    Apparently, pub/sub architecture was all we needed, and some decoupling of the leaderboard updates. We added two ways to update any leaderboard.

    显然,我们只需要发布/订阅体系结构,以及排行榜更新之间的脱钩。 我们添加了两种更新排行榜的方法。

    One — Listen to match-ended event and update game-specific leaderboard.

    一种-听比赛结束的事件并更新特定于游戏的排行榜。

    Two — Listen to user max-score changed event to update the global leaderboard.

    二-收听用户max-score更改的事件以更新全局排行榜。

    详细的解释 (The long explanation)

    The leaderboard service would subscribe to match-end events and update the per game leaderboards for users. The leaderboard service would implement the algorithms used to update the scores based on the match result. So in this way, it would abstract away the complexities of the leaderboard, and no other service needs to worry about how the leaderboard is being updated. This was the easy part; we had something like this in mind when we started. The thing holding us back was the max aggregation for the global leaderboard scores.

    排行榜服务将订阅比赛结束事件并为用户更新每游戏排行榜。 排行榜服务将根据匹配结果实施用于更新得分的算法。 因此,通过这种方式,可以抽象出排行榜的复杂性,而其他服务则无需担心排行榜的更新方式。 这是容易的部分; 我们开始时就想到了这样的事情。 阻碍我们前进的是全球排行榜得分的最高汇总。

    The difficult part was to implement the max score aggregation in a sane manner to update the global leaderboard for a user. I could have just coupled this logic in the leaderboard service and updated the global leaderboard by fetching scores from game-specific leaderboards for that user, but that didn’t feel like the right thing to do because the leaderboard service knows no dependencies between different leaderboards. All leaderboards are independent of each other, at least for the sake of the leaderboard service.

    困难的部分是以理智的方式实施最大分数汇总,以更新用户的全球排行榜。 我可以将这种逻辑结合到排行榜服务中,并通过从特定于游戏的排行榜中为该用户获取分数来更新全球排行榜,但这并不是正确的选择,因为排行榜服务不知道不同排行榜之间的依赖性。 至少出于排行榜服务的考虑,所有排行榜都彼此独立。

    To solve this, I introduced a new leaderboard-updated event which the leaderboard service would emit and the profile service (owner of the user profile) would subscribe to. So when any game-specific leaderboard would update for a user, the leaderboard service would emit the event and the profile service would subscribe to it. The profile service can then get the scores for all games from the leaderboard service and apply the max aggregation on them. The profile service would then send another event to the leaderboard service to update the score directly for the global leaderboard.

    为了解决这个问题,我引入了一个新的排行榜更新事件,排行榜服务将发出该事件,而概要文件服务(用户概要文件的所有者)将订阅该事件。 因此,当任何特定于游戏的排行榜将为用户更新时,排行榜服务将发出事件,而个人档案服务将对其进行订阅。 然后,个人资料服务可以从排行榜服务获取所有游戏的分数,并在其上应用最大汇总。 然后,个人资料服务会将另一个事件发送到排行榜服务,以直接为全球排行榜更新分数。

    结论 (Conclusion)

    So we opened another interface to the leaderboard service that would allow direct score updating in a leaderboard, bypassing the application of algorithms to calculate scores.

    因此,我们为排行榜服务打开了另一个接口,该接口将允许排行榜中的直接分数更新,而无需使用算法来计算分数。

    Finally, this approach seemed sane enough, and the operations were generalised. I could sleep now.

    最终,这种方法看起来很理智,并且对操作进行了概括。 我现在可以睡觉了。

    This was just one example from my personal experience about how hard microservices can be and how they require a lot of thought to implement in a way that will support your requirements. This has opened me up to learn about the different patterns for designing microservices.

    这只是我个人经验中的一个例子,说明微服务的强度以及如何以支持您的需求的方式进行思考。 这使我了解了设计微服务的不同模式 。

    I welcome any improvements or arguments against the above pattern. I’m no expert and would love to hear if we can improve this implementation.

    我欢迎针对上述模式的任何改进或争论。 我不是专家,很想听听我们是否可以改善此实现。

    翻译自: https://medium.com/better-programming/adding-a-new-microservice-be191e6451ac

    微擎添加新模块

    Processed: 0.011, SQL: 8