ipmamnager.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. package cz88ip
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "io"
  6. "net/http"
  7. "os"
  8. "bet24.com/log"
  9. //"bet24.com/public"
  10. "context"
  11. "strings"
  12. "sync"
  13. "time"
  14. )
  15. var mgr *ipmanager
  16. const APPCODE = "d85a9a4247954b5da7babcbaf29eefa2"
  17. func GetIpManager() *ipmanager {
  18. if mgr == nil {
  19. mgr = new(ipmanager)
  20. mgr.ctor()
  21. }
  22. return mgr
  23. }
  24. type ipmanager struct {
  25. iplist map[string]*IpInfo
  26. lock *sync.RWMutex
  27. isDirty bool
  28. }
  29. func (im *ipmanager) ctor() {
  30. im.lock = &sync.RWMutex{}
  31. im.iplist = make(map[string]*IpInfo)
  32. im.isDirty = false
  33. im.loadFromFile()
  34. im.flush()
  35. }
  36. func (im *ipmanager) flush() {
  37. time.AfterFunc(10*time.Minute, im.flush)
  38. if !im.isDirty {
  39. return
  40. }
  41. im.isDirty = false
  42. im.saveToFile()
  43. }
  44. func (im *ipmanager) loadFromFile() {
  45. data, err := os.ReadFile("conf/cz88ipdata.json")
  46. if err != nil {
  47. log.Release("ipmanager.loadFromFile read data failed conf/cz88ipdata.json %v", err)
  48. return
  49. }
  50. im.lock.Lock()
  51. err = json.Unmarshal([]byte(data), &im.iplist)
  52. im.lock.Unlock()
  53. if err != nil {
  54. log.Release("ipmanager.loadFromFile unmarshal failed conf/cz88ipdata.json %v", err)
  55. }
  56. }
  57. func (im *ipmanager) saveToFile() {
  58. im.lock.RLock()
  59. d, _ := json.Marshal(im.iplist)
  60. im.lock.RUnlock()
  61. err := os.WriteFile("conf/cz88ipdata.json", d, 0644)
  62. if err != nil {
  63. log.Release("ipmanager.saveToFile failed %v", err)
  64. }
  65. }
  66. // 获取ip前三位
  67. func (im *ipmanager) getPrefixIp(ipAddress string) string {
  68. ips := strings.Split(ipAddress, ".")
  69. if len(ips) < 4 {
  70. return ipAddress
  71. }
  72. return fmt.Sprintf("%s.%s.%s", ips[0], ips[1], ips[2])
  73. }
  74. func (im *ipmanager) getIpInfo(ipAddress string) *IpInfo {
  75. if ipAddress == "" {
  76. log.Release("ipmanager.getIpInfo invalid argument")
  77. return nil
  78. }
  79. ipPrefix := im.getPrefixIp(ipAddress)
  80. im.lock.RLock()
  81. ii, ok := im.iplist[ipPrefix]
  82. im.lock.RUnlock()
  83. if ok {
  84. return ii
  85. }
  86. // 找不到
  87. return im.requestIpInfoWithTimeout(ipAddress, ipPrefix)
  88. }
  89. func (im *ipmanager) requestIpInfoWithTimeout(ipAddress, ipPrefix string) *IpInfo {
  90. channel_ipInfo := make(chan *IpInfo)
  91. go im.requestIPInfoAndSave(ipAddress, ipPrefix, channel_ipInfo)
  92. c, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  93. select {
  94. case ipInfo := <-channel_ipInfo:
  95. cancel()
  96. return ipInfo
  97. case <-c.Done():
  98. log.Release("requestIpInfoWithTimeout [%s]timeout", ipAddress)
  99. cancel()
  100. return nil
  101. }
  102. //log.Release("requestIpInfoWithTimeout [%s] should not be here", ipAddress)
  103. //return nil
  104. }
  105. func (im *ipmanager) requestIPInfoAndSave(ipAddress, ipPrefix string, ch chan<- *IpInfo) {
  106. newIpInfo := im.requestIPInfo(ipAddress)
  107. c, cancel := context.WithTimeout(context.Background(), 2*time.Second)
  108. select {
  109. case ch <- newIpInfo:
  110. cancel()
  111. break
  112. case <-c.Done():
  113. log.Release("requestIPInfoAndSave channel post [%s]timeout", ipAddress)
  114. cancel()
  115. break
  116. }
  117. if newIpInfo == nil {
  118. return
  119. }
  120. im.lock.Lock()
  121. im.iplist[ipPrefix] = newIpInfo
  122. im.isDirty = true
  123. im.lock.Unlock()
  124. }
  125. func (im *ipmanager) requestIPInfo(ipAddress string) *IpInfo {
  126. //time.Sleep(5 * time.Second)
  127. if ipAddress == "" {
  128. log.Release("ipmanager.requestIPInfo[%s] invalid argument", ipAddress)
  129. return nil
  130. }
  131. url := fmt.Sprintf("http://cz88geoaliyun.cocobaloot.com/search/ip/geo?ip=%s&AppCode=%s", ipAddress, APPCODE)
  132. resp, err := http.Get(url)
  133. if err != nil {
  134. log.Release("ipmanager.requestIPInfo[%s] failed %v", ipAddress, err)
  135. }
  136. if resp == nil {
  137. log.Release("ipmanager.requestIPInfo[%s] failed resp == nil", ipAddress)
  138. return nil
  139. }
  140. defer resp.Body.Close()
  141. bs, err := io.ReadAll(resp.Body)
  142. if err != nil {
  143. log.Release("ipmanager.requestIPInfo[%s] read data failed %v", ipAddress, err)
  144. return nil
  145. }
  146. type retData struct {
  147. Ip string `json:"ip,omitempty"`
  148. Geocode string `json:"geocode,omitempty"`
  149. Country string `json:"country,omitempty"`
  150. CountryCode string `json:"countryCode,omitempty"`
  151. Province string `json:"province,omitempty"`
  152. City string `json:"city,omitempty"`
  153. Districts string `json:"districts,omitempty"`
  154. }
  155. var retJson struct {
  156. Data retData `json:"data,omitempty"`
  157. Success bool `json:"success,omitempty"`
  158. Message string `json:"message,omitempty"`
  159. Code int `json:"code,omitempty"`
  160. }
  161. err = json.Unmarshal(bs, &retJson)
  162. if err != nil {
  163. log.Release("ipmanager.requestIPInfo[%s] unmarshal failed %v", ipAddress, err)
  164. return nil
  165. }
  166. if !retJson.Success {
  167. log.Release("ipmanager.requestIPInfo[%s] return failed %d", ipAddress, retJson.Code)
  168. return nil
  169. }
  170. // 成功了
  171. return &IpInfo{
  172. Country: retJson.Data.Country,
  173. Region: fmt.Sprintf("%s%s%s", retJson.Data.Province, retJson.Data.City, retJson.Data.Districts),
  174. LastUpdate: time.Now().Unix(),
  175. }
  176. }