gRPCの接続が上手くいかない場合の対処方法
仕事でgRPCを使ったツールを使っているのですが(サーバー側C++/クライアント側C#)、接続が失敗する人がいました。その時は環境のせいかなぁ?と思っていたのですが、ちょっと人数が多くなってきて、本気で調べてみたのでその備忘録です。
まずgRPCの詳細ログの機能を有効にします。
上記のページに書いてある通り、以下の環境変数を設定します。
GRPC_TRACE=all
GRPC_VERBOSITY=DEBUG
PCの設定で設定してもよいですし、C#のコードでEnvironment.SetEnvironmentVariableを使って設定しても大丈夫です。
で、接続できない状態を再現してログを確認。
大量のログが出ているので、error等の文字列で少しずつ見ていきます。
今回は以下のようなエラーが出ていました。
handshake_manager ... "HTTP proxy returned response code 503"
これですね。
どうやらproxy環境で環境変数にhttp_proxyやhttps_proxyを設定していると、
localhost接続等でもproxyを見に行ってしまうらしく、接続に失敗するようです。
上記どちらかの対応で接続に成功するようになります。
no_proxy設定はPCの設定でもC#コードで設定しても大丈夫です。
困ったら詳細ログを出してエラーを特定し、ググる。
でとりあえず解決できると思います。
Fody/Costuraを使ったプロジェクトを連続でmsbuildすると失敗する
数日前にFody/Costuraを使った記事を書きました。
で、Fody/Costuraが思いのほか簡単で便利だったので仕事のツールに対応していたら、CI環境(Jenkins)でなぜかビルドエラーになった。
実行順としては
- nuget locals all -clear
- nuget restore
- MSBuild ToolA.sln /m /t:clean,rebuild ~(これは成功)
- nuget locals all -clear
- nuget restore
- MSBuild ToolB.sln /m /t:clean,rebuild ~(これが失敗)
こんな感じ。
で、ToolBのビルド失敗エラーが下記の通り。
MSBUILD : error : Fody: Could not find a weaver named 'Costura'.
MSBUILD : error : If you have nuget package restore turned on you probably need to do a build to download the weavers
MSBUILD : error : Alternatively you may have added a weaver to your 'FodyWeavers.xml' and forgot to add the appropriate NuGet package.
MSBUILD : error : This url may provide more information http://nuget.org/packages/Costura.Fody/ .
ぐぬぬ。わからん。
色々調べてみると、どうやらToolAのmsbuildのプロセスが残っていてFodyのdllを軒並み掴んだままになるため2回目が失敗するということが解りました。
(もしかしたらnugetの取得形式がPackageReferenceの時だけかも)
で、解決策としてはmsbuildの引数に/nr:falseを入れると良いらしい。
↓こんな感じで。
MSBuild ToolA.sln /m /nr:false /t:clean,rebuild ~
teamcity - msbuild.exe staying open, locking files - Stack Overflow
Fody使うと並列ビルドのmsbuildがおかしくなっちゃうんでしょうかね。
とりあえずこれでビルド通ったのでめでたしめでたし。
Prism(2) - DialogServiceInjectionModule
前回Prism(1)としたけど、1日坊主で(2)を一生作らなかったらどうしよう…と思っていたのですが、
意外とモチベがあったので前回の予告通りPrismのModuleを触ってみました。
前回のDialogServiceInjectionをモジュール化しています。
CSharpSapmles/WPF/Prism/DialogServiceInjectionModule at master · nosimo/CSharpSapmles · GitHub
実装については今回もREADMEに大体書いてあるのでそちらを参考に。
Moduleを使ったといっても、コードでの登録とロードだとプラグイン機能としては使いにくいので、
app.config登録版とディレクトリ版も使ってみたいところ。
次回はそこらへんがアップできると良いですね。GW終わっちゃったけど…。
Prism(1) - DialogServiceInjectionサンプル
今までもPrismを使ってWPFアプリを作っていたんですが、ほぼSetPropertyとかDelgateCommandとかのさわりの部分しか使ってこなかったので、
- Bootstrapper
- ViewModelLocator
- Unity
- Module
あたりを使ってみようと思います。
というわけでGitHubに
Bootstrapper、ViewModelLocator、Unity
を使ったサンプルを上げました。
CSharpSapmles/WPF/Prism/DialogServiceInjection at master · nosimo/CSharpSapmles · GitHub
(GitHub久しぶりに更新した…)
IDialogServiceから
- DialogService
- ConfirmDialogService
を実装します。
MainWindowとUserControl1ではDialogServiceを使ってメッセージ表示、UserControl2ではConfirmDialogServiceを使ってYes/No付きのメッセージを表示します。・・・というような感じのサンプルです。
重要なのはBootstrapperのConfigureViewModelLocator関数ですね。
ここで色々と設定しています。
CSharpSapmles/BootStrapper.cs at master · nosimo/CSharpSapmles · GitHub
その他のことはREADMEに書いてあります。
次はModule関連を触ってみる予定ですが、どうなることやら。
参考
GitHub - runceel/PrismEdu: PrismLibrary japanese text.
実行ファイル(.exe)とdllをまとめる方法について
.net環境でexeとdllをまとめる方法について
- ILMarge
- Fody/Costura
のそれぞれについて軽くメモ
ILMarge
''exe dll まとめる''
等で検索するとだいたいILMargeでの方法が載っています。
ILMargeについてはgoogle先生や以下のページが詳しいので割愛。
アプリケーションのEXEファイルやDLLファイルを1つにまとめるには? - @IT
ILMargeの良いところとして、MS純正ツールなのもポイント高いです。
ただ以前、自分の環境で試したときにデバッグが上手くいかないことがあって、
使えないーということがありました。
多分設定があるのでしょうが、その時は簡単には見つからず…。
次のようにマージ後のファイル名を元のファイル名にしてしまうと、デバッガが効かなくなります
これな気がします…。
あと、
- マージがエラーになったときに原因が解りにくい
- ビルド後イベントを追加する必要がある
等々もあって脳死で使えるほど簡単ではないなぁという。
Fody/Costura
最近Fody.PropertyChangedというライブラリを知って色々調べているうちにこちらを発見。
使ってみるととても簡単にexeとdllをまとめることができました。
使い方は
- nugetでCostura.FodyとFodyをインストール
- FodyWeavers.xmlをプロジェクトと同じディレクトリ内に作成
⇒他の紹介記事だとこの手順がないので自動生成される方法があるのかも? - 以下の内容をコピペして保存
<?xml version="1.0" encoding="utf-8"?>
<Weavers>
<Costura/>
</Weavers> - ビルド
と、とても簡単。
ただ、まとめるdllによってはFodyWeavers.xmlにいくつか設定が必要になってきます。
特定のアセンブリを除外する
除外するアセンブリを<ExcludeAssemblies>~</ExcludeAssemblies>に書くだけです。
上手くマージできないdllがあったらとりあえずこれで解決できます。
その分exeとは別のdllが増えていきますが…。
x86/x64のアンマネージドなdllがあるアセンブリについて
ちょっとまだ試せてないですが
<Unmanaged32Assemblies>~</Unmanaged32Assemblies>
<Unmanaged64Assemblies>~</Unmanaged64Assemblies>
で囲めばいけるようです。
Putting DLLs Into a Single WPF Exe
(と思ったけど、実際試すと別に囲んでも囲わなくても動くような…?)
gRPC(C++)をVisualStudioでビルド~HelloWorldまでやってみた
巷で流行りのgRPCに入門してみました。
hello_worldくらい適当に動かして・・・と思って実際やってみたんですが、
C++(VisualStudio)はhello_world動かすだけで大変だったのでメモ。
grpcとprotocのビルドまで
日本語では↓の記事が最高です。
gRPC 1.2.3をVisual Studio C++(2015)でビルドする - Qiita
更新日時も4/27と近いのでこれは勝ったなと思ったのですが、
これgRPCのビルド手順までなんですよねー。
hello_worldはvcprojも無いのでそこからも大変でした。orz
あと、↑の通りgRPC1.2.3でビルドすればよかったんですが、
gRPCのGitHubに行くと1.3.1まで出ていたので、
欲を出してそちらで通そうとしてしまったのも失敗しました。
1.3.xはgrpc.slnに含まれるaresとgrpc++_error_detailsあたりがエラーで通りません。
とりあえず通すだけなら2つのプロジェクトをアンロードすれば通ったので、
それでも良いかもしませんが、なんか気持ち悪いので自分は1.2.xでビルドしました。
(1.2.3と1.2.5で通ることを確認しました。win7,win10でそれぞれ確認)
でgRPCのビルドまでで上記記事と違う感じにした部分を上げていきます。
- gRPCリポジトリをZipでなくでGit Clone(Source Tree)で落とした
→落とした後にタグからv1.2.xをチェックアウトします
こちらだとProtoも一緒に落ちてくるので色々面倒くさくなくて良いです - gRPCのビルド時のエラーで追加をしなくても通った(1.2.5/win10のみ確認)
→上記記事だとgpr_mpscq_initといくつかのものをdefに足したとありましたが、
こちらでは足さなくてもhello_worldは動きました
(もしかしたらもっと複雑なものを作るときには必要なのかも)
くらいでしょうか・・・。もちろん記事のまままでも通ると思います。
CMakeの使い方も図付きで載っていたのでとても助かりました。
hello_worldのビルド
で、ここからが本番。hello_worldを通します。
と、色々書きそうな雰囲気を醸し出してますが、こちらもGitHubに
超スバラシイものが上がっていたのでペタリしておきます。
GitHub - jozefizso/grpc-windows: Build gRPC on Windows x64
設定がpropsファイルにまとめられているのがGoodですね。
上記リポジトリを落としてきて、
- lib_debug.props/lib_release.propsを編集
→<AdditionalIncludeDirectories>の値を自分の環境のgrpcとprotoのヘッダファイルがある場所にそれぞれ変更
→<AdditionalLibraryDirectories>の値を自分の環境のgrpcとprotoのlibファイルがある場所にそれぞれ変更 - test_protoc.batを編集
→protoc_pathを自分の環境のprotoc.exeがある場所に変更
→grpc_protoc_plugins_pathを自分の環境のgrpc_cpp_plugin.exeがある場所に変更 - 編集したtest_protoc.batを実行
→srcフォルダに以下4つのファイルができていればOK
helloworld.grpc.pb.cc
helloworld.grpc.pb.h
helloworld.pb.cc
helloworld.pb.h - ビルド→実行
上記手順を行えば実行できました。素晴らしい...。
デフォルトx64しかない気がした(?)ので、
x86ビルドしたい場合はソリューション構成から追加してください。
上記プロジェクトでなくて自分でプロジェクト作成からやる場合、
結局はpropsの設定とopensslとzlibを落とせばOKです。
- プロジェクト作成してソースやらを入れる
- nugetでopensslとzlibをインストール
→grpc.dependencies.opensslで検索すると出てくるので、
grpc.dependencies.openssl.1.0.204.1をインストールします
そうすると他の3つも同時に落ちてきます。
→もしくはgrpcビルド時にnugetで落としたものにパスを通しても良いと思います - プリプロセッサ定義に_WIN32_WINNT=0x0600;を追加
→これがないとビルドエラーになります。0x600は本当は環境に合わせて変更したほうが良いかも?
- includeパス設定
→grpcとprotoのヘッダーへのパスを追加します - linkパス設定
→grpcとprotoのライブラリへのパスを追加します - 追加のライブラリ設定
→以下を追加します
・libprotobufd.lib(releaseではdがないので注意)
・grpc++.lib
・grpc.lib
・gpr.lib
・ws2_32.lib - ランタイムライブラリ設定変更
→MTに変更(デバッグはMTd) - protoのコンバート
→https://github.com/grpc/grpc/tree/master/examples/cpp/helloworld
↑にコマンド載ってるのでそれでコンバートしてください
上記に書いてある通り4ファイルできればOKです - ビルド→実行
とりあえずここまでで自分の環境だと実行できました。
C++サーバーとC#クライアントで通信
上記まででC++のserverをビルドできていれば、それを立ち上げた状態で
C#のhello_worldのクライアントを動かすと「Greeting:Hello you」と出てきて成功します。
C#のクライアントのビルドですが・・・はい。
exampleのGreeter.slnを開いて実行するだけで動きます。
C++での苦労はなんだったのか・・・と思うくらい簡単に動きます。
おわりに
hello_worldがとりあえず動いたので、これからはその他のサンプルを見たり、
適当なアプリを作っていけるかと思います。
次はきっとProtoの文法を覚えていくのが良いんでしょうか・・・?
色々作りながら覚えていかないとなーという感じです。
ReactivePropertyのPropertyChanged発生タイミングについて
初めてReactivePropertyを使っていて盛大にハマったのでメモ。
(.Net4.0なのでReactivePropertyは2.9を使用しています)
ReactivePropertyのOnNext実行と、PropertyChangedイベント発生タイミングって必ずしも同じではないんですね。
WPFでLivetを使ってVliewModelクラス内で以下のようなコードを書いた。
(IDisposable関連の処理は省いてるのと、外部からInitParam???とか色々あると思いますがテストなので)
public class HogeViewModel : Livet.ViewModel { public ReactiveProperty<Int32> Num;
public HogeViewModel(){ Num = new ReactiveProperty<Int32>(0); } public void InitParam(Int32 num){ //前に登録したイベントの解除とか Num.Value = num; // Value代入後にPropertyChangedイベントリスナ登録(CompositeDisposableに入れてね) new Livet.EventListeners.PropertyChangedEventListener(Num, (s, e) => RaisePropertyChanged(e.PropertyName)); } }
Num.Valueの代入後にEventListenerを登録しているので、
イベントは飛んでこないと思ったのですが、なぜか飛んでくる・・・。
しかもよくわからないタイミングで・・・。
System.Reactive.Core.dll!System.Reactive.Concurrency.Scheduler.Invoke(System.Reactive.Concurrency.IScheduler scheduler, System.Action action) 不明
( ^ω^)・・・???
色々試したところ以下で解決。
- PropertyChangedイベントを捕まえるのではなくReactivePropertyのSubscribe(OnNext)で処理する
- ReactivePropertyのSchedulerをImmidiateSchedulerかCurrentThredSchedulerを設定する(本当にこれで良いのか怪しい)
1.はあたりまえですね。というかReactivePropertyを使いながらなぜEventListenerを使っているのか・・・。
(あーその時はSubscribeだとPropertyNameが解らないじゃん!とか思ったからかもです。後で考えたら別に解らなくてもそこまで困らないですね。)
多分これが正解なはず。
2.ですが、ReactivePropertyのSetValueの処理は以下のようになっています。
this.Source.OnNext(value); this.RaiseEventScheduler.Schedule(() => this.PropertyChanged?.Invoke(this, SingletonPropertyChangedEventArgs.Value));
OnNextはSetValueが呼び出された時に即時呼び出されますが、
PropertyChangedはRaiseEventSchedulerなるものに投げ込まれています。
これがデフォルトではImmidiateではないっぽいです。
作者の方々の説明ページもあったのですが、Rxに詳しくないのでいまいちピンときてません。
かずきさんにコメントいただきまして、UIスレッド外からのPropertyChangedイベント実行でエラーになるプラットフォームがあるためのこと。
ReactivePropertyで自動でUIスレッドにイベント発行を変えるのを抑止する - かずきのBlog@hatena
Reactive Extensions再入門 その45「Scheduler」 - かずきのBlog@hatena
Schedulerに関しては以下のページを参考に。
Rx入門 (15) - スケジューラの利用 - xin9le.net
Reactive Extensions再入門 その45「Scheduler」 - かずきのBlog@hatena
とりあえずReactiveProperty生成時に下記のようにしてやればこちらの望んでいた処理になりました。(元に戻す必要あるのかしら?)
var scheduler = Reactive.Bindings.ReactivePropertyScheduler.Default; Reactive.Bindings.ReactivePropertyScheduler.SetDefault(System.Reactive.Concurrency.Scheduler.Immediate); Num = new ReactiveProperty<Int32>(0);
Reactive.Bindings.ReactivePropertyScheduler.SetDefault(scheduler);
もしくは
Num = new ReactiveProperty<Int32>(0, System.Reactive.Concurrency.Scheduler.Immediate);
ImmidiateはCurrentThreadに変えても挙動は同じでした。
ただ、アプリの初期化でSetDefaultすればよいとあったので、MainWindowのコンストラクタでSetDefaultしたのですが挙動がなぜか変わらず・・・。ReactivePropertyの生成時に設定するようにしたらうまくいきました。
アプリの初期化なのでApp.xaml.csのOnStartupで処理するのが良いようです。
(そういえばWPFで初期化処理書いたことなかった・・・)
とりあえずSubscribeで処理するのが良いとして、デフォルトでImmidiateやCurrentThreadでないのってなぜなんでしょうね。
OnNext実行とPropertyChanged発生が同一タイミングでないことのメリットってあるのだろうか・・・。
うーん。難しい。