研究室の学生さんたちに負荷テストに協力してもらっています。
今、"cannot parse invalid wire-format data" のエラーでGTFS_HUBのダウンを確認しました。
PrumeMobileの方は、そのままにして、GTFS_HUBの再起動をかけました。
(以下、後日対応)
江端智一のホームページ
研究室の学生さんたちに負荷テストに協力してもらっています。
今、"cannot parse invalid wire-format data" のエラーでGTFS_HUBのダウンを確認しました。
PrumeMobileの方は、そのままにして、GTFS_HUBの再起動をかけました。
(以下、後日対応)
C:\Users\ebata\goga\1-10>のI_hate_go_server.md が本体です。
このドキュメントは、絶対的な意味において「無保証」です
Golangで作るサーバは、HandleやらHandlerやら、ハンドル、ハンドルとうるさい! と叫びたくなること、甚しいです。
さすがに、C言語のfork()まで戻りたいとは思えませんが、『あれは、あれで、何をやっているのか分かった』とは言えました。
で、もう正しい理解かどうかは、無視して、もう、誰の話も聞かん! 江端はこういう風に理解すると決めた!! ことを記載しておきます。
http.Handle("/", http.FileServer(http.Dir(".")))
は、https://xxx.xxx/ でアクセスできて(http://xxx.xxx/yyyy のように"yyyy"はない)、index.htmlが、goのサーバのプログラムと同じディレクトリ(".")にいる、と宣言するもの。
http.Handle("/tomo", http.FileServer(http.Dir("./js")))
であれば、https://xxx.xxx/tomo でアクセスできてindex.htmlが、goのサーバのプログラムと同じディレクトリのしたのjs("./js")にいる、と宣言するもの。
具体的には、こちらを読んで頂くと良いと思います。
要するにwebブラウザ(クライアント)からのアクセスがあれば、この関数がfork()の用に立ち上って、Webブラウザとの面倒を見る。→大嘘でした
面倒見ません。
まず第一に、http.HandleFunc()の誤解がありました。私は、これを、fork()のようにプロセスかスレッドを発生さるものと思っていましたが、これは、一言で言えば、単なるコールバック関数でした。
乱暴に言えば、Webからアクセスがあると、func()というファンクションに吹っ飛ばされる、という現象を発生させる"だけ"で、それ意外のことは何にもしてくれないのです。
これは、index.htmlの内容をクライアントの押しつけるfork()関数と考えれば足る。→大嘘でした
(後で述べるが)これで、
http://localhost:8080
でアクセスできるようになる
http.ServerFileというのは、実装されているので、わざわざ main.goに書く必要はない。
一方、
http.HandleFunc("/chat", HandleClients)
は、
func HandleClients(w http.ResponseWriter, r *http.Request) {
//色々
}
で定義されている、コードをfork()のように立ち上げるものである、と考えれば足る。→大嘘でした(現在修正検討中)
単にその関数に飛んでいくだけです(但し、ソケット情報を付けてくれます)
"/chat"とは何か?
(後で述べるが)これで、
http://localhost:8080/chat
でアクセスできるようになる
上記の関数は、"/"やら、"/chat"やらの(相対的)なパスを指定しているが、これは、サーバのアクセスするアドレスとポートを決定するものである。
err := http.ListenAndServe(":8080", nil)
if err != nil {
log.Fatal("error starting http server::", err)
return
}
で、これを宣言することで、サーバとして使えるようになる。
ちなみに、(":8080", nil)の"nil"は、上記のhttp.ServerFile()と、http.HandleFunc()を使うぜ、の意味になる(直接編集することもできるらしい)。
これは"github.com/gorilla/websocket"が提供してくれるもので、HTTP通信(一方通行)からWebSocket通信(相互通行)に更新してくれる便利なものらしい。
websocket, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Fatal("error upgrading GET request to a websocket::", err)
}
こうしてしまえば、websocket.ReadJSON()やら、websocket.WriteHSON()やらが、バカスカ使えるようになる。
http.Handle("/static/", http.StripPrefix("/static", http.FileServer(http.Dir("static"))))
まあ、"/static/" は、通信コネクションでいいして、http.StripPrefix("/static", http.FileServer(http.Dir("static")))については、「/static」をhttp.FileServer()が捜索するURLから除く という意味です。
http.HandleFunc()と、http.ListenAndServe()の』2つだけ覚えておけば、いいんじゃない?、と思う。
一般的にクライアントはWebブラウザなんだけど、これをgolangのプログラムからwebsocketでアクセスしようとする場合は、こんな感じになる。
var addr = flag.String("addr", "0.0.0.0:8080", "http service address")
func bus(bus_num int) {
var bus BUS
///////////// 描画処理ここから ////////////////
_ = websocket.Upgrader{} // use default options
flag.Parse()
log.SetFlags(0)
u := url.URL{Scheme: "ws", Host: *addr, Path: "/echo2"}
log.Printf("connecting to %s", u.String())
c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
if err != nil {
log.Fatal("dial:", err)
}
defer c.Close()
まず、グローバルで、以下のようにサーバを場所を書いておく。
var addr = flag.String("addr", "0.0.0.0:8080", "http service address")
(よく分からないんだけど)以下のような書き方でwebsocket(のインスタンス?)が作れるらしい。
_ = websocket.Upgrader{} // use default options
以下で、/echo2を使うぜ、の宣言
u := url.URL{Scheme: "ws", Host: *addr, Path: "/echo2"}
で、以下で、websocket用のソケットができるらしい。
c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
この後はc.ReadJSON()やら、c.WriteJSON()やらを使い倒す、ことができるようになります。
var addr = flag.String("addr", "0.0.0.0:8080", "http service address")
を"固定するもの"でいいのかな? → 間違っています。→ golang でコマンドライン引数を使う
import "log"
で、logを使う場合に、logの設定をリセットするもの、で良さそうです。
以上
内容間違っていたら、優しくご指摘下さい。
■PruneMobile側(sever.go)
■gtfs_hub側(gtfs_hub.go)
■対策
PruneMobile側(sever.go)の方が先に落ちる(確認) → で書き込みできなくなってgtfs_hub側(gtfs_hub.go)も落ちる、と、多分、そんな感じ。
"repeated read on failed websocket connection"を普通に読めば、「間違ったWebsocket接続で、*何度*も読み込みを繰り返している」なので、read errorが発生したら、即コネクションクローズして、クライアントも落してしまえばいいのかな、と考えました。→ ダメでした。サーバが落ちて処理が止まります。
で、以前、
で、
てなことを書いていたので、read errorqを検知したら、forループから直ちにbreakして、クライアントをクラッシュさせてしまうこと(ができるものと)しました。→ ダメでした。サーバが落ちて処理が止まります。
for (){
(色々)
err = webConn.ReadJSON(&locMsg2) // ここでPanic serving ... repeated read on failed websocket connectionが発生している可能性あり
fmt.Printf("c6: ")
if err != nil{
fmt.Printf("Before webConn.close(), After c6:")
webConn.Close()
break → ダメ
}
}
javascript (onload.js) の方にも、終了理由がでるように、以下のコードを追加しました。
// サーバを止めると、ここに飛んでくる
socket.onclose = function (event) {
console.log("socket.onclose");
let obj = JSON.parse(event.data);
console.log("socket.onclose: obj.id:", obj.id);
console.log("socket.onclose: obj.lat:", obj.lat);
console.log("socket.onclose: obj.lng:", obj.lng);
console.log("socket.onclose: obj.type:", obj.type);
console.log("socket.onclose: obj.popup:", obj.popup);
socket = null;
// 切断が完全に完了したかどうか
if(event.wasClean){
var closed = "完了";
} else {
var closed = "未完了";
}
info.innerHTML += "切断処理:" + closed + "<br>";
info.innerHTML += "コード:" + event.code + "<br>";
info.innerHTML += "理由:" + event.reason + "<br>";
}
window.onunload = function(event){
// 切断
ws.close(4500,"切断理由");
}
上記の「ダメ」の対策中
https://ja.stackoverflow.com/questions/12389/golang%E3%81%A7%E3%83%9A%E3%83%BC%E3%82%B8%E3%82%92%E5%86%8D%E8%AA%AD%E3%81%BF%E8%BE%BC%E3%81%BF%E3%81%99%E3%82%8B%E3%81%A8websocket-server%E3%81%8C%E8%90%BD%E3%81%A1%E3%82%8B
■問題解決編
色々問題があったのですが、順番に説明していきます。
var addr = flag.String("addr", "192.168.0.8:8080", "http service address") // テスト
....
.....
http.Handle("/", http.FileServer(http.Dir(".")))
http.HandleFunc("/echo", echo) // echo関数を登録 (サーバとして必要)
log.Fatal(http.ListenAndServeTLS(*addr, "./cert.pem", "./key.pem", nil)) // localhost:8080で起動をセット
}
まず第一に、http.HandleFunc()の誤解がありました。私は、これを、fork()のようにプロセスかスレッドを発生さるものと思っていましたが、これは、一言で言えば、単なるコールバック関数でした。
乱暴に言えば、Webからアクセスがあると、echo()というファンクションに吹っ飛ばされる、という現象を発生させる"だけ"で、それ意外のことは何にもしてくれないのです。
では、echo()の方はどうなっているかというと、
func echo(w http.ResponseWriter, r *http.Request) { // JavaScriptとの通信
webConn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Println("websocket connection err:", err)
return
}
defer webConn.Close()
とすることで、webSocket通信の準備ができて、その通信路が"webConn"にできる、ということです。
で、それ意外のことは何もしてくれないのです。
つまり、2つのWebブラウザからアクセスがくると、その度、echo()
に飛んできて、その度に、異なるwebConnを生成する、ということです。
ここまでの説明で分かると思いますが、つまり、Webブラウザがくる度に、それをどっかに格納しておかないと、通信路の情報が上書きされてしまいます。
なので、基本的には、以下のwebConnを、配列に格納しておきます。
var clients = make(map[*websocket.Conn]bool) // 接続されるクライアント
// クライアントを新しく登録(だけ)
m1Mutex.Lock() // 同時書き込みを回避するため、ミューテックスロックで囲っておく
clients[webConn] = true // これで、Webからのアクセスがある度に、通信路情報が動的に追加される
m1Mutex.Unlock()
というように、Webブラウザとの通信路を別々に管理しておきます。
もし、fork()みたいなことがしたいのであれば、goroutineを起動して、そのgoroutineにWebブラウザの通信路を明け渡す必要があります。それでも、通信路の全体管理は、echo()が握っているので、webConnを消滅されたい場合は、echo()の方でやって貰う必要があります。
この方法を実現する方法として、GO言語のサンプルプログラムで良く登場するのが、以下のような方法です。
// 江端のPCでは、c:\users\ebata\test20230412\main.go
package main
(中略)
func handleConnections(w http.ResponseWriter, r *http.Request) {
// 送られてきたGETリクエストをwebsocketにアップグレード
ws, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Fatal(err)
}
// 関数が終わった際に必ずwebsocketnのコネクションを閉じる
defer ws.Close()
// クライアントを新しく登録
clients[ws] = true
for {
var msg Message
// 新しいメッセージをJSONとして読み込みMessageオブジェクトにマッピングする
err := ws.ReadJSON(&msg)
if err != nil {
log.Printf("error: %v", err)
delete(clients, ws)
break
}
// 新しく受信されたメッセージをブロードキャストチャネルに送る
broadcast <- msg
}
}
func handleMessages() {
for {
// ブロードキャストチャネルから次のメッセージを受け取る
msg := <-broadcast
// 現在接続しているクライアント全てにメッセージを送信する
for client := range clients {
err := client.WriteJSON(msg)
if err != nil {
log.Printf("error: %v", err)
client.Close()
delete(clients, client)
}
}
}
}
上記の例では、 http.HandleFunc("/ws", handleConnections) で、handleConnectionsをコールバック関数にしておき、こちらにWebブラウザからのアクセスを全部任せます
でもって、ついでにWebからやってくる通信の全てを受けつけています(ReadJSON())。さらに、webブラウザが突然閉じられた場合などは、通信エラーとして検知して、それをクローズした後に、webConnの配列から取り除いています。これで、このブラウザからの通信路は閉じらて、消されます。
で、この通信の内容を、チャネルを使って、go handleMessager
で作った、fork()みたいなgoroutineに流し込んでいます。
結論として、fork()みたなことがやりたければ、「自力で作れ」ということになります。
しかし、WebSocket単位で別々のgoroutine作るのも面倒くさいです。それに、もしそのようなgoroutineを作ったとすれば、ブロードキャストを実現しなければなりませんが、チャネルには、ブロードキャスト機能がありません(どうしても使わなければならない時は、私はredisを使っています)。
ですので、私、チャネルの配列を作って疑似的なブロードキャストを実現しようとしたのですが、このサンプルプログラムが見るからなくて、困っていました。
『echo()関数の中で、全てのWebコネクションを相手にできないかな』と考え始めました。
基本形はこんな形
for {
message <- channel // 外部から送られてきたデータ
for client := range clients{ //clientsはWebConnのリスト
client.WriteJSON(message)
client.ReadJSON(message2}
}
}
問題は、Webブラウザは終了処理などなどをせずに、閉じられてしまうことにあります(私たちが普通にやっていることです)。
とすれば、echo()の中でコネクションの切断を検知して、それをWebConnのリスト
から取り除く必要があります。
で、こんなことやってみたんですよ。
for {
message <- channel // 外部から送られてきたデータ
for client := range clients{ //clientsはWebConnのリスト
err = client.WriteJSON(message)
if err != nil{
client.Close()
delete(client, clients)
err = client.ReadJSON(message2}
if err != nil{
client.Close()
delete(client, clients)
}
}
これでは、for ルーチンの中で、回している変数を減らすという処理が不味かったようで、この場合、 echo()が終了してしまうようでした。当然、repeated.....も含めて、エラーの嵐になりました。
なので、こんな風に変更しました。
var delete_client *websocket.Comm
for {
delete_client = nil
message <- channel // 外部から送られてきたデータ
for client := range clients{ //clientsはWebConnのリスト
err = client.WriteJSON(message)
if err != nil{
delete_client = client
}
err = client.ReadJSON(message2}
if err != nil{
delete_client = client
}
}
if delete_client != nil{
delete_client.Close()
delete(clients, delete_client)
}
つまり、ループの外でコネクション処理を行う、ということです。
ただ、この方法では、ループを回っている途中に2つ以上のWebブラウザが落された場合にどうなるか、という問題が残ります。
この場合、「次のループでエラーを検知するのを待つ」という処理で対応する、と腹を括りました。
なぜなら、repeat..... のエラーは、積り積って発生することを経験的に知っていましたので、それまで通信障害したまま走って貰えばいい、と割り来って考えることにしました。
結論としては、
(1)http.HandleFunc()は、Webからの接続要求時に、飛される先の関数を記述するもの(コールバック関数)であり、
(2)そのコールバック関数の中だけで、入出力処理がしたいのであれば、ループを壊さないような工夫をして、上手く運用すれば、上手くいく(ことがある)
という結論になりそうです。
以下の記述は間違っているようですので、参照にしないで下さい(現在検証中)。
(昨日、ソフト外注会社の方に教えて貰いました)
Golangで、http.HandleFunc と http.Handleについて、ずっと混乱しつづけています。
というか、私は、使い方が分かればよくて、その理屈なんぞ1mmも興味がないので、コードを書きながら理解しています(結局、遅くなっているような気がしますが)。
1. http.Handle()は、index.htmlをベースとしたサーバを立てるもの
// main16.go 現在の居場所は、c:\Users\ebata\hirohakama\199A2\others
/*
.
├── main16.go
├── index.html (A)
└── chart # chartフォルダに静的ファイルがある
└── index.html (B)
*/
package main
import (
"net/http"
)
func main() {
// 静的ファイル配信.
// ディレクトリ名をURLパスに使う場合
// 例:http://localhost:8080/chart/で index.html (B) の方を表示
http.Handle("/chart/", http.FileServer(http.Dir("./")))
// 例:http://localhost:8080/で index.html (A) の方を表示
http.Handle("/", http.FileServer(http.Dir("./")))
// ディレクトリ名とURLパスを変える場合
// 例:http://localhost:8080/mysecret/sample1.txt
// http.Handle("/mysecret/", http.StripPrefix("/mysecret/", http.FileServer(http.Dir("./contents"))))
// 例:http://localhost:8080/で index.html (A) の方を表示
http.Handle("/", http.FileServer(http.Dir("./")))
// 8080ポートで起動
http.ListenAndServe(":8080", nil)
}
これで、main16.goが置いている場所が、基準点となります(それだけです)。
で、色々考えずに、基本は、
http.Handle("/", http.FileServer(http.Dir("./")))
としておきましょう(というか、これがデフォルトなら、記載すらしなくてもいい)
2. http.HandleFunc()は、ソースコードで書いたものをサーバとするもの
// main15.go 現在の場所はc:\Users\ebata\hirohakama\199A2\others
/*
.
└── main15.go
*/
package main
import (
"io"
"log"
"net/http"
)
func h1(w http.ResponseWriter, _ *http.Request) {
io.WriteString(w, "Hello from a HandleFunc #1!\n")
}
func h2(w http.ResponseWriter, _ *http.Request) {
io.WriteString(w, "Hello from a HandleFunc #2!\n")
}
func main() {
// http://localhost:8080/ で h1の内容を表示 (プログラムの内容を)
http.HandleFunc("/", h1)
// http://localhost:8080/endpoint で h2の内容を表示
http.HandleFunc("/endpoint", h2)
log.Fatal(http.ListenAndServe(":8080", nil))
}
3. http.Handle()1つとhttp.handleFunc()1つが混在しているものは、それぞれサーバが2つある、ということ
// main13.go
package main
import (
"fmt"
"log"
"math/rand"
"net/http"
"time"
"github.com/gorilla/websocket"
)
var upgrader = websocket.Upgrader{}
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"`
}
func echo3(w http.ResponseWriter, r *http.Request) {
upgrader.CheckOrigin = func(r *http.Request) bool { return true } // おまじない
conn2, err := upgrader.Upgrade(w, r, nil) //conn2でwebsocketを作成
if err != nil {
log.Println("websocket connection err:", err)
return
}
defer conn2.Close()
for {
gl2 := new(GetLoc)
gl2.ID = rand.Intn(20) // ここで乱数を発生されて、javascriptで受信させる
gl2.Lat = 181.0
gl2.Lng = 181.0
gl2.TYPE = "BUS"
gl2.POPUP = 101
err := conn2.WriteJSON(&gl2)
if err != nil {
log.Println("ReadJSON:", err)
break
}
fmt.Println("echo3:", gl2)
time.Sleep(time.Second * 1)
}
}
//var addr = flag.String("addr", "0.0.0.0:5000", "http service address") // テスト
func main() {
http.Handle("/chart/", http.FileServer(http.Dir("./")))
http.HandleFunc("/echo3", echo3)
//log.Println("server starting...", "http://localhost:8080")
//log.Fatal(http.ListenAndServe("localhost:8080", nil))
log.Fatal(http.ListenAndServe(":8080", nil))
}
index.html
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>test</title>
</head>
<script type="text/javascript" src="moment.js"></script>
<script type="text/javascript" src="Chart.js"></script>
<script type="text/javascript" src="chartjs-plugin-streaming.js"></script>
<script>
var ws;
// websocketのオープン(この段階で接続完了)
ws = new WebSocket('ws://localhost:8080/echo3') // ユーザ登録画面
ws.onopen = function (event) {
}
ws.onmessage = function (event) {
// 送られてきたデータを受信して、JSON形式に変更
var obj = JSON.parse(event.data);
console.log("obj:",obj);
console.log("obj.id:",obj.id);
aa = obj.id;
}
</script>
<body BGCOLOR="black" text="white" STYLE="overflow: hidden;">
<center>
<font size="5">Waking Time(min.) <br></font> <!--- 意味のない表示 -->
<font size="5"> 歩行時間(分)</font> <!--- 意味のない表示 -->
</center>
<canvas id="myChart" width="100" height="85"></canvas>
<script>
var ctx = document.getElementById('myChart').getContext('2d');
var chart = new Chart(ctx, {
type: 'line',
data: {
datasets: [{
data: [], // 1つめ
borderColor: "rgba(255,0,0,1)",
backgroundColor: "rgba(0,0,0,0)",
lineTension: 0,
label: 'Time',
}]
},
options: {
scales: {
xAxes: [{
type: 'realtime',
realtime: {
duration: 30000, // 300000ミリ秒(5分)のデータを表示 (コメントアウトすると早く動く)
onRefresh: function(chart) {
chart.data.datasets.forEach(function(dataset) {
dataset.data.push({
x: Date.now(),
//y: (Math.floor(Math.random()*16)+30) //30-45の乱数(整数)
y: aa, // この"aa"が、送られてきたデータ
});
});
}
}
}],
yAxes: [{
ticks: {
max: 20,
min: 0
}
}]
}
}
});
</script>
</body>
</html>
この場合、
の2つがある、ということ。
実際のところ、echo3は、/chart/index.html のクライアント(データの送信元)でもあるんだけど、要求があれば、ポコポコ作り出される、という点ではサーバでもある、という形になっています。
―― という説明を、次に私が頭を抱えた時に、私が思い出せるのかが、不安です
公開回、秘密鍵の対応
log.Fatal(http.ListenAndServeTLS(*addr, "./cert.pem", "./key.pem", nil)) // localhost:8080で起動をセット
if httpErr = http.ListenAndServeTLS(*addr, "./fullchain.pem", "./privkey.pem", nil);
ということで、 cert.pem = fullchain.pem key.pem = privkey.pem で良いのだろう
T研究所のKさん。ありがとうございました。
(4)PruneClusterにおいて、クラスタリングを解除する方法 → leafletView の定義の後に、「leafletView.Cluster.Size = -1」のよう に負の値を設定することによって、実現可能です。 - 参考URL:https://github.com/SINTEF-9012/PruneCluster/issues/52
バスのリアルタイム情報を格納するサーバを作ることになりました(経緯が色々あって)。
情報を提供しているサーバから、JSONを定期的に取りに行けばいいんだろう、とか思っていたのですが、このProtocol Bufferというデータ形式を、私は聞いたことがありません。
実際にデータを取得してセーブしてみたのですが、明らかにバイナリです。
しかも、どのエディタでも自動整形しない。ということは、どうやらテキストではない。
で、色々調べたのですが、どうも要領を得ないのですが、下の動画でやっと分かった気になりました。
乱暴に言えば、Protocol Buffersとは「圧縮されたJSON または XML(のようなもの)」です。
まあ、JSONもXMLもテキストメッセージで、しかも構造を保持したまま送信するので「通信効率悪そうだなぁ」と前々から思っていましたので、こういうものが必要となるのは分かります。
Googleが提供していることもあり、また、リアルタイム系のデータでは必要となるのは分かりますが ―― また、覚えることが増えたなぁ、と少々ウンザリしています。
とりあえず、Go言語で動かすことを目的として、ここまでの経緯を記載しておきます。
私の環境は、Windows10です。GOはインストール済みです
(1)https://protobuf.dev/downloads/ から "Latest Version" → "*-win64.zip"
をダウンロード。私はC:\の直下に展開しました。
でもって、この両方のフォルダーにpathを通しておきました。必要なのは"protoc.exe"です。
(2)私の場合、C:\Users\ebata\protocol_bufferesというディレクトリを作って、そこにソースを展開することにしました。
とりあえず、main.goという名前でファイルを作っておきます(後で必要になります)
package main
import "fmt"
func main() {
fmt.Println("Hello World!!")
}
さらに、person.proto という名前で、
syntax = "proto3";
package main;
message Person{
string name = 1;
int32 age = 2;
}
というファイルを作ります。これがxmlやらJSONのスタイルファイルにようなものです。
で、ここから、person.pb.goというファイルを作らなければならないのですが、これに苦労しました。
C:\Users\ebata\protocol_bufferesの中で、
$ protoc --go_out=. *.protoprotoc-gen-go: unable to determine Go import path for "person.proto"Please specify either:• a "go_package" option in the .proto source file, or• a "M" argument on the command line.See https://developers.google.com/protocol-buffers/docs/reference/go-generated#package for more information.--go_out: protoc-gen-go: Plugin failed with status code 1.
のようなことを繰り返していたのですが、person.proto の中に、一行追加したら、サクっと通りました。
syntax = "proto3";
option go_package="./;main"; // 理由は分からないけど、この1行で、以下のエラーが消えた
//$ protoc --go_out=. *.proto
//protoc-gen-go: unable to determine Go import path for "person.proto"
//Please specify either:
// • a "go_package" option in the .proto source file, or
// • a "M" argument on the command line.
//See https://developers.google.com/protocol-buffers/docs/reference/go-generated#p
//ackage for more information.
//--go_out: protoc-gen-go: Plugin failed with status code 1.
package main;
message Person{
string name = 1;
int32 age = 2;
}
この結果、以下のようなperson.pb.goが生成されました。
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.26.0
// protoc v4.22.2
// source: person.proto
package main
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type Person struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Age int32 `protobuf:"varint,2,opt,name=age,proto3" json:"age,omitempty"`
}
func (x *Person) Reset() {
*x = Person{}
if protoimpl.UnsafeEnabled {
mi := &file_person_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Person) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Person) ProtoMessage() {}
func (x *Person) ProtoReflect() protoreflect.Message {
mi := &file_person_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Person.ProtoReflect.Descriptor instead.
func (*Person) Descriptor() ([]byte, []int) {
return file_person_proto_rawDescGZIP(), []int{0}
}
func (x *Person) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *Person) GetAge() int32 {
if x != nil {
return x.Age
}
return 0
}
var File_person_proto protoreflect.FileDescriptor
var file_person_proto_rawDesc = []byte{
0x0a, 0x0c, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04,
0x6d, 0x61, 0x69, 0x6e, 0x22, 0x2e, 0x0a, 0x06, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x12, 0x12,
0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52,
0x03, 0x61, 0x67, 0x65, 0x42, 0x09, 0x5a, 0x07, 0x2e, 0x2f, 0x3b, 0x6d, 0x61, 0x69, 0x6e, 0x62,
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_person_proto_rawDescOnce sync.Once
file_person_proto_rawDescData = file_person_proto_rawDesc
)
func file_person_proto_rawDescGZIP() []byte {
file_person_proto_rawDescOnce.Do(func() {
file_person_proto_rawDescData = protoimpl.X.CompressGZIP(file_person_proto_rawDescData)
})
return file_person_proto_rawDescData
}
var file_person_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_person_proto_goTypes = []interface{}{
(*Person)(nil), // 0: main.Person
}
var file_person_proto_depIdxs = []int32{
0, // [0:0] is the sub-list for method output_type
0, // [0:0] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_person_proto_init() }
func file_person_proto_init() {
if File_person_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_person_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Person); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_person_proto_rawDesc,
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_person_proto_goTypes,
DependencyIndexes: file_person_proto_depIdxs,
MessageInfos: file_person_proto_msgTypes,
}.Build()
File_person_proto = out.File
file_person_proto_rawDesc = nil
file_person_proto_goTypes = nil
file_person_proto_depIdxs = nil
}
さて、ここで、
package main
import (
"fmt"
"log"
"google.golang.org/protobuf/proto"
)
func main() {
fmt.Println("Hello World!!")
elliot := &Person{
Name: "Elliot",
Age: 24,
}
data, err := proto.Marshal(elliot)
if err != nil {
log.Fatal("Marshalling error", err)
}
fmt.Println(data)
}
とした上で、
$ go run main.go person.pb.go
をすると、
main.go:7:2: no required module provides package google.golang.org/protobuf/prot
o: go.mod file not found in current directory or any parent directory; see 'go h
elp modules'
person.pb.go:10:2: no required module provides package google.golang.org/protobu
f/reflect/protoreflect: go.mod file not found in current directory or any parent
directory; see 'go help modules'
person.pb.go:11:2: no required module provides package google.golang.org/protobu
f/runtime/protoimpl: go.mod file not found in current directory or any parent di
rectory; see 'go help modules'というエラーがでてくるので、
$go mod init m
$ go get google.golang.org/protobuf/reflect/protoreflect
$ go get google.golang.org/protobuf/proto
$ go get google.golang.org/protobuf/runtime/protoimpl
を実行して、再度、
$ go run main.go person.pb.go
を行うと
Hello World!!
[10 6 69 108 108 105 111 116 16 24]
とい値が出力されます。
package main
import (
"fmt"
"log"
"google.golang.org/protobuf/proto"
)
func main() {
fmt.Println("Hello World!!")
elliot := &Person{
Name: "Elliot",
Age: 24,
}
data, err := proto.Marshal(elliot)
if err != nil {
log.Fatal("Marshalling error", err)
}
fmt.Println(data)
newElliot := &Person{}
err = proto.Unmarshal(data, newElliot)
if err != nil {
log.Fatal("unmarshalling error: ", err)
}
fmt.Println(newElliot.GetAge())
fmt.Println(newElliot.GetName())
}
のプログラムを実行すると、
$ go run main.go person.pb.go
Hello World!!
[10 6 69 108 108 105 111 116 16 24]
24
Elliot
となる。
やっと見つけた
ここからダウンロードすると、こんな感じのprotoファイルが得られます。
で、これを以下のように修正して、
として、
$ protoc --go_out=. *.proto
を実施すると、さくっとgtfs-realtime.pb.goができました。
ここまでできれば、後はクライアントを作れば、できるはずです。
問題は、どうやってサーバにアクセスするか、を考えれば、いくはずです。
(が、基本的に最後に動くまで、どうなるかは分からないですが)。
さて、今回の私の狙いは、
https://ckan.odpt.org/dataset/b_bus_gtfs_rt-yokohamamunicipal
の、リアルタイムデータを取得して保存しておくことです。
これは、こちらに記載されているように
なので、このデータを所得するためには、「発行されたアクセストークン」というのを貰う必要があります。これ、"f4954c3814b207512d8fe4bf10f79f0dc44050f1654f5781dc94c4991a574bf4"(江端ルールで変更済み)というやつになります。
これは、https://developer.odpt.org/ から、申請してメールで付与してもらえます(2日くらいかな)。これがないと、データの取得ができないので注意して下さい。
さて、ここから、とりあえず、この横浜市交通局の市営バスのバス関連リアルタイム情報を取得するコードを、https://gtfs.org/realtime/language-bindings/golang/ をパクって、ちょっと修正してみました。
私は、c:\Users\ebata\protocol_bufferes\gtfs-realtime というディレクトリを掘って、上記のページ通りのことを実施しました。
$ go get github.com/MobilityData/gtfs-realtime-bindings/golang/gtfs
$ go get google.golang.org/protobuf/proto
以下のファイルを作成しました(トークンは自分のものに置き替えて下さい)。エラーの出てくるところは、コメントアウトしています。
// https://gtfs.org/realtime/language-bindings/golang/
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"github.com/MobilityData/gtfs-realtime-bindings/golang/gtfs"
proto "github.com/golang/protobuf/proto"
)
func main() {
var (
username = "xx@gmail.com" // 横浜市交通局の市営バスのサイトでは不要のようだからダミーを放り込んでおく
password = "xx" // (同上)
)
client := &http.Client{}
req, err := http.NewRequest("GET", "https://api.odpt.org/api/v4/gtfs/realtime/YokohamaMunicipalBus_vehicle?acl:consumerKey=f4954c3814b207512d8fe4bf10f79f0dc44050f1654f5781dc94c4991a574bf4", nil)
if err != nil {
log.Fatal(err)
}
req.SetBasicAuth(username, password)
resp, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
if err != nil {
log.Fatal(err)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
fmt.Println(body)
feed := gtfs.FeedMessage{}
err = proto.Unmarshal(body, &feed)
if err != nil {
log.Fatal(err)
}
fmt.Println(feed)
/*
for _, entity := range feed.Entity {
tripUpdate := entity.TripUpdate
trip := tripUpdate.Trip
tripId := trip.TripId
fmt.Printf("Trip ID: %s\n", *tripId)
}
*/
}
で、fmt.Println(body) のところを表示するとこんな感じになっています。
[10 13 10 3 50 46 48 16 0 24 165 246 160 161 6 18 41 10 9 118 105 99 108 95 49 56 48 54 34 28 18 10 13 252 214 13 66 21 192 155 11 67 40 165 205 159 161 6 66 6 10 4 49 56 48 54 72 0 18 41 10 9 118 105 99 108 95 49 48 48 50 34 28 18 10 13 188 173 13 66 21 205 159 11 67 40 233 176 159 161 6 66 6 10 4 49 48 48 50 72 0 18 41 10 9 118 105 99 108 95 51 57 57 50 34 28 18 10 13 83 131 13 66 21 28 146 11 67 40 216 210 159 161 6 66 6 10 4 51 57 57 .....
fmt.Println(feed)は、こんな風に表示されます。
{{{} [] [] 0xc00013f800} 0 [] map[] gtfs_realtime_version:"2.0" incrementality:FULL_DATASET timestamp:1680358181 [id:"vicl_1806" vehicle:{vehicle:{id:"1806"} position:{latitude:35.459946 longitude:139.6084} timestamp:1680336549 occupancy_status:EMPTY} id:"vicl_1002" vehicle:{vehicle:{id:"1002"} position:{latitude:35.419662 longitude:139.62422} timestamp:1680332905 occupancy_status:EMPTY} id:"vicl_3992" vehicle:{vehicle:{id:"3992"} position:{latitude:35.378246 longitude:139.57074} timestamp:1680337240 occupancy_status:EMPTY} id:"vicl_1732" vehicle:{trip:{trip_id:"04117_12202301042041P01910" schedule_relationship:SCHEDULED} vehicle:{id:"1732"}.....
あとは、ここをパースすれば、必要な情報は取り出せるはずです。
さて、ここから ~/protocol_bufferes/gtfs-realtime に環境を作ってみます。
$go mod init m
$ go get google.golang.org/protobuf/reflect/protoreflect
$ go get google.golang.org/protobuf/proto
$ go get google.golang.org/protobuf/runtime/protoimpl
で、
こちらの環境でも、上記と同じ手続で、gtfs-realtime.pb.goを作り、
$ go run main.go gtfs-realtime.pb.go
を実施してみましたところ、
main.go:11:2: no required module provides package github.com/MobilityData/gtfs-realtime-bindings/golang/gtfs; to
add it:
go get github.com/MobilityData/gtfs-realtime-bindings/golang/gtfs
main.go:12:2: missing go.sum entry for module providing package github.com/golang/protobuf/proto; to add:
go mod download github.com/golang/protobuf
と言われたので、言われた通りのことを実施してみました
ebata@DESKTOP-P6KREM0 MINGW64 ~/protocol_bufferes/gtfs-realtime$ go get github.com/MobilityData/gtfs-realtime-bindings/golang/gtfsgo get: added github.com/MobilityData/gtfs-realtime-bindings/golang/gtfs v1.0.0
ebata@DESKTOP-P6KREM0 MINGW64 ~/protocol_bufferes/gtfs-realtime$ go mod download github.com/golang/protobuf
$ go run main.go gtfs-realtime.pb.gogo: updates to go.mod needed; to update it:go mod tidy
$ go mod tidy
$ go run main.go gtfs-realtime.pb.gopanic: proto: file "gtfs-realtime.proto" is already registeredpreviously from: "github.com/MobilityData/gtfs-realtime-bindings/golang/gtfs"currently from: "main"See https://protobuf.dev/reference/go/faq#namespace-conflictgoroutine 1 [running]:google.golang.org/protobuf/reflect/protoregistry.glob..func1({0xea9240, 0xeb92b8}, {0xea9240, 0xc000058d70})c:/go/pkg/mod/google.golang.org/protobuf@v1.30.0/reflect/protoregistry/registry.go:56 +0x1f4(以下、色々)
$ export GOLANG_PROTOBUF_REGISTRATION_CONFLICT=warn
feed := gtfs.FeedMessage{}
err = proto.Unmarshal(body, &feed)
if err != nil {
log.Fatal(err)
}
//fmt.Println(feed)
for _, entity := range feed.Entity {
//tripUpdate := entity.TripUpdate
fmt.Println(entity)
fmt.Println()
//trip := tripUpdate.Trip
//tripId := trip.TripId
//fmt.Printf("Trip ID: %s\n", *tripId)
}
id:"vicl_1780" vehicle:{trip:{trip_id:"05406_09202304031054P00218" schedule_relationship:SCHEDULED} vehicle:{id:"1780"} position:{latitude:35.415165 longitude:139.66798} current_stop_sequence:10 stop_id:"5899_02" current_status:IN_TRANSIT_TO timestamp:1680534894 occupancy_status:EMPTY}id:"vicl_3944" vehicle:{trip:{trip_id:"03903_22202304031012A01309" schedule_relationship:SCHEDULED} vehicle:{id:"3944"} position:{latitude:35.50719 longitude:139.55861} current_stop_sequence:32 stop_id:"6219_01" current_status:IN_TRANSIT_TO timestamp:1680528244 occupancy_status:EMPTY}id:"vicl_1705" vehicle:{vehicle:{id:"1705"} position:{latitude:35.378166 longitude:139.57071} timestamp:1680522432 occupancy_status:FEW_SEATS_AVAILABLE}id:"vicl_4607" vehicle:{vehicle:{id:"4607"} position:{latitude:35.492794 longitude:139.66455} timestamp:1680526330 occupancy_status:NOT_ACCEPTING_PASSENGERS}
for _, entity := range feed.Entity {
//tripUpdate := entity.TripUpdate
//fmt.Println(entity)
// データの読み込み
uniName := *(entity.Vehicle.Vehicle.Id)
lat := *(entity.Vehicle.Position.Latitude)
lon := *(entity.Vehicle.Position.Longitude)
fmt.Println(uniName, lat, lon)
}
wsl -d Ubuntu-20.04で起動しようとして、Error: 0x80040326 Error code: Wsl/Service/0x80040326 と言われた時の対応
で対応できたのは良いのですが、今度はDocker Desktopが動かなくなりました。
いわゆる、"これ"です。
以下のページを参考にさせて頂きながら、色々対策していました。
https://engineer-ninaritai.com/docker-desktop-not-work/
などを調べていましたが、
を最後の手段として腹を括ったところで、動かなかったDocker Desktopでボタン押したアップデートが突然走り出しました。
で、DBのDockerを起動して、psqlでアクセスしたらちゃんとDBが動いていることが確認できました。
こーゆーことが多いので、Docker Desktopは嫌いなんですよ。
(こんなトラブルが「顧客デモの直前」に起きたら、と思うとゾっとします)
person agent か、human agentか?
person:普段「1人の人」を指す。
persons:フォーマルな場面で使う。
people:普段「複数の人」を指す
human:神や動物、ロボットではなく「人間」。
man:「男性」「人」「人類」。最近は「人」「人類」の意味で使うのは避ける傾向にある。
guy、fellow、bloke:口語で「やつ」。
"human agent"で良いように思う。
「自律判断」という英訳で悩む → ここは、"Google裁定"に任せる
"autonomous behavior":149,000
"autonomous decision":383,000
"self-determination": 510,000,000
"autonomous decision" & "algorithm": 82,200
"autonomous behavior algorithm" 146
"autonomous decision" & "algorithm":82,200
"autonomous decision algorithm":1,300
"self-determination" & "algorithm": 421,000
"self-determination algorithm":1,520
結論: "self-determination"が勝者だと思うけど、論文の下書きみたら、"behavioral psychology"が乱発されていた。今、ここで、これを換えると、前後の文章も替えなければならないので、本件は放置する。
どちらも住民という意味ですが
resident は、特定の地域の居住者のことを指し、一時居住者も含むのに対し、
inhabitant は、その地域に長く住む集団に属するものを指します。
どちらも通例では、sをつけた複数形で使われることが多いようです。
resident は、「住民」の他に「居住者」とも訳されますし、名詞の他に形容詞としても使われますが、inhabitant は、名詞のみで、「住民」の他に「定住者」や「生息動物」という意味にもなります。この動詞形は inhabit となり、「に居住している」「に宿る」「巣食う」という意味です。
Problemは「解決すべき問題」という意味で表現したい時に使います。不利益や不都合を来たす物事を指します。
Taskは「やらなければならない作業・仕事」というニュアンスです。日常的な業務レベルで与えられた軽めの仕事や宿題を指します。
[例文1]
The only task I have left for this week is making my presentation slides.
今週残る課題はプレゼンテーションの資料作りです。
[例文2]
Thanks for helping me out. I was able to complete my task by the due date.
手伝ってくさってありがとうございました。なんとか期日までに課題を終えられました。
[例文3]
The president gave me a task to come up with a better marketing message.
社長からより良いマーケティングメッセージを策定するように、という課題をいただきました。
AssignmentはTaskに似た意味合いを持ちますが、「任務」や「任された仕事」と、大きめの課題といたニュアンスです。上司から割り当てられた課題は、このAssignmentで表現します。ビジネスシーンだけでなく、学校でも宿題の意味で使われる表現です。
Issueは「議論すべき事象や問題」というニュアンスの表現です。Problemはそのもの自体がトラブルになりかねない問題を意味しますが、Issueはその事柄について賛成する人・反対する人に二分されている場合など、明らかに問題と言いきれない時や、角が立たないよう「問題」と呼ぶのを避けたい時に使います。
Businessは一般的に「仕事・事業・業務」という意味で使われますが、「片付けなければならない仕事」という意味でも使用されます。頻繁に使われる表現ではありませんが、これもissue のように婉曲な「問題」の言い方になります。