Golang的HTTP服务器应用程序有很多套接字(CLOSE_WAIT)。

huangapple go评论135阅读模式
英文:

Golang http server app have many Socket (CLOSE_WAIT)

问题

应用程序可以工作几天。但在某些时刻,应用程序有许多处于CLOSE_WAIT状态的套接字,并且无法接收新的客户端。

可能是某种洪水攻击(例如:同步洪水)吗?

netstat -ant | grep CLOSE_WAIT | wc

3258 19548 260640

3258个套接字处于CLOSE_WAIT状态

更新:

编写了一些处理程序:

  1. func GetScore(mongo *mgo.Session, redisConn redis.Conn, renderer handlers.Render) http.Handler {
  2. mutex := sync.Mutex{}
  3. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  4. id := bson.ObjectIdHex(r.FormValue("id"))
  5. banner := models.Banner{}
  6. err := mongo.DB("db").C("collection").FindId(id).One(&banner)
  7. if err != nil {
  8. log.Panicf("Banner selecting error: %s", err)
  9. }
  10. mutex.Lock()
  11. sports, _ := util.GetSports(redisConn)
  12. mutex.Unlock()
  13. sport, _ := sports.FindSport(banner.SportId)
  14. comp, err := sport.FindCompetition(banner.CompetitionId)
  15. if err != nil {
  16. comp, _ = sport.FindCompetition(0)
  17. log.Println("Competition not found")
  18. }
  19. game, err := comp.FindGame(banner.GameId)
  20. if err != nil {
  21. game, _ = comp.FindGame(0)
  22. }
  23. mutex.Lock()
  24. scores := util.GetScore(redisConn, game.ID)
  25. mutex.Unlock()
  26. game.Score1 = scores[0]
  27. game.Score2 = scores[1]
  28. w.Header().Set("Content-Type", "application/json;application/json;charset=utf-8")
  29. renderer.RenderJson(w, 200, &game)
  30. })
  31. }
  32. func GetScore(redisConn redis.Conn, gameId int) []float32 {
  33. redisKey := fmt.Sprintf("game-%d", gameId)
  34. bBody, err := redis.Bytes(redisConn.Do("GET", redisKey))
  35. if err != nil || len(bBody) == 0 {
  36. response, err := http.DefaultClient.Get(fmt.Sprintf("%s%d", GameApi, gameId))
  37. if err != nil {
  38. log.Panicf("GetScore error: %s", err)
  39. }
  40. bBody, _ = ioutil.ReadAll(response.Body)
  41. redisConn.Send("SET", redisKey, bBody)
  42. redisConn.Send("EXPIRE", redisKey, 60*5)
  43. redisConn.Flush()
  44. }
  45. events := GameJson{}
  46. err = json.Unmarshal(bBody, &events)
  47. if err != nil {
  48. log.Panicf("GetScore json error: %s", err)
  49. }
  50. var event []struct {
  51. Name string `json:"name"`
  52. Price float32 `json:"price"`
  53. }
  54. if len(events.AllEvents.P1XP2) != 0 {
  55. event = events.AllEvents.P1XP2[0].Events
  56. } else {
  57. event = events.AllEvents.Other[0].Events
  58. }
  59. return []float32{event[0].Price, event[1].Price}
  60. }
英文:

App can works few days. But in some moment, app has many socket with CLOSE_WAIT state, and cannot receive new client.

Maybe it is some kind of flooding(Example: Sync-flood)?

netstat -ant | grep CLOSE_WAIT | wc

3258 19548 260640

3258 - socket in CLOSE_WAIT state

Update:

Code some handler:

  1. func GetScore(mongo *mgo.Session, redisConn redis.Conn, renderer handlers.Render) http.Handler {
  2. mutex := sync.Mutex{}
  3. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  4. id := bson.ObjectIdHex(r.FormValue("id"))
  5. banner := models.Banner{}
  6. err := mongo.DB("db").C("collection").FindId(id).One(&banner)
  7. if err != nil {
  8. log.Panicf("Banner selecting error: %s", err);
  9. }
  10. mutex.Lock();
  11. sports, _ := util.GetSports(redisConn)
  12. mutex.Unlock();
  13. sport, _ := sports.FindSport(banner.SportId)
  14. comp, err := sport.FindCompetition(banner.CompetitionId)
  15. if err != nil {
  16. comp, _ = sport.FindCompetition(0);
  17. log.Println("Competition not found");
  18. }
  19. game, err := comp.FindGame(banner.GameId)
  20. if err != nil {
  21. game, _ = comp.FindGame(0)
  22. }
  23. mutex.Lock();
  24. scores := util.GetScore(redisConn, game.ID)
  25. mutex.Unlock();
  26. game.Score1 = scores[0]
  27. game.Score2 = scores[1]
  28. w.Header().Set("Content-Type", "application/json;application/json;charset=utf-8")
  29. renderer.RenderJson(w, 200, &game)
  30. }
  31. func GetScore(redisConn redis.Conn, gameId int) ([]float32) {
  32. redisKey := fmt.Sprintf("game-%d", gameId);
  33. bBody, err := redis.Bytes(redisConn.Do("GET", redisKey))
  34. if err != nil || len(bBody) == 0 {
  35. response, err := http.DefaultClient.Get(fmt.Sprintf("%s%d", GameApi, gameId))
  36. if err != nil {
  37. log.Panicf("GetScore error: %s", err)
  38. }
  39. bBody, _ = ioutil.ReadAll(response.Body);
  40. redisConn.Send("SET", redisKey, bBody)
  41. redisConn.Send("EXPIRE", redisKey, 60 * 5)
  42. redisConn.Flush()
  43. }
  44. events := GameJson{};
  45. err = json.Unmarshal(bBody, &events)
  46. if err != nil {
  47. log.Panicf("GetScore json error: %s", err)
  48. }
  49. var event []struct {
  50. Name string `json:"name"`
  51. Price float32 `json:"price"`
  52. }
  53. if len(events.AllEvents.P1XP2) != 0 {
  54. event = events.AllEvents.P1XP2[0].Events
  55. } else {
  56. event = events.AllEvents.Other[0].Events
  57. }
  58. return []float32{event[0].Price, event[1].Price}
  59. }

