2022/03,江端さんの技術メモ

PrumeClusterは、Leafletをベースとして動く、スケーラブルオブジェクトビューアです。1万オブジェクトくらいなら軽く表示できます。

しかし、PrumeClusterは、クライアントのブラウザの中にアイコンのオブジェクトを直接作るので、基本的にはサーバとして使うことができません。クライアントとして使うものであて、描画画面は、常に"1つ"です。

で、私が作ったPrumeMobileもベースは、PrumeClusterなので、サーバとして使うことはできないのですが ―― 今週末、Vue.jpとかでスマホクライアント作ろうかと思ったのですが ――『もう新しいこと覚えるのは嫌だ』と思い知り、PrumeMobileのサーバ化を試みています ――ひとえに、考えうる限り、手を抜きたい、という一心からです。

複数のJavaScriptに対して、PrumeClusterがメッセージをブロードキャスト送付してくれるのか、くれないのか、明日調べよう。

 

 

2022/03,江端さんの技術メモ

最近"https"縛りがきつくて、ローカルのindex.htmlを叩くだけでは、画面が出てこなくなりました。正直、面倒くさいなぁ、と思っています。

こちらは、表示画面でブラウザを使いたいだけなのに、ブラウザ(特にchrome)が煩いことこの上もない

これも時代の流れか、と諦めて、index.htmlを書いているディレクトリの内容で、サクッとサーバを立てる方法を、色々やってみましたので、メモを残しておきます。

まず、node.jsをインストールしてnpmを使えるようにしておきます。

面倒なので、私の環境に合わせて説明しますね(このディレクトリを隠す人、多いですけど、はっきり言って読み難い上に、あまり意味ない(外部から、ディレクトリに入れるところまでハックされたら、何をしても無駄))

という訳で、私の作業ディレクトリは、ここ→ ~/kese/leaflet です。

$ npm install -g http-server
$ http-server

と、これだけで、

ebata@DESKTOP-P6KREM0 MINGW64 ~/kese/leaflet
$ http-server
Starting up http-server, serving ./
http-server version: 14.1.0
http-server settings:
CORS: disabled
Cache: 3600 seconds
Connection Timeout: 120 seconds
Directory Listings: visible
AutoIndex: visible
Serve GZIP Files: false
Serve Brotli Files: false
Default File Extension: none
Available on:
  http://192.168.0.8:8080
  http://127.0.0.1:8080
  http://172.28.64.1:8080
  http://172.21.112.1:8080
Hit CTRL-C to stop the server
と、即、Web(http)サーバが立ち上がります。
が、当然これだと、https://localhost:8081 などは使えないので、公開鍵を作る必要があります。
まず、mkcertで、オレオレ証明書を作ります。
ちなみに、ちゃんとアプリを作ろうとする人は、mkcert では問題が発生しますので、注意です。

iPhoneは「オレオレ証明書」では騙せないのかな?

Let's encrypt を試してみた件(整理は明日)

mkcertの入手方法ですが、https://github.com/FiloSottile/mkcertを除いてみたら、バイナリがダウンロードできそうことが分かりました。

でもって、ここから、Windows10で使えそうなバイナリをダウンロードしました。

ダウンロードしたところから、直接叩いてみたら、C:\Users\Ebata\AppData\Local\mkcert の中に、鍵ができていましたが、最初に、mkcer -installしろ、と言われています。

本当はmkcertにリネームした方が良いのでしょうが、面倒なので、そのまま mkcert-v1.4.1-windows-amd64.exe -install を強行しました。
その後、mkcert-v1.4.1-windows-amd64.exe localhost 127.0.0.1 と入力すると、"localhost" と "127.0.0.1"を含む鍵が、カレントディレクトリにできるようです。

で、"localhost+1-key.pem"を "key.pem"とリネームして、"localhost+1.pem"を"cert.pem"とリネームして、~/kese/leafletに放り込みます。

そんでもって、~/kese/leaflet から

$ http-server -S -C cert.pem -o -p 8081

とすると、https 対応のサーバが立ち上がります。

ebata@DESKTOP-P6KREM0 MINGW64 ~/kese/leaflet
$ http-server -S -C cert.pem -o -p 8081
Starting up http-server, serving ./ through https
http-server version: 14.1.0
http-server settings:
CORS: disabled
Cache: 3600 seconds
Connection Timeout: 120 seconds
Directory Listings: visible
AutoIndex: visible
Serve GZIP Files: false
Serve Brotli Files: false
Default File Extension: none
Available on:
  https://192.168.0.8:8081
  https://127.0.0.1:8081
  https://172.28.64.1:8081
  https://172.21.112.1:8081
Hit CTRL-C to stop the server
Open: https://127.0.0.1:8081
 あと、CORSで、問題が発生したら、
$ http-server -cors -S -C cert.pem -o -p 8081
で、何とかなるかもしれません。
https://localhost:8081/
で、やっと出てきてくれました。

index.htmlは以下の通りです。
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <title>Open Street Map Test</title>
  <style type="text/css">
    html,body{ margin: 0px; }
  </style>
  <!--
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
  -->

  
  
  <script type="text/javascript" src="https://code.jquery.com/jquery-2.2.1.min.js"></script>
  <script type="text/javascript" src="https://openlayers.org/api/2.13.1/OpenLayers.js"></script>


  <script type="text/javascript">
    // グーローバル変数の定義 
    var od;
    var des_lonlat;
    var arr_lonlat;
  </script>

  <script>
    function MapInit(){
 
      map = new OpenLayers.Map("MapCanvas");

      var mapnik = new OpenLayers.Layer.OSM();
      map.addLayer(mapnik);
    
      //var lonLat = new OpenLayers.LonLat(139.47552, 35.59857)
      var lonLat = new OpenLayers.LonLat(139.796182, 35.654285)
        .transform(
          new OpenLayers.Projection("EPSG:4326"), 
          new OpenLayers.Projection("EPSG:900913")
        );
      map.setCenter(lonLat, 17); 

      OpenLayers.Control.Click = OpenLayers.Class(OpenLayers.Control, {
        initialize: function(options) {
          this.handler = new OpenLayers.Handler.Click(
            this, {
              'click': this.onClick
            }, this.handlerOptions
          );
        }, 

        onClick: function(e) {
          var lonlat = map.getLonLatFromPixel(e.xy);
          lonlat.transform(
            new OpenLayers.Projection("EPSG:900913"), 
            new OpenLayers.Projection("EPSG:4326")
          );

          var markers = new OpenLayers.Layer.Markers("Markers");
          map.addLayer(markers);
          var marker = new OpenLayers.Marker(
            new OpenLayers.LonLat(lonlat.lon, lonlat.lat)
            .transform(
              new OpenLayers.Projection("EPSG:4326"), 
              new OpenLayers.Projection("EPSG:900913")
            )
          );
          markers.addMarker(marker);
          $("#LonLat").html("lon:" +lonlat.lon+ "  lat:" +lonlat.lat);

          if (od == "arrival"){
            arr_lonlat = lonlat;
            alert("arr_lonlatが設定されました" +  arr_lonlat.lon +" " + arr_lonlat.lat);
          } else if (od == "destination"){
            des_lonlat = lonlat;
            alert("des_lonlatが設定されました" +  des_lonlat.lon +" " + des_lonlat.lat);
          }
        }
      });

      var click = new OpenLayers.Control.Click();
      map.addControl(click);
      click.activate();
    }
  </script>

<script type="text/javascript">
    $(document).ready(function () {
    $("#button01").on('click', function () {
      od = "destination";
      alert(od + "  ボタン1がクリックされました。");
    });
    $("#button02").on('click', function () {
      od = "arrival";
      alert(od + "  ボタン2がクリックされました。");      
    });
    $("#button03").on('click', function () {
      od = "confirmed"
      alert(od + "  ボタン3がクリックされました。");
      // 
    });

  })
</script>


</head>

<body>
  <div id="MapCanvas" style="width:700px;height:700px;"></div>
  <div id="LonLat"></div>
  <input id="button01" type="button" value="Button1"/>
  <input id="button02" type="button" value="Button2" />
  <input id="button03" type="button" value="Button3" />

  <script type="text/javascript">MapInit();</script>



</body>

</html>

2022/03,江端さんの技術メモ

最近の文章は、ほとんど、Visual Studio Code(vscode) でMarkdownを使って書いています。超ラクです。

で、「図面のコピペをMarkdownの文書の中にサクっと入れる」ことができないかな、と、ちょっとググってみたのですが、3秒で見つかりました。

https://marketplace.visualstudio.com/items?itemName=mushan.vscode-paste-image

メモとして、以下に記載しておきます。

■クリップボードから直接マークダウン/asciidoc(または他のファイル)に画像を貼り付けます。

準備: VSCODEの「拡張機能(Ctl+Shift+X)」から、"Paste Image"で検索→インストールを実施

使い方:

(1)画面をクリップボードに取り込む
(2)コマンドパレットを開く。Ctrl+Shift+P (MacではCmd+Shift+P)と入力します。
(3)"Paste Image"と入力するか、Ctrl+Alt+V (MacではCmd+Alt+V)と入力します。
(4)画像は、現在の編集ファイルを含むフォルダに保存されます。相対パスは、現在の編集ファイルに貼り付けられます。

以上

 

 

2022/03,江端さんの技術メモ

Sync.Cond、broadcastを使うには、条件があるようです

の、最後に記載した、

そういえば、ブロードキャストの送信者が1であった場合は問題がなかったけど、今回は、送信者が3になったところから、変な動きをしだしたことから鑑みて、

送信者、受信者は1:Nの関係でないとだめ

なのかもしれないです。

として、別方式を考えているのですが、「Goの複数のgoroutineに対する、一斉ブロードキャスト」の便利さが、どうにも諦めきれなくて、まだ調べています。

そこで、簡易プログラムで以下の検証を行いました。

目的は以下の通り。

・送信者(sender)(ただし1人)や、受信者(receiver)がランダムなタイミングで出現・消滅しても、ちゃんと動くか

を検証してみました。

// go run main3.go
/*
	boardcast sync.bc 実験
	(1) 送信側(sender)をgoroutineにて大丈夫か
	(2) 受信側(sender)のgoroutineを、送信側の前後で、
	適当なタイミングで生成して、消滅させても大丈夫か
*/

package main

import (
	"fmt"
	"log"
	"math/rand"
	"sync"
	"time"
)

type BroadCaster struct {
	cond *sync.Cond
	id   int64
	msg  string
}

func (bc *BroadCaster) Send(msg string) {
	bc.cond.L.Lock()
	defer bc.cond.L.Unlock()
	bc.id++
	bc.msg = msg
	bc.cond.Broadcast()
}

func (bc *BroadCaster) Recv(last int64) (int64, string) {
	bc.cond.L.Lock()
	defer bc.cond.L.Unlock()
	for bc.id == last {
		bc.cond.Wait()
	}
	return bc.id, bc.msg
}

var (
	broadcaster = &BroadCaster{
		cond: sync.NewCond(&sync.Mutex{}),
	}
)

func receiver(i int) {

	delete_count := 5 + rand.Intn(10) // ループ回数は5~14回のどれか

	log.Println("recv:", i, " start")
	defer log.Println("recv:", i, " stop")

	last := int64(0)
	for k := 0; k < delete_count; k++ {
		id, msg := broadcaster.Recv(last)
		last = id
		log.Println("recv:", i, msg)
	}

}

func sender() {
	for i := 0; i < 20; i++ { // 20回程度送ってみる
		time.Sleep(1 * time.Second)
		broadcaster.Send(fmt.Sprintf("hello, world: %d", i))
	}
}

func main() {
	for i := 0; i < 3; i++ {
		go receiver(i)
	}

	/*
		for i := 0; i < 100; i++ {
			time.Sleep(1 * time.Second)
			broadcaster.Send(fmt.Sprintf("hello, world: %d", i))
		}
	*/

	go sender()

	for i := 4; i < 6; i++ {
		go receiver(i)
		time.Sleep(1 * time.Second)
	}

	time.Sleep(100 * time.Second) // 排他処理を書くのが面倒なので、ここでmainを眠らせておく

}

これで得られた結果です。

c:\Users\ebata\goga\1-7\test>go run main3.go
go run main3.go
go run main3.go
2022/03/18 12:39:02 recv: 0  start
2022/03/18 12:39:02 recv: 2  start
2022/03/18 12:39:02 recv: 4  start
2022/03/18 12:39:02 recv: 1  start
2022/03/18 12:39:03 recv: 1 hello, world: 0
2022/03/18 12:39:03 recv: 4 hello, world: 0
2022/03/18 12:39:03 recv: 2 hello, world: 0
2022/03/18 12:39:03 recv: 0 hello, world: 0
2022/03/18 12:39:03 recv: 5  start
2022/03/18 12:39:03 recv: 5 hello, world: 0
2022/03/18 12:39:04 recv: 4 hello, world: 1
2022/03/18 12:39:04 recv: 5 hello, world: 1
2022/03/18 12:39:04 recv: 2 hello, world: 1
2022/03/18 12:39:04 recv: 0 hello, world: 1
2022/03/18 12:39:04 recv: 1 hello, world: 1
2022/03/18 12:39:05 recv: 5 hello, world: 2
2022/03/18 12:39:05 recv: 1 hello, world: 2
2022/03/18 12:39:05 recv: 4 hello, world: 2
2022/03/18 12:39:05 recv: 2 hello, world: 2
2022/03/18 12:39:05 recv: 0 hello, world: 2
2022/03/18 12:39:06 recv: 1 hello, world: 3
2022/03/18 12:39:06 recv: 5 hello, world: 3
2022/03/18 12:39:06 recv: 4 hello, world: 3
2022/03/18 12:39:06 recv: 2 hello, world: 3
2022/03/18 12:39:06 recv: 0 hello, world: 3
2022/03/18 12:39:07 recv: 0 hello, world: 4
2022/03/18 12:39:07 recv: 4 hello, world: 4
2022/03/18 12:39:07 recv: 2 hello, world: 4
2022/03/18 12:39:07 recv: 5 hello, world: 4
2022/03/18 12:39:07 recv: 1 hello, world: 4
2022/03/18 12:39:08 recv: 1 hello, world: 5
2022/03/18 12:39:08 recv: 4 hello, world: 5
2022/03/18 12:39:08 recv: 0 hello, world: 5
2022/03/18 12:39:08 recv: 5 hello, world: 5
2022/03/18 12:39:08 recv: 0  stop
2022/03/18 12:39:08 recv: 5  stop
2022/03/18 12:39:08 recv: 2 hello, world: 5
2022/03/18 12:39:09 recv: 2 hello, world: 6
2022/03/18 12:39:09 recv: 1 hello, world: 6
2022/03/18 12:39:09 recv: 4 hello, world: 6
2022/03/18 12:39:10 recv: 4 hello, world: 7
2022/03/18 12:39:10 recv: 2 hello, world: 7
2022/03/18 12:39:10 recv: 1 hello, world: 7
2022/03/18 12:39:11 recv: 1 hello, world: 8
2022/03/18 12:39:11 recv: 4 hello, world: 8
2022/03/18 12:39:11 recv: 2 hello, world: 8
2022/03/18 12:39:12 recv: 2 hello, world: 9
2022/03/18 12:39:12 recv: 4 hello, world: 9
2022/03/18 12:39:12 recv: 1 hello, world: 9
2022/03/18 12:39:13 recv: 1 hello, world: 10
2022/03/18 12:39:13 recv: 2 hello, world: 10
2022/03/18 12:39:13 recv: 4 hello, world: 10
2022/03/18 12:39:14 recv: 4 hello, world: 11
2022/03/18 12:39:14 recv: 1 hello, world: 11
2022/03/18 12:39:14 recv: 1  stop
2022/03/18 12:39:14 recv: 2 hello, world: 11
2022/03/18 12:39:14 recv: 4  stop
2022/03/18 12:39:15 recv: 2 hello, world: 12
2022/03/18 12:39:16 recv: 2 hello, world: 13
2022/03/18 12:39:16 recv: 2  stop

