Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master'
Browse files Browse the repository at this point in the history
Conflicts:
	HashServer/Program.cs
  • Loading branch information
FoKycHuK committed Mar 28, 2015
2 parents c9bab25 + 986b3a6 commit 6a202d9
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 11 deletions.
22 changes: 22 additions & 0 deletions Balancer/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# HTTP-балансировщик

На базе кода асинхронного Proxy-сервера, который мы писали на занятии, необходимо реализовать Balancer - балансировщик http-запросов. Он должен уметь:

* Читать топологию кластера однотипных серверов, умеющих отвечать на http GET-запросы на url-ы вида:
http://\<server\>:\<port\>/method?query=\<q\> Топология задается в файле настроек как построчное перечисление записей вида host:port. Пример содержимого файла настроек:
```
127.0.0.1:20000
localhost:20001
127.0.0.1:20002
```
* Слушать http-префикс /method и при получении запроса от клиента, случайным образом выбирать любую реплику кластера и проксировать запрос клиента на нее. Например:
```
http://<balancer_ip>:<balancer_port>/method?qqq=lalala
```
должен проксироваться на одну из случайно выбранных реплик, например
```
http://127.0.0.1:20001/method?qqq=lalala
```
* В случае ошибки (отсутствия соединения до реплики, сетевой ошибки при передаче данных, возврата репликой 500-го HTTP-кода ошибки, …), а также в случае таймаута ожидания ответа от реплики сервера, балансировщик должен выбирать следующую случайную реплику и повторять запрос к ней. Повторять до тех пор, пока какая-нибудь реплика успешно не ответит. Если ни одна реплика так и не ответит успешно на запрос, балансировщик должен вернуть клиенту HTTP-код ошибки 500.
* В случае, если клиент в HTTP-заголовке _Accept-Encoding_ передает значение **deflate** (то есть сообщает балансировщику, что поддерживает сжатие передаваемых данных алгоритмом deflate), балансировщик перед тем как отдавать результат обработки запроса клиенту, должен осуществлять сжатие ответа реплики кластера, например, с помощью DeflateStream-а, не забывая указывать о примененном deflate-сжатии клиенту с помощью HTTP-заголовка _Content-Encoding_ в ответе.
* По желанию, можно реализовать алгоритм “серых списков”, когда балансировщик запоминает на некоторое время, что реплика не ответила успешно и в течение этого времени не делает запросов к этой реплике, чтобы не тратить зря время на таймаут. В случае, если все реплики кластера попадают в серый список, а от клиента приходит запрос, балансировщик должен все же попытаться получить ответ хоть от какой-нибудь реплики из серого списка.
50 changes: 39 additions & 11 deletions HashServer/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,31 @@ namespace HashServer
{
class Program
{
private static int delayMs = 1000;

static void Main(string[] args)
{
XmlConfigurator.Configure();
try
{
foreach (var port in ports)
{
var listener = new Listener(port, "method", OnContextAsync);
listener.Start();
var listener = new Listener(port, "method", OnContextAsync);
listener.Start();

var listenerSync = new ListenerSync(port, "methodSync", OnContext);
listenerSync.Start();

log.InfoFormat("Server started on port {0}", port);
log.InfoFormat("Server started!");
Console.WriteLine("Enter server delay in ms");
while (true)
{
var timeToSleepString = Console.ReadLine();
int timeToSleep;
if (int.TryParse(timeToSleepString, out timeToSleep))
delayMs = timeToSleep;
else
Console.WriteLine("Couldn't parse \"{0}\" as valid int.", timeToSleepString);
Console.WriteLine("Delay is {0} ms", delayMs);
}
new ManualResetEvent(false).WaitOne();
}
catch(Exception e)
{
Expand All @@ -40,8 +52,7 @@ private static async Task OnContextAsync(HttpListenerContext context)
log.InfoFormat("{0}: received {1} from {2}", requestId, query, remoteEndPoint);
context.Request.InputStream.Close();

await Task.Delay(1000);
// Thread.Sleep(1000);
await Task.Delay(delayMs);

var hash = Convert.ToBase64String(CalcHash(Encoding.UTF8.GetBytes(query)));
var encryptedBytes = Encoding.UTF8.GetBytes(hash);
Expand All @@ -51,15 +62,32 @@ private static async Task OnContextAsync(HttpListenerContext context)
log.InfoFormat("{0}: {1} sent back to {2}", requestId, hash, remoteEndPoint);
}

private static void OnContext(HttpListenerContext context)
{
var requestId = Guid.NewGuid();
var query = context.Request.QueryString["query"];
var remoteEndPoint = context.Request.RemoteEndPoint;
log.InfoFormat("{0}: received {1} from {2}", requestId, query, remoteEndPoint);
context.Request.InputStream.Close();

Thread.Sleep(delayMs);

var hash = Convert.ToBase64String(CalcHash(Encoding.UTF8.GetBytes(query)));
var encryptedBytes = Encoding.UTF8.GetBytes(hash);

context.Response.OutputStream.WriteAsync(encryptedBytes, 0, encryptedBytes.Length);
context.Response.OutputStream.Close();
log.InfoFormat("{0}: {1} sent back to {2}", requestId, hash, remoteEndPoint);
}

private static byte[] CalcHash(byte[] data)
{
using(var hasher = new HMACMD5(Key))
return hasher.ComputeHash(data);
}

private const int defaultPort = 20000;
private static readonly int[] ports = new int[] { 21612 };//, 21613, 21614, 21615, 21616 };
private const int port = 20000;
private static readonly byte[] Key = Encoding.UTF8.GetBytes("Контур.Шпора");
private static readonly ILog log = LogManager.GetLogger(typeof(Program));
}
}
}

0 comments on commit 6a202d9

Please sign in to comment.