728x90
반응형

신규 서버를 만들면서 두가지 기능이 필요했다.

 

첫째는 명령어를 동시에 실행해서 속도를 높이는 것과 데이터가 있는 디렉토리를 제거하는 것이었다.

  1. 명령어 동시 실행하기

Golang에서 WaitGroup은 모든 goroutine이 종료될 때까지 기다린다.

 

채널을 통해서 커맨드를 전달하고 goroutine에서 이 커맨드를 실행하고 종료한다.

 

명령어를 단순 반복문으로 수행하면 오래 걸리지만 이를 활용하면 빠르게 수행할 수 있었다.

tasks := make(chan *exec.Cmd, 64)
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
    wg.Add(1)
    go func(w *sync.WaitGroup) {
        defer w.Done()
        for cmd := range tasks {
            out, err := cmd.Output()
            if err != nil {
                fmt.Println("can't get stdout: %v", err)
            }
            fmt.Println(string(out))
        }
    }(&wg)
}
for i := 0; i < 10; i++ {
    tasks <- exec.Command("echo", strconv.Itoa(i))
}
close(tasks)
wg.Wait()
  1. 디렉토리 제거(rm -rf)

간단하게 exec.Command를 활용하면 되겠지만 최대한 Golang 함수로 해결해보려고 했다.

 

그래서 아래와 같이 디렉토리 밑의 데이터를 지우고 마지막으로 디렉토리를 삭제하도록 했다.

func removeContents(dir string) error {
    d, err := os.Open(dir)
    if err != nil {
        return err
    }
    defer d.Close()
    names, err := d.Readdirnames(-1)
    if err != nil {
        return err
    }
    for _, name := range names {
        err = os.RemoveAll(filepath.Join(dir, name))
        if err != nil {
            return err
        }
    }
    syscall.Rmdir(dir)
    return nil
}

 

참고 문헌

  1. https://stackoverflow.com/questions/40247726/go-execute-a-bash-command-n-times-using-goroutines-and-store-print-its-resul

  2. https://stackoverflow.com/questions/33450980/how-to-remove-all-contents-of-a-directory-using-golang

반응형
728x90
반응형
  1. 각 패키지는 단일 목적을 수행하라

  2. 명시적으로 에러를 다뤄라

  3. 깊게 중첩하는 것보다 빠르게 반환하라

  4. 호출자에게 동시성을 맡겨라

  5. goroutine을 실행하기 전, 언제 멈출지 알라

  6. 패키지 수준의 상태를 피하라

  7. 단순함은 중요하다

  8. 패키지 API의 제약을 위해 테스트 코드를 작성하라

  9. 느리다고 생각되면 우선 벤치마크로 증명하라

  10. 중용은 미덕이다

  11. 유지 보수를 생각하라

 

참고 문헌

  1. https://the-zen-of-go.netlify.app
반응형
728x90
반응형

Go로 만든 프로젝트에서 kafka 연동하여 로그를 수집하는데 confluent-kafka-go를 사용하고 있었다.

package main

import (
    "fmt"

    "github.com/confluentinc/confluent-kafka-go/kafka"
)

var kafkaClient *kafka.Producer

func main() {
    kafkaClient, _ = kafka.NewProducer(&kafka.ConfigMap{
        "bootstrap.servers": "broker:9092",
        "acks": 1,
    })

    go func() {
        for e := range p.Events() {
            switch ev := e.(type) {
            case *kafka.Message:
                m := ev
                if m.TopicPartition.Error != nil {
                    fmt.Printf("Delivery failed: %v, queue: %d\n", m.TopicPartition.Error, p.Len())
                }
                return
            default:
                fmt.Printf("Ignored event: %s\n", ev)
            }
        }
    }()
}

func SendMessage(topic string, message string) {
    kafkaClient.ProduceChannel() <- &kafka.Message{TopicPartition: kafka.TopicPartition{Topic: &topic, Partition: kafka.PartitionAny}, Value: []byte(message)}
}

