Skip to content
Permalink
Browse files

- added rates_manager.go

- optimized fetch function
  • Loading branch information...
maZahaca committed Mar 13, 2019
1 parent 42a6506 commit 0166b516bcf5bc87089d3b7cdc54e96479097249
Showing with 140 additions and 60 deletions.
  1. +18 −1 dto/Rates.go
  2. +41 −59 main.go
  3. +81 −0 pkg/rates_manager.go
@@ -1,7 +1,10 @@
//go:generate easyjson -all
package dto

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

//easyjson:json
type Rates struct {
@@ -22,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
}
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
}
@@ -0,0 +1,81 @@
package pkg

import (
"encoding/json"
"github.com/maZahaca/go-exchange-rates-service/dto"
"io/ioutil"
"log"
"net/http"
)

type RatesManger struct {
rates dto.Rates
urls []string
}

func NewRatesManager(base string, urls []string) *RatesManger {
return &RatesManger{
rates: *dto.NewRates(base),
urls: urls,
}
}

func (m *RatesManger) Update() {
urlsCount := len(m.urls)
done := make(chan error, urlsCount)

for i := 0; i < urlsCount; i++ {
url := m.urls[i]
go func() {
bodyBytes, err := fetch(url)
if err != nil {
done <- err
return
}
jsonMap := make(map[string]map[string]float32)
err = json.Unmarshal(bodyBytes, &jsonMap)
if err != nil {
done <- err
return
}
err = m.rates.AddFromMap(jsonMap)
if err != nil {
done <- err
return
}
log.Println(jsonMap)
log.Printf("Fetched result: %s", bodyBytes)
done <- nil
}()
}

for i := 0; i < urlsCount; i++ {
err := <-done
if err != nil {
log.Fatal(err)
}
}
}

func (m *RatesManger) Get() dto.Rates {
return m.rates
}

func fetch(url string) (bodyBytes []byte, err error) {
log.Printf("fetching url: %s", url)
resp, err := http.Get(url)
defer func() {
errClose := resp.Body.Close()
if err == nil {
err = errClose
}
}()
if err != nil {
return
}
bodyBytes, err = ioutil.ReadAll(resp.Body)
if err != nil {
return
}
return
}

0 comments on commit 0166b51

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