Говоря о методе HTTP
Чтобы реализовать HTTP-сервер (или клиент) с помощью netty, вы должны сначала понять протокол HTTP.
HTTP используется в вычислительной модели клиент-сервер какответ на запроспротокол.
Например, веб-браузер может быть клиентом, а приложение, работающее на компьютере, на котором размещен веб-сайт, может быть сервером. Клиент отправляет сообщение HTTP-запроса на сервер.
Сервер предоставляет ресурсы, такие как HTML-файлы и другой контент, или выполняет другие функции от имени клиента, возвращая клиенту ответное сообщение. Ответ содержит информацию о статусе завершения запроса, а также может содержать запрошенное содержимое в теле сообщения.
Что такое метод HTTP?
Любой, кто писал веб-формы, должен быть знаком с GET и POST, но вы знаете, что такоеGETиPOSTНе так ли!? Сегодняшние инструменты веб-дизайна достаточно развиты, и вам даже не нужно прикасаться к синтаксису HTML, чтобы завершить масштабный веб-сайт. Постепенно многие люди забывают принцип реализации нижнего уровня HTTP, что приводит к сбоям в работе. в случае ошибок Отлаживайте корректно.
В первые дни при написании синтаксиса HTML-форм были написаны следующие методы записи, но большинство разработчиков программного обеспечения использовали POST для передачи формы.
<form action="" method="POST/GET">
</form>
Однако, чтобы получить переменные формы в нашей веб-программе, нам нужно только вызвать методы, уже инкапсулированные системой, такие как PHP, используя $_REQUEST, JAVA, используя getParameter(), ASP, используя Request.Form() и так далее. . Из приведенного выше метода кажется, что использование POST или GET не очень важно. Многие веб-инженеры помнят использование методов форм, таких как «POST может передавать больше данных», «Использовать POST при отправке файлов в форме», «POST безопаснее, чем GET» и другие странные концепции.
На самом деле, есть разница между использованием POST или GET.Давайте сначала объясним метод HTTP.В версии HTTP 1.1 определены восемь методов, а именно:
-
OPTIONS
-
GET
-
HEAD
-
POST
-
PUT
-
DELETE
-
TRACE
-
CONNECT
МОЙ БОГ! Эти методы кажутся незнакомыми. Форма, которую мы используем, использует только два из этих методов (GET/POST), а другие методы используются редко, но в архитектуре дизайна RESTful будет использоваться больше методов для упрощения дизайна.
GET- и POST-методы
В качестве примера, если бы HTTP представлял собой механизм отправки писем в нашей реальной жизни сегодня.
:speaker: Тогда формат конверта HTTP. Назовем содержимое вне конверта http-заголовком, а письмо внутри конверта телом сообщения, тогда HTTP-метод — это правила доставки письма, которые нужно сообщить почтальону.
Предположим, GET указывает, что конверт не должен содержать букв.Так же, как и на открытке, вы можете писать информацию для доставки на конверте (http-header), пока он не будет заполнен, и цена не будет дешевле. Однако POST - это метод отправки писем с конвертами (конверты имеют содержимое).Конверты могут быть не только написаны, но информация или файлы, которые вы хотите отправить, также могут быть помещены в конверт (тело сообщения), что более важно. дорогой.
При использовании GET мы напрямую добавляем данные для отправки на адрес (URL), который мы хотим отправить, с помощью строки запроса (метод кодирования Key/Vaule), а затем отправляем их почтальону.
При использовании POST адрес доставки (URL) пишется на конверте, а отправляемая информация пишется на другом листе почтовой бумаги.
ПОЛУЧИТЬ метод
Далее, позвольте мне представить фактическую операцию:
Давайте сначала посмотрим, как GET передает данные.Когда мы отправляем форму GET, следующий пример:
<form method="get" action="">
<input type="text" name="id" />
<input type="submit" />
</form>
Когда форма отправляется, URL-адрес браузера становится "xxx.toright.com/?id=010101", браузер автоматически преобразует содержимое формы в строку запроса и добавит его к URL-адресу для подключения.
На этом этапе давайте взглянем на содержимое пакета HTTP-запроса:
GET /?id=010101 HTTP/1.1
Host: xxx.toright.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-TW; rv:1.9.2.13) Gecko/20101203 Firefox/3.6.13 GTB7.1 ( .NET CLR 3.5.30729)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-tw,en-us;q=0.7,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 115
Connection: keep-alive
В методе HTTP GET не разрешено передавать данные в теле сообщения, потому что это GET, что означает получение данных.
В адресной строке браузера вы можете увидеть информацию, которая будет отправлена нашей формой.Если вы хотите отправить пароль, не будет ли это «с первого взгляда» ... Это проблема безопасности, о которой все часто упоминают.
POST-метод
Давайте посмотрим на данные передачи POST
<form method="post" action="">
<input type="text" name="id" />
<input type="submit" />
</form>
Столбец URL не изменился, поэтому давайте посмотрим на содержимое пакета HTTP-запроса:
POST / HTTP/1.1
Host: xxx.toright.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-TW; rv:1.9.2.13) Gecko/20101203 Firefox/3.6.13 GTB7.1 ( .NET CLR 3.5.30729)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-tw,en-us;q=0.7,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 115
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
</code><code>Content-Length: 9
id=020202
Понять, почему? Оказывается, POST должен отправлять данные формы в теле сообщения, что кажется более безопасным, не заглядывая в пакет......:yellow_heart: . Кроме того, при отправке файлов будет использоваться многокомпонентное кодирование, и файлы будут отправляться вместе с другими полями формы в теле сообщения. В этом разница между GET и POST для отправки формы.
HTTP-кодек Netty
Чтобы обрабатывать HTTP-запросы через Netty, вам нужно сначала кодировать и декодировать.
Кодек NettyHTTP
public class HttpHelloWorldServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
public void initChannel(SocketChannel ch) {
ChannelPipeline p = ch.pipeline();
/**
* 或者使用HttpRequestDecoder & HttpResponseEncoder
*/
p.addLast(new HttpServerCodec());
/**
* 在处理POST消息体时需要加上
*/
p.addLast(new HttpObjectAggregator(1024*1024));
p.addLast(new HttpServerExpectContinueHandler());
p.addLast(new HttpHelloWorldServerHandler());
}
}
- Строка 8. Вызовите метод **#new HttpServerCodec()**. Кодек поддерживает частичный анализ HTTP-запроса. Например, параметры, передаваемые HTTP-запросом GET, включаются в uri, поэтому параметры запроса можно анализировать с помощью HttpRequest. .
- HttpRequestDecoderТо есть декодировать ByteBuf в HttpRequest и HttpContent.
- HttpResponseEncoderТо есть закодируйте HttpResponse или HttpContent в ByteBuf.
- HttpServerCodecТо есть комбинация HttpRequestDecoder и HttpResponseEncoder.
Однако для запросов HTTP POST информация о параметрах помещается в тело сообщения (что соответствует HttpMessage для netty), поэтому приведенные выше кодеки не могут полностью анализировать запросы HTTP POST.
Что делать в этой ситуации? Не паникуйте, netty предоставляет обработчик для этого.
-
Строка 12: вызовите метод **#new HttpObjectAggregator(1024*1024)**, который может объединять HttpMessage и HttpContent в FullHttpRequest или FullHttpResponse (в зависимости от того, обрабатывается ли запрос или ответ), а также может помочь вам в декодировании. Игнорируется, является ли это "блочным" режимом передачи.
Поэтому при разборе HTTP-запросов POST обязательно включите HttpObjectAggregator в ChannelPipeline. (Пожалуйста, обратитесь к коду для получения подробной информации)
-
Строка 13: Функция этого метода: http 100-continue используется клиентом для консультации с сервером перед отправкой данных POST на сервер, чтобы узнать, обрабатывает ли сервер данные POST.Если нет, клиент не будет загружать данные. Данные POST. Если они обработаны, загружаются данные POST. В реальных приложениях протокол 100-continue используется только при отправке больших данных.
Реализация ответных сообщений HTTP
Процесс инкапсуляции объектов Java в пакеты двоичных данных в соответствии с протоколом HTTP называется кодированием, а процесс разбора объектов Java из пакетов двоичных данных называется декодированием. Прежде чем научиться использовать Netty для кодирования и декодирования протокола HTTP, мы сначала определить Давайте посмотрим на объекты Java, которые клиент взаимодействует с сервером.
Java-объект
Мы определяем объекты Java в процессе связи следующим образом.
@Data
public class User {
private String userName;
private String method;
private Date date;
}
- Выше приведен абстрактный класс Java-объектов в процессе связи.Как видите, мы определили имя пользователя (значение по умолчанию — sanshengshui ), метод http-запроса и текущее время и дату.
- @DataАннотированоlombokПри условии, что это автоматически поможет нам создать методы получения/установки, сократив количество повторяющегося кода, рекомендуется использовать
После определения объекта Java нам необходимо определить правило преобразования объекта Java в двоичные данные Это правило называется сериализацией объектов Java.
Сериализация
Мы определяем интерфейс сериализации следующим образом
/**
* 序列化接口类
*/
public interface Serializer {
/**
* java 对象转换成二进制
*/
byte[] serialize(Object object);
/**
* 二进制转换成 java 对象
*/
<T> T deserialize(Class<T> clazz, byte[] bytes);
}
Интерфейс сериализации имеет два метода: serialize() преобразует объект Java в массив байтов, а deserialize() преобразует массив байтов в определенный тип объекта Java.fastjsonв качестве основы сериализации.
public class JSONSerializer implements Serializer {
@Override
public byte[] serialize(Object object) {
return JSON.toJSONBytes(object);
}
@Override
public <T> T deserialize(Class<T> clazz, byte[] bytes) {
return JSON.parseObject(bytes,clazz);
}
}
кодирование
User user = new User();
user.setUserName("sanshengshui");
user.setDate(new Date());
user.setMethod("get");
JSONSerializer jsonSerializer = new JSONSerializer();
//将Java对象序列化成为二级制数据包
byte[] content = jsonSerializer.serialize(user);
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, Unpooled.wrappedBuffer(content));
response.headers().set(CONTENT_TYPE, "text/plain");
response.headers().setInt(CONTENT_LENGTH, response.content().readableBytes());
boolean keepAlive = HttpUtil.isKeepAlive(request);
if (!keepAlive) {
ctx.write(response).addListener(ChannelFutureListener.CLOSE);
} else {
response.headers().set(CONNECTION, KEEP_ALIVE);
ctx.write(response);
}
Практика парсинга HTTP GET
Как упоминалось выше, параметры HTTP-запроса GET содержатся в uri, и uri можно разобрать следующими способами:
HttpRequest request = (HttpRequest) msg;
String uri = request.uri();
Важно отметить, что при использовании браузера для инициации HTTP-запроса ему часто мешает uri="/favicon.ico", поэтому лучше всего обрабатывать его специально:
if(uri.equals(FAVICON_ICO)){
return;
}
Следующим шагом является разбор uri. Здесь нужно использоватьQueryStringDecoder:
Splits an HTTP query string into a path string and key-value parameter pairs.
This decoder is for one time use only. Create a new instance for each URI:
QueryStringDecoder decoder = new QueryStringDecoder("/hello?recipient=world&x=1;y=2");
assert decoder.getPath().equals("/hello");
assert decoder.getParameters().get("recipient").get(0).equals("world");
assert decoder.getParameters().get("x").get(0).equals("1");
assert decoder.getParameters().get("y").get(0).equals("2");
This decoder can also decode the content of an HTTP POST request whose
content type is application/x-www-form-urlencoded:
QueryStringDecoder decoder = new QueryStringDecoder("recipient=world&x=1;y=2", false);
Как видно из приведенного выше описания, функция QueryStringDecoder состоит в том, чтобы разделить HTTP uri на пары параметров пути и значения, а также может использоваться для декодирования HTTP POST с Content-Type = "application/x-www-form -urlencoded". Обратите внимание, что этот декодер можно использовать только один раз.
Код разбора выглядит следующим образом:
String uri = request.uri();
HttpMethod method = request.method();
if(method.equals(HttpMethod.GET)){
  QueryStringDecoder queryDecoder = new QueryStringDecoder(uri, Charsets.toCharset(CharEncoding.UTF_8));
  Map<String, List<String>> uriAttributes = queryDecoder.parameters();
  //此处仅打印请求参数(你可以根据业务需求自定义处理)
  for (Map.Entry<String, List<String>> attr : uriAttributes.entrySet()) {
    for (String attrVal : attr.getValue()) {
      System.out.println(attr.getKey() + "=" + attrVal);
    }
  }
}
Практика парсинга HTTP POST
Как упоминалось ранее, для анализа тела сообщения HTTP-запроса POST обязательно используйте HttpObjectAggregator. Но нужно ли конвертировать msg в FullHttpRequest? Ответ нет, и смотрите ниже.
Сначала объясните, что такое FullHttpRequest:
Combinate the HttpRequest and FullHttpMessage, so the request is a complete HTTP request.
То есть FullHttpRequest содержит HttpRequest и FullHttpMessage, которые представляют собой полное тело HTTP-запроса.
И способ конвертировать msg в FullHttpRequest очень прост:
FullHttpRequest fullRequest = (FullHttpRequest) msg;
Следующим шагом будет его анализ в нескольких Content-Types.
private void dealWithContentType() throws Exception{
String contentType = getContentType();
//可以使用HttpJsonDecoder
if(contentType.equals("application/json")){
String jsonStr = fullRequest.content().toString(Charsets.toCharset(CharEncoding.UTF_8));
JSONObject obj = JSON.parseObject(jsonStr);
for(Map.Entry<String, Object> item : obj.entrySet()){
logger.info(item.getKey()+"="+item.getValue().toString());
}
}else if(contentType.equals("application/x-www-form-urlencoded")){
//方式一:使用 QueryStringDecoder
String jsonStr = fullRequest.content().toString(Charsets.toCharset(CharEncoding.UTF_8));
QueryStringDecoder queryDecoder = new QueryStringDecoder(jsonStr, false);
Map<String, List<String>> uriAttributes = queryDecoder.parameters();
for (Map.Entry<String, List<String>> attr : uriAttributes.entrySet()) {
for (String attrVal : attr.getValue()) {
logger.info(attr.getKey() + "=" + attrVal);
}
}
}else if(contentType.equals("multipart/form-data")){
//TODO 用于文件上传
}else{
//do nothing...
}
}
private String getContentType(){
String typeStr = headers.get("Content-Type").toString();
String[] list = typeStr.split(";");
return list[0];
}
функциональный тест
я используюPostmanСделайте запрос на http-сервер, реализованный netty.Если вы чувствуете, что можете, вы можете загрузить его самостоятельно.
Получить запрос
Postman:
Server:
16:58:59.130 [nioEventLoopGroup-3-1] DEBUG io.netty.util.Recycler - -Dio.netty.recycler.maxSharedCapacityFactor: 2
16:58:59.130 [nioEventLoopGroup-3-1] DEBUG io.netty.util.Recycler - -Dio.netty.recycler.linkCapacity: 16
16:58:59.130 [nioEventLoopGroup-3-1] DEBUG io.netty.util.Recycler - -Dio.netty.recycler.ratio: 8
//打印请求url
16:58:59.159 [nioEventLoopGroup-3-1] INFO com.sanshengshui.netty.HttpHelloWorldServerHandler - http uri: /
Отправить запрос
Postman:
Server:
16:58:59.130 [nioEventLoopGroup-3-1] DEBUG io.netty.util.Recycler - -Dio.netty.recycler.maxSharedCapacityFactor: 2
16:58:59.130 [nioEventLoopGroup-3-1] DEBUG io.netty.util.Recycler - -Dio.netty.recycler.linkCapacity: 16
16:58:59.130 [nioEventLoopGroup-3-1] DEBUG io.netty.util.Recycler - -Dio.netty.recycler.ratio: 8
16:58:59.159 [nioEventLoopGroup-3-1] INFO com.sanshengshui.netty.HttpHelloWorldServerHandler - http uri: /
17:03:59.813 [nioEventLoopGroup-2-1] INFO io.netty.handler.logging.LoggingHandler - [id: 0x0f3f5fdd, L:/0:0:0:0:0:0:0:0:8888] READ: [id: 0xfd00cb1b, L:/0:0:0:0:0:0:0:1:8888 - R:/0:0:0:0:0:0:0:1:45768]
17:03:59.813 [nioEventLoopGroup-2-1] INFO io.netty.handler.logging.LoggingHandler - [id: 0x0f3f5fdd, L:/0:0:0:0:0:0:0:0:8888] READ COMPLETE
//打印post请求的url
17:03:59.817 [nioEventLoopGroup-3-2] INFO com.sanshengshui.netty.HttpHelloWorldServerHandler - http uri: /ttt
Гатлинг производительность, нагрузочное тестирование
Если вы не знакомы с инструментами тестирования Гатлинга, вы можете взглянуть на мою предыдущую статью:
Отчет о тестировании производительности выглядит примерно так:
================================================================================
---- Global Information --------------------------------------------------------
> request count 1178179 (OK=1178179 KO=0 )
> min response time 0 (OK=0 KO=- )
> max response time 12547 (OK=12547 KO=- )
> mean response time 1 (OK=1 KO=- )
> std deviation 32 (OK=32 KO=- )
> response time 50th percentile 0 (OK=0 KO=- )
> response time 75th percentile 1 (OK=1 KO=- )
> response time 95th percentile 2 (OK=2 KO=- )
> response time 99th percentile 5 (OK=5 KO=- )
> mean requests/sec 10808.982 (OK=10808.982 KO=- )
---- Response Time Distribution ------------------------------------------------
> t < 800 ms 1178139 (100%)
> 800 ms < t < 1200 ms 0 ( 0%)
> t > 1200 ms 40 ( 0%)
> failed 0 ( 0%)
================================================================================
разное
На этом подробное объяснение высокопроизводительного HTTP-сервера Netty заканчивается.
Netty реализует высокопроизводительный HTTP-сервер Адрес проекта проекта:GitHub.com/Саншэншу…
Оригинал не просто, если вы чувствуете себя хорошо, я надеюсь дать рекомендацию! Ваша поддержка - самая большая мотивация для моего письма!
Уведомление об авторских правах:
Автор: Му Шувэй
Источник блога сада:блог woo woo woo.cn на.com/Sanshenghu…
источник на гитхабе:GitHub.com/Саншэншу…
Источник личного блога:sanshengshui.github.io/