使用channel管理数据传递与同步:
- 传递请求数据:如果处理请求的
goroutine
需要从其他goroutine
获取数据(例如从数据库查询结果),可以使用channel
。例如,假设有一个数据库查询函数返回查询结果,我们可以通过channel
将结果传递给处理HTTP响应的goroutine
。
package main
import (
"fmt"
"net/http"
)
func queryDatabase(query string) string {
// 模拟数据库查询
return "查询结果"
}
func handleRequest(w http.ResponseWriter, r *http.Request) {
resultCh := make(chan string)
go func() {
result := queryDatabase("一些查询语句")
resultCh <- result
close(resultCh)
}()
for result := range resultCh {
fmt.Fprintf(w, "数据库查询结果: %s", result)
}
}
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
go handleRequest(w, r)
})
fmt.Println("服务器在:8080 启动")
http.ListenAndServe(":8080", nil)
}
- 同步与控制并发:
channel
还可以用于控制并发数量。比如,可以创建一个带缓冲的channel
作为信号量,用来限制同时进行的数据库查询数量,避免过多的I/O操作耗尽资源。
package main
import (
"fmt"
"net/http"
)
const maxConcurrentQueries = 5
func queryDatabase(query string, semaphore chan struct{}) string {
semaphore <- struct{}{}
defer func() { <-semaphore }()
// 模拟数据库查询
return "查询结果"
}
func handleRequest(w http.ResponseWriter, r *http.Request) {
semaphore := make(chan struct{}, maxConcurrentQueries)
resultCh := make(chan string)
go func() {
result := queryDatabase("一些查询语句", semaphore)
resultCh <- result
close(resultCh)
}()
for result := range resultCh {
fmt.Fprintf(w, "数据库查询结果: %s", result)
}
}
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
go handleRequest(w, r)
})
fmt.Println("服务器在:8080 启动")
http.ListenAndServe(":8080", nil)
}
错误处理与资源清理:
- 在并发处理中,需要妥善处理错误。如果在数据库查询等I/O操作中发生错误,可以通过
channel
传递错误信息,让处理HTTP响应的goroutine
能够正确处理并返回合适的HTTP状态码。
package main
import (
"fmt"
"net/http"
)
func queryDatabase(query string) (string, error) {
// 模拟数据库查询错误
return "", fmt.Errorf("数据库查询错误")
}
func handleRequest(w http.ResponseWriter, r *http.Request) {
resultCh := make(chan string, 1)
errCh := make(chan error, 1)
go func() {
result, err := queryDatabase("一些查询语句")
if err!= nil {
errCh <- err
return
}
resultCh <- result
close(resultCh)
}()
select {
case result := <-resultCh:
fmt.Fprintf(w, "数据库查询结果: %s", result)
case err := <-errCh:
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
go handleRequest(w, r)
})
fmt.Println("服务器在:8080 启动")
http.ListenAndServe(":8080", nil)
}
- 对于打开的资源(如数据库连接),要确保在
goroutine
结束时正确关闭,避免资源泄漏。可以使用defer
语句来确保资源的清理。例如,如果使用了数据库连接池,在使用完连接后要将其归还到连接池。
package main
import (
"database/sql"
"fmt"
"net/http"
_ "github.com/go - sql - driver/mysql"
)
func queryDatabase(db *sql.DB, query string) (string, error) {
row := db.QueryRow(query)
var result string
err := row.Scan(&result)
if err!= nil {
return "", err
}
return result, nil
}
func handleRequest(w http.ResponseWriter, r *http.Request) {
db, err := sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/database")
if err!= nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer db.Close()
resultCh := make(chan string, 1)
errCh := make(chan error, 1)
go func() {
result, err := queryDatabase(db, "一些查询语句")
if err!= nil {
errCh <- err
return
}
resultCh <- result
close(resultCh)
}()
select {
case result := <-resultCh:
fmt.Fprintf(w, "数据库查询结果: %s", result)
case err := <-errCh:
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
go handleRequest(w, r)
})
fmt.Println("服务器在:8080 启动")
http.ListenAndServe(":8080", nil)
}