Xamarin.FormsでUnity.Configurationしてみた。
はじめに
引越しつらい現実逃避で記事書きました/(^o^)\
Unity
ゲーム作る方のじゃなくてDIとかする方のUnityのお話です。
現在、開発が止まって放置されているらしくて今後メンテの期待薄なので今更Unityのネタを書こうか迷ったんですがとりあえず書いてみることにしました。
UnityContainerの拡張メソッドにLoadConfigurationってあるのご存知ですか?
app.Configとかweb.configにContainerへのレジストを記述してLoadConfigurationすると、その設定どおりにContainerに登録を行ってくれます。
これ.NET45なプロジェクトだと使えるんだけどXamarinとかPCLでは使えないんです。
どうしてかというとSystem.Cofigurationに依存している拡張なのでSystem.Configurationが基本的に使えない環境を外してるんですね。
それを今回はPCLプロジェクトでもある程度使ってみるサンプルを作りました。
Xamarin.Formsへの対応
Windows8.1とかは対象外です。今回はAndroidで動作確認しています。たぶんiOSでも動くはずです。Macを既に梱包しちゃったので試せません。
UWPは頑張れば対応できそうなんですが今回は見送りました。
解説
プロジェクトの作成
まずはプロジェクトの作成を行います。
Xamarin.FormsプロジェクトをPCLにて作成してください。
PCLを.NET Standardに対応
こちらの記事を参考にPCLプロジェクトを.NET Standardに対応させてください。
.NET Standardのバージョンは1.5としてください。
Unity.Configurationの追加
UnityのConfiguration機能をPCLで使用するためにUnity,Configurationというパッケージを使用します。
ソリューションのnugetマネージャーを開いてUnity.Configurationというパッケージを追加します。
Pre版ですので「プレリリースを含める」にチェックを入れて検索してください。
テスト動作用のインターフェースと実装クラスを追加します。
めんどくさいのでこんな感じで。
namespace App4 { public interface IHogeHoge { string GetHoge(); string GetHogeHoge(); } public class HogeHogeA : IHogeHoge { public string GetHoge() => "HogeA"; public string GetHogeHoge()=> "HogeHogeA"; } }
PCLにconfigファイルを追加します。
unity.configという名前でconfigファイルを追加します。
ビルドアクションは埋込リソースにしてください。
ファイルの中はこんな感じで。
<unity xmlns="http://schemas.microsoft.com/practices/2010/unity"> <!-- Define Assemblies--> <assembly name="App4" /> <!-- End Assemblies--> <!-- Define Namespaces--> <namespace name="App4" /> <!-- End Namespaces--> <container> <register type="App4.IHogeHoge, App4" mapTo="App4.HogeHogeA, App4" /> </container> </unity>
他にも色々とできますがUnity.Configurationの使い方は別途ぐぐってください。
ConfigurationSectionReaderクラスを追加する
c# - How to Read a Configuration Section from XML in a Database? - Stack Overflow
こちらを参考にしました(コピペ)
こんな感じのクラスを追加しておきます。
public class ConfigurationSectionReader<T> where T : ConfigurationSection, new() { public T GetSection(string sectionXml) { T section = new T(); using (StringReader stringReader = new StringReader(sectionXml)) using (XmlReader reader = XmlReader.Create(stringReader, new XmlReaderSettings() { CloseInput = true })) { reader.Read(); section.GetType().GetMethod("DeserializeElement", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(section, new object[] { reader, true }); } return section; } }
LoadConfigurationする
appクラスにてcontainerを作ってLoadConfigurationします。
public App() { InitializeComponent(); IUnityContainer container = null; var assembly = typeof(App).GetTypeInfo().Assembly; using (var stream = assembly.GetManifestResourceStream("App4.unity.config")) { using (TextReader reader = new StreamReader(stream)) { var configtext = reader.ReadToEnd(); var configreader = new ConfigurationSectionReader<UnityConfigurationSection>(); var sect = configreader.GetSection(configtext); container = new UnityContainer(); container.LoadConfiguration(sect); } } MainPage = new App4.MainPage(container.Resolve<IHogeHoge>().GetHogeHoge()); }
結果
おわりに
Unity.Configurationライブラリに関しては余裕ができたらもう少し真面目に取り組んでみようかと思います。
ではでは( `ー´)ノ
Microsoft MVPを受賞いたしました
はじめに
まじ引越し進まないんだけどぉ( ;∀;)
全然物が捨てられないんだけどぉ( ;∀;)
あるジャンルの書物が捨てられないんだけどぉ( ;∀;)
捨てられないどころか読み返しちゃってるんだけどぉ(^▽^)
ごあいさつ
2連続で技術に関係ないエントリで大変恐縮なのですが
このたびMicrosoft MVPを受賞いたしましたのでご報告いたします。
受賞カテゴリはVisual Studio and Development Technologiesになります。
なぜこんな時期に? と思われる方もいるかと思います。
これまでMicrosoft MVPは年4回(1月、4月、7月、10月)でしたが、今月より毎月審査と表彰が行われるように変更となりました。
2月に42人が受賞したということで、そのうちの一人ということになります。
感想
正直、私が受賞した事が私自身予想外で信じられず、またMVP受賞時期の変更もわからなかったため、日本の事務局のほうに確認のメールを送ったほどです。
確かに去年末にノリでと言いますか落ちること前提で自薦してみました。
これまで受賞されてきている方たちと比べ私は明らかに実績も少なく技術力・知識も少なく、応募するってどういう感じなのかな?っていうのを知りたくて実際に応募を行った感じです。
これから何年か新しい環境で色々と学んでアウトプットしていって、その結果にもし受賞することができたら嬉しいなぁ。
くらいの気持ちでいました。
なので受賞が確定した当初には辞退させていただこうかと迷っていました。
ただ自薦していて辞退というのも変な話なのと
よく相談事をしている方から、受賞はあくまでもMSからの「去年いろいろありがとね」という意味だし「不満ならこれから精進すればよいよ」というアドバイスを貰いまして自身で気持ちが固まり受賞する事といたしました。
去年何してたの?
今回の受賞ですが、ほぼコード書いて技術記事かいただけでの受賞になります。
JXUGでメンターは一度していますが
興味を持ったことにサンプルコード書いて記事書いてライブラリ作ったらnugetに登録してサンプルコード書いて記事書いてみたいな事しかしていません。
ということで、去年読まれた記事のランキングを作成してみました。
ランキング生成はしばやんさんのやつです。
WatsonのSpeech To TextをXamarin.Formsで試してみたよ(‘◇’)ゞ
いきなりAzureでなくBluemixのWatsonなネタなのですが、WatsonのSpeechToTextを使用してXamarin.Formsで音声入力をテキストに変換してみるコンテンツになります。
去年ObservableVoiceCaptureというライブラリをnugetに公開しました。
このライブラリはXamarinにて音声入力を扱うためのライブラリで、このライブラリを使用したサンプルを作りたいなって思いから音声認識を実装してみることにしました。
この記事はC# Xamarin界隈の方だけでなくJS界隈などの方からも読んでいただいたみたいで凄くうれしかったです。
MVVMっぽくXamarin.Formsアプリ作ってみました。その2
ReactivePropertyを使用したデザインでの実装パターンサンプルです。
一回では書ききれなかったので4回に分けての投稿でしたが、なぜ2番目が断トツでアクセスが多いのかは不明です。
現在はこのデザインをベースに色々と発展させた考えを持っていて、これに関してはもし機会があればどこかでお話しできたらなぁって思っています。
-
おととしXamarin.Formsを実際に開発で使ってみた結果、開発でどんな知識が必要になったかの記事です。
Windows環境を中心に開発してきた方がXamarin.Formsをやってみる場合に少し役にたつかもしれません。
-
Edge.jsという.NET環境からnode.jsを使用してJavaScriptを動作させて結果を得るサンプルです。
深夜にツイッターを眺めていたらRakuten MAという形態素解析ライブラリに関するツイートを見つけて思いついて調べてみて実装したら朝になってました( ゚Д゚)
-
おととしにXamarin.Formsを実戦で使用してみたポエムです。
使ったライブラリとかだけでなく、どんな開発体制でどんな風に進めたとか書いているので今読み返すと思い出がよみがえります( 一一)
Netjsを使ってC#からTypeScriptへの変換をしてみた。
NetjsというC#で作成したdllを読み込んでTypeScriptを生成するライブラリを試した記事です。
これを使用して後にNSpeex.TypeScriptというNSpeexをブラウザ上で使えるパッケージ(一部機能だけ)を作成出来たことは私にとっての去年一番の実績だったと思っています(たぶん需要はない)http://tamafuyou.hatenablog.com/entry/2016/05/15/230615
今後
これまで受賞されてきた方と比べて色々な面で見劣りしてしまっている事は重々承知ですが「MVPの質が落ちた」等言われないように、ゆっくりですが自分なりに頑張っていこうと思います。
また今年は勉強会等に参加しやすい環境になりましたので積極的に参加していければ良いなぁと思っております。
あとはもし機会があってその時に話したいことがあれば登壇にも挑戦していきたいと考えています。
さいごに
まだまだ全然MVPのすごい方たちには遠く及びませんが、少しでも好きな技術が広まり長続きして発展していけるように貢献できればと思っております。
今後ともよろしくお願いいたします。
約10年勤めたソフトウェア会社を退職いたしました。
先ほど9年11ヵ月という長い間勤めました会社を退職することとなりました。
これまで本当にありがとうございました。
在職中には本当に沢山のご迷惑をおかけしてしまい申し訳ございませんでした。
この先もこれまでの経験をベースに頑張ってエンジニア人生を進めていきたいと思います。
10年間どんな仕事をして何を考え何を得たか
私がブログを書き始めて1年もたっていません。
Xamarin族な人というイメージはあるかなと思うのですが私がどんなキャリアを歩いているのかちょっと書いてみたいと思います。技術ブログなので技術の側面をメインに書きます。
どんな分野の仕事だったか
私は10年間、二つのジャンルの市場に向けたシステムの製造を行ってきました。
これらのシステムは大きく基幹システムとネットシステム、リアルタイムシステムの3つに分かれ、私は6割リアルタイムシステム3割ネットシステム1割基幹システムくらいの仕事を行ってきました。
どんなキャリアだったか
1年目~3年目
主に試験と現地納品でした。
印刷すると厚さ40センチ以上にもなる試験仕様書を2~3人でこなし試験が完了するとお客様に納品し使用方法の説明などを行う。
システムの切り替え時は正月、ゴールデンウィーク、お盆などですので1年目の元旦に新幹線でお客様先に移動することになったのは結構驚きました。
これらの仕事でシステムの弱点の探し方、サーバ構成、ネットワーク、ロードバランス、などの知識を得ることができました。
特にシステムの弱点の探し方を覚えたことが大きくて
色々なシステムのテストをした事によって、「こういうシステムはこういう風にできているから、こういうところにバグが生まれやすい」というような事を考える力を身に着けることができました。
メインの仕事ではないですがASP.NETWebForms、PL/SQL、ストアド、C++MFC、ColdFusionなどのプログラムを多少触っていました。
サーバセットアップなどの仕事も少しずつはじめて、主にWindowsサーバですがクラスタを組んだりミラーリングを組んだりロードバランスを組んだり、そういうインフラ面での基本的な知識を身に着けることができました。
4~5年目
4年目のプロジェクトで
WPFを使用したアプリケーションの開発を任されました。
WPFを使用する事も私自身が考えて、一つのアプリケーションを自分で考えて自分で作るということを初めて行いました。
これまでに上司からGoFデザインパターンを勉強するといいよって言われて勉強していたのですが、WPFで開発するにあたってそれらのデザインパターンを適用する事とMVVMというデザインパターンを勉強して開発に取り入れました。
この頃からプログラムをデザインするという事がすごく楽しく感じるようになりました。
きれいに構成できた時の楽しさに惹かれていました。
またネットワーク、サーバ室、サーバラック設計、設置、配線などプログラム以外にも色々な事に挑戦して勉強することができました。
ダウンタイム少なく冗長化でするにはどうしたらよいのか、機器故障をどうやって検出するか、などインフラに関して色々と勉強できました。
またWebSocketを使用したプロジェクトにかかわりDraft76の頃からC++やC#でWebSocketサーバを書いたりブラウザのパフォーマンス検証をしたりしていました。
RFCドキュメントをちゃんと読んだのはその時が初めてだったと思います。
5年目には
ASP.NET MVCを使用してECシステムを1から作成しました。
一部にはKnockout.jsを取り入れてSPAな構成を試したりある意味挑戦的なプロジェクトでした。
このプロジェクトは非常に苦しくて炎上してしまい半年の残業時間が酷いことになってしまいました。
ASP.NET MVCを使ったから、Knockout.jsを使ったから炎上したわけではなかったのですが、とにかく色々な面が苦しかったです。
その後Mono For Android(現Xamarin.Android)を使用したアプリの開発などを行いました。
一人でサーバもクライアントも開発しなければならなかったので出来る限り楽をしたくてサーバサイドをWCFで作っていたんですが、それならクライアントもC#で作った方が良いと上司を説得して1年10万円もする開発ライセンスを買ってもらいました。
6~7年目
これまで収めたシステムの改修であったりサブPLとしてプロジェクトの技術面のサポートをするような感じでの仕事をしてました。
この時期はプログラミングやアーキテクチャの側面よりもプロジェクトをどのように運用するべきか、ドキュメントの意味、協力会社さんとの連携の仕方、お客様との仕事の進め方など、そういうマネージメントの側面の方を考える事の方が多かったです。
8年目
以前に作成したWPFアプリのモバイル版の作成案件ができたためXamarinおよびモバイルサービスを提供するためのシステム作成に挑戦しました。
Xamarin.FormsにるフロントエンドだけでなくWebAPIやプッシュ通知などモバイル用のシステム全体を設計して作成しました。オンプレミスな環境なのでMBAASなどを利用する事はできない点で、逆に色々な事を細部まで知ることが出来ました。
このころは、何のためのデザインパターンなのか、何のためのUnitテストなのか、色々と理由を意識しながらアーキテクチャを考えていました。
9年目
転職を本格的に考えたのは9年目のはじめあたりです。
今年は・・・特に何もしていない年になってしまいました。
体調の事情があり3か月程の休職したこと、また所属部署の組織変更が大きくあり思うように仕事ができなくなってじまいました。
ただJXUGに参加したりブログを色々とかいたり仕事以外が充実した年でもあったと思います。
上司のお話
色々な事を教えていただいた二人の上司の方達には大変感謝しています。
2年目くらいに試験アプリを作るためにC++MFCを触っていて「お前はメモリ管理というか、そもそもプログラムを何もわかってない」って怒られたり、問題解決に対する調査方法とかアプローチの方法とか色々と教わったり、デザインパターンの基本を教えてもらったり、数多くのことを教えてもらえましすごく素敵なお二人で今でも大変尊敬しているエンジニアの方々です。
おかねのはなし
残業込みで450万円くらいが平均だったかと思います。残業代なしだと400万円をきるくらいです。2016年は休職していた事もあり350万円くらいでした。
退職理由
色々な理由が積み重なった結果、として退職・・・というか転職することにしたのですが
前向きな理由、後ろ向きな理由もあわせて、大きかった点をあげてみたいと思います。
クラウドネイティブな開発がしたくなっていた
キャリアのところに一切クラウドな用語が出てきません。
今の部署の仕事はオンプレミスの仕事のみであり、クラウドの利用、クラウドネイティブな開発とは程遠いです。
ですがこの先のシステムのバックエンドはどう考えてもクラウドが主流だと考えています。(現時点でそうなのかも・・・)
ですのでクラウドネイティブでの開発を行える環境、色々と学習できる環境に移りたかったのです。
もちろん個人の範囲で色々と触ったり勉強したりすることはできるのですけど仕事で使うことの重要さもあると思っています。
Xamarin記事が多いので意外と思われる方もいるかと思いますが、私自身はフロントエンド・バッグエンド等の場所的なこだわりはなく必要なシステムを良い形で作れる人になりたいという目指すものがあり、その為に必要な技術知識を取得できる環境で仕事がしたいと思っています。
そんな中で「今の私に必要なものはクラウドネイティブな開発技術だなぁ」と思っていたタイミングで良いお話をいただく事ができて転職を決意しました。
関東で暮らしたくなった
勉強会とか関東が多いんですよね。
土日とかなら出られるんですけど平日はちょっと無理です。また往復でお金が結構かかります。(新幹線を使うと往復8000円以上)
できればこの先は勉強会などに参加しやすい地域で暮らしたくなりました。
職場の雰囲気に合わなくなってしまった
私の所属している部署の方たちは皆さん真面目です。8時という早い始業時間に遅刻する人はほとんどいないですし休む人も少ないです。
何事にも完璧を求めバグゼロを目指して常に活動しています。バグを出すことに対して厳しく何とか不具合ゼロを目指そうと頑張られています。
私が入社した当時と同じ技術同じコードを今でも大切に守っています。開発環境も入社当時から殆どかわっていません。世の中がどんなに変わろうと貫き通す芯の強さがあります。
本当に私から見たら凄い方たちなのです。私にはとても真似ができません。
それに比べて私はあまりにも怠惰で不真面目です。
無駄だと思った事はとにかくしたくない。常に楽をしたい。
楽できる仕組みやツールが世の中にあるのであれば積極的に取り入れて試したい。
朝の早起きは苦手。
問題を運用で解決する事は私には難しくて運用ルールを覚えられない。
運用ルールよりもシステム、仕組みで解決したい。
バグはどうやったって出るもの、バグを責任問題だって取り締まって嫌な空気になるよりバグがでにくい開発プロセスや試験方法を考えて萎縮せずに開発できる環境を作ることを頑張りたい。
常に変化し続けたい。
そんな性根の腐っている不真面目な私が真面目な人達の中で仕事をする事がつらくなってしまい仕事が楽しめなくなってしまいました。
やはり楽しくないと感じる事を続ける事は私のような人間にはとてもつらくて長期間モチベーションが低い状態で仕事を続けても質の高いアウトプットも出来ませんしパフォーマンスも下がってしまいます。こんなグダグダ状態の私がいても他の社員さん達に迷惑をかけてしまうだけだと思い、この先のことを考えても転職するしかないと決断する大きな理由となりました。
これから
3月から蛎殻町某企業にお世話になる予定です。
物凄い方たちが沢山いてよい機会だしチャンスだと思って色々な勉強ができればと思っています。
入社後にしばらくしましたらエントリなども書いてみたいと思います。凄い人たちの中に入ったらどう感じるかなど書けると思います。
2月は引っ越しに休息にちょっとの旅行。そんな感じで過ごしたいと思います。
引っ越し先が運よくネコOKな物件だったので小さい頃から夢だった黒猫と暮らせる生活がおくれそうです。
この先もXamarinは当然使っていきますしAzureを中心としてクラウドも使っていきます。
JXUGやJAZUG、C#界隈、Webフロントエンド界隈などの勉強会等参加することもあるかと思いますがよろしくお願いいたします。
去年は勉強会等で名刺などを受け取るばかりでご挨拶もちゃんとできていませんでしたが3月以降は名刺もちゃんとお渡しできるかと思います。
私はこれまで、Microsoft MVPの方々を含めて多数の凄い方々のブログや著書などをたくさん見て勉強させていただいてきました。
これからも沢山勉強させていただくと思います。
そんな方々には遠く及びはしませんが、こんな私の知識や考えた事では役に立たないかもしれませんが、好きな技術の発展に少しでも貢献できればと思いますのでブログを中心として公開を続けていきたいと思っております。
今後ともよろしくお願いいたします。
ASP.NET Coreで作成したWebSocketサーバをAzure Service Busを使ってスケールアウトに対応してみた
はじめに
前回の記事で失敗しましたので、今回はAzure Service Busを使ってスケールアウトを実装してみました。
実装
コード
今回のコードはここに保存されています。
NET CoreでのService busの利用
良い感じのパッケージがなかったので今回はメッセージの送受信は
AMQP.Net.Liteを使いました。
Service Busの操作はRest APIを使用して実装しました。
Service Bus
AzureでService Busの設定を行います。
まずはリソースグループにServiceBusを作成します。
価格レベルは、スケールアウトにTopicを使うためStandard以上にしてください。
次にTopicの作成を行います。
これで、今回のAzure側での操作は完了です。
では、プログラムの解説を行っていきましす。
Service Busへのメッセージの送信
送信は特に難しい事は無くて
AmqpLiteを使えば簡単に送信できます。
public void OnNext(ChatMessage value) { if (senderLink == null || senderLink.IsClosed) { senderLink = new SenderLink(GetSession(GetConnection()), SenderSubscriptionId, Topic); } var message = new Amqp.Message(JsonConvert.SerializeObject(value)); message.Properties = new Properties { MessageId = Guid.NewGuid().ToString() }; message.ApplicationProperties = new ApplicationProperties(); message.ApplicationProperties["Message.Type.FullName"] = typeof(string).FullName; senderLink.Send(message); }
こんなイメージです。
ちなみにSenderLinkクラスのコンストラクタ引数ですが
public abstract class TopicsClientBase { public string NameSpaceUrl { get; set; } = ""; public string BaseUrl => $"https://{NameSpaceUrl}/"; public string PolicyName { get; set; } = ""; public string Key { get; set; } = ""; public string ConnectionString => $"amqps://{WebUtility.UrlEncode(PolicyName)}:{WebUtility.UrlEncode(Key)}@{NameSpaceUrl}/"; public string Topic { get; set; } = ""; protected Address GetAddress() => new Address(ConnectionString); protected Connection GetConnection() => new Connection(new Address(ConnectionString)); protected Session GetSession(Connection connection) => new Session(connection); }
ベースクラスがこんな感じになっていて
NameSpaceUrlにはxxxxxxxxxx.servicebus.windows.netみたいな感じのUrlを設定します。xxxxxxxの部分は作成したServiceBusの名前です。
PolicyNameとKeyは
この辺の値を設定します。
Topicは先ほど作成したTopicの名前です。
Service Busからの受信
Service Bus Topicからの受信を行うクラスです。
受信開始時にTopicに送信されたメッセージを購読するためにSubscriptionと追加します。
Subscriptionの名前はGUIDを作成して使用しています。
今回はRest APIで実装していましてこんな感じです。
private static async Task<HttpResponseMessage> CreateSubscriptionAsync(string baseAddress, string topicName, string subscriptionName, string token) { var subscriptionAddress = baseAddress + topicName + "/Subscriptions/" + subscriptionName; var client = new HttpClient(); client.DefaultRequestHeaders.Authorization = AuthenticationHeaderValue.Parse(token); var putData = @"<entry xmlns=""http://www.w3.org/2005/Atom""> <title type=""text"">" + subscriptionName + @"</title> <content type=""application/xml""> <SubscriptionDescription xmlns:i=""http://www.w3.org/2001/XMLSchema-instance"" xmlns=""http://schemas.microsoft.com/netservices/2010/10/servicebus/connect"" /> </content> </entry>"; return await client.PutAsync(subscriptionAddress, new ByteArrayContent(Encoding.UTF8.GetBytes(putData))) .ConfigureAwait(false); }
ほとんどマイクロソフトのサンプル通りな感じです。
そして購読を追加したらReceiverLinkクラスを使用して受信を行います。
受信の方法はいくつかあってReceiverLink.ReceiveAsyncメソッドによって受信処理を行うこともできるしReceiverLink.Startメソッドでコールバックを登録する形式でも受信できます。
今回はStartメソッドを使用しました。
そしてTopicReceiveクラスにIDisposableを設定してDispose時に、先ほど作成したSubscriptionを削除するようにしています。
削除関数はこんな感じ
private static async Task<HttpResponseMessage> DeleteSubscriptionAsync(string baseAddress, string topicName, string subscriptionName, string token) { var subscriptionAddress = baseAddress + topicName + "/Subscriptions/" + subscriptionName; var client = new HttpClient(); client.DefaultRequestHeaders.Authorization = AuthenticationHeaderValue.Parse(token); return await client.DeleteAsync(subscriptionAddress).ConfigureAwait(false); }
削除しないとSubscriptionが残ったままになっちゃいます。
今回も送信/受信はObserver/Observableパターンで実装しています。
なのでChatServerクラスをこんな感じにします。
今回の一番の変更点はTopicReceiverクラスがIDisposableインターフェースを持っているため、TopicReceiverをメンバに持つChatServerクラスもIDisposableを実装するようにしました。
ASP.NET MVC Coreでアプリケーションシャットダウン時に処理する方法ですが、StartUpクラスのConfigureメソッドの引数にIApplicationLifetimeインターフェースの引数を追加し、
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public async void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IApplicationLifetime applicationLifetim) { loggerFactory.AddConsole(); if (env.IsDevelopment()) app.UseDeveloperExceptionPage(); applicationLifetim.ApplicationStopping.Register(Stopping); } private void Stopping() { _chatServer.Dispose(); }
こんな感じで処理を登録してあげることで終了開始時、終了時などに処理を実行することができます。
おわりに
SigalRを使わずにWebSocketサーバを作成することはそんなに難しくなく行うことができます。
もちろん、これまでのサンプル実装はエラー処理なども入れていないので、実践的に使用するためにはもっときちんとした実装が必要です、
ただSignalRでは大きすぎる、もっと細かくゴリゴリしたい、みたいな時にはこんな感じで実装していけばよいよっていう一端をご紹介できたかと思います。
それではまた(^^)/
ASP.NET Coreで作成したWebSocketサーバをAzure Event Hubsを使ってスケールアウトに対応してみたけど・・・
注意
2017年1月17日に大幅訂正を行いました。
今回実装してみた結果、WebSocketのスケールアウトにEvent Hubsを用いることは不適切です。
後半に何故EventHubsでスケールアウトを実装することが不適切であったかを書きました。
後日、より適切な実装を行ってみたいと思っています。
はじめに
前回の記事
で、ASP.NET Coreを使ってWebSocketチャットサーバを作成してみました。
ただ、前回の実装ではスケールアウトすることができませんでした。
1プロセスで1チャットサーバとなるためマルチプロセスなサーバで動かした時にはダメな感じになってしまいますしロードバランスで負荷分散した時などもダメダメです。
今回はAzure Event Hubsを使用してスケールアウトに対応したWebSocketサーバを作成してみました。
ソースコードはこちらになります。
解説
使用した主なライブラリ
NuGet Gallery | Microsoft.Azure.EventHubs 0.0.4-preview
.NET Standard対応しているEventHubs用のライブラリです。
現時点でpreview版です。
NuGet Gallery | Microsoft.Azure.EventHubs.Processor 0.0.4-preview
.NET Standard対応しているEventHubsからメッセージを受信する為のライブラリです。
現時点でpreview版です。
前回からの変更点
Comparing master...Backplane · yuka1984/aspnetcore_webspcket_sample · GitHub
前回サンプルからの差分になります。
Azure側での操作
Event Hubsの作成
まずはMarketPlaceからEvent Hubsを選択して作成を押下します。
Nameを入力した後にPrincing TierをBasicにします。
Basicのほうが安いです。
作成を押下します。
Event Hubsが作成できたらEventHubEntityの追加を行います。
EvnetHubの名前を入力して作成します。
Azure Blob Strageの作成
リソースグループにストレージアカウントを追加します。
作成したストレージアカウントでBlobコンテナーを追加します。
以上でAzure上での作成は終了です。
送信クラス
まずはAzure Event Hubsへ送信を行うクラスを作成します。
Azure Event Hubsライブラリを使用して送信します。
EHConnectionStringはEvent Hubs内のShared access policiesにて確認できるConnection string primary keyを設定します。
EHEntityPathには先ほど作成したEventHubEntityの名前を指定します。
前回、WebSocketの接続をObserver/Observableパターンで作成しましたので引き続き送信クラスもObsreverな形で実装しました。
受信クラス
Event Hubsへの接続は特殊な用途でない場合にはEventProcessorHostクラスを用いて受信します。
このクラスで受信を行うとパーティションへの接続の排他制御、オフセットの管理をAzure Blob Storageを用いて行ってくれます。
C# での Event Hubs の使用 | Microsoft Docs
こちらのページが参考になるでしょう。
しかし、スケールアウトでEvent hubsを用いるためには、EventProcessorHostを用いるとうまくいきません。
そこでDirectRecieveを使用します。
DirectReceiveはイベントの受信を低レベルで行うことが可能です。
今回はDirectEventReceiveManagerというクラスを作成して受信処理を行います。
Event Hubsに対して送信したメッセージはどのパーティションに送信されるかは基本的に不明です。
なんでスケールアウトを達成するためにすべてのパーティションを監視します。
ますはEventhubClient.GetRuntimeInformationAsyncにてEventHubsの情報を取得します。
PartitionIdsプロパティにてパーティションIdを取得することができます。
パーティション毎に監視を行う際にGetPartitionRuntimeInformationAsyncにてパーティションの情報を取得します。
LastEnqueuedOffsetプロパティにてこのパーティションにエンキューされた最後のオフセット情報を取得できます。
取得開始時にこのオフセットを用いることで起動以前のメッセージを受信しないようにします。
それ以降はCreateReceiverでレシーバーを作成し受信メソッド行い、受信が成功した場合にはオフセットを更新します。
DirectEventReceiveManagerはIObserver
チャットへの組み込み
ChatServerクラスを変更してEvent Hubsを用いてチャットメッセージのスケールアウトに対応します。
前回からの変更点として
SendChatMessageToEventHubsObserverクラス DirectEventReceiveManagerクラスのインスタンスをもって
CreateEventProcessorの戻り値にChatMessageProcessorのインスタンスを常に返すように実装。
また前回は 送信/受信をObserver/Obesrvableで実装したWebSocket管理クラスを相互にSubScribeし合うことでチャットを実現していましたが、管理クラスのSubscribeにEventHubsObserverを繋げることで、チャットメッセージを受信した場合にEvent Hubsに送信されるように変更し、DirectEventReceiveManagerのSubscribeに管理クラスを繋げることでEvent Hubsからメッセージを受信した場合にWebSocketで送信が行われるように変更しました。
Observer/Observableパターンで実装すると繋ぎ方の変更で処理を変更できたりします。
このチェインが実装していると楽しいです(^◇^)
最後にstartupクラスにてChatServer.EventRecieveEventAsyncを実行してDirectEventReceiveManagerクラスのRecieveAsyncを実行してすれば完了です。
この実装の問題点
今回の実装には大きな問題点があります。
実は一つのPartitionへの接続は最大5クライアントまでしか行うことができません。
要するに今回の実装ではWebSocketサーバ5プロセス分までしかスケールアウトすることができないのです。
標準的な受信を実現してるEventProcessorHostでは、Partitionへの接続の排他制御をAzure Blob StorageとEpoch受信という仕組みを用いて行っています。
Epoch受信はpartitionへの接続を行う際にlong値であるEpochを指定して接続を行います。
Epoch接続を行った場合、Epoch値が大きい接続が優先して接続されます。
例えばEpoch10のクライアントがパーティション1に接続していて他のクライアントがEpoch20で接続を行ってきた場合、Epoch10は切断されEpoch20が接続されます。
またEpoch20が接続されている状態でEpoch10のクライアントが接続を行った場合、Epoch10クライアントは接続を行うことができません。
DirectRecieveでもエポックに対応した接続を行うことが可能ですが、別途クライアント間でパーティション毎のエポック管理を行う必要があります。
このようなことから、Event Hubsはスケールアウトのバックグラウンドとしては適切でありませんでした。
最後に
次回は頑張ります(´・ω・`)
今回は以上になります。
ではでは
ASP.NET CoreでWebSocketを使用するサンプルを作ってみたよ( `ー´)ノ
ごあいさつ
新年あけましておめでとうございます。
2017年もよろしくお願いします。
正月太り(´ ・ ω ・ `)
本題
SignalRではなくASP.NET CoreでWebSocketを使ったサンプルアプリを書いてみました。
今回作ったコードはこちらにあります。
ASP.NET CoreでWebSocketサーバを作るには
を使用すれば簡単に作成することができます。
今回のサンプリアプリはクライアントサイドにはVue.jsを使用しています。
解説
完成品の動き
アクセスしたらユーザ名の入力を行って接続を行います。
接続後にチャットで発言を行うと他のクライアントにも表示されてチャットができます。
接続、切断したユーザがいる場合にはその都度メッセージが送信されます。
参考にしたサイト
基本的にここに書かれている通りに作っていけばよい感じです。
ASP.NET CoreでVue使う場合にどうすればよいか考えるのに参考にしました。
使用したnugetパッケージ
- Microsoft.AspNetCore.WebSockets.Server
- Microsoft.AspNetCore.StaticFiles
- System.Reactive
- Newtonsoft.Json
サーバサイド
基本的に前述の参考サイトの通りに作成します。
今回はChatClientクラス ChatServerクラスの2クラスで構成しました。
ChatClientクラス
aspnetcore_webspcket_sample/ChatClient.cs at master · yuka1984/aspnetcore_webspcket_sample · GitHub
System.Net.WebSockets.WebSocketクラスを内包してクライアントとの通信を行うクラスです。
RecieveJoinAsyncでチャット参加メッセージを受け取って、RecieveAsyncでチャットメッセージを継続して受け取ります。
WebSocketのRecieveAsyncメソッドの引数にArraySegmentが使用されています。
WebSocketってプロトコルの仕様上メッセージが分割されることがあるのですが、連続してデータを取得しやすくするためにArraySegmentを使用してバッファ内でセグメント毎に受信します。
単純なテキストメッセージで分割するようなパターンってないと思いますが。
今回はObserver/Observableパターンで送信と受信を実装を行ってみました。
RecieveAsync中に受信したメッセージはIObservable.Subscribeにて購読を行っている人に配信されて、WebSocketがクローズされた場合にはOnCompleteが流れるて購読が解除される形になります。
OnNextが呼ばれるとメッセージをクライアントに送信します。
次にIObserver/IObservableをどのように利用しているかを見てみます。
ChatServerクラス
aspnetcore_webspcket_sample/ChatServer.cs at master · yuka1984/aspnetcore_webspcket_sample · GitHub
public void Map(IApplicationBuilder app) { app.UseWebSockets(); app.Use(Acceptor); }
マップ関数にてIApplicationBuilderへの登録を行っていてリクエストが来た時にAcceptor関数が実行されます。
Accepter関数はTask型になっていますが、この関数が終了するとHttpリクエストが返答されて終了します。
WebSocket接続だった場合にはクローズされます。
ですのでWebSocketを繋ぎっぱなしにしておくにはawaitで留まっている必要があります。
Accepter関数はこのようになっているのですが
まず最初にWebSocketリクエストなのかを判定しています。
そうでなかった場合はMiddlewareチェインの次を実行して関数を終了しています。
これは別にチェインの次を実行する必要はなくてhttpcontextのResponseにステータスコード400を指定してBadReqestにして終了してしまったりしてもOKです。
WebSocketリクエストであった場合には AcceptWebSocketAsyncを実行してWebSocketクラスを取得します。
このWebSocketクラスを使用して、先ほど説明したChatClientクラスを作成します。
ChatClient.RecieveJoinAsyncを実行してチャット参加メッセージを受信します。
参加がOKだった場合には接続中のクライアントに新規参加メッセージを送信し、クライアント間での接続を行っています。
// ほかのクライアントと相互接続 x.Subscribe(client); client.Subscribe(x);
この部分でIObserver/IObservableパターンが活きています。
クライアント間でお互いに購読登録を行うことで、メッセージストリームが流れた時にそのまま送信が行われます。
今回はそのままお互いにSubscribeしていますが、例えばWhereをいれて流れるメッセージを制御したりSelectでメッセージを調整したり色々な事が出来たりします。
Reactiveプログラミング面白い(^^)/
相互接続が完了したら受信待機します。
これだけでサーバ側でチャットが成り立ってしまいます(^◇^)
aspnetcore_webspcket_sample/Startup.cs at master · yuka1984/aspnetcore_webspcket_sample · GitHub
あとはStartupクラスでmiddlewareの登録を行ってあげればOKです。
クライアントサイド
aspnetcore_webspcket_sample/index.html at master · yuka1984/aspnetcore_webspcket_sample · GitHub
このファイル一つです。さぼってhtmlファイルの中にコードも書いちゃいました。
Vue.jsとかbootstrapとか取り込んでいる部分は前述の参考サイトを見てください。
実際のスクリプトはこんな感じです。
あんまり工夫もなくoldなJavaScriptな感じで大変申し訳ないです。
新しめのJavaScriptは今勉強中です。
Htmlはこんな感じ。
結構良い感じで書けます。
Vue.jsって凄くよくできてて感心してます。
今回はあまり使っていませんがC#のMVVMでReactivePropertyを使用したパターンをVue.jsだけで実現できる感じなんですよね。
今回は以上です。 ではでは(^^)/
ASP.NET MVCでVue.jsを使用するWeb開発環境を作って動かしてみよう('ω')ノ
ごあいさつ
年末ですね。皆さんはいかがお過ごしでしょうか。
今年もクリスマスとかいう私には縁の無いイベントを無事何事もなく家で寝て過ごし、来年に向けて断捨離を進めなければいけないのに全く進まず、色々とやってみたい事だけが溜まっていって一つも消化できていない、そんな年末を過ごしています\(^o^)/
はじめに
最近。WebフロントエンドとAzure Mobile AppとAzure Functionsの組み合わせでついぶくというサービスを作っていて今はWebフロントエンドの勉強などをしています。
4年くらい前で止まっていたWebフロントエンドの知識を、今時な?Webフロントエンドな知識に更新することが一つの目標です。
暫くはサービス開発で得た知識を記事にしていきたいと思います。
今回はVisualStudio2015でASP.NET WebAPIプロジェクトにてVue.js + Bootstrapな開発環境の構築を行ってみたいと思います。
インストール関連
まずはnode.jsをインストールします。VS2015にてnode.jsをインストールすることも可能なのですがバージョンが古くて(´・ω・`)なので最新版を入れてしまいます。
コマンドプロンプトにてnode -vを実行してインストールを行ったnodeバージョンが出ればOKです。
次にVisual Studioを開き[ツール]-[オプション]-[プロジェクトおよびソリューション]-[外部Webツール]にて
PATHの順位を一番に持っていきます。
さらに[ツール]-[拡張機能と更新プログラム]にて NPM Task Runnerをインストールします。
さらにWeb Compilerをインストールします。
プロジェクト作成
まずはWeb APIプロジェクトを作成します。
次のBundle処理の削除と余分なnugetパッケージの削除を行います。
BundleConfigクラスのRegusterBundle関数内をコメントアウトしてしまってください。
nugetパッケージマネージャにて以下のパッケージを削除します。
- bootstrap
- jquery
- Modernizr
- Respond
次にViews/Shared/_Layout.cshtmlにてBundleしたファイルの読み込みコードを削除してしまいます。
この状態にてデバッグを開始すると
スタイルがない状態になります。
Bootstrapの組み込み
今回はBootstrapをそのまま使うのではなく
を適用して、さらにscssにてスタイルの管理を行えるようにしてしまいます。
方法はこちらの記事を参考にしました。
まずはpackge.jsonをプロジェクトに追加します。
中身はこんな感じ
{ "version": "0.0.0", "name": "vuesample", "private": true, "devDependencies": { "grunt": "~0.4.5", "grunt-bower-task": "~0.4.0" } }
package.jsonはnpmというnode.js版nuget的なパッケージ管理ツールの設定を記述するためのファイルです。
作成しましたらタスクランナーエクスプローラよりInstallを実行します。
ここから表示できます。
次にbower.jsonファイルをプロジェクトに追加します。
bowerはWebフロントエンドに特化したパッケージマネージャーです。
bower.jsonはその設定ファイルになります。
中身はこんな感じ
{ "name": "VueSample", "private": true, "dependencies": { "jquery": "^3.1.1", "bootstrap-sass": "3.3.7", "Honoka": "^3.3.7" }, "exportsOverride": { "jquery": { "js": "dist/*.*" }, "bootstrap-sass": { "bootstrap.scss": "assets/stylesheets/bootstrap", "bootstrap.fonts": "assets/fonts/bootstrap/*.*", "js": "assets/javascripts/bootstrap.*" }, "Honoka": { "honoka.scss": "scss", "honoka.css": "dist/css", "honoka.font": "dist/fonts", "honoka.js": "dist/js" } } }
exportsOverrideという設定が重要です。 これにより各パッケージのファイルを、展開用にグループ分けしています。
次にgrundfile.jsというJavaScriptファイルをプロジェクトに追加します。
中身はこんな感じ
/// <binding /> module.exports = function (grunt) { grunt.initConfig({ bower: { install: { options: { targetDir: "", cleanTargetDir: false, layout: function (type, component, source) { if (type === "js") { return "Scripts"; } else if (type === "honoka.scss") { return "Content"; } else if (type === "bootstrap.scss") { return "Content/honoka/bootstrap"; } else if (type === "bootstrap.fonts") { return "fonts"; } else { return "__untyped__"; } } } } } }); grunt.registerTask("default", ["bower:install"]); grunt.loadNpmTasks("grunt-bower-task"); };
先ほどbower.jsonのexportsOverrideにて設定したグループごとにレイアウトを設定しています。
これによりreturnで返答しているフォルダにファイルが展開されます。
タスクランナーエクスプローラよりgruntfile.jsのbower:installを実行してください。
もしoperation not permitted, mkdir というエラーが出ていたら手作業でフォルダを作成して再度実行してください。
これらのファイルをプロジェクトに取り込みたいのでcsprojファイルを少し修正します。
csprojファイルをエディタで開いて以下を追加します。
<Content Include="Content\**\*.scss" /> <Content Include="Scripts\**\*.js" /> <Content Include="Scripts\**\*.map" /> <Content Include="fonts\**\*.*" />
これによりプロジェクトツリーのContentフォルダ下にbootstrap.scssファイルが追加されていますので右クリックしてコンパイルをしてみてください。
cssファイルが出来上がっていると思います。
ではViews/Shared/_Layout.cshtmlファイルに作成したcssファイルを指定してデバッグを実行します。
これでようやくスタイルが適用できた状態です。
ここからVue.jsの組み込みと簡単なコーディングを行っていきます。
Vue.jsの組み込み
まずは先ほど作成したpackage.jsonに追記を以下のように行ってください。
"version": "0.0.0", "name": "vuesample", "private": true, "dependencies": { "vue": "^2.1.7", "vue-route": "^1.5.1" }, "devDependencies": { "webpack": "^1.14.0", "webpack-dev-middleware": "^1.9.0", "webpack-hot-middleware": "^2.13.2", "webpack-merge": "^2.0.0", "babel-core": "6.21.0", "babel-loader": "6.2.10", "babel-preset-es2015": "^6.13.2", "grunt": "~0.4.5", "grunt-bower-task": "~0.4.0" }, "scripts": { "webpack": "webpack -d" } }
webpackというツールとbabelというツールのインストール、Vue.jsのインストールを行う設定を追記しました。
webpackは色々なライブラリを参照追加して作成したコードをコンパイルしてブラウザで実行できるスクリプトにしてくれるツールです。
それだけでなくスタイルとか画像とか色々と凄い事をしてくれるのですが詳しくは調べてみてください。
babelは新しい書き方をしているJavaScriptを古い形(ブラウザが理解できる形)にコンパイルしてくれるツールです。
追記後タスクランナーエクスプローラからpackageのインストールを行ってください。
次にプロジェクトにwebpack.config.jsというファイルを追加します。
中身は以下のようにしてください、
var path = require('path'); module.exports = { resolve: { root: path.resolve('./'), alias: { 'vue$': 'vue/dist/vue.common.js' } }, entry: { home: 'VueModels/home/Index.js' }, output: { path: "Scripts/app", filename: "[name].bundle.js" }, themeLoader: { themes: ['./node_modules/vuestrap/theme/bootstrap.scss'], // docs theme, default bootstrap }, module: { loaders: [{ test: /\.js$/, loader: 'babel' }, { test: /\.html$/, loader: 'html' }, { test: /\.json$/, loader: 'json' }] } };
次にプロジェクトに以下の画像のようにVueModelsフォルダ、その下にHomeフォルダを作成し、HomeフォルダにIndex.jsファイルを作成してください。
中身はこのような感じにしてください。
var Vue = require('vue'); var model = { message: "ASP.NET MVCでVue.jsを使用するWeb開発環境を作って動かしてみよう('ω')ノ" }; new Vue({ data: model, }).$mount('#app');
そうしたらタスクランナーエクスプローラからpackage.json/Custom/Webpackを実行してください。
もしWebPackというタスクがないのであればVisualStudioを再起動してみてください。
するとScriptsフォルダの下にapp/home.bundle.jsというファイルが作成されているかと思います。
これがIndex.jsをwebpackでコンパイルしてブラウザで使用できる形にしたファイルです。
Viewを修正して動かしてみましょう。
まずは_layout.cshtmlを修正します。
- classにContainerが設定されているdivタグにid=appを追加
- Script.Renderでコントローラ名.bundle.jsファイルを追加
次にHome/Index.cshtmlを修正します。
Jumbotronの中のPタグの中身を{{message}}と書き換えます。
これでデバッグ実行すると
とVueクラスとhtmlがバインドされるようになります。
ちょっとプログラムしてみよう
では、ちょっとそれっぽくプログラムしてみましょう。
ですがその前に、開発しやすくするためのタスクを追加します。
package.jsonを開きscriptsに"watch": "webpack -d -w"を追加しタスクランナーエクスプローラで実行します。
これを実行するとindex.jsファイルを監視し変更があった場合に自動的にビルドを行ってくれるようになります。
まずはIndex.jsをこのように修正してみましょう。
var Vue = require('vue'); var model = { message: "ASP.NET MVCでVue.jsを使用するWeb開発環境を作って動かしてみよう('ω')ノ", speakers:[] }; new Vue({ data: model, methods: { LoadSpeakers: function() { fetch("http://demo4404797.mockable.io/speakers") .then(function (response) { return response.json(); }) .then(function (json) { model.speakers = json; }); } } }).$mount('#app');
modelクラスにspeakersというプロパティを追加します。speakersは配列として宣言しておきます。
次にvueクラスにmethodsを追加しLoadSpeakersメソッドを追加します。
LoadSpeakersメソッドではfetchを使ってWebAPIにアクセスしてjsonを取得してmodelクラスのspeakersにセットします。
http://demo4404797.mockable.io/speakersにアクセスすればjsonを確認することができます。
次にIndex.cshtmlを修正します。
<div class="jumbotron"> <h1>ASP.NET</h1> <p class="lead">{{message}}</p> <p><button v-on:click="LoadSpeakers" class="btn btn-primary btn-lg">Get Speakers »</button></p> </div> <div class="row"> <div class="media" v-for="speaker in speakers"> <a class="media-left"> <img style="width: 128px" class="media-object" v-bind:src="speaker.Avatar"> </a> <div class="media-body"> <h4 class="media-heading">{{speaker.Name}} ({{speaker.Title}})</h4> {{speaker.Description}} </div> </div> </div>
jumbotron内にあったaタグをbuttonタグに書き換えてv-on:click="LoadSpeakers"を追加します。
これによりボタンがクリックされた場合には先ほどのLoadSpeakersメソッドが呼ばれます。
次にjumbotronに続くrowの中身をすべて消しmedia objectを追加します。
media objectはbootstrapのLayoutで画像付きの紹介文を書いたりするのに適しています。
mediaクラスを設定したdivタグにv-forアトリビュートを追加し speakersプロパティをループするように設定します。 これによりリストレンダリングを行うことができます。
子要素ではimgタグにv-bind:srcで画像アドレスをバインド、h4タグには名前や役割を表記、説明文にはDescriptionをバインドさせました。
では、デバッグ実行してみましょう。
ボタンを押してLoadSpeakersが実行されると
このような感じになると思います。
おしまいに
私自身始めたばっかりで全然知識が追い付いていない状態ですが、特に環境構築に関しては苦労しています。
この先VSでもWebフロントエンド開発が行いやすいように色々と進化していってくれると嬉しいなぁと思います。