Skip to content
Permalink
Browse files

Merge pull request #1 from maZahaca/feature-improved-easyjson-generation

easyjson improvements
  • Loading branch information...
maZahaca committed Mar 13, 2019
2 parents 646fe16 + 0166b51 commit 58fdb2d140b3c804f432b4fc673bdefbb76accdd
Showing with 182 additions and 203 deletions.
  1. +2 −1 .gitignore
  2. +11 −6 Dockerfile
  3. +28 −0 Makefile
  4. +19 −1 dto/Rates.go
  5. +0 −136 dto/dto_easyjson.go
  6. +41 −59 main.go
  7. +81 −0 pkg/rates_manager.go
@@ -1 +1,2 @@
.idea
.idea
*_easyjson.go
@@ -2,14 +2,17 @@
# STEP 1 build executable binary
############################
# golang alpine 1.11.5
FROM golang@sha256:8dea7186cf96e6072c23bcbac842d140fe0186758bcc215acb1745f584984857 as builder

ENV GO111MODULE=on
#FROM golang:1.11.4-alpine3.8 as builder
FROM golang:1.11.5-alpine3.8 as builder

# Install git + SSL ca certificates.
# Git is required for fetching the dependencies.
# Ca-certificates is required to call HTTPS endpoints.
RUN apk update && apk add --no-cache git ca-certificates tzdata && update-ca-certificates
RUN apk add --update --no-cache make bash git openssh-client ca-certificates build-base musl-dev curl wget tzdata \
&& update-ca-certificates \
&& go get -u github.com/mailru/easyjson/easyjson

ENV GO111MODULE=on

# Create appuser
RUN adduser -D -g '' appuser
@@ -22,9 +25,11 @@ COPY . .
# Using go mod.
RUN go mod download

