вступление

Хорошие новости! Пакет IP2Proxy Go версии 2.3.0 был изменен для поддержки одновременных запросов прокси с использованием файлов базы данных IP2Proxy BIN. Это означает, что вы можете писать более мощные и быстрые коды, чтобы выполнить свою задачу быстрее. Ниже приведен наш простой пример, демонстрирующий параллелизм с пакетом IP2Proxy Go.

Пример кода

Ниже у нас есть полный пример кода, где мы запускаем 20 потоков. Затем каждый поток будет запрашивать информацию о прокси-сервере, используя пакет IP2Proxy Go для 1000 случайных IP-адресов. Случайные IP-адреса и несколько потоков представляют собой более реалистичный сценарий использования в современной многопоточной среде.

package main

import (
  "fmt"
  "strings"
  "strconv"
  "time"
  "github.com/ip2location/ip2proxy-go"
  crypto_rand "crypto/rand"
  "encoding/binary"
  math_rand "math/rand"
  "sync"
)

func main() {
  maxthreads := 20
  respond := make(chan string, maxthreads)
  var wg sync.WaitGroup
  wg.Add(maxthreads)
  
  for i := 1; i <= maxthreads; i++ {
    go doQuery(respond, &wg)
  }
  
  wg.Wait()
  close(respond)
  
  for queryResp := range respond {
    fmt.Printf("Got Response:\t %s\n", queryResp)
  }
}

func doQuery(respond chan<- string, wg *sync.WaitGroup) {
  defer wg.Done()
  
  db, err := ip2proxy.OpenDB("./IP2PROXY-IP-PROXYTYPE-COUNTRY-REGION-CITY-ISP-DOMAIN-USAGETYPE-ASN-LASTSEEN.BIN")
  if err != nil {
    return
  }
  
  var ips []string
  var totaltime int64 = 0
  finalresults := ""
  maxtests := 1000
  
  ip := ""
  for x := 0; x < maxtests; x++ {
    ip = getIp()
    ips = append(ips, ip)
  }
  
  for index, _ := range ips {
    start := time.Now()
    all, err := db.GetAll(ips[index])
    
    if err != nil {
      fmt.Print(err)
      return
    }
    
    end := time.Now()
    duration := end.Sub(start)
    totaltime = totaltime + duration.Nanoseconds()
    
    finalresults += fmt.Sprintf("IP: %s\n", ips[index])
    finalresults += fmt.Sprintf("ModuleVersion: %s\n", ip2proxy.ModuleVersion())
    finalresults += fmt.Sprintf("PackageVersion: %s\n", db.PackageVersion())
    finalresults += fmt.Sprintf("DatabaseVersion: %s\n", db.DatabaseVersion())
    finalresults += fmt.Sprintf("isProxy: %s\n", all["isProxy"])
    finalresults += fmt.Sprintf("ProxyType: %s\n", all["ProxyType"])
    finalresults += fmt.Sprintf("CountryShort: %s\n", all["CountryShort"])
    finalresults += fmt.Sprintf("CountryLong: %s\n", all["CountryLong"])
    finalresults += fmt.Sprintf("Region: %s\n", all["Region"])
    finalresults += fmt.Sprintf("City: %s\n", all["City"])
    finalresults += fmt.Sprintf("ISP: %s\n", all["ISP"])
    finalresults += fmt.Sprintf("Domain: %s\n", all["Domain"])
    finalresults += fmt.Sprintf("UsageType: %s\n", all["UsageType"])
    finalresults += fmt.Sprintf("ASN: %s\n", all["ASN"])
    finalresults += fmt.Sprintf("AS: %s\n", all["AS"])
    finalresults += fmt.Sprintf("LastSeen: %s\n", all["LastSeen"])
    finalresults += fmt.Sprintln("=========================================================")
  }
  
  db.Close()
  
  totaltime64 := float64(totaltime / 1000000)
  maxtests64 := float64(maxtests)
  finalresults += fmt.Sprintf("Total time: %vms\n", totaltime64)
  finalresults += fmt.Sprintf("Average time: %vms\n", (totaltime64 / maxtests64))
  
  respond <- finalresults
}

func getIp() string {
  var b [8]byte
  _, err := crypto_rand.Read(b[:])
  
  if err != nil {
    panic("Cannot seed math/rand package with cryptographically secure random number generator")
  }
  math_rand.Seed(int64(binary.LittleEndian.Uint64(b[:])))
  
  var s []string
  p := math_rand.Perm(255)
  for _, r := range p[:4] {
    // fmt.Println(r)
    s = append(s, strconv.Itoa(r))
  }
  ip := strings.Join(s, ".")
  
  return ip
}

ПРИМЕЧАНИЕ. Функция ModuleVersion вызывается в объекте ip2proxy, поскольку она связана с основным объектом, а не с конкретным объектом базы данных.

Основная функция

В нашей основной функции мы создаем буферизованный канал, чтобы мы могли получать значения из наших 20 потоков. Мы также создаем группу ожидания, чтобы мы могли собирать результаты всех 20 потоков, прежде чем выводить их.

Чтобы создать новый поток, мы вызываем нашу функцию doQuery с ключевым словом go. И канал, и группа ожидания передаются в качестве параметров функции doQuery, чтобы они могли возвращать свои результаты и уведомлять группу ожидания, когда функция завершает обработку.

Функция «дозапрос»

В этой функции мы создаем экземпляр объекта БД, вызывая функцию ip2proxy.OpenDB. Этот объект БД с именем db функционирует независимо от объекта db в других потоках. Объект db выполняет прокси-запросы.

После создания экземпляра объекта db мы генерируем список случайных IP-адресов для наших тестов, используя функцию getIp. Затем мы передаем тестовые IP-адреса методу db.GetAll, который возвращает всю соответствующую информацию о прокси-сервере. Мы добавим результаты в строку. Каждый цикл будет проверять данные прокси для текущего IP-адреса в цикле, а затем продолжать добавлять результаты. Когда цикл завершится, мы вызовем db.Close, чтобы закрыть объект db.

После завершения всех тестов строка результата возвращается вызывающему коду через канал. Наш код также вычисляет среднее время, затрачиваемое на запрос в этом конкретном потоке.

Вывод

Любой достойный пакет должен быть пригоден для использования в многопоточной среде, и наш вышеприведенный пример ясно демонстрирует, что пакет IP2Proxy Go теперь способен на это. Мы надеемся, что эта новая возможность позволит вам создавать еще лучшие коды для ваших пользователей.

Первоначально опубликовано на https://blog.ip2location.com 2 марта 2020 г.