go ai编程(七爪源码用Go)

端到端的实现

go ai编程(七爪源码用Go)(1)

我们大多数人都喜欢知识竞赛,对吧? 有许多应用程序可以通过让我们回答来自不同职业的问题来解渴。

在这篇文章中,我将解释我是如何使用 Golang 实现一个实时竞赛应用程序的。

申请流程

go ai编程(七爪源码用Go)(2)

需要遵循一些业务规则。

  • 当连接用户达到两个时,比赛将在 3 秒内自动开始。
  • 我们的比赛共有三个州。这些是 NOT_STARTED、STARTED、FINISHED。
  • 问题有四个选项,用户必须在 10 秒内回答一个问题。
  • 比赛结束后,向用户展示排行榜以查看比赛结果。

我的架构决策

在本节中,我将尝试解释为什么我做出了一些决定,并在开始之前尝试对我们的项目提出一些观点。这部分就像电影剧透一样。

  • Websocket 是实现实时应用程序的基本协议。它提供客户端和服务器之间的双向通信。有很多技术文章介绍了它的概念,我就不赘述了。我用它来发送问题并获得连接用户的答案。
  • 我为每个连接的用户使用了一个唯一的 id(如 Session-id)。这样做,我可以轻松区分用户。在我们的例子中,我们用他们的会话 ID 存储我们的用户,我们的服务器使用它们来管理读写操作。
  • 为了支持并发读写操作,我使用了 sync.Map 。我使用 sessionID 作为键,使用 Client 结构作为如下所示的值。客户端结构由两个字段组成,一个用于写入和读取的客户端 WebSocket 连接以及用于计算排行榜的 totalScore。

go ai编程(七爪源码用Go)(3)

  • 广播是一个特殊术语,表示同时将消息传输给所有接收者的方法。 不幸的是,gorilla/websocket 中没有广播方法; 因此,我们将使用我们自定义的广播方法向所有用户发送消息。

go ai编程(七爪源码用Go)(4)

  • 要将问题发送给我们的用户,我们需要定义一个模型。 在我们的应用程序中,我不想使用 Question 结构,因为它具有 correct_answer 字段。 我想对连接的用户隐藏这些信息,所以我创建了另一个名为 QuestionDTO 的模型,如下所示。

go ai编程(七爪源码用Go)(5)

比赛流程

我曾经使用 RunCompetition() 方法管理比赛流程,如下所示。

我通过创建一个 goroutine 在应用程序的主要流程中调用了这个函数。

func RunCompetition() { CompetitionState = CompetitionNotStartedState for { if CompetitionState == CompetitionNotStartedState { time.Sleep(CompetitionStateDuration) numberOfClients := CountClient() msg := DetermineCompetitionState(numberOfClients) BroadcastMessage([]byte(msg)) if numberOfClients == 2 { time.Sleep(CompetitionStartDuration) CompetitionState = CompetitionStartedState } } else if CompetitionState == CompetitionStartedState { PrepaRequestions() StartSendingQuestions() CompetitionState = CompetitionFinish } else if CompetitionState == CompetitionFinish { leaderBoard := CreateLeaderBoard() jsonBytes, _ := json.Marshal(leaderBoard) BroadcastMessage(jsonBytes) BroadcastMessage([]byte(CompetitionFinishedStateMessage)) break } } }

RunCompetition() 方法中有三个 CompetitionState。

  • CompetitionNotStartedState:要开始比赛,必须有两个用户。 当 numberOfClients 等于 2 时,状态变为 CompetitionStartedState。
  • CompetitionStartedState:在这种状态下,我们每 10 秒向所有连接的用户发送问题。

func StartSendingQuestions() { for i := range Questions { Questions[i].IsTimeout = false questionDTO := Questions[i].ToDTO() questionDTOBytes, _ := json.Marshal(questionDTO) BroadcastMessage(questionDTOBytes) time.Sleep(QuestionResponseIntervalDuration) Questions[i].IsTimeout = true } }

发送问题时,我们将 Question 结构体转换为 questionDTO 以向连接的用户隐藏正确答案以防止作弊。

10 秒后,我们将 IsTimeout 更改为 true,因为给问题的时间到了。

  • CompetitionFinish:发送完所有问题后, CompetitionState 变为 CompetitionFinish。 比赛结束后,我们必须创建排行榜并将其发送给所有连接的用户。

处理客户端应答流

我们需要从用户那里得到答案来检查和计算他们的分数。

func HandleClientAnswer(sessionID string, message []byte) { var ClientMsg ClientMessage json.Unmarshal(message, &ClientMsg) for _, question := range Questions { if question.ID == ClientMsg.QuestionId { if question.IsTimeout == true { fmt.Println("Response Time is out") } else { load, _ := Clients.Load(sessionID) client := load.(Client) if ClientMsg.Answer == question.CorrectAnswer { client.totalScore = ScoreForCorrectAnswer Clients.Store(sessionID, client) fmt.Printf("Right Answer!!! SessionId: %s, TotalScore: %d\n", sessionID, client.totalScore) } else { fmt.Printf("Wrong Answer!! Your Answer is : %s, Right Answer is : %s, SessionId: %s, TotalScore: %d\n", ClientMsg.Answer, question.CorrectAnswer, sessionID, client.totalScore) } } } } }

用户在指定的时间间隔内正确回答问题可以获得分数( 10)。

这里有一些重要的点:

  • IsTimeout:指定时间间隔
  • 比较 question.ID 和 ClientMsg.QuestionId :判断是否被问到问题
  • 比较 ClientMsg.Answer 和 question.CorrectAnswer :判断用户是否给出了正确答案。

满足这些条件后,用户的分数通过 sessionID 存储在地图上。 我们在 ws() 方法中调用这个函数。

func ws(c echo.Context) error { numberOfClients := CountClient() if numberOfClients >= 2 { return c.string(http.StatusBadRequest, "") } wsConn, err := Upgrader.Upgrade(c.Response(), c.Request(), nil) if err != nil { return err } defer wsConn.Close() sessionID := IDGenerator() Clients.Store(sessionID, Client{ wsConn: wsConn, totalScore: 0, }) for { _, message, err := wsConn.ReadMessage() if err != nil { Clients.Delete(sessionID) c.Logger().Errorf("Client disconnect msg=%s err=%s", string(message), err.Error()) return nil } HandleClientAnswer(sessionID, message) } }

为了让我们的应用程序简单,比赛将从两个用户开始。 如果用户的数量大于 2,我们的应用会发送 http.StatusBadRequest 给用户。

关注七爪网,获取更多APP/小程序/网站源码资源!

,

免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com

    分享
    投诉
    首页