hello.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. package main
  2. import (
  3. "context"
  4. "fmt"
  5. "github.com/cloudwego/hertz/pkg/app"
  6. "github.com/cloudwego/hertz/pkg/app/server"
  7. "github.com/cloudwego/hertz/pkg/common/config"
  8. "github.com/cloudwego/hertz/pkg/common/utils"
  9. "log"
  10. "math/rand"
  11. "runtime"
  12. "sort"
  13. "strconv"
  14. "database/sql"
  15. _ "github.com/go-sql-driver/mysql"
  16. )
  17. const (
  18. // Database
  19. worldSelect = "SELECT id, randomNumber FROM World WHERE id = ?"
  20. worldUpdate = "UPDATE World SET randomNumber = ? WHERE id = ?"
  21. fortuneSelect = "SELECT id, message FROM Fortune;"
  22. worldRowCount = 10000
  23. maxConnectionCount = 256
  24. )
  25. type World struct {
  26. Id uint16 `json:"id"`
  27. RandomNumber uint16 `json:"randomNumber"`
  28. }
  29. type Fortune struct {
  30. Id uint16 `json:"id"`
  31. Message string `json:"message"`
  32. }
  33. type Fortunes []*Fortune
  34. func (s Fortunes) Len() int { return len(s) }
  35. func (s Fortunes) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
  36. type ByMessage struct{ Fortunes }
  37. func (s ByMessage) Less(i, j int) bool { return s.Fortunes[i].Message < s.Fortunes[j].Message }
  38. var (
  39. // Database
  40. worldStatement *sql.Stmt
  41. fortuneStatement *sql.Stmt
  42. updateStatement *sql.Stmt
  43. )
  44. func parseQueries(c context.Context, ctx *app.RequestContext) int {
  45. n, err := strconv.Atoi(ctx.Query("queries"))
  46. if err != nil {
  47. n = 1
  48. } else if n < 1 {
  49. n = 1
  50. } else if n > 500 {
  51. n = 500
  52. }
  53. return n
  54. }
  55. // / Test 1: JSON serialization
  56. func json(c context.Context, ctx *app.RequestContext) {
  57. ctx.JSON(200, utils.H{"message": "Hello, World!"})
  58. }
  59. // / Test 2: Single database query
  60. func db(c context.Context, ctx *app.RequestContext) {
  61. var world World
  62. err := worldStatement.QueryRow(rand.Intn(worldRowCount)+1).Scan(&world.Id, &world.RandomNumber)
  63. if err != nil {
  64. ctx.AbortWithError(500, err)
  65. return
  66. }
  67. ctx.JSON(200, &world)
  68. }
  69. // / Test 3: Multiple database queries
  70. func dbs(c context.Context, ctx *app.RequestContext) {
  71. numQueries := parseQueries(c, ctx)
  72. worlds := make([]World, numQueries)
  73. for i := 0; i < numQueries; i++ {
  74. err := worldStatement.QueryRow(rand.Intn(worldRowCount)+1).Scan(&worlds[i].Id, &worlds[i].RandomNumber)
  75. if err != nil {
  76. ctx.AbortWithError(500, err)
  77. return
  78. }
  79. }
  80. ctx.JSON(200, &worlds)
  81. }
  82. // / Test 4: Fortunes
  83. func fortunes(c context.Context, ctx *app.RequestContext) {
  84. rows, err := fortuneStatement.Query()
  85. if err != nil {
  86. ctx.AbortWithError(500, err)
  87. return
  88. }
  89. fortunes := make(Fortunes, 0, 16)
  90. for rows.Next() { //Fetch rows
  91. fortune := Fortune{}
  92. if err := rows.Scan(&fortune.Id, &fortune.Message); err != nil {
  93. ctx.AbortWithError(500, err)
  94. return
  95. }
  96. fortunes = append(fortunes, &fortune)
  97. }
  98. fortunes = append(fortunes, &Fortune{Message: "Additional fortune added at request time."})
  99. sort.Sort(ByMessage{fortunes})
  100. ctx.HTML(200, "fortune.html", fortunes)
  101. }
  102. // / Test 5: Database updates
  103. func update(c context.Context, ctx *app.RequestContext) {
  104. numQueries := parseQueries(c, ctx)
  105. world := make([]World, numQueries)
  106. for i := 0; i < numQueries; i++ {
  107. if err := worldStatement.QueryRow(rand.Intn(worldRowCount)+1).Scan(&world[i].Id, &world[i].RandomNumber); err != nil {
  108. ctx.AbortWithError(500, err)
  109. return
  110. }
  111. world[i].RandomNumber = uint16(rand.Intn(worldRowCount) + 1)
  112. if _, err := updateStatement.Exec(world[i].RandomNumber, world[i].Id); err != nil {
  113. ctx.AbortWithError(500, err)
  114. return
  115. }
  116. }
  117. ctx.JSON(200, world)
  118. }
  119. // / Test 6: plaintext
  120. func plaintext(c context.Context, ctx *app.RequestContext) {
  121. ctx.String(200, "Hello, World!")
  122. }
  123. func main() {
  124. h := server.New(config.Option{F: func(o *config.Options) {
  125. o.Addr = ":8080"
  126. },
  127. })
  128. serverHeader := []string{"Hertz"}
  129. h.Use(func(c context.Context, ctx *app.RequestContext) {
  130. ctx.Header("Server", serverHeader[0])
  131. })
  132. h.LoadHTMLGlob("/templates/fortune.html")
  133. h.GET("/json", json)
  134. h.GET("/db", db)
  135. h.GET("/dbs", dbs)
  136. h.GET("/fortunes", fortunes)
  137. h.GET("/update", update)
  138. h.GET("/plaintext", plaintext)
  139. h.Spin()
  140. }
  141. func init() {
  142. runtime.GOMAXPROCS(runtime.NumCPU())
  143. dsn := "benchmarkdbuser:benchmarkdbpass@tcp(%s:3306)/hello_world"
  144. dbhost := "tfb-database"
  145. db, err := sql.Open("mysql", fmt.Sprintf(dsn, dbhost))
  146. if err != nil {
  147. log.Fatalf("Error opening database: %v", err)
  148. }
  149. db.SetMaxIdleConns(maxConnectionCount)
  150. worldStatement, err = db.Prepare(worldSelect)
  151. if err != nil {
  152. log.Fatal(err)
  153. }
  154. fortuneStatement, err = db.Prepare(fortuneSelect)
  155. if err != nil {
  156. log.Fatal(err)
  157. }
  158. updateStatement, err = db.Prepare(worldUpdate)
  159. if err != nil {
  160. log.Fatal(err)
  161. }
  162. }