public Task Heartbeat(byte[] data) { HeartbeatData heartbeatData = HeartbeatDataDotNetSerializer.Deserialize(data); IGameGrain game = GrainFactory.GetGrain <IGameGrain>(heartbeatData.Game); return(game.UpdateGameStatus(heartbeatData.Status)); }
public Task HeartbeatAsync(byte[] data) { var heartbeatData = HeartbeatDataDotNetSerializer.Deserialize(data); var game = GrainFactory.GetGrain <IGameGrain>(heartbeatData.GameKey); return(game.UpdateGameStatusAsync(heartbeatData.Status)); }
/// <summary> /// Simulates periodic updates for a bunch of games similar to what game consoles of mobile apps do. /// </summary> static void Main(string[] args) { try { GrainClient.Initialize(); int nGames = 10; // number of games to simulate int nPlayersPerGame = 4; // number of players in each game TimeSpan sendInterval = TimeSpan.FromSeconds(2); // interval for sending updates int nIterations = 100; // Precreate base heartbeat data objects for each of the games. // We'll modify them before every time before sending. HeartbeatData[] heartbeats = new HeartbeatData[nGames]; for (int i = 0; i < nGames; i++) { heartbeats[i] = new HeartbeatData(); heartbeats[i].Game = Guid.NewGuid(); for (int j = 0; j < nPlayersPerGame; j++) { heartbeats[i].Status.Players.Add(GetPlayerId(i * nPlayersPerGame + j)); } } int iteration = 0; IPresenceGrain presence = PresenceGrainFactory.GetGrain(0); // PresenceGrain is a StatelessWorker, so we use a single grain ID for auto-scale List <Task> promises = new List <Task>(); while (iteration++ < nIterations) { Console.WriteLine("Sending heartbeat series #{0}", iteration); promises.Clear(); try { for (int i = 0; i < nGames; i++) { heartbeats[i].Status.Score = String.Format("{0}:{1}", iteration, iteration > 5 ? iteration - 5 : 0); // Simultate a meaningful game score // We serialize the HeartbeatData object to a byte[] only to simulate the real life scenario where data comes in // as a binary blob and requires an initial processing before it can be routed to the proper destination. // We could have sent the HeartbeatData object directly to the game grain because we know the game ID. // For the sake of simulation we just pretend we don't. Task t = presence.Heartbeat(HeartbeatDataDotNetSerializer.Serialize(heartbeats[i])); promises.Add(t); } // Wait for all calls to finish. // It is okay to block the thread here because it's a client program with no parallelism. // One should never block a thread in grain code. Task.WaitAll(promises.ToArray()); } catch (Exception exc) { Console.WriteLine("Exception: {0}", exc.GetBaseException()); } Thread.Sleep(sendInterval); } } catch (Exception exc) { Console.WriteLine("Unexpected Error: {0}", exc.GetBaseException()); } }
private async Task RunAsync() { // number of games to simulate var nGames = 10; // number of players in each game var nPlayersPerGame = 4; // interval for sending updates var sendInterval = TimeSpan.FromSeconds(2); // number of updates to send var nIterations = 100; // Precreate base heartbeat data objects for each of the games. // We'll modify them before every time before sending. var heartbeats = new HeartbeatData[nGames]; for (var i = 0; i < nGames; i++) { heartbeats[i] = new HeartbeatData( Guid.NewGuid(), new GameStatus( Enumerable.Range(0, nPlayersPerGame).Select(j => GetPlayerId(i * nPlayersPerGame + j)).ToImmutableHashSet(), string.Empty)); } var iteration = 0; // PresenceGrain is a StatelessWorker, so we use a single grain ID for auto-scale var presence = _client.GetGrain <IPresenceGrain>(0); var promises = new Task[nGames]; while (++iteration < nIterations) { _logger.LogInformation("Sending heartbeat series #{@Iteration}", iteration); try { for (var i = 0; i < nGames; i++) { // Simultate a meaningful game score heartbeats[i] = heartbeats[i].WithNewScore($"{iteration}:{(iteration > 5 ? iteration - 5 : 0)}"); // We serialize the HeartbeatData object to a byte[] only to simulate the real life scenario where data comes in // as a binary blob and requires an initial processing before it can be routed to the proper destination. // We could have sent the HeartbeatData object directly to the game grain because we know the game ID. // For the sake of simulation we just pretend we don't. promises[i] = presence.HeartbeatAsync(HeartbeatDataDotNetSerializer.Serialize(heartbeats[i])); } // Wait for all calls to finish. await Task.WhenAll(promises); // check for cancellation request if (_executionCancellation.IsCancellationRequested) { return; } } catch (Exception error) { _logger.LogError(error, "Error while sending hearbeats to Orleans cluster"); } try { await Task.Delay(sendInterval, _executionCancellation.Token); } catch (OperationCanceledException) { return; } } }
private static async Task RunAsync(string[] args, CancellationToken token) { // build the orleans client var client = new ClientBuilder() .UseLocalhostClustering() .ConfigureLogging(_ => { _.AddConsole(); }) .Build(); // keep a logger for general use var logger = client.ServiceProvider.GetService <ILogger <Program> >(); // connect to the orleans cluster var attempt = 0; var maxAttempts = 100; var delay = TimeSpan.FromSeconds(1); await client.Connect(async error => { if (++attempt < maxAttempts) { logger.LogWarning(error, "Failed to connect to Orleans cluster on attempt {@Attempt} of {@MaxAttempts}.", attempt, maxAttempts); await Task.Delay(delay, token); return(true); } else { logger.LogError(error, "Failed to connect to Orleans cluster on attempt {@Attempt} of {@MaxAttempts}.", attempt, maxAttempts); return(false); } }); // number of games to simulate var nGames = 10; // number of players in each game var nPlayersPerGame = 4; // interval for sending updates var sendInterval = TimeSpan.FromSeconds(2); // number of updates to send var nIterations = 100; // Precreate base heartbeat data objects for each of the games. // We'll modify them before every time before sending. var heartbeats = new HeartbeatData[nGames]; for (var i = 0; i < nGames; i++) { heartbeats[i] = new HeartbeatData( Guid.NewGuid(), new GameStatus( Enumerable.Range(0, nPlayersPerGame).Select(j => GetPlayerId(i * nPlayersPerGame + j)).ToImmutableHashSet(), string.Empty)); } var iteration = 0; // PresenceGrain is a StatelessWorker, so we use a single grain ID for auto-scale var presence = client.GetGrain <IPresenceGrain>(0); var promises = new Task[nGames]; while (++iteration < nIterations) { logger.LogInformation("Sending heartbeat series #{@Iteration}", iteration); try { for (var i = 0; i < nGames; i++) { // Simultate a meaningful game score heartbeats[i] = heartbeats[i].WithNewScore($"{iteration}:{(iteration > 5 ? iteration - 5 : 0)}"); // We serialize the HeartbeatData object to a byte[] only to simulate the real life scenario where data comes in // as a binary blob and requires an initial processing before it can be routed to the proper destination. // We could have sent the HeartbeatData object directly to the game grain because we know the game ID. // For the sake of simulation we just pretend we don't. promises[i] = presence.HeartbeatAsync(HeartbeatDataDotNetSerializer.Serialize(heartbeats[i])); } // Wait for all calls to finish. await Task.WhenAll(promises); } catch (Exception error) { logger.LogError(error, "Error while sending hearbeats to Orleans cluster"); } await Task.Delay(sendInterval, token); } }