最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

golang高并發(fā)限流操作 ping / telnet

 更新時(shí)間:2020年12月21日 10:41:36   作者:出逃的迪斯尼貓咪  
這篇文章主要介紹了golang高并發(fā)限流操作 ping / telnet,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧

需求

當(dāng)需要同時(shí)ping/telnet多個(gè)ip時(shí),可以通過引入ping包/telnet包實(shí)現(xiàn),也可以通過go調(diào)用cmd命令實(shí)現(xiàn),不過后者調(diào)用效率較差,所以這里選擇ping包和telnet包

還有就是高并發(fā)的問題,可以通過shell腳本或者go實(shí)現(xiàn)高并發(fā),所以我選擇的用go自帶的協(xié)程實(shí)現(xiàn),但是如果要同時(shí)處理1000+個(gè)ip,考慮到機(jī)器的性能,需要ratelimit控制開辟的go協(xié)程數(shù)量,這里主要寫一下我的建議和淌過的坑

ping

參考鏈接: https://github.com/sparrc/go-ping

import "github.com/sparrc/go-ping"
import "time"
func (p *Ping) doPing(timeout time.Duration, count int, ip string) (err error) {
 pinger, cmdErr := ping.NewPinger(ip)
 if cmdErr != nil {
  glog.Error("Failed to ping " + p.ipAddr)
  err = cmdErr
  return
 }
 pinger.Count = count
 pinger.Interval = time.Second
 pinger.Timeout = timeout
 // true的話,代表是標(biāo)準(zhǔn)的icmp包,false代表可以丟包類似udp
 pinger.SetPrivileged(false)
 // 執(zhí)行
 pinger.Run() 
 // 獲取ping后的返回信息
 stats := pinger.Statistics() 
 //延遲
 latency = float64(stats.AvgRtt) 
 // 標(biāo)準(zhǔn)的往返總時(shí)間 
 jitter = float64(stats.StdDevRtt) 
 //丟包率 
 packetLoss = stats.PacketLoss 
 return
}

注意: pinger.Run() 這里執(zhí)行的時(shí)候是阻塞的,如果并發(fā)量大的時(shí)候,程序會(huì)卡死在這里,所以當(dāng)有高并發(fā)的需求時(shí)建議如下處理:

go pinger.Run()

time.Sleep(timeout)

telnet

package main
import (
 "github.com/reiver/go-telnet"
)
func doTelnet(ip string, port int) {
 var caller telnet.Caller = telnet.StandardCaller
 address := ip + ":"+ strconv.Itoa(port)
 // DialToAndCall 檢查連通性并且調(diào)用
 telnet.DialToAndCall(address, caller)
 }
}

bug出現(xiàn)報(bào)錯(cuò):

lookup tcp/: nodename nor servname provided, or not known

解決:

修改string(port)為strconv.Itoa(port)

DialToAndCall這種方式telnet無法設(shè)置超時(shí)時(shí)間,默認(rèn)的超時(shí)時(shí)間有1分鐘,所以使用DialTimeout這個(gè)方式實(shí)現(xiàn)telnet

import "net"
func doTelnet(ip string, ports []string) map[string]string {
 // 檢查 emqx 1883, 8083, 8080, 18083 端口
 results := make(map[string]string)
 for _, port := range ports {
 address := net.JoinHostPort(ip, port)
 // 3 秒超時(shí)
 conn, err := net.DialTimeout("tcp", address, 3*time.Second)
 if err != nil {
 results[port] = "failed"
 } else {
 if conn != nil {
 results[port] = "success"
 _ = conn.Close()
 } else {
 results[port] = "failed"
 }
 }
 }
 return results
}

shell高并發(fā)

本質(zhì)就是讀取ip.txt文件里的ip,然后調(diào)用ping方法,實(shí)現(xiàn)高并發(fā)也是借助&遍歷所有的ip然后同一交給操作系統(tǒng)去處理高并發(fā)

while read ip
do
 {
 doPing(ip)
 } &
done < ip.txt

go高并發(fā)限速

