缓存在内存中并使用go-routine更新的最佳方法是什么?

The Case: Weather API - I will assume that the task is simple and I just want to make an API to return the weather based on another API

The code

package main

import (
    "encoding/json"
    "io/ioutil"
    "net/http"

    "github.com/gorilla/mux"
)

type ResponseBody struct {
    CurrentObservation struct {
        Weather         string `json:"weather"`
        Temperature     string `json:"temperature_string"`
        DisplayLocation struct {
            City string `json:"city"`
        } `json:"display_location"`
    } `json:"current_observation"`
}

var weather ResponseBody

func main() {
    // start the api
    r := mux.NewRouter()
    r.HandleFunc("/", HomeHandler)
    http.ListenAndServe(":8080", r)
}

// handler
func HomeHandler(w http.ResponseWriter, r *http.Request) {
    // load the weather first
    weather = getWeather()
    b, _ := json.Marshal(weather)
    w.Write(b)
}

// get wether from wunderground api
func getWeather() ResponseBody {
    url := "http://api.wunderground.com/api/MY_API_KEY/conditions/q/CA/San_Francisco.json"
    req, err := http.NewRequest("GET", url, nil)
    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()
    body, _ := ioutil.ReadAll(resp.Body)
    var rb ResponseBody
    json.Unmarshal([]byte(body), &rb)
    return rb
}

Now every time someone hits the API it will send a request to the weather API, but this won't be efficient when I will have concurrent requests, so I will cache it in memory and will update the data in a go-routine every one second

First: I will move the getWeather call to the main function

func main() {
    // load the weather first
    weather = getWeather()
    // start the api
    r := mux.NewRouter()
    r.HandleFunc("/", HomeHandler)
    http.ListenAndServe(":8080", r)
}

// handler
func HomeHandler(w http.ResponseWriter, r *http.Request) {
    b, _ := json.Marshal(weather)
    w.Write(b)
}

and will start a go-routine in the main function too

func main() {
    // load the weather first
    weather = getWeather()
    // update data every 1 second
    go func() {
        for {
            time.Sleep(time.Second)
            weather = getWeather()
        }
    }()
    // start the api
    r := mux.NewRouter()
    r.HandleFunc("/", HomeHandler)
    http.ListenAndServe(":8080", r)
}

so now the application could handle concurrent requests up to minimum 250 concurrent after testing with siege tool

Transactions:                250 hits
Availability:             100.00 %
Elapsed time:               0.47 secs
Data transferred:           0.03 MB
Response time:              0.00 secs
Transaction rate:         531.91 trans/sec
Throughput:             0.07 MB/sec
Concurrency:                2.15
Successful transactions:         250
Failed transactions:               0
Longest transaction:            0.04
Shortest transaction:           0.00

So is it right to cache and update data in this way? Or there is something wrong and I should do it in a better way?

The basic approach is OK, but there's a data race on weather. Use a mutex to protect the variable:

var mu sync.RWMutex
var weather ResponseBody

func main() {
    // load the weather first
    weather = getWeather()
    // update data every 1 second
    go func() {
        for {
            time.Sleep(time.Second)
            mu.Lock()
            weather = getWeather()
            mu.Unlock()
        }
    }()
    // start the api
    r := mux.NewRouter()
    r.HandleFunc("/", HomeHandler)
    http.ListenAndServe(":8080", r)
}

func HomeHandler(w http.ResponseWriter, r *http.Request) {
    mu.RLock()
    b, _ := json.Marshal(weather)
    mu.RUnlock()
    w.Write(b)
}

It is not necessary to guard the first assignment to weather in main because the assignment is guaranteed to happen before the updating goroutine and the request handlers started by ListenAndServer.

An improvement is to cache the response body bytes:

var mu sync.RWMutex
var resp []byte

func main() {
    // load the weather first
    weather := getWeather()
    resp, _ = json.Marshal(weather)
    // update data every 1 second
    go func() {
        for {
            time.Sleep(time.Second)
            mu.Lock()
            weather = getWeather()
            resp, _ = json.Marshal(weather)
            mu.Unlock()
        }
    }()
    // start the api
    r := mux.NewRouter()
    r.HandleFunc("/", HomeHandler)
    http.ListenAndServe(":8080", r)
}

func HomeHandler(w http.ResponseWriter, r *http.Request) {
    mu.RLock()
    b := resp
    mu.RUnlock()
    w.Write(b)
}