答案1

得分: 4

CLOSE-WAIT表示TCP正在等待本地应用程序关闭套接字,已经收到对等方的关闭。

>也许这是某种洪水攻击(例如:同步洪水)?

不是的。这是你的代码中的一个错误。你在某个地方没有关闭套接字。

>问题似乎不在代码中。

问题就在你的代码中。

>代码已经测试了几次。

但没有针对这种情况进行测试,或者在产生这个问题的条件下进行测试。

>但是昨天我没有遇到这个问题。

所以昨天没有出现这些条件。

>(代码没有更改。)

所以这个错误一直存在。

英文:

CLOSE-WAIT means that TCP is waiting for the local application to close the socket, having already received a close from the peer.

> Maybe it is some kind of flooding(Example: Sync-flood)?

No. It is a bug in your code. You aren't closing a socket somewhere.

> The problem seems to be not in the code.

The problem is in your code.

> Code tested several times.

But not for this condition, or under the conditions that produce this problem.

> But yesterday I did not have this problem.

So yesterday those condtiions did not occur.

> (Code was not changed.)

So the bug has always been there.

答案2

得分: 0

模拟情况:

当客户端超时时,服务器上的套接字不会在15秒内关闭。
在我的情况下,当应用程序有很多请求时,我有超过10,000个处于CLOSE_WAIT状态的套接字。

互斥模拟:

  1. time.Sleep(30 * time.Second)

服务器:

  1. package main
  2. import "net/http"
  3. import "time";
  4. func main() {
  5. http.ListenAndServe(":81", http.HandlerFunc(func (w http.ResponseWriter, r *http.Request) {
  6. println("Sleep")
  7. time.Sleep(30 * time.Second)
  8. println("After sleep");
  9. }));
  10. }

客户端:

  1. import java.io.IOException;
  2. import java.net.HttpURLConnection;
  3. import java.net.URL;
  4. public class Main {
  5. public static void main(String[] args) throws IOException, InterruptedException {
  6. HttpURLConnection connection = (HttpURLConnection) new URL("http://link").openConnection();
  7. connection.setRequestMethod("GET");
  8. connection.setDoOutput(false);
  9. connection.setDoInput(true);
  10. connection.setReadTimeout(15000);
  11. connection.getInputStream().read();
  12. System.out.println("Close");
  13. connection.disconnect();
  14. }
  15. }
英文:

Emulation of situation:

When client timeout works. Socket on server will not closed 15 sec.
In my case, when app have many request, I have > 10k socket in CLOSE_WAIT STATE.

Emulation of mutex:

  1. time.Sleep(30 * time.Second)

Server:

  1. package main
  2. import "net/http"
  3. import "time";
  4. func main() {
  5. http.ListenAndServe(":81", http.HandlerFunc(func (w http.ResponseWriter, r *http.Request) {
  6. println("Sleep")
  7. time.Sleep(30 * time.Second)
  8. println("After sleep");
  9. }));
  10. }

Client:

  1. import java.io.IOException;
  2. import java.net.HttpURLConnection;
  3. import java.net.URL;
  4. public class Main {
  5. public static void main(String[] args) throws IOException, InterruptedException {
  6. HttpURLConnection connection = (HttpURLConnection) new URL("http://link").openConnection();
  7. connection.setRequestMethod("GET");
  8. connection.setDoOutput(false);
  9. connection.setDoInput(true);
  10. connection.setReadTimeout(15000);
  11. connection.getInputStream().read();
  12. System.out.println("Close");
  13. connection.disconnect();
  14. }
  15. }

huangapple
  • 本文由 发表于 2016年12月4日 21:47:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/40959277.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定