import (
 "context"
 "fmt"
 "log"
 "time"
 "sync"
 "golang.org/x/time/rate"
)
func Limit(ips []string)([]string, []string, error) {
 //第一個(gè)參數(shù)是每秒鐘最大的并發(fā)數(shù),第二個(gè)參數(shù)是桶的容量,第一次的時(shí)候每秒可執(zhí)行的數(shù)量就是桶的容量,建議這兩個(gè)值都寫成一樣的
 r := rate.NewLimiter(10, 10)
 ctx := context.Background()
 
 wg := sync.WaitGroup{}
 wg.Add(len(ips))
 
 lock := sync.Mutex{}
 var success []string
 var fail []string
 
 defer wg.Done()
 for _,ip:=range ips{
 //每次消耗2個(gè),放入一個(gè),消耗完了還會(huì)放進(jìn)去,如果初始是5個(gè),所以這段代碼再執(zhí)行到第4次的時(shí)候筒里面就空了,如果當(dāng)前不夠取兩個(gè)了,本次就不取,再放一個(gè)進(jìn)去,然后返回false
 err := r.WaitN(ctx, 2)
 if err != nil {
  log.Fatal(err)
 }
 go func(ip string) {
 defer func() {
 wg.Done()
 }()
 err := doPing(time.Second, 2, ip)
 lock.Lock()
 defer lock.Unlock()
 if err != nil {
 fail = append(fail, ip)
 return
 } else {
 success = append(success, ip)
 }
 }(ip)
 }
 // wait等待所有g(shù)o協(xié)程結(jié)束
 wg.wait()
 return success,fail,nil
}
func main() {
 ips := [2]string{"192.168.1.1","192.168.1.2"}
 success,fail,err := Limit(ips)
 if err != nil {
 fmt.Printf("ping error")
 }
}

這里注意一個(gè)并發(fā)實(shí)現(xiàn)的坑,在for循環(huán)里使用goroutine時(shí)要把遍歷的參數(shù)傳進(jìn)去才能保證每個(gè)遍歷的參數(shù)都被執(zhí)行,否則只能執(zhí)行一次

(拓展)管道、死鎖

先看個(gè)例子:

func main() {
 go print() // 啟動(dòng)一個(gè)goroutine
 print()
}
func print() {
fmt.Println("*******************")
}

輸出結(jié)果:

*******************

沒錯(cuò),只有一行,因?yàn)楫?dāng)go開辟一個(gè)協(xié)程想去執(zhí)行print方法時(shí),主函數(shù)已經(jīng)執(zhí)行完print并打印出來,所以goroutine還沒有機(jī)會(huì)執(zhí)行程序就已經(jīng)結(jié)束了,解決這個(gè)問題可是在主函數(shù)里加time.sleep讓主函數(shù)等待goroutine執(zhí)行完,也可以使用WaitGroup.wait等待goroutine執(zhí)行完,還有一種就是信道

信道分無緩沖信道和緩沖信道

無緩沖信道

無緩沖信道也就是定義長(zhǎng)度為0的信道,存入一個(gè)數(shù)據(jù),從無緩沖信道取數(shù)據(jù),若信道中無數(shù)據(jù),就會(huì)阻塞,還可能引發(fā)死鎖,同樣數(shù)據(jù)進(jìn)入無緩沖信道, 如果沒有其他goroutine來拿走這個(gè)數(shù)據(jù),也會(huì)阻塞,記住無緩沖數(shù)據(jù)并不存儲(chǔ)數(shù)據(jù)

func main() {
 var channel chan string = make(chan string)
 go func(message string) {
 channel<- message // 存消息
 }("Ping!")
 fmt.Println(<-messages) // 取消息
}

緩存信道

顧名思義,緩存信道可以存儲(chǔ)數(shù)據(jù),goroutine之間不會(huì)發(fā)生阻塞,for循環(huán)讀取信道中的數(shù)據(jù)時(shí),一定要判斷當(dāng)管道中不存在數(shù)據(jù)時(shí)的情況,否則會(huì)發(fā)生死鎖,看個(gè)例子

channel := make(chan int, 3)
channel <- 1
channel <- 2
channel <- 3
// 顯式關(guān)閉信道
close(channel)
for v := range channel {
 fmt.Println(v)
 // 如果現(xiàn)有數(shù)據(jù)量為0,跳出循環(huán),與顯式關(guān)閉隧道效果一樣,選一個(gè)即可
 if len(ch) <= 0 { 
 break
 }
}

但是這里有個(gè)問題,信道中數(shù)據(jù)是可以隨時(shí)存入的,所以我們遍歷的時(shí)候無法確定目前的個(gè)數(shù)就是信道的總個(gè)數(shù),所以推薦使用select監(jiān)聽信道

// 創(chuàng)建一個(gè)計(jì)時(shí)信道
timeout := time.After(1 * time.Second) 
// 監(jiān)聽3個(gè)信道的數(shù)據(jù)
select {
 case v1 := <- c1: fmt.Printf("received %d from c1", v1)
 case v2 := <- c2: fmt.Printf("received %d from c2", v2)
 case v3 := <- c3: fmt.Printf("received %d from c3", v3)
 case <- timeout: 
 is_timeout = true // 超時(shí)
 break
 }
}

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教。

