Upgrade to Pro — share decks privately, control downloads, hide ads and more …

gopherDojo#1_LT

nao
May 29, 2018

 gopherDojo#1_LT

20180528

nao

May 29, 2018
Tweet

Other Decks in Technology

Transcript

  1. WHO Nao Yamaguchi Job: - Telecommunications business Github: - https://github.com/naoyamaguchi

    Interest: - Golang, Angular - HTTP2.0, grpc, TLS1.3, QUIC - GCP, Container
  2. Today’s Contents!! Network Programming ? 01 Why did I choose

    Go ? What I wanted to make What I made Impressions 02 03 04 05
  3. Why did I choose Go ? 1. Precondition ◦ C/C++は書きたくない

    ◦ 1から全部設定したくないけど、なぜその処理になったかを知りたい ◦ どこがボトルネックになっているか最低限アプリ被疑を晴らしたい 2. Do not guess. Measure it. ◦ 標準libraryがGoで書かれているのでGoさえ読めれば中身を理解できるのではないか ◦ Benchmark, pprofが標準で存在しており簡単に調査 /改善することができるのではない か 3. Community ◦ Kubernetesやgrpc等の新しい技術や人と触れ合える ◦ 会社の指示で仕方なく。。。みたいな人がいない(はず)
  4. What I wanted to make • PGW ◦ LTEで利用されるキャリア終端設備で接続・課金・通信処理等を行う ◦

    キャリアとの接続はGTPというUDPベースのトンネリングプロトコル ▪ 相互接続に関する技術的条件集 ◦ Internet方向はRawSocketでencap/decap処理 ▪ 現状goではEthernetを直接扱う標準ライブラリはなさそう UE eNB Carrier Equipment (SGW/MME/HSS) PGW The Internet GTP C/U (UDP Sock) Any Protocols (Raw Sock)
  5. What I made • UDP Proxy 1. 標準パッケージでUDP 2. Uplink

    / Downlinkの並行処理 3. context, x/time/rateを利用した帯域制御 4. Benchmark 5. (todo) pprofによるプロファイリング Uplink Downlink
  6. What I made • 標準パッケージでUDP ◦ どんな設定のどんなシステムコールがなされるのかを知りたい UdpAddr := &net.UDPAddr{IP:net.ParseIP("0.0.0.0"),

    Port:2152} UdpConn, _ := net.ListenUDP("udp", UdpAddr) buf := make([]byte, 1550) for { n, addr, _ := UpdConn.ReadFromUDP(buf) go func() { UdpConn.WriteTo(buf[:n], addr) }() } func ListenUDP(network string, laddr *UDPAddr) (*UDPConn, error) { -- snip -- c, err := listenUDP(context.Background(), network, laddr) -- snip -- } net/udpsock.go
  7. What I made func listenUDP(ctx context.Context,network string,laddr *UDPAddr) (*UDPConn,error){ fd,

    err := internetSocket(ctx,network,laddr,nil,syscall.SOCK_DGRAM,0,"listen") -- snip -- net/udpsock_posix.go func internetSocket(ctx context.Context, net string, laddr, raddr sockaddr, sotype, proto int, mode string) (fd *netFD, err error) { -- snip -- return socket(ctx, net, family, sotype, proto, ipv6only, laddr, raddr) -- snip -- net/ipsock_posix.go func socket(ctx context.Context, net string, family, sotype, proto int, ipv6only bool, laddr, raddr sockaddr) (fd *netFD, err error) { s, err := sysSocket(family, sotype, proto) // システムコールsocket()を呼んでいる -- snip -- if err := fd.dial(ctx, laddr, raddr); // システムコールbind()を呼んでいる net/sock_posix.go
  8. What I made • 標準パッケージでUDP:straceで答え合わせ ◦ go build udp.go &&

    strace -e 'trace=!pselect6,futex,sched_yield' ./udp →システムコールにみる Go言語のnetパッケージの実装 でTCPの分析している --- net.ListenUDP() --- socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, IPPROTO_IP) = 3 setsockopt(3, SOL_SOCKET, SO_BROADCAST, [1], 4) = 0 bind(3, {sa_family=AF_INET, sin_port=htons(2152), sin_addr=inet_addr("127.0.0.1")}, 16) = 0 epoll_ctl(4, EPOLL_CTL_ADD, 3, {EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLET, {u32=1772719872, u64=140515922775808}}) = 0 --- updConn.ReadFromUDP() --- recvfrom(3, 0xc42004f8ba, 1550, 0, 0xc42004f5d8, [112]) = -1 EAGAIN (Resource temporarily unavailable) epoll_pwait(4, [{EPOLLOUT, {u32=4207877888, u64=140097451138816}}], 128, 0, NULL, 140097451138816) = 1
  9. What I made • Uplink / Downlinkの並行処理 ◦ sync.WaitGroupを利用しDoneさせないことで複数loopを保持(Chanelで返したいものがない func

    main() { var wg sync.WaitGroup wg.Add(1) go uplink() go downlink() wg.Wait() } func uplink() { for { n, addr , _ := conn.ReadFromUDP(buf) go func() { conn.WriteTo(buf[:n], addr) }() -- snip -- func downlink() { for { n, addr , _ := conn.ReadFromUDP(buf) go func() { conn.WriteTo(buf[:n], addr) }() -- snip --
  10. What I made • context, x/time/rateを利用した帯域制御 ◦ golang.org/x/time/rateで速度制限を行う という@lufiaさまの記事にてチャネルへの送信数制御を受信し たbyte数(bit数)に置き換えることでいわゆる Token

    Bucket Filterを実装できそう const ( M = 80000000 // 1秒あたりの処理制限 ) func udpProxy() { updConn, _ := net.ListenUDP("udp", udpAddr) ctx := context.Background() n := rate.Every(time.Second / M) // 1秒あたりのトークン補充数 l := rate.NewLimiter(n, M) // トークン補充上限値 for { n, raddr, _ := updLn.ReadFromUDP(buffer) if err := l.WaitN(ctx, n); err != nil { // n個のトークン(受信したバイト数)を消費(ゆえに bpsではない) log.Fatalln(err) } go func() { updLn.WriteTo(buffer[:n], raddr) }() } }
  11. What I made • Benchmark ◦ httptestのようにserve自体をmock?してくれる標準ライブラリはなさそう func BenchmarkServer(b *testing.B)

    { conn, _ := net.Dial("udp", "127.0.0.1:2152") defer conn.Close() b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = conn.Write([]byte("0123456789")) recvBuf := make([]byte, 1500) n, _ := conn.Read(recvBuf) log.Printf("Received data: %s", string(recvBuf[:n])) } } 実行回数 1回あたりの実行時間 1回あたりの実行で確保した容量 1回あたりのアロケーション回数 300000 4225 ns/op 112 B/op 1 allocs/op
  12. Impressions • Conclusion ◦ 標準libraryがGoで書かれているので Goさえ読めれば中身を理解できるのではないか ▪ できそう。標準ライブラリを読みたいと思える( pythonで生じなかった感情) ◦

    Benchmark, pprofが標準で存在しており簡単に調査 /改善することができるのではないか ▪ できそう。pprofを理解したい。 ▪ 特に何もチューニングせずともそこそこの速度。 • TODO ◦ pprofの理解 ◦ 標準ライブラリとしての syscallの理解 / Raw socketを利用したGTPサーバの開発 ◦ grpcを利用した サービス/課金情報の連携 ◦ Docker-composeを脱却してk8sへ ◦ 状態管理と分散処理 • 謝辞