• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
AWS + Windows(C#)で構築する.NET最先端技術によるハイパフォーマンスウェブアプリケーション開発実践
 

AWS + Windows(C#)で構築する.NET最先端技術によるハイパフォーマンスウェブアプリケーション開発実践

on

  • 380 views

AWS Summit Tokyo 2014

AWS Summit Tokyo 2014

Statistics

Views

Total Views
380
Views on SlideShare
354
Embed Views
26

Actions

Likes
9
Downloads
1
Comments
0

1 Embed 26

https://twitter.com 54

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    AWS + Windows(C#)で構築する.NET最先端技術によるハイパフォーマンスウェブアプリケーション開発実践 AWS + Windows(C#)で構築する.NET最先端技術によるハイパフォーマンスウェブアプリケーション開発実践 Presentation Transcript

    • @仕事 http://grani.jp/ C# @個人活動 http://neue.cc/ @neuecc https://www.facebook.com/neuecc
    • 神獄のヴァルハラゲート モンスタハンターロアオブカード
    • AWS+C#によるウェブソーシャルゲーム 汎用的
    • C# 50% AWS 20% その他 30%
    • using
    • 何故C#? リリース後わずか半年でC#に全面移行
    • Windows WinForms, WPF Mac Xamarin.Mac Windows Tablet Windows Store Application Web Application ASP.NET MVC/WebAPI, OWIN Cloud Windows Azure, AWS C# Everywhere Game Unity, PlayStation Mobile SDK Mobile Xamarin.iOS Xamarin.Android Windows Phone 8 SDK Embedded Windows Embedded .NET Micro Framework NUI Kinect, LeapMotion
    • Windows WinForms, WPF Mac Xamarin.Mac Windows Tablet Windows Store Application Web Application ASP.NET MVC/WebAPI, OWIN Cloud Windows Azure, AWS C# Everywhere - Current Game Unity, PlayStation Mobile SDK Mobile Xamarin.iOS Xamarin.Android Windows Phone 8 SDK Embedded Windows Embedded .NET Micro Framework NUI Kinect, LeapMotion
    • Windows WinForms, WPF Mac Xamarin.Mac Windows Tablet Windows Store Application Web Application ASP.NET MVC/WebAPI, OWIN Cloud Windows Azure, AWS C# Everywhere - Future Game Unity, PlayStation Mobile SDK Mobile Xamarin.iOS Xamarin.Android Windows Phone 8 SDK Embedded Windows Embedded .NET Micro Framework NUI Kinect, LeapMotion
    • on AWS
    • 問題ない。 C#によるウェブソーシャルゲーム開発 現在の規模感 100 アプリケーションサーバー 10,000 リクエスト/秒 100,000,000 ページビュー/日
    • IIS8 (EC2 Windows Server 2012) MySQL 5.6(RDS) Redis(EC2 Amazon Linux)
    • Database
    • NoSQLでいい? RDBMSの利点
    • RDSこそAWSを使う最大の理由! SQL Server vs MySQL AWS + C#の企業としては、 RDSとしての良さのほうを選ぶ
    • r3.8xlargeったら最強ね!
    • 機能単位の垂直分割を採用 水平分割は避ける
    • 機能単位の垂直分割を採用 水平分割は避ける ヴァルハラゲートレベルの負荷でも、垂直分割で 耐えられているので(r3.8xlargeは素晴らしい)、ほ とんどのアプリケーションは、大きなデメリット を抱える水平分割する必要性はないのでは?
    • DB管理はGUIツールを使いたい 水平分割はツールと相性最悪
    • アプリケーションからはMasterのみ参照 同一AvailabilityZoneへ配置する
    • public interface ITypedConnection : IDisposable { DbConnection Slave { get; } DbConnection Master { get; } } public BattleEntity SelectById(BattleConnection battle, int id) { return battle.Master.Query<BattleEntity>("select * from battle where id = @id", new { id }); } public UserEntity SelectById(UserInfoConnection user, int id) { return user.Master.Query<UserEntity>("select * from user where id = @id", new { id }); } コーディング時のミス防止(間違った接続の利用はコンパイル時に弾かれる) テーブルの別DBへの分割時にも完全にコンパイルチェックが効くので安全に行える C#(というか型付き言語)を使う利点
    • クエリは生SQLを手書き Dapper https://code.google.com/p/dapper-dot-net/ http://neue.cc/2013/08/06_423.html connection.Query<Dog>("select * from dogs where id = @id", new { id = 100 })
    • テーブルと1:1で関連づいたクラス T4テンプレート + ADO.NET GetSchema
    • Int → Enumへの変換などは生成後、手で修 正している。半自動生成、初回の雛形作成、 というぐらいの位置づけ [Serializable] [DataContract] public class GuideTemplateMaster { [DataMember(Order = 1)] public GuideCode GuideId { get; private set; } [DataMember(Order = 2)] public Int32 No { get; private set; } [DataMember(Order = 3)] public String Title { get; private set; } [DataMember(Order = 4)] public String Body { get; private set; } [DataMember(Order = 5)] public String LinkController { get; private set; } [DataMember(Order = 6)] public String LinkAction { get; private set; } [DataMember(Order = 7)] public Int32 Priority { get; private set; } [DataMember(Order = 8)] public NavicoCharacter NaviId { get; private set; } [DataMember(Order = 9)] public NavicoFaceType NaviPattern { get; private set; } public override string ToString() { return "" + "GuideId : " + GuideId + "|" + "No : " + No + "|" + "Title : " + Title + "|" + "Body : " + Body + "|" + "LinkController : " + LinkController + "|" + "LinkAction : " + LinkAction + "|" + "Priority : " + Priority + "|" + "NaviId : " + NaviId + "|"
    • 永久に保存する領域 – データベースなど 期間保存 – Memcached/RedisなどExpire付き リクエスト単位 - HttpContext.Items アプリケーション単位 – Static変数
    • 永久に保存する領域 – データベースなど 期間保存 – Memcached/RedisなどExpire付き リクエスト単位 - HttpContext.Items アプリケーション単位 – Static変数
    • アイテム名など不変の情報はキャッシュ public static class GuideTemplateMasterCache { public static readonly ReadOnlyCollection<GuideTemplateMaster> All; public static readonly ReadOnlyDictionary<Tuple<GuideCode, Int32>, GuideTemplateMas public static readonly ILookup<GuideCode, GuideTemplateMaster> ByGuideCode; static GuideTemplateMasterCache() { using (var connection = new GeneralConnection()) { All = connection.Master.QueryEnumerable<GuideTemplateMaster>( "select * from GuideTemplateMaster").ToList().AsReadOnly(); } ByGuideIdAndNo = All.ToDictionary(x => Tuple.Create(x.GuideId, x.No)).AsReadOnl ByGuideCode = All.ToLookup(x => x.GuideId); } } キャッシュ用コードもインデックス見 て使い方が判別できるものも、テーブ ル定義と一緒に自動生成してしまう (マスタじゃない普通のテーブルに関 しても、インデックスを見てデータ ベースアクセサの雛形は自動生成して ます)
    • 結合はアプリケーション側で LINQ to Objectsで簡単
    • Redis
    • インメモリKey-Valueストア RDSの不得意な部分を補える
    • 用途毎のグループ分けと単純分散 定時(21:00~21:30, 22:00~22:30など)開催のバト ルの時だけ忙しいがそれ以外はほぼ0という極 端なグラフになるBattleグループ、など
    • Slave vs ElastiCache Redis
    • Protocol Buffers Speed?
    • Performance + Async
    • Time To First Byte
    • 言語構文レベルでサポートされる非同期 var names = Members.Select(x => new { Name = x.GetName() }) .ToArray(); var names = await Members.Select(async x => new { Name = await x.GetNameAsync() }) .WhenAll(); Membersが10人だとして、GetNameが 2msかかると、同期だと10 * 2 = 20ms 非同期で一気に同時に取得すれば 2ms で済む
    • // 例えばmemcachedの場合 var memcached = new MemcachedClient(); // 3回アクセスがあって辛ぽよ var a = memcached.Get("hoge"); // +10ms = 10ms var b = memcached.Get("hage"); // +10ms = 20ms var c = memcached.Get("huga"); // +10ms = 30ms // 1度に問い合わせて、分配 var all = memcached.Get(new[] { "hoge", "hage", "huga" }); // +10ms var a2 = all["hoge"]; var b2 = all["hage"]; var c2 = all["huga"]; 別に非同期構文とかなくてもできるじゃん!?
    • // 例えばmemcachedの場合 var memcached = new MemcachedClient(); // 3回アクセスがあって辛ぽよ var a = memcached.Get("hoge"); // +10ms = 10ms var b = memcached.Get("hage"); // +10ms = 20ms var c = memcached.Get("huga"); // +10ms = 30ms // 1度に問い合わせて、分配 var all = memcached.Get(new[] { "hoge", "hage", "huga" }); // +10ms var a2 = all["hoge"]; var b2 = all["hage"]; var c2 = all["huga"]; でもIncrとか、Get以外のコマンドは? それに、こうしたコードってオブジェク トモデルでまとめにくい! 性能優先 vs 設計優先の対立になるの?
    • 全コマンドがパイプライン化可能 Client-Server間で4回の応答待ちが発生 パイプラインで呼ぶと、全部まとまってコマンド飛ば せるので往復遅延時間が削減
    • 全てが非同期で自動でパイプライン化される var a = redis.TryGet("hoge"); // Taskなのでひどぅーき var b = redis.TryGet("huga"); var c = redis.TryGet("hage"); await Task.WhenAll(a, b, c); // 10ms
    • var frontHPs = await field.OwnGuild.Members .Where(x => x.Position == Position.Front) .Select(async x => new { Name = await x.Name, CurrentHP = (await x.UserStatus).CurrentHP }) .WhenAll(); x.Nameやx.UserStatusはRedisへの 通信、こうして書いたコードは、自 動的にパイプライン化されて非同期 実行されている // 自分の実行可能(TP不足じゃないとか)なアビリティをActionTypeでグループ分け var abilities = (await field.OwnStatus.GetCommandAbilities()) .Where(x => x.CanExecute == CanExecuteReason.CanExecute) .GroupBy(x => x.ActionType); LINQと相性良い、 IntelliSensable超大事 そうしたLINQableのための 設計と性能が両立できる
    • Log for Performance
    • 外部通信(HTTP, SQL, Redis)を全て記録する アプリ側から行う利点 http://neue.cc/2013/07/30_420.html
    • public class HttpProfilingHandler : DelegatingHandler { static readonly Logger httpLogger = NLog.LogManager.GetLogger("Http"); public HttpProfilingHandler() : base(new HttpClientHandler()) { } public HttpProfilingHandler(HttpMessageHandler innerHandler) : base(innerHandler){ } protected override async Task<HttpResponseMessage> SendAsync( HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) { // 通信の前後をStopwatchで測る var sw = Stopwatch.StartNew(); var result = await base.SendAsync(request, cancellationToken).ConfigureAwait(false); sw.Stop(); // 以下に好きなようにログ仕込む、例えばJSON化 httpLogger.Trace(ApplicationPerformanceLog.ToJson( DateTime.Now, request.Method.ToString(), request.RequestUri.ToString(), sw.ElapsedMilliseconds)); return result; } HttpClientに対してDelegatingHandlerを挟むこ とで処理の前後を簡単にフックできる new HttpClient(new HttpProfilingHandler());
    • public class LoggingDbProfiler : IDbProfiler { // 中略 // コマンドが完了された時に呼ばれる public void ExecuteFinish(System.Data.IDbCommand pro ExecuteType executeType, S { commandText = profiledDbCommand.CommandText; if (executeType != ExecuteType.Reader) { stopwatch.Stop(); sqlLogger.Trace(Newtonsoft.Json.JsonConvert. { date = DateTime.Now, command = executeType, key = commandText, ms = stopwatch.ElapsedMilliseconds }, Newtonsoft.Json.Formatting.None)); } } } MiniProfilerに用意されている IDbProfilerをカスタムし て,ADO.NETのコネクションとして 使うことで自由に仕込める var conn = new ProfiledDbConnection(new SqlConnection(), new LoggingDbProfiler());
    • CloudStructures(自社製のRedisラ イブラリ)に用意されてるプロファ イラの口に通すことで、送った/受 け取ったオブジェクトなどがモニ タできる public class RedisProfiler : ICommandTracer { static readonly Logger redisLogger = NLog.LogMan Stopwatch stopwatch; RedisSettings usedSettings; public void CommandStart(RedisSettings usedSetti { this.usedSettings = usedSettings; stopwatch = Stopwatch.StartNew(); } public void CommandFinish(object sentObject, obj { stopwatch.Stop(); var ms = (long)System.Math.Round(stopwatch.E redisLogger.Trace(ApplicationPerformanceLog .ToJsonWithHost(DateTime.Now, usedSettings.Host, command, key, ms)); } }
    • 記録したら簡単に見れなければならない Glimpse http://getglimpse.com/
    • 目に見えてRedis並列実行 全体の実行時間のほか、 あらゆるメトリクスを 常時可視化
    • 開発環境上では、常に全SQL発行に 対して、explain結果も出すように している(手動でやるようだと絶 対にやらないので、機械的に自動 でやって、常に見えるところに置 かなければならない) 明らかにヤヴァそうなもの(Using filesortとか)は警告する。これによ り、開発者が「開発中」に、自分 で気づけるように誘導
    • ・同一キーの重複時警告 ・送信、受信オブジェクトのダンプ ・オブジェクトサイズ ・Expire残り時間 ・通信時間 などの表示
    • Workflow
    • Git + GitHub(Business) Jenkins Deploy https://github.com/guitarrapc/valentia
    • Next Generation Log Management & Analytics ログをクエリ
    • 最高のモニタリングSaaS 自社製プラグインで Performance Counterなどの情報 も集積・表示 SDKを叩いてアプリ 側からRedisの利用 具合を可視化
    • Analytics
    • アプリケーション分析のためのロギング Semantic Logging Application Block https://slab.codeplex.com/ Tableau
    • アプリケーション分析のためのロギング Semantic Logging Application Block https://slab.codeplex.com/ Tableau とかやってたら、東京リージョンに 来たKinesisがきになるぅぅぅ!
    • Conclusion
    • C# + AWSは現実解 構成は堅く、シンプルに 環境は常に最新に