相關(guān)文章

  • 詳解golang中Context超時(shí)控制與原理

    詳解golang中Context超時(shí)控制與原理

    Context本身的含義是上下文,我們可以理解為它內(nèi)部攜帶了超時(shí)信息、退出信號(hào),以及其他一些上下文相關(guān)的值,本文給大家詳細(xì)介紹了golang中Context超時(shí)控制與原理,文中有相關(guān)的代碼示例供大家參考,需要的朋友可以參考下
    2024-01-01
  • Go語(yǔ)言用map實(shí)現(xiàn)堆棧功能的方法

    Go語(yǔ)言用map實(shí)現(xiàn)堆棧功能的方法

    這篇文章主要介紹了Go語(yǔ)言用map實(shí)現(xiàn)堆棧功能的方法,實(shí)例分析了Go語(yǔ)言使用map操作堆棧的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-02-02
  • 使用go語(yǔ)言實(shí)現(xiàn)查找兩個(gè)數(shù)組的異同操作

    使用go語(yǔ)言實(shí)現(xiàn)查找兩個(gè)數(shù)組的異同操作

    這篇文章主要介紹了使用go語(yǔ)言實(shí)現(xiàn)查找兩個(gè)數(shù)組的異同操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • Go?gRPC服務(wù)雙向流式RPC教程

    Go?gRPC服務(wù)雙向流式RPC教程

    這篇文章主要為大家介紹了Go?gRPC服務(wù)雙向流式RPC教程示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • 使用Go?goroutine實(shí)現(xiàn)并發(fā)的Clock服務(wù)

    使用Go?goroutine實(shí)現(xiàn)并發(fā)的Clock服務(wù)

    這篇文章主要為大家詳細(xì)介紹了如何使用Go?goroutine實(shí)現(xiàn)并發(fā)的Clock服務(wù),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-06-06
  • Go語(yǔ)言中常量的實(shí)現(xiàn)

    Go語(yǔ)言中常量的實(shí)現(xiàn)

    Go語(yǔ)言支持單常量和多常量的定義方法,通過const關(guān)鍵字實(shí)現(xiàn),常量用于存儲(chǔ)不變的值,如圓周率或固定的錯(cuò)誤信息,旨在提高代碼的維護(hù)性和可讀性,感興趣的可以了解一下
    2024-10-10
  • 關(guān)于Go你不得不知道的一些實(shí)用小技巧

    關(guān)于Go你不得不知道的一些實(shí)用小技巧

    開發(fā)語(yǔ)言上Go成為高并發(fā)業(yè)務(wù)開發(fā)的主流語(yǔ)言,再加上云原生技術(shù)底座的驅(qū)動(dòng),Go語(yǔ)言風(fēng)光無限,下面這篇文章主要給大家介紹了關(guān)于Go你不得不知道的一些實(shí)用小技巧,需要的朋友可以參考下
    2022-11-11
  • go連接mysql的項(xiàng)目實(shí)踐

    go連接mysql的項(xiàng)目實(shí)踐

    本文主要介紹了go連接mysql的項(xiàng)目實(shí)踐,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-03-03
  • Golang學(xué)習(xí)筆記(六):struct

    Golang學(xué)習(xí)筆記(六):struct

    這篇文章主要介紹了Golang學(xué)習(xí)筆記(六):struct,本文講解了struct的聲明及初始化、struct的匿名字段(繼承)、method、method繼承和重寫等內(nèi)容,需要的朋友可以參考下
    2015-05-05
  • 淺析Go語(yǔ)言容器之?dāng)?shù)組和切片的使用

    淺析Go語(yǔ)言容器之?dāng)?shù)組和切片的使用

    在?Java?的核心庫(kù)中,集合框架可謂鼎鼎大名:Array?、List、Set等等,隨便拎一個(gè)出來都值得開發(fā)者好好學(xué)習(xí)如何使用甚至是背后的設(shè)計(jì)源碼。雖然Go語(yǔ)言沒有如此豐富的容器類型,但也有一些基本的容器供開發(fā)者使用,接下來讓我們認(rèn)識(shí)一下這些容器類型吧
    2022-11-11

最新評(píng)論

甘孜县| 犍为县| 湖南省| 湟中县| 安徽省| 井冈山市| 武功县| 蛟河市| 虹口区| 铜山县| 广灵县| 东城区| 镇坪县| 彰化县| 刚察县| 湘乡市| 化德县| 清镇市| 青浦区| 台江县| 通城县| 闵行区| 碌曲县| 绵竹市| 泊头市| 南江县| 清远市| 凉山| 兖州市| 扶余县| 错那县| 翁牛特旗| 礼泉县| 静海县| 苍山县| 阿合奇县| 成都市| 三原县| 澎湖县| 石楼县| 娄底市|