Задать вопрос

Почему приложение на Go постепенно потребляет все больше и больше оперативки?

Были проблемы с серверной частью, написанной на Php, при отправке более 50 тысяч Push уведомлений. Использовал ApnsPhp и он очень долго отправлял. Там, как я понял, особенности реализации. Так как немного изучал Go, решил на другой виртуалке поднять простенький сервис на Go, который отправляет Push-уведомления, получая токены по запросу на мой старый сервер. Для отправки использую вот это.
func main() {
	c, err := apns.NewClientWithFiles(apns.ProductionGateway, "/root/go/src/myapp/resources/pushcert.pem", "/root/go/src/myapp/resources/apnskey.pem")
	if err != nil {
		addStringToLog(fmt.Sprintf("%s", err))
		log.Fatal("Could not create client", err.Error())
	}
	go func() {
		for f := range c.FailedNotifs {
			addStringToLog(fmt.Sprintf("Notif", f.Notif.ID, "failed with", f.Err.Error()))
			fmt.Println("Notif", f.Notif.ID, "failed with", f.Err.Error())
		}
	}()
	prepareSendingPush(c)
	tickChan := time.NewTicker(time.Second * 30).C

	for {
		select {
		case <-tickChan:
			prepareSendingPush(c)
		}
	}
}


Функция prepareSendingPush(c) делает HTTP запрос на мой сервер и, если получает токены, то происходит отправка.
Все работает отлично, сервис стучится каждые 30 сек и если надо отправляет уведомления, кстати, отправка происходит за 5 секунд, вместо 150 и более у ApnsPHP. Запускаю так start-stop-daemon -Sbvx /root/go/src/myapp/myapp.
В итоге смотрю через htop сколько ресурсов ест мое приложение. И во-первых наблюдаю, что мое приложение отображается как 6 запущенных процессов. Что это? И во-вторых, что потихоньку количество потребления оперативки начинает расти и расти. Что я делаю не так? На виртуалке стоит Ubuntu 14.04.

Вот код, в комменте нельзя вставить нормально.
func prepareSendingPush(client apns.Client) {
	addStringToLog("Start get tokens")
	fmt.Printf("Start get tokens")
	response, err := http.Get("https://domain.com/gettokens.php")
	if err != nil {
		addStringToLog(fmt.Sprintf("%s", err))
		fmt.Printf("%s", err)
	} else {
		defer response.Body.Close()
		contents, err := ioutil.ReadAll(response.Body)
		if err != nil {
			addStringToLog(fmt.Sprintf("%s", err))
			fmt.Printf("%s", err)
		}
		type JsonObj struct {
			Status string   `json:"status"`
			Text   string   `json:"text"`
			Tokens []string `json:"tokens"`
		}
		js := new(JsonObj)
		err = json.Unmarshal(contents, &js)
		if err != nil {
			addStringToLog(fmt.Sprintf("%s", err))
			fmt.Println("error:", err)
		}
		if len(js.Tokens) > 0 {
			addStringToLog("Start sending")
			fmt.Printf("Start sending")
			for i := 0; i < len(js.Tokens); i++ {
				tok := js.Tokens[i]
				if tok != "" {
					sendPush(client, tok, js.Text, 1, i)
				}
			}
			addStringToLog("End sending")
			fmt.Printf("End sending")
		}
		addStringToLog("End get tokens")
		fmt.Printf("End get tokens")
	}
}

func sendPush(client apns.Client, token string, body string, badge uint, identifier int) {
	p := apns.NewPayload()
	p.APS.Alert.Body = body
	p.APS.Sound = "default"
	p.APS.Badge.Set(badge)
	//p.APS.ContentAvailable = 1

	m := apns.NewNotification()
	m.Payload = p
	m.DeviceToken = token
	m.Priority = apns.PriorityImmediate
	m.Identifier = uint32(identifier)

	client.Send(m)
}
  • Вопрос задан
  • 805 просмотров
Подписаться 6 Оценить 5 комментариев
Решения вопроса 1
Проследите за количеством горутин. Возможно, где-то они у вас неявно создаются и зависают на чтении из канала, например, в который вы забыли записать или который забыли закрыть.
Это можно сделать с помощью модуля runtime, получая значение runtime.NumGoroutine

Количество процессов обычно берется из количества ядер, поэтому у вас их запускается 6. Упрощенно говоря, рантайм go раскидывает между этими процессами горутины на выполнение.
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
uvelichitel
@uvelichitel Куратор тега Go
habrahabr.ru/users/uvelichitel
Функциям sendPush(), preparesendPush()вы передаете apns.Clientв качестве параметра. Go всегда передает параметры 'copy_by_value', то есть создается и передается копия client при каждом вызове.
apns.Client // contains filtered or unexported fields
, то есть неизвестно что копируется и насколько оно поддается мусорщику. Попробуйте передавать один и тот же client ссылкой
func prepareSendingPush(client *apns.Client), func sendPush(client *apns.Client...
вызывая prepareSendingPush(&c)
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы