Commit 40c5d21d authored by Marc Gravell's avatar Marc Gravell

tidy up kestrel code; flush more eagerly when writing

parent 7906ad3b
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Connections;
using Microsoft.Extensions.Logging;
using StackExchange.Redis.Server; using StackExchange.Redis.Server;
namespace KestrelRedisServer namespace KestrelRedisServer
{ {
public class RedisConnectionHandler : ConnectionHandler public class RedisConnectionHandler : ConnectionHandler
{ {
private readonly MemoryCacheRedisServer _server; private readonly RespServer _server;
public RedisConnectionHandler(ILogger<RedisConnectionHandler> logger) public RedisConnectionHandler(RespServer server) => _server = server;
{ public override Task OnConnectedAsync(ConnectionContext connection)
_server = new MemoryCacheRedisServer(); => _server.RunClient(connection.Transport);
}
public override async Task OnConnectedAsync(ConnectionContext connection)
{
var client = _server.AddClient();
try
{
while (true)
{
var read = await connection.Transport.Input.ReadAsync();
var buffer = read.Buffer;
bool makingProgress = false;
while (_server.TryProcessRequest(ref buffer, client, connection.Transport.Output))
{
makingProgress = true;
await connection.Transport.Output.FlushAsync();
}
connection.Transport.Input.AdvanceTo(buffer.Start, buffer.End);
if (!makingProgress && read.IsCompleted) break;
}
}
catch (ConnectionResetException) { } // swallow
finally
{
_server.RemoveClient(client);
connection.Transport.Input.Complete();
connection.Transport.Output.Complete();
}
}
} }
} }
using Microsoft.AspNetCore.Builder; using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using StackExchange.Redis.Server;
namespace KestrelRedisServer namespace KestrelRedisServer
{ {
public class Startup public class Startup : IDisposable
{ {
RespServer _server = new MemoryCacheRedisServer();
// This method gets called by the runtime. Use this method to add services to the container. // This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services) public void ConfigureServices(IServiceCollection services)
{ => services.Add(new ServiceDescriptor(typeof(RespServer), _server));
}
public void Dispose() => _server.Dispose();
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env) public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{ {
if (env.IsDevelopment()) if (env.IsDevelopment()) app.UseDeveloperExceptionPage();
{ app.Run(context => context.Response.WriteAsync(_server.GetStats()));
app.UseDeveloperExceptionPage();
}
app.Run(async (context) =>
{
await context.Response.WriteAsync("Redis-ish server should be running");
});
} }
} }
} }
...@@ -16,12 +16,29 @@ public static bool IsMatch(string pattern, string key) ...@@ -16,12 +16,29 @@ public static bool IsMatch(string pattern, string key)
protected RedisServer(int databases = 16, TextWriter output = null) : base(output) protected RedisServer(int databases = 16, TextWriter output = null) : base(output)
{ {
if (databases < 1) throw new ArgumentOutOfRangeException(nameof(databases)); if (databases < 1) throw new ArgumentOutOfRangeException(nameof(databases));
Databases = databases;
var config = ServerConfiguration; var config = ServerConfiguration;
config["timeout"] = "0"; config["timeout"] = "0";
config["slave-read-only"] = "yes"; config["slave-read-only"] = "yes";
config["databases"] = databases.ToString(); config["databases"] = databases.ToString();
config["slaveof"] = ""; config["slaveof"] = "";
} }
protected override void AppendStats(StringBuilder sb)
{
base.AppendStats(sb);
sb.Append("Databases: ").Append(Databases).AppendLine();
lock (ServerSyncLock)
{
for (int i = 0; i < Databases; i++)
{
try
{
sb.Append("Database ").Append(i).Append(": ").Append(Dbsize(i)).AppendLine(" keys");
}
catch { }
}
}
}
public int Databases { get; } public int Databases { get; }
[RedisCommand(-3)] [RedisCommand(-3)]
...@@ -374,7 +391,7 @@ StringBuilder AddHeader() ...@@ -374,7 +391,7 @@ StringBuilder AddHeader()
break; break;
case "Stats": case "Stats":
AddHeader().Append("total_connections_received:").Append(TotalClientCount).AppendLine() AddHeader().Append("total_connections_received:").Append(TotalClientCount).AppendLine()
.Append("total_commands_processed:").Append(CommandsProcesed).AppendLine(); .Append("total_commands_processed:").Append(TotalCommandsProcesed).AppendLine();
break; break;
case "Replication": case "Replication":
AddHeader().AppendLine("role:master"); AddHeader().AppendLine("role:master");
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment