高并发

先要搞清楚高并发是什么东西,高并发有别于高吞吐量,高并发面对的问题是 C10K

作为一个写了七八年java的程序员,一开始我是不服的,为什么我深爱的java不能高并发了,非要用go。然后做了一下研究,高并发这东西和线程模型) (查看 8.models, hexo 渲染锚点有问题) 有很大关系。说白了,单位资源上,要支撑更高的并发,势必要在每一个连接上尽量的节省资源。

NIONETTY

但是相对于go的解决方案:

goroutineG-P-M

基于go开发一个web应用

参考 这里,写了demo。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package main

import (
"fmt"
"net/http"
"log"
"html/template"
)


func version(w http.ResponseWriter, r *http.Request){
fmt.Fprintf(w, "1.0.0")
}

func login(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
fmt.Println("method:", r.Method) //获取请求的方法
if r.Method == "GET" {
t, _ := template.ParseFiles("login.gtpl")
log.Println(t.Execute(w, nil))
} else {
//请求的是登录数据,那么执行登录的逻辑判断
fmt.Println("username:", r.Form["username"])
fmt.Println("password:", r.Form["password"])
}
}

func main() {
http.HandleFunc("/version", version)
http.HandleFunc("/login", login)
err := http.ListenAndServe(":9090", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}

http通过handle function来定义每个请求的处理器,其实每一个请求进来都是交给goroutine来执行handle function,就凭这一点,这种程序在高并发的场景(比如大量的长连接的场景)下就肯定比tomcat合适了(tomcat是每一请求进来,交给线程池中的线程去处理,java的线程是与内核里的线程一对一的,所以并发量会受制于操作系统的线程数)。

下面是html代码:

1
2
3
4
5
6
7
8
9
10
11
12
<html>
<head>
<title></title>
</head>
<body>
<form action="/login" method="post">
用户名:<input type="text" name="username">
密码:<input type="password" name="password">
<input type="submit" value="登录">
</form>
</body>
</html>

基于gin开发rest api

上面的demo写下来感觉只能是demo,比较原始,想研究下go的web框架,查了一下有很多,(go的生态圈还没到成熟期吧,不像java, spring已经一统江湖了),随便选了一个gin尝尝鲜。

安装

1
go get -u github.com/gin-gonic/gin

这个动作需要几分钟且没什么反应,一度怀疑拉不下来。

撸代码

照着README里的例子简单的写了几个场景。

  • 代码框架
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import (
"github.com/gin-gonic/gin"
"net/http"
"log"
"fmt"
)

/**
* https://github.com/gin-gonic/gin#installation
*/
func main () {
router := gin.Default()
//路由信息
router.Run(":8999") // listen and serve on 0.0.0.0:8080
}
  • Helloworld
1
2
3
4
5
router.GET("/ping", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "pong",
})
})
  • 路径参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// This handler will match /user/john but will not match neither /user/ or /user
router.GET("/user/:name", func(c *gin.Context) {
name := c.Param("name")
c.String(http.StatusOK, "Hello %s", name)
})


// However, this one will match /user/john/ and also /user/john/send
// If no other routers match /user/john, it will redirect to /user/john/
router.GET("/user/:name/*action", func(c *gin.Context) {
name := c.Param("name")
action := c.Param("action")
message := name + " is " + action
c.String(http.StatusOK, message)
})
  • 查询参数
1
2
3
4
5
6
7
8
9
10
11
// Query string parameters are parsed using the existing underlying request object.
// The request responds to a url matching: /welcome?firstname=Jane&lastname=Doe
router.GET("/list/student", func(c *gin.Context) {
p_name := c.Query("name")
p_age := c.Query("age")

c.JSON(http.StatusOK, gin.H{
"name" : p_name,
"age" : p_age,
})
})
  • Form表单提交
1
2
3
4
5
6
7
8
9
10
router.POST("/student", func(c *gin.Context) {
message := c.PostForm("name")
age := c.DefaultPostForm("age", "10")

c.JSON(http.StatusCreated, gin.H{
"status": "created",
"message": message + " is created.",
"age": age,
})
})
  • 文件上传
1
2
3
4
5
6
7
8
9
10
router.POST("/upload", func(c *gin.Context) {
// single file
file, _ := c.FormFile("file")
log.Println(file.Filename)

// Upload the file to specific dst.
// c.SaveUploadedFile(file, dst)

c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename))
})

结束语

go,不是一门很新的语言了,但对我来说绝对是比较新,虽然之前小打小闹写过一些,但现在写起来还是不熟练。接下来想做一个简单的web项目的demo,包括之前做spring项目的方方面面(session方案、统一异常处理、表单校验方案、数据库操作、性能扩展方案、性能调优方案…..),看下go的最佳实践是什么。当然go的长处肯定不是在做业务系统,权当对语法做一个熟悉吧。