結論としては、

・recvierは、データを重複することなく、またロストすることなく、1つづつキレイに受けとっていた

ということになります。


さて、ここで、今度は、送信者を2人にしてみます。送信者の名前が見易いように、"111"と"999"の番号を付けてます(冗長ですが、プログラムリスト全部を掲載します(私の為に))

// go run main3.go
/*
	boardcast sync.bc 実験
	(1) 送信側(sender)のgoroutineを2つにしたらどうなるか
*/

package main

import (
	"fmt"
	"log"
	"math/rand"
	"sync"
	"time"
)

type BroadCaster struct {
	cond *sync.Cond
	id   int64
	msg  string
}

func (bc *BroadCaster) Send(msg string) {
	bc.cond.L.Lock()
	defer bc.cond.L.Unlock()
	bc.id++
	bc.msg = msg
	bc.cond.Broadcast()
}

func (bc *BroadCaster) Recv(last int64) (int64, string) {
	bc.cond.L.Lock()
	defer bc.cond.L.Unlock()
	for bc.id == last {
		bc.cond.Wait()
	}
	return bc.id, bc.msg
}

var (
	broadcaster = &BroadCaster{
		cond: sync.NewCond(&sync.Mutex{}),
	}
)

func receiver(i int) {

	delete_count := 5 + rand.Intn(10) // ループ回数は5~14回のどれか

	log.Println("recv:", i, " start")
	defer log.Println("recv:", i, " stop")

	last := int64(0)
	for k := 0; k < delete_count; k++ {
		id, msg := broadcaster.Recv(last)
		last = id
		log.Println("recv:", i, msg)
	}

}

func sender(i int) {
	for k := 0; k < 20; k++ { // 20回程度送ってみる
		time.Sleep(1 * time.Second)
		broadcaster.Send(fmt.Sprintf("hello, world: %d from %d", k, i))
	}
}

func main() {
	for i := 0; i < 3; i++ {
		go receiver(i)
	}

	/*
		for i := 0; i < 100; i++ {
			time.Sleep(1 * time.Second)
			broadcaster.Send(fmt.Sprintf("hello, world: %d", i))
		}
	*/

	go sender(111) // ここに注意
	go sender(999) // ここに注意

	for i := 4; i < 6; i++ {
		go receiver(i)
		time.Sleep(1 * time.Second)
	}

	time.Sleep(100 * time.Second) // 排他処理を書くのが面倒なので、ここでmainを眠らせておく

}

結果は以下の通りになりました。

2022/03/18 12:56:45 recv: 4  start
2022/03/18 12:56:45 recv: 1  start
2022/03/18 12:56:45 recv: 0  start
2022/03/18 12:56:45 recv: 2  start
2022/03/18 12:56:46 recv: 2 hello, world: 0 from 999
2022/03/18 12:56:46 recv: 2 hello, world: 0 from 111
2022/03/18 12:56:46 recv: 5  start
2022/03/18 12:56:46 recv: 5 hello, world: 0 from 111
2022/03/18 12:56:46 recv: 0 hello, world: 0 from 111
2022/03/18 12:56:46 recv: 4 hello, world: 0 from 111
2022/03/18 12:56:46 recv: 1 hello, world: 0 from 111
2022/03/18 12:56:47 recv: 1 hello, world: 1 from 111
2022/03/18 12:56:47 recv: 4 hello, world: 1 from 999
2022/03/18 12:56:47 recv: 1 hello, world: 1 from 999
2022/03/18 12:56:47 recv: 2 hello, world: 1 from 999
2022/03/18 12:56:47 recv: 0 hello, world: 1 from 999
2022/03/18 12:56:47 recv: 5 hello, world: 1 from 111
2022/03/18 12:56:47 recv: 5 hello, world: 1 from 999
2022/03/18 12:56:48 recv: 1 hello, world: 2 from 999
2022/03/18 12:56:48 recv: 5 hello, world: 2 from 999
2022/03/18 12:56:48 recv: 2 hello, world: 2 from 999
2022/03/18 12:56:48 recv: 0 hello, world: 2 from 999
2022/03/18 12:56:48 recv: 4 hello, world: 2 from 999
2022/03/18 12:56:49 recv: 4 hello, world: 3 from 999
2022/03/18 12:56:49 recv: 1 hello, world: 3 from 999
2022/03/18 12:56:49 recv: 5 hello, world: 3 from 999
2022/03/18 12:56:49 recv: 2 hello, world: 3 from 999
2022/03/18 12:56:49 recv: 0 hello, world: 3 from 999
2022/03/18 12:56:49 recv: 0 hello, world: 3 from 111
2022/03/18 12:56:49 recv: 1 hello, world: 3 from 111
2022/03/18 12:56:49 recv: 4 hello, world: 3 from 111
2022/03/18 12:56:49 recv: 5 hello, world: 3 from 111
2022/03/18 12:56:49 recv: 5  stop
2022/03/18 12:56:49 recv: 2 hello, world: 3 from 111
2022/03/18 12:56:49 recv: 1  stop
2022/03/18 12:56:50 recv: 2 hello, world: 4 from 111
2022/03/18 12:56:50 recv: 2 hello, world: 4 from 999
2022/03/18 12:56:50 recv: 0 hello, world: 4 from 111
2022/03/18 12:56:50 recv: 0 hello, world: 4 from 999
2022/03/18 12:56:50 recv: 4 hello, world: 4 from 999
2022/03/18 12:56:51 recv: 4 hello, world: 5 from 999
2022/03/18 12:56:51 recv: 4 hello, world: 5 from 111
2022/03/18 12:56:51 recv: 2 hello, world: 5 from 111
2022/03/18 12:56:51 recv: 0 hello, world: 5 from 111
2022/03/18 12:56:52 recv: 0 hello, world: 6 from 999
2022/03/18 12:56:52 recv: 0 hello, world: 6 from 111
2022/03/18 12:56:52 recv: 4 hello, world: 6 from 111
2022/03/18 12:56:52 recv: 2 hello, world: 6 from 111
2022/03/18 12:56:53 recv: 2 hello, world: 7 from 111
2022/03/18 12:56:53 recv: 2 hello, world: 7 from 999
2022/03/18 12:56:53 recv: 0 hello, world: 7 from 999
2022/03/18 12:56:53 recv: 4 hello, world: 7 from 999
2022/03/18 12:56:54 recv: 4 hello, world: 8 from 111
2022/03/18 12:56:54 recv: 4 hello, world: 8 from 999
2022/03/18 12:56:54 recv: 4  stop
2022/03/18 12:56:54 recv: 2 hello, world: 8 from 111
2022/03/18 12:56:54 recv: 2 hello, world: 8 from 999
2022/03/18 12:56:54 recv: 2  stop
2022/03/18 12:56:54 recv: 0 hello, world: 8 from 111
2022/03/18 12:56:54 recv: 0  stop

ちょっと見難いので、"recv: 2"を、それぞれ異なる送信者ごとに整理してみます。

