Нека изградим Pokedex REPL, за да научим за кеша!
Ако не сте запознати с Pokemon или Pokedex, това е добре!
Pokedex е просто измислено устройство, което ни позволява да търсим информация за Pokemon - неща като тяхното име, тип и статистика.
Въпреки че има много функции, но за този блог искам да подчертая изграждането на следното:
- карта: показва страница с 20 местоположения от света на Pokemon
- mapb: точно обратното на mapb и показва предишната страница с 20 местоположения от света на Pokemon.
Преглед на високо ниво
- Извлечете 20-те резултата от API
- Кеширайте резултатите, тъй като командата mapb се нуждае от предишни местоположения и извличането отново би било много скъпо
- Изтрийте остарелите записи в кеша след интервал от време „t“.
Кодиране
Нека го разделим на две части:
- Изграждане на кеш
- Изграждане на командите
1. Изграждане на кеш
Дефиниране на кеша:
type cacheEntry struct { createdAt time.Time val []byte } type Cache struct { cache map[string]cacheEntry mux sync.Mutex }
Дефиниране на метода за добавяне на запис към кеша:
func (c *Cache) Add(key string, val []byte) { c.mux.Lock() c.cache[key] = cacheEntry{ createdAt: time.Now(), val: val, } c.mux.Unlock() }
Дефиниране на метода за достъп до запис от кеша:
func (c *Cache) Get(key string) ([]byte, bool) { c.mux.Lock() res, ok := c.cache[key] c.mux.Unlock() if !ok { return nil, false } return res.val, true }
Дефиниране на метода за премахване на остарели записи от кеша след интервал „t“:
func (c *Cache) reap(t time.Duration) { c.mux.Lock() for k, v := range c.cache { if time.Since(v.createdAt) >= t { delete(c.cache, k) } } c.mux.Unlock() } func (c *Cache) reapLoop(t time.Duration) { ticker := time.NewTicker(t) for range ticker.C { c.reap(t) } }
Накрая конструкторска функция за инстанциране на нашия кеш и едновременно стартиране на нашия цикъл на reap, който ще проверява остарелите записи:
func NewCache(interval time.Duration) *Cache { c := Cache{ cache: make(map[string]cacheEntry), mux: sync.Mutex{}, } go c.reapLoop(interval) return &c }
2. Изграждане на командата: map и mapb
Кодиране на JSON отговора на API със структура:
(Тази стъпка е специфична за езика, тъй като JSON в Go се кодира като структура)
type Location struct { LocationID int `json:"id"` Name string `json:"name"` PokemonEncounters []struct { Pokemon struct { Name string `json:"name"` } `json:"pokemon"` } `json:"pokemon_encounters"` }
Създайте екземпляр на кеша:
(моята кеш функция беше в имена на пакети pokecache)
var cache *pokecache.Cache = pokecache.NewCache(20 * time.Second)
Дефиниране на функция за извличане на заявките и съхраняването им в кеша:
func GetLocation(id string) ([]byte, error) { fetch_url := fmt.Sprintf("%s/%s/%v", base_url, location_endpoint, id) // finding in cache first res, ok := cache.Get(fetch_url) // making GET request if not found in cache if !ok { res, err := http.Get(fetch_url) if err != nil { return nil, err } body, err := io.ReadAll(res.Body) if res.StatusCode > 299 { return nil, fmt.Errorf("response failed with status code: %d and \nbody: %s\n url: %s", res.StatusCode, body, fetch_url) } if err != nil { return nil, err } cache.Add(fetch_url, body) return body, nil } return res, nil }
Дефиниране на глобална променлива за съхраняване на текущия идентификатор на местоположение, от който трябва да извлечем:
var LocationID = 1
И накрая нашите функции map и mapb:
func Map() { locations := pokeapi.Location{} limit := 20 if pokeapi.LocationID != 1 { limit = limit + pokeapi.LocationID - 1 } for ; pokeapi.LocationID <= limit; pokeapi.LocationID++ { res, err := pokeapi.GetLocation(pokeapi.LocationID) if err != nil { fmt.Print(err) } err = json.Unmarshal(res, &locations) if err != nil { fmt.Print(err) } fmt.Println(locations.Name) } } func Mapb() { if pokeapi.LocationID == 1 { fmt.Printf("No previous locations to look back\n") return } locations := pokeapi.Location{} for i := pokeapi.LocationID - 20; i < pokeapi.LocationID; i++ { res, err := pokeapi.GetLocation(i) if err != nil { fmt.Println(err) } err = json.Unmarshal(res, &locations) if err != nil { fmt.Println(err) } fmt.Println(locations.Name) } pokeapi.LocationID = pokeapi.LocationID - 20 }
Това е всичко!
Ето връзката към пълния проект: https://github.com/afreen23/pokedex
Благодарим ви, че прочетохте до края. Моля, обмислете следването на писателя и тази публикация. Посетете Stackademic, за да разберете повече за това как демократизираме безплатното обучение по програмиране по света.
Справка: Проектът pokedex на Boot.dev