테스트를 했을 때 잘 동작하여 아무 문제가 없었지만 며칠간 실행시켜보니 Local: Queue full 에러가 나면서 더이상 메시지를 전송하지 못했다.

queue.buffering.max.messages 크기를 조절해가면서 테스트를 했지만 해당 에러가 계속 발생해 Flush()로 급한 불을 끌 수 있었다.

librdkafka에서 쓰는 버퍼가 해제되지 않은 문제인 듯 했으나 정확한 원인을 몰라 더 이상의 사용이 불안하여 다른 라이브러리를 찾아봤다.

Shopify에서 만든 sarama가 유명하다고 추천받아 테스트를 해보고 적용하기로 했다.

package main

import (
    "fmt"

    "github.com/Shopify/sarama"
)

var kafkaClient sarama.AsyncProducer

func main() {
    brokers := []string{"broker:9092"}
    kafkaClient, _ = sarama.NewAsyncProducer(brokers, nil)
}

func SendMessage(topic string, message string) {
    kafkaClient.Input() <- &sarama.ProducerMessage{
        Topic: topic,
        Partition: -1,
        Value: sarama.StringEncoder(message),
    }
}

테스트는 1000만건의 메시지를 전송하도록 진행했다.

 

confluent-kafka-go와 비교해보니 메모리를 더 적게 사용하면서 성능은 비슷했다.

 

confluent-kafka-go는 메모리를 20MB 정도 기본으로 사용하고 sarma는 최대 3MB를 사용했다.

 

confluent-kafka-go의 경우 Flush를 하지 않으면 GC를 수행해도 메모리가 조금씩 증가했지만

 

sarama는 메모리가 증가하는 것 없이 동작을 잘 하는 것을 확인할 수 있었다.

 

참고 문헌

  1. https://github.com/confluentinc/confluent-kafka-go

  2. https://github.com/Shopify/sarama

반응형
728x90
반응형
  1. 테스트 타임아웃

Go로 테스트를 하는데 다음과 같은 에러가 발생하면서 종료됐다.

golang panic: test timed out after 10m0s

이유는 테스트를 수행할 때 기본으로 10분으로 타임아웃이 걸려있었기 때문이었다.

$ go test -timeout 1h

위와 같이 타임아웃 시간을 늘려서 수행하면 된다.

  1. 410 gone

이전에 Go 버전을 올리고 410 Gone 에러가 나서 환경변수를 설정했었는데

 

go get으로 다운로드가 안되는 경우가 발생했다.

 

비공개 저장소에서 받는 경우 발생하는 문제로

 

다음과 같이 설정하면 정상적으로 다운로드받는 것을 확인할 수 있다.

$ export GOPRIVATE=private_repo_url
  1. 모듈 버전 업 문제

모듈의 버전은 v1에서 v2로 변경해서 다운로드받으려는 경우 다음과 같이 에러가 발생했다.

go get github.com/gomodule/module@v2.0.0: github.com/gomodule/module@v2.0.0: invalid version: module contains a go.mod file, so major version must be compatible: should be v0 or v1, not v2

다음과 같이 go.mod 파일을 수정하면 다운로드받을 수 있다.

module github.com/gomodule/module/v2

 

참고 문헌

  1. https://github.com/golang/go/issues/25886

  2. https://golang.org/cmd/go/#hdr-Module_configuration_for_non_public_modules

  3. https://github.com/golang/go/issues/35732

반응형

'Golang' 카테고리의 다른 글

[Golang] Go의 선(The Zen of Go)  (0) 2020.06.04
[Golang] Kafka 연동 문제  (0) 2020.04.30
[Golang] Java gzip migration  (0) 2020.01.30
[Golang] 410 Gone  (0) 2020.01.25
[Golang] Echo 415 에러 해결  (0) 2020.01.25

+ Recent posts