# Generate easyjson files
RUN go generate ./dto/*

# Build the binary
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-w -s" -a -installsuffix cgo -o /go/bin/exchange-rates-server
#RUN go build -o /go/bin/exchange-rates-server

############################
# STEP 2 build a small image
@@ -43,4 +48,4 @@ COPY --from=builder /go/bin/exchange-rates-server /go/bin/exchange-rates-server
USER appuser

# Run binary.
ENTRYPOINT ["/go/bin/exchange-rates-server", "--base", "USD", "--", "GBP", "EUR", "CAD", "RUB", "CHF", "BTC", "ETH", "ETC"]
ENTRYPOINT ["/go/bin/exchange-rates-server", "--base", "USD", "--", "GBP", "EUR", "CAD", "RUB", "CHF", "BTC", "ETH", "ETC", "ALF", "AGS", "AMC", "APEX", "ARCH", "ARI", "BCX", "BET", "BLK"]
@@ -0,0 +1,28 @@
VERSION=`git rev-parse HEAD`
BUILD=`date +%FT%T%z`
LDFLAGS=-ldflags "-X main.Version=${VERSION} -X main.Build=${BUILD}"

.PHONY: help
help: ## - Show help message
@printf "\033[32m\xE2\x9c\x93 usage: make [target]\n\n\033[0m"
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'

.PHONY: build
build: ## - Build the smallest and secured golang docker image based on scratch
@printf "\033[32m\xE2\x9c\x93 Build the smallest and secured golang docker image based on scratch\n\033[0m"
@docker build -f Dockerfile -t exchange-rates-server .

.PHONY: build-no-cache
build-no-cache: ## - Build the smallest and secured golang docker image based on scratch with no cache
@printf "\033[32m\xE2\x9c\x93 Build the smallest and secured golang docker image based on scratch\n\033[0m"
@docker build --no-cache -f Dockerfile -t exchange-rates-server .

.PHONY: ls
ls: ## - List 'exchange-rates-server' docker images
@printf "\033[32m\xE2\x9c\x93 Look at the size dude !\n\033[0m"
@docker image ls exchange-rates-server

.PHONY: run
run: ## - Run the smallest and secured golang docker image based on scratch
@printf "\033[32m\xE2\x9c\x93 Run the smallest and secured golang docker image based on scratch\n\033[0m"
@docker run -p 8000:8000 exchange-rates-server:latest
@@ -1,6 +1,10 @@
//go:generate easyjson -all
package dto

import "sync"
import (
"errors"
"sync"
)

//easyjson:json
type Rates struct {
@@ -21,3 +25,17 @@ func (r *Rates) Add(currency string, rate float32) {
r.Items[currency] = rate
r.mu.Unlock()
}

func (r *Rates) AddFromMap(jsonMap map[string]map[string]float32) error {
r.mu.Lock()
for currency, rate := range jsonMap {
var value float32
var ok bool
if value, ok = rate[r.Base]; !ok {
return errors.New("cannot fetch rate by base currency")
}
r.Items[currency] = 1 / value
}
r.mu.Unlock()
return nil
}

This file was deleted.

Oops, something went wrong.
100 main.go
@@ -2,107 +2,89 @@ package main

import (
"bytes"
"encoding/json"
"flag"
"fmt"
"github.com/gorilla/mux"
"github.com/maZahaca/go-exchange-rates-service/dto"
"io/ioutil"
"github.com/maZahaca/go-exchange-rates-service/pkg"
"log"
"net/http"
"os"
"strconv"
"time"
)

var (
baseCurrency = flag.String("base", "USD", "base currency to get rates base on it")
urlsChunk = 3
)

func main() {
flag.Parse()

var buf bytes.Buffer
var urls []string
args := flag.Args()
for i := 1; i <= len(args); i++ {
buf.WriteString(args[i-1])
if i%urlsChunk == 0 || i == len(args) {
log.Println("i=", i)
urls = append(
urls,
fmt.Sprintf("https://min-api.cryptocompare.com/data/pricemulti?fsyms=%s&tsyms=%s",
buf.String(), *baseCurrency),
)
buf.Reset()
continue
}
buf.WriteString(",")
urlsChunk, err := strconv.ParseInt(getEnv("URL_CHUNKS", "3"), 10, 8)
if err != nil {
log.Fatal(err)
}
urls, err := getUrlsFromArgs(flag.Args(), urlsChunk, *baseCurrency)
if err != nil {
log.Fatal(err)
}

rates := dto.NewRates(*baseCurrency)

updateRates(urls, *rates)
manager := pkg.NewRatesManager(*baseCurrency, urls)
manager.Update()

go func() {
for {
time.Sleep(10 * time.Second)
updateRates(urls, *rates)
manager.Update()
}
}()

r := mux.NewRouter()

r.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
rawResponse, err := rates.MarshalJSON()
rawResponse, err := manager.Get().MarshalJSON()
if err != nil {
log.Fatal(err)
}
writer.Write(rawResponse)
if _, err = writer.Write(rawResponse); err != nil {
log.Fatal(err)
}
})

port := getEnv("PORT", "8000")
srv := &http.Server{
Handler: r,
Addr: ":8000",
// Good practice: enforce timeouts for servers you create!
Handler: r,
Addr: fmt.Sprintf(":%s", port),
WriteTimeout: 15 * time.Second,
ReadTimeout: 15 * time.Second,
}

log.Fatal(srv.ListenAndServe())
}

func updateRates(urls []string, rates dto.Rates) {
done := make(chan string, len(urls))

for i := 0; i < len(urls); i++ {
go fetch(urls[i], done)
}

for i := 0; i < len(urls); i++ {
res := <-done
jsonMap := make(map[string]map[string]float32)
err := json.Unmarshal([]byte(res), &jsonMap)
if err != nil {
panic(err)
}
for currency, rate := range jsonMap {
rates.Add(currency, 1/rate[*baseCurrency])
}
log.Println(rates)
log.Printf("Fetched result: %s", res)
func getEnv(key string, defaultVal string) string {
val, ok := os.LookupEnv(key)
if !ok {
return defaultVal
}
return val
}

func fetch(url string, done chan string) {
log.Printf("fetching url: %s", url)
resp, err := http.Get(url)
defer resp.Body.Close()
if err != nil {
log.Fatalf("Cannot fetch url: %+q", err)
}
bodyBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatalf("Cannot read response: %+q", err)
func getUrlsFromArgs(args []string, urlsChunk int64, base string) ([]string, error) {
var buf bytes.Buffer
var urls []string
for i := 1; i <= len(args); i++ {
buf.WriteString(args[i-1])
if i%int(urlsChunk) == 0 || i == len(args) {
urls = append(
urls,
fmt.Sprintf("https://min-api.cryptocompare.com/data/pricemulti?fsyms=%s&tsyms=%s",
buf.String(), base),
)
buf.Reset()
continue
}
buf.WriteString(",")
}
done <- string(bodyBytes)
return urls, nil
}
Oops, something went wrong.

0 comments on commit 58fdb2d

Please sign in to comment.
You can’t perform that action at this time.