c:\Users\ebata\goga\1-7\test>grep "recv: 2" dummy.txt | grep 999
grep "recv: 2" dummy.txt | grep 111
2022/03/18 12:56:46 recv: 2 hello, world: 0 from 111
2022/03/18 12:56:49 recv: 2 hello, world: 3 from 111
2022/03/18 12:56:50 recv: 2 hello, world: 4 from 111
2022/03/18 12:56:51 recv: 2 hello, world: 5 from 111
2022/03/18 12:56:52 recv: 2 hello, world: 6 from 111
2022/03/18 12:56:53 recv: 2 hello, world: 7 from 111
2022/03/18 12:56:54 recv: 2 hello, world: 8 from 111

2番がロストしています。

c:\Users\ebata\goga\1-7\test>grep "recv: 2" dummy.txt | grep 999
grep "recv: 2" dummy.txt | grep 999
2022/03/18 12:56:46 recv: 2 hello, world: 0 from 999
2022/03/18 12:56:47 recv: 2 hello, world: 1 from 999
2022/03/18 12:56:48 recv: 2 hello, world: 2 from 999
2022/03/18 12:56:49 recv: 2 hello, world: 3 from 999
2022/03/18 12:56:50 recv: 2 hello, world: 4 from 999
2022/03/18 12:56:53 recv: 2 hello, world: 7 from 999
2022/03/18 12:56:54 recv: 2 hello, world: 8 from 999

5、6番がロストしています。


送信者を1人にすれば、問題が発生する可能性はなくなる」という仮説は成り立ちそうです。

考えてみれば、このプログラムでは、2つの送信者を区別する方法を入れていないのですから、当然かもしれません。

という訳で、送信者を1人にする方法の実装で、もうちょっとがんばってみたいと思います。

ただ、この方式では、送受信の際にロックをかけているので、受信者の数が膨大になれば、プログラム全体のパフォーマンスが劣化する可能性があります。

リアルタイム系のプログラムには、使わない方が良いかもしれません。

ただ、プログラムの動作状況を簡単に見たいようなケースでは、とても便利なので、当面は手放せないと思います。

以上

 

 

 

 

 

2022/03,江端さんの技術メモ

Go の channel 処理パターン集

func main() {
	ii := 10

	queue := make(chan int, 100)
	defer close(queue)

	for i := 0; i < 5; i++ {
		go worker(i, queue)
	}

	for {
		//k := rand.Intn(1000)

		for i := 0; i < 200; i++ {
			queue <- ii
		}

		time.Sleep(1 * time.Second) // 2秒待つ

		ii++
		go worker(ii, queue)

	}

}

func worker(i int, queue chan int) {
	for j := range queue {
		fmt.Println(i, ",", j)
	}
}

2022/03,江端さんの技術メモ

Goの複数のgoroutineに対して、一斉ブロードキャストを行いたい

を教えて貰って、喜んでいたのですが、私のようなカルトな使い方をしているケースでは、正しくデータ送信ができない場合があるようです。

動的に登場して、自動的に消滅するような複数の(かなりの数の)goroutineに対しては、受信データの値が変な値になるようです。

下記は、データ送信元のオブジェクトが、自分の位置情報を撒き散らしながらbroadcast送信をしています。

下記は、この途中からgoroutineをバラバラに30個くらい作って受信したものの一つです。

なんども調べてみたのですが、やはりバグが原因ではないようです。

本日、バグを発見しました! 構造体に送信元のオブジェクトを指定する変数が入っていなかった為、全部同じブロードキャストとして受信先が受信をしてしまっていました(03/18)。

ここから得られる結論は、Sync.Cond、broadcastを使うには、

・broadcast、sync.cond は、最初からgoroutineができあがっている場合

・少量、低速にデータ配信を行う場合

でないと、安定的に動作させるには厳しいようです。

これは、まあ、間違ってはいませんです。

====  追記 =====

そういえば、ブロードキャストの送信者が1であった場合は問題がなかったけど、今回は、送信者が3になったところから、変な動きをしだしたことから鑑みて、

送信者、受信者は1:Nの関係でないとだめ

なのかもしれないです。

これは確定です。詳しくは以下をご覧下さい↓

