hello.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. package main
  2. import (
  3. "database/sql"
  4. "encoding/json"
  5. "flag"
  6. "html/template"
  7. "log"
  8. "math/rand"
  9. "net"
  10. "net/http"
  11. "os"
  12. "os/exec"
  13. "runtime"
  14. "sort"
  15. "strconv"
  16. _ "github.com/go-sql-driver/mysql"
  17. )
  18. type Message struct {
  19. Message string `json:"message"`
  20. }
  21. type World struct {
  22. Id uint16 `json:"id"`
  23. RandomNumber uint16 `json:"randomNumber"`
  24. }
  25. type Fortune struct {
  26. Id uint16 `json:"id"`
  27. Message string `json:"message"`
  28. }
  29. const (
  30. // Database
  31. connectionString = "benchmarkdbuser:benchmarkdbpass@tcp(localhost:3306)/hello_world"
  32. worldSelect = "SELECT id, randomNumber FROM World WHERE id = ?"
  33. worldUpdate = "UPDATE World SET randomNumber = ? WHERE id = ?"
  34. fortuneSelect = "SELECT id, message FROM Fortune;"
  35. worldRowCount = 10000
  36. maxConnectionCount = 256
  37. helloWorldString = "Hello, World!"
  38. )
  39. var (
  40. // Templates
  41. tmpl = template.Must(template.ParseFiles("templates/layout.html", "templates/fortune.html"))
  42. // Database
  43. worldStatement *sql.Stmt
  44. fortuneStatement *sql.Stmt
  45. updateStatement *sql.Stmt
  46. helloWorldBytes = []byte(helloWorldString)
  47. )
  48. var prefork = flag.Bool("prefork", false, "use prefork")
  49. var child = flag.Bool("child", false, "is child proc")
  50. func main() {
  51. var listener net.Listener
  52. flag.Parse()
  53. if !*prefork {
  54. runtime.GOMAXPROCS(runtime.NumCPU())
  55. } else {
  56. listener = doPrefork()
  57. }
  58. db, err := sql.Open("mysql", connectionString)
  59. if err != nil {
  60. log.Fatalf("Error opening database: %v", err)
  61. }
  62. db.SetMaxIdleConns(maxConnectionCount)
  63. worldStatement, err = db.Prepare(worldSelect)
  64. if err != nil {
  65. log.Fatal(err)
  66. }
  67. fortuneStatement, err = db.Prepare(fortuneSelect)
  68. if err != nil {
  69. log.Fatal(err)
  70. }
  71. updateStatement, err = db.Prepare(worldUpdate)
  72. if err != nil {
  73. log.Fatal(err)
  74. }
  75. http.HandleFunc("/db", dbHandler)
  76. http.HandleFunc("/queries", queriesHandler)
  77. http.HandleFunc("/json", jsonHandler)
  78. http.HandleFunc("/fortune", fortuneHandler)
  79. http.HandleFunc("/update", updateHandler)
  80. http.HandleFunc("/plaintext", plaintextHandler)
  81. if !*prefork {
  82. http.ListenAndServe(":8080", nil)
  83. } else {
  84. http.Serve(listener, nil)
  85. }
  86. }
  87. func doPrefork() (listener net.Listener) {
  88. var err error
  89. var fl *os.File
  90. var tcplistener *net.TCPListener
  91. if !*child {
  92. var addr *net.TCPAddr
  93. addr, err = net.ResolveTCPAddr("tcp", ":8080")
  94. if err != nil {
  95. log.Fatal(err)
  96. }
  97. tcplistener, err = net.ListenTCP("tcp", addr)
  98. if err != nil {
  99. log.Fatal(err)
  100. }
  101. fl, err = tcplistener.File()
  102. if err != nil {
  103. log.Fatal(err)
  104. }
  105. children := make([]*exec.Cmd, runtime.NumCPU()/2)
  106. for i := range children {
  107. children[i] = exec.Command(os.Args[0], "-prefork", "-child")
  108. children[i].Stdout = os.Stdout
  109. children[i].Stderr = os.Stderr
  110. children[i].ExtraFiles = []*os.File{fl}
  111. err = children[i].Start()
  112. if err != nil {
  113. log.Fatal(err)
  114. }
  115. }
  116. for _, ch := range children {
  117. var err error = ch.Wait()
  118. if err != nil {
  119. log.Print(err)
  120. }
  121. }
  122. os.Exit(0)
  123. } else {
  124. fl = os.NewFile(3, "")
  125. listener, err = net.FileListener(fl)
  126. if err != nil {
  127. log.Fatal(err)
  128. }
  129. runtime.GOMAXPROCS(2)
  130. }
  131. return listener
  132. }
  133. // Test 1: JSON serialization
  134. func jsonHandler(w http.ResponseWriter, r *http.Request) {
  135. w.Header().Set("Content-Type", "application/javascript")
  136. json.NewEncoder(w).Encode(&Message{helloWorldString})
  137. }
  138. // Test 2: Single database query
  139. func dbHandler(w http.ResponseWriter, r *http.Request) {
  140. var world World
  141. err := worldStatement.QueryRow(rand.Intn(worldRowCount)+1).Scan(&world.Id, &world.RandomNumber)
  142. if err != nil {
  143. log.Fatalf("Error scanning world row: %s", err.Error())
  144. }
  145. w.Header().Set("Content-Type", "application/json")
  146. json.NewEncoder(w).Encode(&world)
  147. }
  148. // Test 3: Multiple database queries
  149. func queriesHandler(w http.ResponseWriter, r *http.Request) {
  150. n := 1
  151. if nStr := r.URL.Query().Get("queries"); len(nStr) > 0 {
  152. n, _ = strconv.Atoi(nStr)
  153. }
  154. if n < 1 {
  155. n = 1
  156. } else if n > 500 {
  157. n = 500
  158. }
  159. world := make([]World, n)
  160. for i := 0; i < n; i++ {
  161. err := worldStatement.QueryRow(rand.Intn(worldRowCount)+1).Scan(&world[i].Id, &world[i].RandomNumber)
  162. if err != nil {
  163. log.Fatalf("Error scanning world row: %s", err.Error())
  164. }
  165. }
  166. w.Header().Set("Content-Type", "application/json")
  167. json.NewEncoder(w).Encode(world)
  168. }
  169. // Test 4: Fortunes
  170. func fortuneHandler(w http.ResponseWriter, r *http.Request) {
  171. rows, err := fortuneStatement.Query()
  172. if err != nil {
  173. log.Fatalf("Error preparing statement: %v", err)
  174. }
  175. fortunes := make(Fortunes, 0, 16)
  176. for rows.Next() { //Fetch rows
  177. fortune := Fortune{}
  178. if err := rows.Scan(&fortune.Id, &fortune.Message); err != nil {
  179. log.Fatalf("Error scanning fortune row: %s", err.Error())
  180. }
  181. fortunes = append(fortunes, &fortune)
  182. }
  183. fortunes = append(fortunes, &Fortune{Message: "Additional fortune added at request time."})
  184. sort.Sort(ByMessage{fortunes})
  185. w.Header().Set("Content-Type", "text/html")
  186. if err := tmpl.Execute(w, fortunes); err != nil {
  187. http.Error(w, err.Error(), http.StatusInternalServerError)
  188. }
  189. }
  190. // Test 5: Database updates
  191. func updateHandler(w http.ResponseWriter, r *http.Request) {
  192. n := 1
  193. if nStr := r.URL.Query().Get("queries"); len(nStr) > 0 {
  194. n, _ = strconv.Atoi(nStr)
  195. }
  196. w.Header().Set("Content-Type", "application/json")
  197. encoder := json.NewEncoder(w)
  198. if n < 1 {
  199. n = 1
  200. } else if n > 500 {
  201. n = 500
  202. }
  203. world := make([]World, n)
  204. for i := 0; i < n; i++ {
  205. if err := worldStatement.QueryRow(rand.Intn(worldRowCount)+1).Scan(&world[i].Id, &world[i].RandomNumber); err != nil {
  206. log.Fatalf("Error scanning world row: %s", err.Error())
  207. }
  208. world[i].RandomNumber = uint16(rand.Intn(worldRowCount) + 1)
  209. if _, err := updateStatement.Exec(world[i].RandomNumber, world[i].Id); err != nil {
  210. log.Fatalf("Error updating world row: %s", err.Error())
  211. }
  212. }
  213. encoder.Encode(world)
  214. }
  215. // Test 6: Plaintext
  216. func plaintextHandler(w http.ResponseWriter, r *http.Request) {
  217. w.Header().Set("Content-Type", "text/plain")
  218. w.Write(helloWorldBytes)
  219. }
  220. type Fortunes []*Fortune
  221. func (s Fortunes) Len() int { return len(s) }
  222. func (s Fortunes) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
  223. type ByMessage struct{ Fortunes }
  224. func (s ByMessage) Less(i, j int) bool { return s.Fortunes[i].Message < s.Fortunes[j].Message }