Goの複数のgoroutineに対する一斉ブロードキャストを、まだ諦めきれない

以上

2022/03,江端さんの技術メモ

1. サーバの方の改造
(a) POPUP int `json:"popup"` の追加

// GetLoc GetLoc
type GetLoc struct {
	ID    int     `json:"id"`
	Lat   float64 `json:"lat"`
	Lng   float64 `json:"lng"`
	TYPE  string  `json:"type"` // "PERSON","BUS","CONTROL
	POPUP int     `json:"popup"`
	//Address string  `json:"address"`
}

(b) javascriptに、this.popup = popup;を追加

function obj(id, lat, lng, type, popup){
	this.id = id;
	this.lat = lat;
	this.lng = lng;
	this.type = type;
	this.popup = popup;
}

(c) javascriptに、

var i = obj.popup;

popup: "Person " + i,

を追加

var i = obj.popup;

		if (obj.id == 0) {
			if (obj.type == "PERSON"){
				var marker = new PruneCluster.Marker(obj.lat, obj.lng, {
					popup: "Person " + i,
					icon: L.icon({
						iconUrl: 'http://localhost:8080/static/person-icon.png', 
					})
				});
			}
			else if (obj.type == "BUS"){
				var marker = new PruneCluster.Marker(obj.lat, obj.lng, {
					popup: "Bus " + i,
					icon: L.icon({
						iconUrl: 'http://localhost:8080/static/bus-icon.png', 
					})
				});

			}

2. クライアントの方の改造

(1)以下はサーバと同じ
// GetLoc GetLoc
type GetLoc struct {
	ID    int     `json:"id"`
	Lat   float64 `json:"lat"`
	Lng   float64 `json:"lng"`
	TYPE  string  `json:"type"` // "USER","BUS","CONTROL
	POPUP int     `json:"popup"`
	//Address string  `json:"address"`
}
gl := ldarp.GetLoc{
		ID: 0, // "0"としなければならない
		//	Lat:  35.64553503, // 初期の場所
		//	Lng:  139.78468208,
		TYPE: "PERSON", // "PERSON","BUS","CENTER"のいずれかが選べる
	}

	// glの要素を外出し
	gl.Lat = person.Destination.Lat
	gl.Lng = person.Destination.Lon
	gl.POPUP = person_num

keyword ポップアップ PruneMobile PruneCluster  Golang JavaScript

2022/03,江端さんの技術メモ

参考文献  【レッツカスタマイズ】Visual Code StudioにGo言語の開発環境を整えてみよう!

1. Goの開発環境をインストール

2. setting.jsonにgopathを書き込む

Windowsの場合は、"C:\go"は、"C:\\go"と書かないと動かない(みたい)

3. (Ctr+Shift+P)でショートカットで検索バーを表示し、「GO: Install/Update tools」を入力してヒットしたメニューをクリック

全部選んで、インストールする

4. VSCodeを離れて、コマンドプロンプトから、以下のコマンドを実行する

$ go get -u -v github.com/go-delve/delve/cmd/dlv

5. launch.jsonに以下のように書き込む

{
    // IntelliSense を使用して利用可能な属性を学べます。
    // 既存の属性の説明をホバーして表示します。
    // 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Launch",
            "type": "go",
            "request": "launch",
            "mode": "auto",
            "program": "${fileDirname}",
            "env": {},
            "args": []
        }
    ]
}

これで、

GO_1_could not launch process: not an executable file_CC课堂-CSDN博客

加えて、(バカみたいな感じもするが)ライブラリのコードでブレークする場合であっても、main.goのウインドウの方をアクティブにしておかなければならないみたい

これで、エラーは出てこなくなる(みたい)。

 

2022/03,江端さんの技術メモ

参考文献

時々、vscodeの日本語化が外れるので、直し方をメモっておきます。

日本語化のための拡張機能をインストールする(2)

displayと入力して

これで、"jp"を選べば、日本語化が戻ります。