Сервер может предоставлять сообщения об ошибках клиентам многопоточным

В процессе автоматизации деятельности предприятия при помощи 1С: Предприятие часто
возникают задачи интеграции и обмена с оборудованием и другими сторонними информационными
системами, например, банками, веб-сайтами, информационными системами партнеров.
Традиционно 1С: Предприятие выступает в качестве потребителя услуг, и реже – в качестве
поставщика. До этого момента у разработчиков популярными технологиями при получении
информации от 1C были COM и веб-сервисы, которые появились только в версии 8.1.

У обеих технологий (COM и веб-сервисы) есть свои минусы. Главные недостатки заключаются
в следующем. COM-технология позволяет в каждый момент времени выполнять только один
запрос. Обращение возможно только внутри своей локальной сети. Обработка одновременных
запросов для COM возможна, но требует затрат на организацию пула соединений и отладку
многопоточного приложения. Веб-сервисы же сложны по настройке и негибкие для программирования:
жестко привязаны к SOAP-стандартам. Подключение устройств, обменивающихся простыми
пакетами, работающих по http-протоколу, невозможно.

Новая идея организации веб-сервера внутри 1С, предложенная в статье, опирается на
проверенную временем богатую функциональность .Net Framework. Решение на основе
идеи лишены недостатков COM и веб-сервисов 1С. По сравнению с COM http-сервер можно
использовать вне локальной сети, поддерживается одновременная обработка нескольких
запросов. По сравнению с веб-сервисами 1С решение на базе http-сервера обладает
большей гибкостью, так как программист волен сам выбирать формат ответа сервера
(в том числе HTML, JSON, графические изображения, RSS и т.д.), а также контролировать
при ответе URL-адрес, идентификацию пользователей, коды ошибок, куки, кодировку,
осуществлять кеширование. Настройка же http-сервера внутри 1С сводится к простому
запуску внешней обработки.

Описание примера

Пример, приложенный к статье, состоит из двух файлов: http-сервера (внешняя обработка
1С 8.2 HttpServer82) и тестового приложения эмуляции одновременных запросов к серверу
(внешняя обработка 1С 8.2 TestHttpServer82). Обе обработки выполнены на основе управляемых
форм. По умолчанию оба приложения настроены на работу с портом 8082.

Сервер и тестовое приложение разработаны на 1С: Предприятие 8.2 и используют .Net
Framework 4.0 и компонент Elisy .Net Bridge 4. Соответственно, для работы примера
требуется установленный .Net Framework 4.0 и версия библиотеки Elisy .Net Bridge
v.4.0.2.0 и выше. Elisy .Net Bridge позволяет гармонично использовать классы и технологии
.Net Framework на 1С, ведущую роль оставляя 1С.

Для проверки работы достаточно запустить внешнюю обработку HttpServer82.epf из 1С: Предприятие
8.2. Если у вас Windows с включенным UAC, то запустить 1С: Предприятие необходимо
под администратором, иначе у приложения не будет достаточно прав на прослушивание
запросов.

Внешняя обработка позволяет задать порт, по которому будет осуществляться прослушивание
и число создаваемых потоков для обработки одновременных запросов. По умолчанию установлен
порт 8082 и 50 потоков.

После нажатия на кнопку Старт сервер переходит в рабочее состояние и осуществляет
обработку запросов на заданный порт. Например, теперь можно обратиться из вашего
браузера по адресу localhost:8082 и открыть
страницу, которую вернет сервер. В запросе же можно передавать параметры, например,
так: localhost:8082/test?x=1

Для проверки сервера в многопоточном режиме придумана внешняя обработка TestHttpServer82.epf,
которая осуществляет одновременный запуск запросов в цикле. В основу обработки для
организации параллельной работы положена замечательная технология PLINQ (Parallel
LINQ) из .Net framework 4.

Запускать тестовую обработку TestHttpServer82.epf следует из отдельного от сервера
сеанса 1С: Предприятие, иначе одновременный запуск в одном сеансе двух обработок
приведет к зависанию. В качестве параметров тестового примера выступают адрес запроса,
число одновременных запросов и число циклов. По умолчанию запуск алгоритма приведет
к 3 циклам обращений по адресу localhost:8082
с 20 одновременными запросами (надо отметить, что число одновременных запросов ограничено
числом ядер процессора).

Принцип работы

.Net framework предлагает своим разработчикам класс HttpListener, отвечающий за прослушивание http-протокола. Используя HttpListener,
вы можете создать прослушивание http-трафика, которое отвечает на http-запросы.
Вы можете использовать этот класс только на операционных системах Windows XP SP2
или Windows Server 2003 и выше. Попытка использования класса на более ранних системах
вызовет исключение.

Ниже приведен пример кода для 1С, который инициализирует объект типа HttpListener,
настраивая его на прослушивание всех URL по порту 8082. При запуске в 1С работа
программы приостанавливается, пока не последует запрос на порт, например, из браузера.
Как только вы пошлете из браузера запрос, например, 127.0.0.1:8082/ 1С вернет описанную в программе html-строку.

За что ответственен каждый участок кода, можно понять из комментариев, приведенных
внутри исходного кода.

ПодключитьВнешнююКомпоненту("Elisy.NetBridge4");
AddIn = New("AddIn.ElisyNetBridge4");
net = AddIn.GetNet();

Если НЕ net.GetStatic("System.Net.HttpListener","IsSupported") Тогда
    Сообщить("Для класса HttpListener нужна ОС Windows XP SP2/2003 и выше");
    Возврат;
КонецЕсли;

listener = net.New("System.Net.HttpListener");
listener.Prefixes.Add("http://*:8082/");
listener.Start();

Сообщить("Прослушивание...");

//Метод GetContext блокирует выполнение программы пока ждет запрос. 
context = listener.GetContext();
request = context.Request;
// Получить объект ответа
response = context.Response;
// Создать ответ - HTML-строку
responseString = "Ответ от HttpListener";
buffer = net.GetStatic("System.Text.Encoding", "UTF8").GetBytes(responseString);
// Получить поток ответа и записать ответ в него.
response.ContentLength64 = buffer.Length;
output = response.OutputStream;
output.Write(buffer,0,buffer.Length);
// Необходимо закрыть выходной поток и остановить прослушивание
output.Close();
listener.Stop();

Это самый простой пример. Главный смысл простейшего примера в том, что 1С превращается
в полнофункциональный http-сервер, а 1С-программист получает инструмент для гибкой
настройки ответа с сервера. Он может вернуть его в любом формате (html, рисунок
или JSON), затребовать идентификацию пользователя или вообще вернуть ошибку. Но
вместе с тем есть недостатки: блокирование всего приложения, обработка только одного
запроса. Пример к статье содержит более сложный код, за счет чего устранены недостатки
простейшего примера из листинга.

От простого к сложному

В готовый пример вошел видоизмененный код работы с HttpListener, в котором вызов
метода listener.GetContext() заменен вызовом асинхронных аналогов listener.BeginGetContext()
и listener.EndGetContext(). Кроме этого создаются N отдельных потоков и заложена
синхронизация между потоками с вызовом кода-обработки запроса на стороне 1С: Предприятие.

Достоинством предлагаемой реализации http-сервера является возможность одновременной
обработки N запросов в разных потоках с передачей логики обработки в метод формы
1С: Предприятие. Пример при каждом запросе возвращает html с описанием источника
и URL запроса. Решение очень гибкое, так как все доработки можно делать прямо из
конфигуратора 1С. Никакие дополнительные проекты (например, C# или VB.Net) не задействованы.

Пример условно можно разделить на 2 части: код, который выполняется на стороне 1С: Предприятие
и код, который выполняется на стороне .Net framework. При этом .Net framework взял
на себя все, что нельзя реализовать средствами 1С: Предприятие, например, создание
и синхронизацию потоков.

Http-сервер оформлен в виде управляемой формы. При нажатии на кнопку Старт происходит
создание объектов классов HttpServer и Helper. Оба класса описываются на C# в макете
обработки ИсходныйКод и компилируются «на лету» в обработчике ПриОткрытии формы.
Класс Helper отвечает за перенаправление .Net-события в функцию ОбработатьЗапрос
на форме 1С и формирование сообщение об ошибке.

Опустив не относящиеся к теме задачи по инициализации формы и вспомогательных классов,
следует остановиться на методе ОбработатьЗапрос, который отвечает за возврат результата
http-клиенту. Вызывается обработчик из класса Helper, в качестве параметра принимает
объект контекста, может возвратить сообщение об ошибке, которое будет возвращено
клиенту.

Объект контекста типа HttpListenerContext, передаваемый в метод ОбработатьЗапрос
содержит два важных свойства: Request и Response, отвечающие за информацию о запросе
и ответе соответственно.

Свойство Request позволяет получить параметры и содержимое, переданные клиентом
при запросе. Информация о запросе, помещенная в свойство Request содержит такие
основные свойства как:

AcceptTypes – MIME-типы, поддерживаемые клиентом
ContentEncoding – информация о кодировке при ответе
Headers – набор заголовков
HttpMethod – метод HTTP, определенный клиентом
InputStream – поток, содержащий данные тела, пришедшие от клиента
IsAuthenticated – булево значение, показывающее идентифицирован ли пользователь
IsLocal – значение, показывающее локальный ли запрос (через localhost)
QueryString – строка запроса из запроса
RawUrl – информация об URL без хоста и порта
UrlReferrer – URL ресурса-источник данного перехода
UserAgent – информация о агенте-браузере пользователя

Через свойство Response осуществляется возврат контента клиенту, передача информации
об ошибке или перенаправление. Сопровождаться все может выставлением необходимых
заголовков. Многие свойства ответа (Response) схожи со свойствами запроса. Значимые
среди них следующие:

ContentEncoding – информация о кодировке при ответе
Headers – набор заголовков
OutputStream – поток, в который будет записан ответ (например, текст Html, XML или
байтовый массив изображения)
RedirectLocation – свойство отвечает за HTTP-заголовок Location и позволяет перенаправить
вызов
StatusCode – код статсуса при возврате клиенту, например: 200 (OK), 404 (ресурс не
найден)
StatusDescription – описание статуса при возврате клиенту

Следующий код метода ОбработатьЗапрос преобразует сформированную строку РезультатHTML
с HTML-кодом в набор байт и записывает этот набор байт в выходной поток, который
будет возвращен клиенту. Ссылка на выходной поток получена через параметр метода.

ответСервера = context.Response; 
массивБайт = net.GetStatic("System.Text.Encoding", "UTF8").GetBytes(РезультатHTML);
ответСервера.ContentLength64 = массивБайт.Length;
выходнойПоток = ответСервера.OutputStream;
выходнойПоток.Write(массивБайт, 0, массивБайт.Length);
выходнойПоток.Close();

Основой обработки является класс HttpServer, который создает объект HttpListener
и нужное число потоков для обработки. При вызове метода Start происходит запуск
всех потоков-обработчиков и отдельного потока для работы HttpListener. Благодаря
этому, можно продолжать работать с 1С во время работы http-сервера. При поступлении
запроса HttpListener помещает запрос в очередь, где каждый запрос последовательно
обрабатывает первый свободный поток. При обработке потока срабатывает цепочка вызовов:
событие HttpServer.ProcessRequest, обработчик события Helper. HttpServer_ProcessRequest,
1С-функция Форма. ОбработатьЗапрос. Код C# класса HttpServer при запуске 1C из макета
ИсходныйКод компилируется «на лету».

_listener.AuthenticationSchemes = authenticationScheme;
_listener.Prefixes.Add(String.Format(@"http://+:{0}/", port));
_listener.Start();
_listenerThread.Start();

for (int i = 0; i < _workers.Length; i++)
{
    _workers[i] = new Thread(Worker);
    _workers[i].Start();
 }

Начало получения запросов происходит в методе HandleRequests до тех пор, пока обработка
не прекратится пользователем. При поступлении запроса, запрос передается в метод
ContextReady и работа процесса прослушивания продолжается.

private void HandleRequests()
{
    while (_listener.IsListening)
    {
         var context = _listener.BeginGetContext(ContextReady, null);

         if (0 == WaitHandle.WaitAny(new[] { _stop, context.AsyncWaitHandle }))
         return;
    }
}

private void Worker()
{
    WaitHandle[] wait = new[] { _ready, _stop };
    while (0 == WaitHandle.WaitAny(wait))
    {
        HttpListenerContext context;
        lock (_queue)
        {
            if (_queue.Count > 0)
                context = _queue.Dequeue();
            else
            {
               _ready.Reset();
                continue;
            }
        }

        try { ProcessRequest(context); }
        catch (Exception e) { Console.Error.WriteLine(e); }
    }
}

Заключение

Многопоточный сервер, описанный в статье, по простоте настройки и функциональности
превосходит традиционные методы доступа извне к информационным базам 1С: Предприятие
8.х. Это простой по запуску метод, позволяющий одновременно обрабатывать несколько
запросов. При этом приложение 1С не блокируется, и пользователь может продолжать
работу после запуска сервера.

Основное преимущество предложенного подхода – полная подконтрольность программисту
процесса от получения запроса до формирования ответа. Например, на этапе получения
запроса может быть выполнен парсинг URL, получена информация о том, как себя идентифицировал
пользователь, а также полная информация о клиенте (поддерживаемые языки, записанные
куки, заголовки, метод доступа). Ответ же можно вернуть практически любой, начиная
от ошибки 404 Not found, заканчивая разными графическими форматами, форматами Word,
Excel и популярными форматами на основе XML (JSON, HTML, RSS).

Пример, приложенный к статье, спроектирован так, что его функциональность можно
легко расширить. Например, для организации кеша применить System.Web.Caching.Cache
класс из .Net framework. А при парсинге URL попробовать поработать с классом RouteCollection
из Asp.Net MVC. При создании RSS-ленты вам поможет класс System.ServiceModel.Syndication
.SyndicationFeed. А при Json-сериализации обратите внимание на класс System.Runtime.Serialization.Json.DataContractJsonSerializer.

Конкретно для предложенного подхода на данный момент не выявлено явных недостатков.
Есть вероятность того, что 1С: Предприятие в силу своих ограничений не сможет обеспечить
на своей стороне должного распараллеливания и значительного увеличения производительности.
Тем не менее опыты проведенные ранее для 1С: Предприятие 8.2 показали, что в схожем
применении 1С достигается увеличение производительности, и 1С работает при этом
стабильно.

Необходимо обратить внимание разработчиков еще на несколько моментов. Любая публикация
информации в Интернете связана с риском взлома вне зависимости от способа публикации.
Но в предложенном способе благодаря гибкости есть больше возможностей противостоять
угрозам извне. Например, в запросе теперь доступен IP-адрес клиента, который можно
блокировать по каким-то правилам (осуществляя поиск в черном списке локально или
в специализированных сервисах). Параметры запроса доступны в виде строк и их можно
анализировать на стороне 1С или .Net framework и блокировать опасное содержимое.
Кроме этого есть несколько специализированных .Net-библиотек, доступных для решения
данной проблемы, которые можно привлечь в 1С, например AntiXSS.

Второй момент заключается в том, что известные методы, в том числе и этот, предоставления
услуг со стороны 1С не предназначены для массовых обращений и будут всегда уступать
с этой точки зрения профессиональным серверам, например, IIS.

Примеры к статье:

HttpServer82.epf (11.50 kb)

TestHttpServer82.epf (8.30 kb)

Вопросы для собеседования

Servlets, JSP, JSTL

  • Что такое «сервлет»?
  • В чем заключаются преимущества технологии сервлетов над CGI (Common Gateway Interface)?
  • Какова структура веб-проекта?
  • Что такое «контейнер сервлетов»?
  • Зачем нужны сервера приложений, если есть контейнеры сервлетов?
  • Как контейнер сервлетов управляет жизненным циклом сервлета, когда и какие методы вызываются?
  • Что такое «дескриптор развертывания»?
  • Какие действия необходимо проделать при создании сервлетов?
  • В каком случае требуется переопределять метод service()?
  • Есть ли смысл определять для сервлета конструктор? Каким образом лучше инициализировать данные?
  • Почему необходимо переопределить только init() метод без аргументов?
  • Какие наиболее распространенные задачи выполняются в контейнере сервлетов?
  • Что вы знаете о сервлетных фильтрах?
  • Зачем в сервлетах используются различные listener?
  • Когда стоит использовать фильтры сервлетов, а когда слушателей?
  • Как реализовать запуск сервлета одновременно с запуском приложения?
  • Как обработать в приложении исключения, выброшенные другим сервлетом?
  • Что представляет собой ServletConfig?
  • Что представляет собой ServletContext?
  • В чем отличия ServletContext и ServletConfig?
  • Для чего нужен интерфейс ServletResponse?
  • Для чего нужен интерфейс ServletRequest?
  • Что такое Request Dispatcher?
  • Как из одного сервлета вызвать другой сервлет?
  • Чем отличается sendRedirect() от forward()?
  • Для чего используются атрибуты сервлетов и как происходит работа с ними?
  • Каким образом можно допустить в сервлете deadlock?
  • Как получить реальное расположение сервлета на сервере?
  • Как получить информацию о сервере из сервлета?
  • Как получить IP адрес клиента на сервере?
  • Какие классы-обертки для сервлетов вы знаете?
  • В чем отличия GenericServlet и HttpServlet?
  • Почему HttpServlet класс объявлен как абстрактный?
  • Какие основные методы присутствуют в классе HttpServlet?
  • Стоит ли волноваться о многопоточной безопасности работая с сервлетами?
  • Какой метод HTTP не является неизменяемым?
  • Какие есть методы отправки данных с клиента на сервер?
  • В чем разница между методами GET и POST?
  • В чем разница между PrintWriter и ServletOutputStream?
  • Можно ли одновременно использовать в сервлете PrintWriter и ServletOutputStream?
  • Расскажите об интерфейсе SingleThreadModel.
  • Что означает URL encoding? Как это осуществить в Java?
  • Какие различные методы управления сессией в сервлетах вы знаете?
  • Что такое cookies?
  • Какие методы для работы с cookies предусмотрены в сервлетах?
  • Что такое URL Rewriting?
  • Зачем нужны и чем отличаются методы encodeURL() и encodeRedirectURL()?
  • Что такое «сессия»?
  • Как уведомить объект в сессии, что сессия недействительна или закончилась?
  • Какой существует эффективный способ удостоверится, что все сервлеты доступны только для пользователя с верной сессией?
  • Как мы можем обеспечить transport layer security для нашего веб приложения?
  • Как организовать подключение к базе данных, обеспечить журналирование в сервлете?
  • Какие основные особенности появились в спецификации Servlet 3?
  • Какие способы аутентификации доступны сервлету?
  • Что такое Java Server Pages (JSP)?
  • Зачем нужен JSP?
  • Опишите, как обрабатываются JSP страницы, начиная от запроса к серверу, заканчивая ответом пользователю.
  • Расскажите об этапах (фазах) жизненного цикла JSP.
  • Расскажите о методах жизненного цикла JSP.
  • Какие методы жизненного цикла JSP могут быть переопределены?
  • Как можно предотвратить прямой доступ к JSP странице из браузера?
  • Какая разница между динамическим и статическим содержимым JSP?
  • Как закомментировать код в JSP?
  • Какие существуют основные типы тегов JSP?
  • Что вы знаете о действиях JSP (Action tag и JSP Action Elements).
  • Взаимодействие JSP — сервлет — JSP.
  • Какие области видимости переменных существуют в JSP?
  • Какие неявные, внутренние объекты и методы есть на JSP странице?
  • Какие неявные объекты не доступны в обычной JSP странице?
  • Что вы знаете о PageContext и какие преимущества его использования?
  • Как сконфигурировать параметры инициализации для JSP?
  • Почему не рекомендуется использовать скриплеты (скриптовые элементы) в JSP?
  • Можно ли определить класс внутри JSP страницы?
  • Что вы знаете о Языке выражений JSP (JSP Expression Language – EL)?
  • Какие типы EL операторов вы знаете?
  • Назовите неявные, внутренние объекты JSP EL и их отличия от объектов JSP.
  • Как отключить возможность использования EL в JSP?
  • Как узнать тип HTTP метода используя JSP EL?
  • Что такое JSTL (JSP Standard tag library)?
  • Из каких групп тегов состоит библиотека JSTL?
  • Какая разница между <c:set> и <jsp:useBean>?
  • Чем отличается <c:import> от <jsp:include> и директивы <%@include %>?
  • Как можно расширить функциональность JSP?
  • Что вы знаете о написании пользовательских JSP тегов?
  • Приведите пример использования собственных тегов.
  • Как сделать перенос строки в HTML средствами JSP?
  • Почему не нужно конфигурировать стандартные JSP теги в web.xml?
  • Как можно обработать ошибки JSP страниц?
  • Как происходит обработка ошибок с помощью JSTL?
  • Как конфигурируется JSP в дескрипторе развертывания.
  • Можно ли использовать Javascript на JSP странице?
  • Всегда ли создается объект сессии на JSP странице, можно ли отключить его создание?
  • Какая разница между JSPWriter и сервлетным PrintWriter?
  • Опишите общие практические принципы работы с JSP.

Что такое «сервлет»?

Сервлет является интерфейсом, реализация которого расширяет функциональные возможности сервера. Сервлет взаимодействует с клиентами посредством принципа запрос-ответ. Хотя сервлеты могут обслуживать любые запросы, они обычно используются для расширения веб-серверов.

Большинство необходимых для создания сервлетов классов и интерфейсов содержатся в пакетах javax.servlet и javax.servlet.http.

Основные методы сервлета:

  • public void init(ServletConfig config) throws ServletException запускается сразу после загрузки сервлета в память;
  • public ServletConfig getServletConfig() возвращает ссылку на объект, который предоставляет доступ к информации о конфигурации сервлета;
  • public String getServletInfo() возвращает строку, содержащую информацию о сервлете, например: автор и версия сервлета;
  • public void service(ServletRequest request, ServletResponse response) throws ServletException, java.io.IOException вызывается для обработки каждого запроса;
  • public void destroy() выполняется перед выгрузкой сервлета из памяти.

Текущая спецификация — Servlet 3.1 описана в JSR-340 и принята в 2013 году.

к оглавлению

В чем заключаются преимущества технологии сервлетов над CGI (Common Gateway Interface)?

  • Сервлеты предоставляют лучшую производительность обработки запросов и более эффективное использование памяти за счет использования преимущество многопоточности (на каждый запрос создается новая нить, что быстрее выделения памяти под новый объект для каждого запроса, как это происходит в CGI).
  • Сервлеты, как платформа и система являются независимыми. Таким образом веб-приложение, написанное с использованием сервлетов может быть запущена в любом контейнере сервлетов, реализующим этот стандарт и в любой операционной системе.
  • Использование сервлетов повышает надежность программы, т.к. контейнер сервлетов самостоятельно заботится о жизненном цикле сервлетов (а значит и за утечками памяти), безопасности и сборщике мусора.
  • Сервлеты относительно легки в изучении и поддержке, таким образом разработчику необходимо заботиться только о бизнес-логике приложения, а не внутренней реализации веб-технологий.

к оглавлению

Какова структура веб-проекта?

src/main/java Исходники приложения/библиотеки

src/main/resources Ресурсные файлы приложения/библиотеки

src/main/filters Файлы сервлетных фильтров

src/main/webapp Исходники веб-приложения

src/test/java Исходники тестов

src/test/resources Ресурсные файлы тестов

src/test/filters Тесты сервлетных фильтров

src/it Интеграционные тесты

src/assembly Описание сборки

src/site Сайт

LICENSE.txt Лицензия проекта

NOTICE.txt Замечания и определения библиотек зависимостей.

README.txt Описание проекта

к оглавлению

Что такое «контейнер сервлетов»?

Контейнер сервлетов — программа, представляющая собой сервер, который занимается системной поддержкой сервлетов и обеспечивает их жизненный цикл в соответствии с правилами, определёнными в спецификациях. Может работать как полноценный самостоятельный веб-сервер, быть поставщиком страниц для другого веб-сервера, или интегрироваться в Java EE сервер приложений.

Контейнер сервлетов обеспечивает обмен данными между сервлетом и клиентами, берёт на себя выполнение таких функций, как создание программной среды для функционирующего сервлета, идентификацию и авторизацию клиентов, организацию сессии для каждого из них.

Наиболее известные реализации контейнеров сервлетов:

  • Apache Tomcat
  • Jetty
  • JBoss
  • WildFly
  • GlassFish
  • IBM WebSphere
  • Oracle Weblogic

к оглавлению

Зачем нужны сервера приложений, если есть контейнеры сервлетов?

  • Пулы соединений с БД
    • Возможность периодического тестирования доступности СУБД и обновления соединения в случае восстановления после сбоев
    • Замена прав доступа при подключении
    • Балансировка нагрузки между несколькими СУБД, определение доступность или недоступность того или иного узла
    • Защита пула соединений от некорректного кода в приложении, которое по недосмотру не возвращает соединения, просто отбирая его после какого-то таймаута.
  • JMS
    • Доступность сервера очередей сообщений «из-коробки».
    • Возможность кластеризации очередей, т.е. доступность построения распределенных очередей, расположенных сразу на нескольких серверах, что существенно увеличивает масштабируемость и доступность приложения
    • Возможность миграции очередей — в случае падения одного из серверов, его очереди автоматически перемещаются на другой, сохраняя необработанные сообщения.
    • В некоторых серверах приложений поддерживается Unit-of-Order — гарантированный порядок обработки сообщений, удовлетворяющих некоторым критериям.
  • JTA Встроенная поддержка распределенных транзакций для обеспечения согласованности данных в разные СУБД или очереди.
  • Безопасность
    • Наличие множества провайдеров безопасности и аутентификации:
      • во встроенном или внешнем LDAP-сервере
      • в базе данных
      • в различных Internet-directory (специализированных приложениях для управления правами доступа)
    • Доступность Single-Sign-On (возможности разделения пользовательской сессии между приложениями) посредством Security Assertion Markup Language (SAML) 1/2 или Simple and Protected Negotiate (SPNEGO) и Kerberos: один из серверов выступает в роли базы для хранения пользователей, все другие сервера при аутентификации пользователя обращаются к этой базе.
    • Возможность авторизации посредством протокола eXtensible Access Control Markup Language (XACML), позволяющего описывать довольно сложные политики (например, приложение доступно пользователю только в рабочее время).
    • Кластеризация всего вышеперечисленного
  • Масштабируемость и высокая доступность Для контейнера сервлетов обычно так же возможно настроить кластеризацию, но она будет довольно примитивной, так как в случае его использования имеются следующие ограничения:
    • Сложность передачи пользовательской сессии из одного центра обработки данных (ЦоД) в другой через Интернет
    • Отсутствие возможности эффективно настроить репликации сессий на большом (состоящем из 40-50 экземпляров серверов) кластере
    • Невозможность обеспечения миграции экземпляров приложения на другой сервер
    • Недоступность механизмов автоматического мониторинга и реакции на ошибки
  • Управляемость
    • Присутствие единого центра управления, т.н. AdminServer и аналога NodeManager’а, обеспечивающего
      • Возможность одновременного запуска нескольких экземпляров сервера
      • Просмотр состояния запущенных экземпляров сервера, обработчиков той или иной очереди, на том или ином сервере, количества соединений с той или иной БД
  • Административный канал и развертывание в промышленном режиме Некоторые сервера приложений позволяют включить так называемый «административный канал» — отдельный порт, запросы по которому имеют приоритет.
    • Просмотр состояния (выполняющихся транзакций, потоков, очередей) в случае недоступности («зависания») сервера
    • Обновление приложений «на-лету», без простоя:
      • добавление на сервер новой версии приложения в «закрытом» режиме, пока пользователи продолжают работать со предыдущей
      • тестирование корректности развертывания новой версии
      • «скрытый» перевод на использование новой версии всех пользователей

к оглавлению

Как контейнер сервлетов управляет жизненным циклом сервлета, когда и какие методы вызываются?

Контейнер сервлетов управляет четырьмя фазами жизненного цикла сервлета:

  • Загрузка класса сервлета — когда контейнер получает запрос для сервлета, то происходит загрузка класса сервлета в память и вызов его конструктора без параметров.
  • Инициализация класса сервлета — после того как класс загружен контейнер инициализирует объект ServletConfig для этого сервлета и внедряет его через init() метод. Это и есть место где сервлет класс преобразуется из обычного класса в сервлет.
  • Обработка запросов — после инициализации сервлет готов к обработке запросов. Для каждого запроса клиента сервлет контейнер порождает новый поток и вызывает метод service() путем передачи ссылки на объекты ответа и запроса.
  • Удаление — когда контейнер останавливается или останавливается приложение, то контейнер сервлетов уничтожает классы сервлетов путем вызова destroy() метода.

Таким образом, сервлет создаётся при первом обращении к нему и живёт на протяжении всего времени работы приложения (в отличии от объектов классов, которые уничтожаются сборщиком мусора после того, как они уже не используются) и весь жизненный цикл сервлета можно описать как последовательность вызова методов:

  • public void init(ServletConfig config) – используется контейнером для инициализации сервлета. Вызывается один раз за время жизни сервлета.
  • public void service(ServletRequest request, ServletResponse response) – вызывается для каждого запроса. Метод не может быть вызван раньше выполнения init() метода.
  • public void destroy() – вызывается для уничтожения сервлета (один раз за время жизни сервлета).

к оглавлению

Что такое «дескриптор развертывания»?

Дескриптор развертывания — это конфигурационный файл артефакта, который будет развернут в контейнере сервлетов. В спецификации Java Platform, Enterprise Edition дескриптор развертывания описывает то, как компонент, модуль или приложение (такое, как веб-приложение или приложение предприятия) должно быть развернуто.

Этот конфигурационный файл указывает параметры развертывания для модуля или приложения с определенными настройками, параметры безопасности и описывает конкретные требования к конфигурации. Для синтаксиса файлов дескриптора развертывания используется язык XML.

<?xml version="1.0" encoding="UTF-8" ?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
    version="2.4">

    <display-name>Display name.</display-name>
    <description>Description text.</description>

    <servlet>
        <servlet-name>ExampleServlet</servlet-name>
        <servlet-class>xyz.company.ExampleServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
        <init-param>
            <param-name>configuration</param-name>
            <param-value>default</param-value>
        </init-param>       
    </servlet>

    <servlet-mapping>
        <servlet-name>ExampleServlet</servlet-name>
        <url-pattern>/example</url-pattern>
    </servlet-mapping>

    <servlet>
        <servlet-name>ExampleJSP</servlet-name>
        <jsp-file>/sample/Example.jsp</jsp-file>
    </servlet>

    <context-param>
        <param-name>myParam</param-name>
        <param-value>the value</param-value>
    </context-param>
</web-app>

Для веб-приложений дескриптор развертывания должен называться web.xml и находиться в директории WEB-INF, в корне веб-приложения. Этот файл является стандартным дескриптором развертывания, определенным в спецификации. Также есть и другие типы дескрипторов, такие, как файл дескриптора развертывания sun-web.xml, содержащий специфичные для Sun GlassFish Enterprise Server данные для развертывания именно для этого сервера приложений или файл application.xml в директории META-INF для приложений J2EE.

к оглавлению

Какие действия необходимо проделать при создании сервлетов?

Чтобы создать сервлет ExampleServlet, необходимо описать его в дескрипторе развёртывания:

<servlet-mapping>
    <servlet-name>ExampleServlet</servlet-name>
    <url-pattern>/example</url-pattern>
</servlet-mapping>
<servlet>
    <servlet-name>ExampleServlet</servlet-name>
    <servlet-class>xyz.company.ExampleServlet</servlet-class>
    <init-param>
        <param-name>config</param-name>
        <param-value>default</param-value>
    </init-param>       
</servlet>

Затем создать класс xyz.company.ExampleServlet путём наследования от HttpServlet и реализовать логику его работы в методе service() или методах doGet()/doPost().

к оглавлению

В каком случае требуется переопределять метод service()?

Метод service() переопределяется, когда необходимо, чтобы сервлет обрабатывал все запросы (и GET, и POST) в одном методе.

Когда контейнер сервлетов получает запрос клиента, то происходит вызов метода service(), который в зависимости от поступившего запроса вызывает или метод doGet() или метод doPost().

к оглавлению

Есть ли смысл определять для сервлета конструктор? Каким образом лучше инициализировать данные?

Большого смысла определять для сервлета конструктор нет, т.к. инициализировать данные лучше не в конструкторе, а переопределив метод init(), в котором имеется возможность доступа к параметрам инициализации сервлета через использование объекта ServletConfig.

к оглавлению

Почему необходимо переопределить только init() метод без аргументов?

Метод init() переопределяется, если необходимо инициализировать какие-то данные до того как сервлет начнет обрабатывать запросы.

При переопределении метода init(ServletConfig config), первым должен быть вызван метод super(config), который обеспечит вызов метода init(ServletConfig config) суперкласса. GenericServlet предоставляет другой метод init() без параметров, который будет вызываться в конце метода init(ServletConfig config).

Необходимо использовать переопределенный метод init() без параметров для инициализации данных во избежание каких-либо проблем, например ошибку, когда вызов super() не указан в переопределенном init(ServletConfig config).

к оглавлению

Какие наиболее распространенные задачи выполняются в контейнере сервлетов?

  • Поддержка обмена данными. Контейнер сервлетов предоставляет легкий способ обмена данными между веб клиентом (браузером) и сервлетом. Благодаря контейнеру нет необходимости создавать слушателя сокета на сервере для отслеживания запросов от клиента, а также разбирать запрос и генерировать ответ. Все эти важные и комплексные задачи решаются с помощью контейнера и разработчик может сосредоточиться на бизнес логике приложения.
  • Управление жизненным циклом сервлетов и ресурсов. Начиная от загрузки сервлета в память, инициализации, внедрения методов и заканчивая уничтожением сервлета. Контейнер так же предоставляет дополнительные утилиты, например JNDI, для управления пулом ресурсов.
  • Поддержка многопоточности. Контейнер самостоятельно создает новую нить для каждого запроса и предоставляет ей запрос и ответ для обработки. Таким образом сервлет не инициализируется заново для каждого запроса и тем самым сохраняет память и уменьшает время до обработки запроса.
  • Поддержка JSP. JSP классы не похожи на стандартные классы джавы, но контейнер сервлетов преобразует каждую JSP в сервлет и далее управляется контейнером как обычным сервлетом.
  • Различные задачи. Контейнер сервлетов управляет пулом ресурсов, памятью приложения, сборщиком мусора. Предоставляются возможности настройки безопасности и многое другое.

к оглавлению

Что вы знаете о сервлетных фильтрах?

Сервлетный фильтр — это Java-код, пригодный для повторного использования и позволяющий преобразовать содержание HTTP-запросов, HTTP-ответов и информацию, содержащуюся в заголовках HTML. Сервлетный фильтр занимается предварительной обработкой запроса, прежде чем тот попадает в сервлет, и/или последующей обработкой ответа, исходящего из сервлета.

Сервлетные фильтры могут:

  • перехватывать инициацию сервлета прежде, чем сервлет будет инициирован;
  • определить содержание запроса прежде, чем сервлет будет инициирован;
  • модифицировать заголовки и данные запроса, в которые упаковывается поступающий запрос;
  • модифицировать заголовки и данные ответа, в которые упаковывается получаемый ответ;
  • перехватывать инициацию сервлета после обращения к сервлету.

Сервлетный фильтр может быть конфигурирован так, что он будет работать с одним сервлетом или группой сервлетов. Основой для формирования фильтров служит интерфейс javax.servlet.Filter, который реализует три метода:

  • void init(FilterConfig config) throws ServletException;
  • void destroy();
  • void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException;

Метод init() вызывается прежде, чем фильтр начинает работать,и настраивает конфигурационный объект фильтра. Метод doFilter() выполняет непосредственно работу фильтра. Таким образом, сервер вызывает init() один раз, чтобы запустить фильтр в работу, а затем вызывает doFilter() столько раз, сколько запросов будет сделано непосредственно к данному фильтру. После того, как фильтр заканчивает свою работу, вызывается метод destroy().

Интерфейс FilterConfig содержит метод для получения имени фильтра, его параметров инициации и контекста активного в данный момент сервлета. С помощью своего метода doFilter() каждый фильтр получает текущий запрос request и ответ response, а также FilterChain, содержащий список фильтров, предназначенных для обработки. В doFilter() фильтр может делать с запросом и ответом всё, что ему захочется — собирать данные или упаковывать объекты для придания им нового поведения. Затем фильтр вызывает chain.doFilter(), чтобы передать управление следующему фильтру. После возвращения этого вызова фильтр может по окончании работы своего метода doFilter() выполнить дополнительную работу над полученным ответом. К примеру, сохранить регистрационную информацию об этом ответе.

После того, как класс-фильтр откомпилирован, его необходимо установить в контейнер и «приписать» (map) к одному или нескольким сервлетам. Объявление и подключение фильтра отмечается в дескрипторе развёртывания web.xml внутри элементов <filter> и <filter-mapping>. Для подключение фильтра к сервлету необходимо использовать вложенные элементы <filter-name> и <servlet-name>.

Объявление класс-фильтра FilterConnect с именем FilterName:

  <filter>
        <filter-name>FilterName</filter-name>
        <filter-class>FilterConnect</filter-class>
        <init-param>
                <!--- фильтр имеет параметр инициализации `active`, которому присваивается значение `true`. -->
                <param-name>active</param-name>
                <param-value>true</param-true>
        </init-param>
  </filter>

Подключение фильтра FilterName к сервлету ServletName:

  <filter-mapping>
        <filter-name>FilterName</filter-name>
        <servlet-name>ServletName</servlet-name>
  </filter-mapping>

Для связи фильтра со страницами HTML или группой сервлетов необходимо использовать тег <url-pattern>:

Подключение фильтра FilterName ко всем вызовам .html страниц

  <filter-mapping>
          <filter-name>FilterName</filter-name>
          <url-pattern>*.html</url-pattern>
  </filter-mapping>

Порядок, в котором контейнер строит цепочку фильтров для запроса определяется следующими правилами:

  • цепочка, определяемая <url-pattern>, выстраивается в том порядке, в котором встречаются соответствующие описания фильтров в web.xml;
  • последовательность сервлетов, определенных с помощью <servlet-name>, также выполняется в той последовательности, в какой эти элементы встречаются в дескрипторе развёртывания web.xml.

к оглавлению

Зачем в сервлетах используются различные listener?

Listener (слушатель) работает как триггер, выполняя определённые действия при наступлении какого-либо события в жизненном цикле сервлета.

Слушатели, разделённые по области видимости (scope):

  • Request:
    • ServletRequestListener используется для того, чтобы поймать момент создания и уничтожения запроса;
    • ServletRequestAttributeListener используется для прослушивания событий, происходящих с атрибутами запроса.
  • Context:
    • ServletContextListener позволяет поймать момент, когда контекст инициализируется либо уничтожается;
    • ServletContextAttributeListener используется для прослушивании событий, происходящих с атрибутами в контексте.
  • Session:
    • HttpSessionListener позволяет поймать момент создания и уничтожения сессии;
    • HttpSessionAttributeListener используется при прослушивании событий происходящих с атрибутами в сессии;
    • HttpSessionActivationListener используется в случае, если происходит миграция сессии между различными JVM в распределённых приложениях;
    • HttpSessionBindingListener так же используется для прослушивания событий, происходящих с атрибутами в сессии. Разница между HttpSessionAttributeListener и HttpSessionBindingListener слушателями: первый декларируется в web.xml; экземпляр класса создается контейнером автоматически в единственном числе и применяется ко всем сессиям; второй: экземпляр класса должен быть создан и закреплён за определённой сессией «вручную», количество экземпляров также регулируется самостоятельно.

Подключение слушателей:

<web-app>
    ...
    <listener>
        <listener-class>xyz.company.ExampleListener</listener-class>
    </listener>
    ...
</web-app>

HttpSessionBindingListener подключается в качестве атрибута непосредственно в сессию, т.е., чтобы его подключить необходимо:

  • создать экземпляр класса, реализующего этот интерфейс;
  • положить созданный экземпляр в сессию при помощи setAttribute(String, Object).

к оглавлению

Когда стоит использовать фильтры сервлетов, а когда слушателей?

Следует использовать фильтры, если необходимо обрабатывать входящие или исходящие данные (например: для аутентификации, преобразования формата, компрессии, шифрования и т.д.), в случае, когда необходимо реагировать на события — лучше применять слушателей.

к оглавлению

Как реализовать запуск сервлета одновременно с запуском приложения?

Контейнер сервлетов обычно загружает сервлет по первому запросу клиента.

Если необходимо загрузить сервлет прямо на старте приложения (например если загрузка сервлета происходит длительное время) следует использовать элемент <load-on-startup> в дескрипторе или аннотацию @loadOnStartup в коде сервлета, что будет указывать на необходимость загрузки сервлета при запуске.

Если целочисленное значение этого параметра отрицательно, то сервлет будет загружен при запросе клиента. В противном случае — загрузится на старте приложения, при этом, чем число меньше, тем раньше в очереди на загрузку он окажется.

<servlet>
    <servlet-name>ExampleServlet</servlet-name>
    <servlet-class>xyz.company.ExampleServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>

к оглавлению

Как обработать в приложении исключения, выброшенные другим сервлетом?

Когда приложение выбрасывет исключение контейнер сервлетов обрабатывает его и создаёт ответ в формате HTML. Это аналогично тому, что происходит при кодах ошибок вроде 404, 403 и т.д.

В дополнении к этому существует возможность написания собственных сервлетов для обработки исключений и ошибок с указанием их в дескрипторе развертывания:

<error-page>
    <error-code>404</error-code>
    <location>/AppExceptionHandler</location>
</error-page>

<error-page>
    <exception-type>javax.servlet.ServletException</exception-type>
    <location>/AppExceptionHandler</location>
</error-page>

Основная задача таких сервлетов — обработать ошибку/исключение и сформировать понятный ответ пользователю. Например, предоставить ссылку на главную страницу или же описание ошибки.

к оглавлению

Что представляет собой ServletConfig?

Интерфейс javax.servlet.ServletConfig используется для передачи сервлету конфигурационной информации. Каждый сервлет имеет свой собственный экземпляр объекта ServletConfig, создаваемый контейнером сервлетов.

Для установки параметров конфигурации используются параметры init-param в web.xml:

<servlet>
    <servlet-name>ExampleServlet</servlet-name>
    <servlet-class>xyz.company.ExampleServlet</servlet-class>
    <init-param>
        <param-name>exampleParameter</param-name>
        <param-value>parameterValue</param-value>
    </init-param>
</servlet>

или аннотации @WebInitParam:

@WebServlet(
    urlPatterns = "/example",
    initParams = {
        @WebInitParam(name = "exampleParameter", value = "parameterValue")
    }
)
public class ExampleServlet extends HttpServlet {
    //...
}

Для получения ServletConfig сервлета используется метод getServletConfig().

к оглавлению

Что представляет собой ServletContext?

Уникальный (в рамках веб-приложения) объект ServletContext реализует интерфейс javax.servlet.ServletContext и предоставляет сервлетам доступ к параметрам этого веб-приложения. Для предоставления доступа используется элемент <context-param> в web.xml:

<web-app>
    ...
    <context-param>
        <param-name>exampleParameter</param-name>
        <param-value>parameterValue</param-value>
    </context-param>
    ...
</web-app>

Объект ServletContext можно получить с помощью метода getServletContext() у интерфейса ServletConfig. Контейнеры сервлетов так же могут предоставлять контекстные объекты, уникальные для группы сервлетов. Каждая из групп будет связана со своим набором URL-путей хоста. В спецификации Servlet 3 ServletContext был расширен и теперь предоставляет возможности программного добавления слушателей и фильтров в приложение. Так же у этого интерфейса имеется множество полезных методов таких как getServerInfo(), getMimeType(), getResourceAsStream() и т.д.

к оглавлению

В чем отличия ServletContext и ServletConfig?

  • ServletConfig уникален для сервлета, а ServletContext — для приложения;
  • ServletConfig используется для предоставления параметров инициализации конкретному сервлету, а ServletContext для предоставления параметров инициализации для всех сервлетов приложения;
  • для ServletConfig возможности модифицировать атрибуты отсутствуют, атрибуты в объекте ServletContext можно изменять.

к оглавлению

Для чего нужен интерфейс ServletResponse?

Интерфейс ServletResponse используется для отправки данных клиенту. Все методы данного инструмента служат именно этой цели:

  • String getCharacterEncoding() — возвращает MIME тип кодировки (к примеру — UTF8), в которой будет выдаваться информация;
  • void setLocale(Locale locale)/Locale getLocale() — указывают на язык используемый в документе;
  • ServletOutputStream getOutputStream()/PrintWriter getWriter() — возвращают потоки вывода данных;
  • void setContentLength(int len) — устанавливает значение поля HTTP заголовка Content-Length;
  • void setContentType(String type) — устанавливает значение поля HTTP заголовка Content-Type.
  • void reset() — позволяет сбросить HTTP заголовок к значениям по-умолчанию, если он ещё не был отправлен
  • и др.

к оглавлению

Для чего нужен интерфейс ServletRequest?

Интерфейс ServletRequest используется для получения параметров соединения, запроса, а также заголовков, входящего потока данных и т.д.

к оглавлению

Что такое Request Dispatcher?

Интерфейс RequestDispatcher используется для передачи запроса другому ресурсу, при этом существует возможность добавления данных, полученных из этого ресурса к собственному ответу сервлета. Так же этот интерфейс используется для внутренней коммуникации между сервлетами в одном контексте.

В интерфейсе объявлено два метода:

  • void forward(ServletRequest var1, ServletResponse var2) — передает запрос из сервлета к другому ресурсу (сервлету, JSP или HTML файлу) на сервере.
  • void include(ServletRequest var1, ServletResponse var2) — включает контент ресурса (сервлет, JSP или HTML страница) в ответ.

Доступ к интерфейсу можно получить с помощью метода интерфейса ServletContextRequestDispatcher getRequestDispatcher(String path), где путь начинающийся с /, интерпретируется относительно текущего корневого пути контекста.

к оглавлению

Как из одного сервлета вызвать другой сервлет?

Для вызова сервлета из того же приложения необходимо использовать механизм внутренней коммуникации сервлетов (inter-servlet communication mechanisms) через вызовы методов RequestDispatcher:

  • forward() — передаёт выполнение запроса в другой сервлет;
  • include() — предоставляет возможность включить результат работы другого сервлета в возвращаемый ответ.

Если необходимо вызывать сервлет принадлежащий другому приложению, то использовать RequestDispatcher уже не получится, т.к. он определен только для текущего приложения. Для подобных целей необходимо использовать метод ServletResponsesendRedirect() которому предоставляется полный URL другого сервлета. Для передачи данных между сервлетами можно использовать cookies.

к оглавлению

Чем отличается sendRedirect() от forward()?

forward():

  • Выполняется на стороне сервера;
  • Запрос перенаправляется на другой ресурс в пределах того же сервера;
  • Не зависит от протокола клиентского запроса, так как обеспечивается контейнером сервлетов;
  • Нельзя применять для внедрения сервлета в другой контекст;
  • Клиент не знает о фактически обрабатываемом ресурсе и URL в строке остается прежним;
  • Выполняется быстрее метода sendRedirect();
  • Определён в интерфейсе RequestDispatcher.

sendRedirect():

  • Выполняется на стороне клиента;
  • Клиенту возвращается ответ 302 (redirect) и запрос перенаправляется на другой сервер;
  • Может использоваться только с клиентами HTTP;
  • Разрешается применять для внедрения сервлета в другой контекст;
  • URL адрес изменяется на адрес нового ресурса;
  • Медленнее forward() т.к. требует создания нового запроса;
  • Определён в интерфейсе HttpServletResponse.

к оглавлению

Для чего используются атрибуты сервлетов и как происходит работа с ними?

Атрибуты сервлетов используются для внутренней коммуникации сервлетов.

В веб-приложении существует возможность работы с атрибутами используя методы setAttribute(), getAttribute(), removeAttribute(), getAttributeNames(), которые предоставлены интерфейсами ServletRequest, HttpSession и ServletContext (для областей видимости request, session, context соответственно).

к оглавлению

Каким образом можно допустить в сервлете deadlock?

Можно получить блокировку, например, допустив циклические вызовы метода doPost() в методе doGet() и метода doGet() в методе doPost().

к оглавлению

Как получить реальное расположение сервлета на сервере?

Реальный путь к расположению сервлета на сервере можно получить из объекта ServletContext:

getServletContext().getRealPath(request.getServletPath()).

к оглавлению

Как получить информацию о сервере из сервлета?

Информацию о сервере можно получить из объекта ServletContext:

getServletContext().getServerInfo().

к оглавлению

Как получить IP адрес клиента на сервере?

IP адрес клиента можно получить вызвав request.getRemoteAddr().

к оглавлению

Какие классы-обертки для сервлетов вы знаете?

Собственные обработчики ServletRequest и ServletResponse можно реализовать, добавив новые или переопределив существующие методы у классов-обёрток ServletRequestWrapper (HttpServletRequestWrapper) и ServletResponseWrapper (HttpServletRequestWrapper).

к оглавлению

В чем отличия GenericServlet и HttpServlet?

Абстрактный класс GenericServlet — независимая от используемого протокола реализация интерфейса Servlet, а абстрактный класс HttpServlet в свою очередь расширяет GenericServlet для протокола HTTP..

к оглавлению

Почему HttpServlet класс объявлен как абстрактный?

Класс HTTPServlet предоставляет лишь общую реализацию сервлета для HTTP протокола. Реализация ключевых методов doGet() и doPost(), содержащих основную бизнес-логику перекладывается на разработчика и по умолчанию возвращает HTTP 405 Method Not Implemented error.

к оглавлению

Какие основные методы присутствуют в классе HttpServlet?

  • doGet() — для обработки HTTP запросов GET;
  • doPost() — для обработки HTTP запросов POST;
  • doPut() — для обработки HTTP запросов PUT;
  • doDelete() — для обработки HTTP запросов DELETE;
  • doHead() — для обработки HTTP запросов HEAD;
  • doOptions() — для обработки HTTP запросов OPTIONS;
  • doTrace() — для обработки HTTP запросов TRACE.

к оглавлению

Стоит ли волноваться о многопоточной безопасности работая с сервлетами?

Методы init() и destroy() вызываются один раз за жизненный цикл сервлета — поэтому по поводу них беспокоиться не стоит.

Методы doGet(), doPost(), service() вызываются на каждый запрос клиента и т.к. сервлеты используют многопоточность, то здесь задумываться о потокобезопасной работе обязательно. При этом правила использования многопоточности остаются теми же: локальные переменные этих методов будут созданы отдельно для каждого потока, а при использовании глобальных разделяемых ресурсов необходимо использовать синхронизацию или другие приёмы многопоточного программирования.

к оглавлению

Какой метод HTTP не является неизменяемым?

HTTP метод называется неизменяемым, если он на один и тот же запрос всегда возвращает одинаковый результат. HTTP методы GET, PUT, DELETE, HEAD и OPTIONS являются неизменяемыми, поэтому необходимо реализовывать приложение так, чтобы эти методы возвращали одинаковый результат постоянно. К изменяемым методам относится метод POST, который и используется для реализации чего-либо, что изменяется при каждом запросе.

К примеру, для доступа к статической HTML странице используется метод GET, т.к. он всегда возвращает одинаковый результат. При необходимости сохранять какую-либо информацию, например в базе данных, нужно использовать POST метод.

к оглавлению

Какие есть методы отправки данных с клиента на сервер?

  • GET — используется для запроса содержимого указанного ресурса, изображения или гипертекстового документа. Вместе с запросом могут передаваться дополнительные параметры как часть URI, значения могут выбираться из полей формы или передаваться непосредственно через URL. При этом запросы кэшируются и имеют ограничения на размер. Этот метод является основным методом взаимодействия браузера клиента и веб-сервера.
  • POST — используется для передачи пользовательских данных в содержимом HTTP-запроса на сервер. Пользовательские данные упакованы в тело запроса согласно полю заголовка Content-Type и/или включены в URI запроса. При использовании метода POST под URI подразумевается ресурс, который будет обрабатывать запрос.

к оглавлению

В чем разница между методами GET и POST?

  • GET передает данные серверу используя URL, тогда как POST передает данные, используя тело HTTP запроса. Длина URL ограничена 1024 символами, это и будет верхним ограничением для данных, которые можно отослать через GET. POST может отправлять гораздо большие объемы данных. Лимит устанавливается web-server и составляет обычно около 2 Mb.
  • Передача данных методом POST более безопасна, чем методом GET, так как секретные данные (например пароль) не отображаются напрямую в web-клиенте пользователя, в отличии от URL, который виден почти всегда. Иногда это преимущество превращается в недостаток — вы не сможете послать данные за кого-то другого.
  • GETметод является неизменяемым, тогда как POST — изменяемый.

к оглавлению

В чем разница между PrintWriter и ServletOutputStream?

PrintWriter — класс для работы с символьным потоком, экземпляр которого можно получить через метод ServletResponse getWriter();

ServletOutputStream — класс для работы байтовым потоком. Для получения его экземпляра используется метод ServletResponse getOutputStream().

к оглавлению

Можно ли одновременно использовать в сервлете PrintWriter и ServletOutputStream?

Так сделать не получится, т.к. при попытке одновременного вызова getWriter() и getOutputStream() будет выброшено исключение java.lang.IllegalStateException с сообщением, что уже был вызван другой метод.

к оглавлению

Расскажите об интерфейсе SingleThreadModel.

Интерфейс SingleThreadModel является маркерным — в нем не объявлен ни один метод, однако, если сервлет реализует этот интерфейс, то метод service() этого сервлета гарантированно не будет одновременно выполняться в двух потоках. Контейнер сервлетов либо синхронизирует обращения к единственному экземпляру, либо обеспечивает поддержку пула экземпляров и перенаправление запроса свободному сервлету.
Другими словами, контейнер гарантирует отсутствие конфликтов при одновременном обращении к переменным или методам экземпляра сервлета. Однако существуют также и другие разделяемые ресурсы, которые даже при использовании этого интерфейса, остаются всё так же доступны обработчикам запросов в других потоках. Т.о. пользы от использования этого интерфейса немного и в спецификации Servlet 2.4 он был объявлен deprecated.

к оглавлению

Что означает URL encoding? Как это осуществить в Java?

URL Encoding — процесс преобразования данных в форму CGI (Common Gateway Interface), не содержащую пробелов и нестандартных символов, которые заменяются в процессе кодирования на специальные escape-символы. В Java для кодирования строки используется метод java.net.URLEncoder.encode(String str, String unicode). Обратная операция декодирования возможна через использование метода java.net.URLDecoder.decode(String str, String unicode).

Hello мир! преобразовывается в Hello%20%D0%BC%D0%B8%D1%80!.

к оглавлению

Какие различные методы управления сессией в сервлетах вы знаете?

При посещении клиентом Web-ресурса и выполнении вариантов запросов, контекстная информация о клиенте не хранится. В протоколе HTTP нет возможностей для сохранения и изменения информации о предыдущих посещениях клиента. Сеанс (сессия) – соединение между клиентом и сервером, устанавливаемое на определенное время, за которое клиент может отправить на сервер сколько угодно запросов. Сеанс устанавливается непосредственно между клиентом и Web-сервером. Каждый клиент устанавливает с сервером свой собственный сеанс. Сеансы используются для обеспечения хранения данных во время нескольких запросов Web-страницы или на обработку информации, введенной в пользовательскую форму в результате нескольких HTTP-соединений (например, клиент совершает несколько покупок в интернет-магазине; студент отвечает на несколько тестов в системе дистанционного обучения).

Существует несколько способов обеспечения уникального идентификатора сессии:

  • User Authentication – Предоставление учетных данных самим пользователем в момент аутентификации. Переданная таким образом информация в дальнейшем используется для поддержания сеанса. Это метод не будет работать, если пользователь вошёл в систему одновременно из нескольких мест.
  • HTML Hidden Field – Присвоение уникального значения скрытому полю HTML страницы, в момент когда пользователь начинает сеанс. Этот метод не может быть использован со ссылками, потому что нуждается в подтверждении формы со скрытым полем каждый раз во время формирования запроса. Кроме того, это не безопасно, т.к. существует возможность простой подмены такого идентификатора.
  • URL Rewriting – Добавление идентификатора сеанса как параметра URL. Достаточно утомительная операция, потому что требует постоянного отслеживания этого идентификатора при каждом запросе или ответе.
  • Cookies – Использование небольших фрагментов данных, отправленных web-сервером и хранимых на устройстве пользователя. Данный метод не будет работать, если клиент отключает использование cookies.
  • Session Management API – Использование специального API для отслеживания сеанса, построенный на основе и на методах, описанных выше и который решает частные проблемы перечисленных способов:
    • Чаще всего недостаточно просто отслеживать сессию, необходимо ещё и сохранять какие-либо дополнительные данные о ней, которые могут потребоваться при обработке последующих запросов. Осуществление такого поведения требует много дополнительных усилий.
    • Все вышеперечисленные методы не являются универсальными: для каждого из них можно подобрать конкретный сценарий, при котором они не будут работать.

к оглавлению

Что такое cookies?

Сookies («куки») — небольшой фрагмент данных, отправленный web-сервером и хранимый на устройстве пользователя. Всякий раз при попытке открыть страницу сайта, web-клиент пересылает соответствующие этому сайту cookies web-серверу в составе HTTP-запроса. Применяется для сохранения данных на стороне пользователя и на практике обычно используется для:

  • аутентификации пользователя;
  • хранения персональных предпочтений и настроек пользователя;
  • отслеживания состояния сеанса доступа пользователя;
  • ведения разнообразной статистики.

к оглавлению

Какие методы для работы с cookies предусмотрены в сервлетах?

Servlet API предоставляет поддержку cookies через класс javax.servlet.http.Cookie:

  • Для получения массива cookies из запроса необходимо воспользоваться методом HttpServletRequest.getCookies(). Методов для добавления cookies в HttpServletRequest не предусмотрено.
  • Для добавления cookie в ответ используется HttpServletResponse.addCookie(Cookie c). Метода получения cookies в HttpServletResponse отсутствует.

к оглавлению

Что такое URL Rewriting?

URL Rewriting — специальная перезапись (перекодирование) оригинального URL. Данный механизм может использоваться для управления сессией в сервлетах, когда cookies отключены.

к оглавлению

Зачем нужны и чем отличаются методы encodeURL() и encodeRedirectURL()?

HttpServletResponse.encodeURL() предоставляет способ преобразования URL в HTML гиперссылку с преобразованием спецсимволов и пробелов, а так же добавления session id к URL. Такое поведение аналогично java.net.URLEncoder.encode(), но с добавлением дополнительного параметра jsessionid в конец URL.

Метод HttpServletResponse.encodeRedirectURL() преобразует URL для последующего использования в методе sendRedirect().

Таким образом для HTML гиперссылок при URL rewriting необходимо использовать encodeURL(), а для URL при перенаправлении — encodeRedirectUrl().

к оглавлению

Что такое «сессия»?

Сессия — это сеанс связи между клиентом и сервером, устанавливаемый на определенное время. Сеанс устанавливается непосредственно между клиентом и веб-сервером в момент получения первого запроса к веб-приложению. Каждый клиент устанавливает с сервером свой собственный сеанс, который сохраняется до окончания работы с приложением.

к оглавлению

Как уведомить объект в сессии, что сессия недействительна или закончилась?

Чтобы быть уверенным в том, что объект будет оповещён о прекращении сессии, нужно реализовать интерфейс javax.servlet.http.HttpSessionBindingListener. Два метода этого интерфейса: valueBound() и valueUnbound() используются при добавлении объекта в качестве атрибута к сессии и при уничтожении сессии соответственно.

к оглавлению

Какой существует эффективный способ удостоверится, что все сервлеты доступны только для пользователя с верной сессией?

Сервлет фильтры используются для перехвата всех запросов между контейнером сервлетов и сервлетом. Поэтому логично использовать соответствующий фильтр для проверки необходимой информации (например валидности сессии) в запросе.

к оглавлению

Как мы можем обеспечить transport layer security для нашего веб приложения?

Для обеспечения transport layer security необходимо настроить поддержку SSL сервлет контейнера. Как это сделать зависит от конкретной реализации сервлет-контейнера.

к оглавлению

Как организовать подключение к базе данных, обеспечить журналирование в сервлете?

При работе с большим количеством подключений к базе данных рекомендуется инициализировать их в servlet context listener, а также установить в качестве атрибута контекста для возможности использования другими сервлетами.

Журналирование подключается к сервлету стандартным для логгера способом (например для log4j это может быть property-файл или XML-конфигурация) , а далее эта информация используется при настройке соответствующего context listener.

к оглавлению

Какие основные особенности появились в спецификации Servlet 3?

  • Servlet Annotations. До Servlet 3 вся конфигурация содержалась в web.xml, что приводило к ошибкам и неудобству при работе с большим количестве сервлетов. Примеры аннотаций: @WebServlet, @WebInitParam, @WebFilter, @WebListener.
  • Web Fragments. Одностраничное веб приложение может содержать множество модулей: все модули прописываются в fragment.xml в папке META-INF. Это позволяет разделять веб приложение на отдельные модули, собранные как .jar-файлы в отдельной lib директории.
  • Динамическое добавление веб компонентов. Появилась возможность программно добавлять фильтры и слушатели, используя ServletContext объект. Для этого применяются методы addServlet(), addFilter(), addListener(). Используя это нововведение стало доступным построение динамической системы, в которой необходимый объект будет создан и вызван только по необходимости.
  • Асинхронное выполнение. Поддержка асинхронной обработки позволяет передать выполнение запроса в другой поток без удержания всего сервера занятым.

к оглавлению

Какие способы аутентификации доступны сервлету?

Спецификация сервлетов определяет четыре типа проверки подлинности:

  • HTTP Basic AuthenticationBASIC. При доступе к закрытым ресурсам появится окно, которое попросит ввести данные для аутентификации.
  • Form Based LoginFORM. Используется собственная html форма:
  • HTTP Digest AuthenticationDIGEST. Цифровая аутентификация с шифрованием.
  • HTTPS AuthenticationCLIENT-CERT. Аутентификация с помощью клиентского сертификата.
<login-config>
    <auth-method>FORM</auth-method>
    <form-login-config>
        <form-login-page>/login.html</form-login-page>
        <form-error-page>/error.html</form-error-page>
    </form-login-config>
</login-config>

к оглавлению

Что такое Java Server Pages (JSP)?

JSP (JavaServer Pages) — платформонезависимая переносимая и легко расширяемая технология разработки веб-приложений, позволяющая веб-разработчикам создавать содержимое, которое имеет как статические, так и динамические компоненты. Страница JSP содержит текст двух типов: статические исходные данные, которые могут быть оформлены в одном из текстовых форматов HTML, SVG, WML, или XML, и JSP-элементы, которые конструируют динамическое содержимое. Кроме этого могут использоваться библиотеки JSP-тегов, а также EL (Expression Language), для внедрения Java-кода в статичное содержимое JSP-страниц.

Код JSP-страницы транслируется в Java-код сервлета с помощью компилятора JSP-страниц Jasper, и затем компилируется в байт-код JVM.

JSP-страницы загружаются на сервере и управляются Java EE Web Application. Обычно такие страницы упакованы в файловые архивы .war и .ear.

к оглавлению

Зачем нужен JSP?

JSP расширяет технологию сервлетов обеспечивая возможность создания динамических страницы с HTML подобным синтаксисом.

Хотя создание представлений поддерживается и в сервлетах, но большая часть любой веб-страницы является статической, поэтому код сервлета в таком случае получается чересчур перегруженным, замусоренным и поэтому при его написании легко допустить ошибку.

Еще одним преимуществом JSP является горячее развертывание — возможность заменить одну страницу на другую непосредственно в контейнере без необходимости перекомпилировать весь проект или перезапускать сервер.

Однако рекомендуется избегать написания серьёзной бизнес-логики в JSP и использовать страницу только в качестве представления.

к оглавлению

Опишите, как обрабатываются JSP страницы, начиная от запроса к серверу, заканчивая ответом пользователю.

Когда пользователь переходит по ссылке на страницу page.jsp, он отправляет http-запрос на сервер GET /page.jsp. Затем, на основе этого запроса и текста самой страницы, сервер генерирует java-класс, компилирует его и выполняет полученный сервлет, формирующий ответ пользователю в виде представления этой страницы, который сервер и перенаправляет обратно пользователю.

к оглавлению

Расскажите об этапах (фазах) жизненного цикла JSP.

Если посмотреть код внутри созданной JSP страницы, то он будет выглядеть как HTML и не будет похож на java класс. Конвертацией JSP страниц в HTML код занимается контейнер, который так же создает и сервлет для использования в веб приложении.

Жизненный цикл JSP состоит из нескольких фаз, которыми руководит JSP контейнер:

  • Translation – проверка и парсинг кода JSP страницы для создания кода сервлета.
  • Compilation – компиляция исходного кода сервлета.
  • Class Loading – загрузка скомпилированного класса в память.
  • Instantiation – внедрение конструктора без параметра загруженного класса для инициализации в памяти.
  • Initialization – вызов init() метода объекта JSP класса и инициализация конфигурации сервлета с первоначальными параметрами, которые указаны в дескрипторе развертывания (web.xml). После этой фазы JSP способен обрабатывать запросы клиентов. Обычно эти фазы происходят после первого запроса клиента (т.е. ленивая загрузка), но можно настроить загрузку и инициализацию JSP на старте приложения по аналогии с сервлетами.
  • Request Processing – длительный жизненный цикл обработки запросов клиента JSP страницей. Обработка является многопоточной и аналогична сервлетам — для каждого запроса создается новый поток, объекты ServletRequest и ServletResponse, происходит выполнение сервис методов.
  • Destroy – последняя фаза жизненного цикла JSP, на которой её класс удаляется из памяти. Обычно это происходит при выключении сервера или выгрузке приложения.

к оглавлению

Расскажите о методах жизненного цикла JSP.

Контейнер сервлетов (например, Tomcat, GlassFish) создает из JSP-страницы класс сервлета, наследующего свойства интерфейса javax.servlet.jsp.HttpJspBase и включающего следующие методы:

  • jspInit() — метод объявлен в JSP странице и реализуется с помощью контейнера. Этот метод вызывается один раз в жизненном цикле JSP для того, чтобы инициализировать конфигурационные параметры указанные в дескрипторе развертывания. Этот метод можно переопределить с помощью определения элемента JSP scripting и указания необходимых параметров для инициализации;
  • _jspService() — метод переопределяется контейнером автоматически и соответствует непосредственно коду JSP, описанному на странице. Этот метод определен в интерфейсе HttpJspPage, его имя начинается с нижнего подчеркивания и он отличается от других методов жизненного цикла тем, что его невозможно переопределить;
  • jspDestroy() — метод вызывается контейнером для удаления объекта из памяти (на последней фазе жизненного цикла JSP — Destroy). Метод вызывается только один раз и доступен для переопределения, предоставляя возможность освободить ресурсы, которые были созданы в jspInit().

к оглавлению

Какие методы жизненного цикла JSP могут быть переопределены?

Возможно переопределить лишь jspInit() и jspDestroy() методы.

к оглавлению

Как можно предотвратить прямой доступ к JSP странице из браузера?

Прямой доступ к директории /WEB-INF/ из веб-приложения отсутствует. Поэтому JSP-страницы можно расположить внутри этой папки и тем самым запретить доступ к странице из браузера. Однако, по аналогии с описанием сервлетов, будет необходимо настроить дескриптор развертывания:

<servlet>
    <servlet-name>Example</servlet-name>
    <jsp-file>/WEB-INF/example.jsp</jsp-file>
    <init-param>
        <param-name>exampleParameter</param-name>
        <param-value>parameterValue</param-value>
    </init-param>
</servlet>
    
<servlet-mapping>
    <servlet-name>Example</servlet-name>
    <url-pattern>/example.jsp</url-pattern>
</servlet-mapping>

к оглавлению

Какая разница между динамическим и статическим содержимым JSP?

Статическое содержимое JSP (HTML, код JavaScript, изображения и т.д.) не изменяется в процессе работы веб приложения.

Динамические ресурсы созданы для того, чтобы отображать свое содержимое в зависимости от пользовательских действий. Обычно они представлены в виде выражений EL (Expression Language), библиотек JSP-тегов и пр.

к оглавлению

Как закомментировать код в JSP?

  • <!—- HTML комментарий; отображается на странице JSP —-> такие комментарии будут видны клиенту при просмотре кода страницы.
  • <%—- JSP комментарий; не отображается на странице JSP —-%> такие комментарии описываются в созданном сервлете и не посылаются клиенту. Для любых комментариев по коду или отладочной информации необходимо использовать именно такой тип комментариев.

к оглавлению

Какие существуют основные типы тегов JSP?

  • Выражение JSP: <%= expression %> — выражение, которое будет обработано с перенаправлением результата на вывод;
  • Скриплет JSP: <% code %> — код, добавляемый в метод service().
  • Объявление JSP: <%! code %> — код, добавляемый в тело класса сервлета вне метода service().
  • Директива JSP page: <%@ page att="value" %> — директивы для контейнера сервлетов с информацией о параметрах.
  • Директива JSP include: <%@ include file="url" %> — файл в локальной системе, подключаемый при трансляции JSP в сервлет.
  • Комментарий JSP: <%-- comment --%> — комментарий; игнорируется при трансляции JSP страницы в сервлет.

к оглавлению

Что вы знаете о действиях JSP (Action tag и JSP Action Elements).

Action tag и JSP Action Elements предоставляют методы работы с Java Beans, подключения ресурсов, проброса запросов и создания динамических XML элементов. Такие элементы всегда начинаются с записи jsp: и используются непосредственно внутри страницы JSP без необходимости подключения сторонних библиотек или дополнительных настроек.

Наиболее часто используемыми JSP Action Elements являются:

  • jsp:include: <jsp:include page="относительный URL" flush="true"/> — подключить файл при запросе страницы. Если необходимо, чтобы файл подключался в процессе трансляции страницы, то используется директива page совместно с атрибутом include;
  • jsp:useBean: <jsp:useBean att=значение*/> или <jsp:useBean att=значение*>...</jsp:useBean> — найти или создать Java bean;
  • jsp:setProperty: <jsp:setProperty att=значение*/> — установить свойства Java bean, или явно, или указанием на соответствующее значение, передаваемое при запросе;
  • jsp:forward: <jsp:forward page="относительный URL"/> — передать запрос другой странице;
  • jsp:plugin: <jsp:plugin attribute="значение"*>...</jsp:plugin> — сгенерировать (в зависимости от типа браузера) тэги OBJECT или EMBED для апплета, использующего технологию Java Plugin.

к оглавлению

Взаимодействие JSP — сервлет — JSP.

«JSP — сервлет — JSP» архитектура построения приложений носит название MVC (Model/View/Controller):

  • Model — классы данных и бизнес-логики;

  • View — страницы JSP;

  • Controller — сервлеты.

к оглавлению

Какие области видимости переменных существуют в JSP?

Область видимости объектов определяется тем контекстом, в который помещается данный объект. В зависимости от той или иной области действия так же определяется время существования объекта.

В JSP предусмотрены следующие области действия переменных (объектов):

  • request область действия запроса — объект будет доступен на текущей JSP странице, странице пересылки (при использовании jsp:forward) или на включаемой странице (при использовании jsp:include);
  • session область действия сессии — объект будет помещен в сеанс пользователя, будет доступен на всех JSP страницах и будет существовать пока существует сессия пользователя, или он не будет из нее принудительно удален.
  • application область действия приложения — объект будет доступен для всех пользователей на всех JSP страницах и будет существовать на протяжении всей работы приложения или пока не будет удален принудительно и контекста приложения.
  • page область действия страницы — объект будет доступен только на той странице, где он определен. На включаемых (jsp:include) и переадресуемых (jsp:forward) страницах данный объект уже не будет доступен.

Таким образом, чтобы объект был доступен всем JSP страницам, необходимо указать область видимости application или session, в зависимости от того требуется ли доступ к объекту всем пользователям или только текущему.

Для указания требуемой области действия при определении объекта на JSP странице используется атрибут scope тега jsp:useBean:

<jsp:useBean id="myBean" class="ru.javacore.MyBean" scope="session"/>

Если не указывать атрибут scope, то по умолчанию задается область видимости страницы page

к оглавлению

Какие неявные, внутренние объекты и методы есть на JSP странице?

JSP implicit objects (неявные объекты) создаются контейнером при конвертации JSP страницы в код сервлета для помощи разработчикам. Эти объекты можно использовать напрямую в скриптлетах для передачи информации в сервис методы, однако мы не можем использовать неявные объекты в JSP Declaration, т.к. такой код пойдет на уровень класса.

Существует 9 видов неявных объектов, которые можно использовать прямо на JSP странице. Семь из них объявлены как локальные переменные в начале _jspService() метода, а два оставшихся могут быть использованы как аргументы метода _jspService().

  • out Object :
<strong>Current Time is</strong>: <% out.print(new Date()); %><br>
  • request Object :
<strong>Request User-Agent</strong>: <%=request.getHeader("User-Agent") %><br>
  • response Object :
<strong>Response</strong>: <%response.addCookie(new Cookie("Test","Value")); %>
  • config Object :
<strong>User init param value</strong>: <%=config.getInitParameter("User") %><br>
  • application Object :
<strong>User context param value</strong>: <%=application.getInitParameter("User") %><br>
  • session Object :
<strong>User Session ID</strong>: <%=session.getId() %><br>
  • pageContext Object :
<% pageContext.setAttribute("Test", "Test Value"); %> 
<strong>PageContext attribute</strong>: {Name="Test",Value="<%=pageContext.getAttribute("Test") %>"}<br>
  • page Object :
<strong>Generated Servlet Name</strong>: <%=page.getClass().getName() %>
  • exception Object :
<strong>Exception occured</strong>: <%=exception %><br>

к оглавлению

Какие неявные объекты не доступны в обычной JSP странице?

Неявный объект исключений JSP недоступен в обычных JSP страницах и используется на страницах ошибок JSP (errorpage) только для того, чтобы перехватить исключение, выброшенное JSP страницей и далее предоставить какую-либо полезную информацию клиенту.

к оглавлению

Что вы знаете о PageContext и какие преимущества его использования?

Неявный объект JSP — экземпляр класса javax.servlet.jsp.PageContext предоставляет доступ ко всем пространствам имён, ассоциированным с JSP-страницей, а также к различным её атрибутам.
Остальные неявные объекты добавляются к pageContext автоматически.

Класс PageContext это абстрактный класс, а его экземпляр можно получить через вызов метода JspFactory.getPageContext(), и освободить через вызов метода JspFactory.releasePageContext().

PageContext обладает следующим набором особенностей и возможностей:

  • единый API для обслуживания пространств имён различных областей видимости;
  • несколько удобных API для доступа к различным public-объектам;
  • механизм получения JspWriter для вывода;
  • механизм обслуживания использования сессии страницей;
  • механизм экспонирования («показа») атрибутов директивы page среде скриптинга;
  • механизмы направления или включения текущего запроса в другие компоненты приложения;
  • механизм обработки процессов исключений на страницах ошибок errorpage;

к оглавлению

Как сконфигурировать параметры инициализации для JSP?

Параметры инициализации для JSP задаются в web.xml файле аналогично сервлетам — элементами servlet и servlet-mapping. Единственным отличием будет указание местонахождения JSP страницы:

<servlet>
    <servlet-name>Example</servlet-name>
    <jsp-file>/WEB-INF/example.jsp</jsp-file>
    <init-param>
        <param-name>exampleParameter</param-name>
        <param-value>parameterValue</param-value>
    </init-param>
</servlet>

к оглавлению

Почему не рекомендуется использовать скриплеты (скриптовые элементы) в JSP?

JSP страницы используются в основном для целей отображения представления (view), а вся бизнес-логика (controller) и модель (model) должны быть реализованы в сервлетах или классах-моделях. Обязанность JSP страницы — создание HTML ответа из переданных через атрибуты параметров. Большая часть JSP содержит HTML код, а для того, чтобы помочь верстальщикам понять JSP код страницы предоставляется функционал элементов action, JSP EL, JSP Standart Tag Library. Именно их и необходимо использовать вместо скриптлетов для создания моста между (JSP)HTML и (JSP)Java частями.

к оглавлению

Можно ли определить класс внутри JSP страницы?

Определить класс внутри JSP страницы можно, но это считается плохой практикой:

<%!
private static class ExampleOne {
  //...
}
%>

<%
private class ExampleTwo {
  //...
}
%>

к оглавлению

Что вы знаете о Языке выражений JSP (JSP Expression Language – EL)?

JSP Expression Language (EL) — скриптовый язык выражений, который позволяет получить доступ к Java компонентам (JavaBeans) из JSP. Начиная с JSP 2.0 используется внутри JSP тегов для отделения Java кода от JSP для обеспечения лёгкого доступа к Java компонентам, уменьшая при этом количество кода Java в JSP-страницах, или даже полностью исключая его.

Развитие EL происходило с целью сделать его более простым для дизайнеров, которые имеют минимальные познания в языке программирования Java. До появления языка выражений, JSP имел несколько специальных тегов таких как скриптлеты (англ.), выражения и т. п. которые позволяли записывать Java код непосредственно на странице. С использованием языка выражений веб-дизайнер должен знать только то, как организовать вызов соответствующих java-методов.

Язык выражений JSP 2.0 включает:

  • Создание и изменение переменных.
  • Управление потоком выполнения программы: ветвление, выполнение различных типов итераций и т.д.
  • Упрощенное обращение к встроенным JSP-объектам.
  • Возможность создавать собственные функции.

Язык выражений используется внутри конструкции ${ ... }. Подобная конструкция может размещаться либо отдельно, либо в правой части выражения установки атрибута тега.

к оглавлению

Какие типы EL операторов вы знаете?

Операторы в EL поддерживают наиболее часто используемые манипуляции данными.

Типы операторов:

  • Стандартные операторы отношения: == (или eq), != (или neq), < (или lt), > (или gt), <= (или le), >= (или ge).
  • Арифметические операторы: +, , *, / (или div), % (или mod).
  • Логические операторы: && (или and), || (или or), ! (или not).
  • Оператор empty – используется для проверки переменной на null, или «пустое значение», который зависит от типа проверяемого объекта. Например, нулевая длина для строки или нулевой размер для коллекции.

к оглавлению

Назовите неявные, внутренние объекты JSP EL и их отличия от объектов JSP.

Язык выражений JSP предоставляет множество неявных объектов, которые можно использовать для получения атрибутов в различных областях видимости (scopes) и для значений параметров. Важно отметить, что они отличаются от неявных объектов JSP и содержат атрибуты в заданной области видимости. Наиболее часто использующийся implicit object в JSP EL и JSP page — это объект pageContext. Ниже представлена таблица неявных объектов JSP EL.

к оглавлению

Как отключить возможность использования EL в JSP?

Для игнорирования выполнения языка выражений на странице существует два способа:

  • использовать директиву <%@ page isELIgnored = «true» %>,
  • настроить web.xml (лучше подходит для отключения EL сразу на нескольких страницах):
<jsp-config>
    <jsp-property-group>
        <url-pattern>*.jsp</url-pattern>
        <el-ignored>true</el-ignored>
    </jsp-property-group>
</jsp-config>

к оглавлению

Как узнать тип HTTP метода используя JSP EL?

${pageContext.request.method}.

к оглавлению

Что такое JSTL (JSP Standard tag library)?

JavaServer Pages Standard Tag Library, JSTL, Стандартная библиотека тегов JSP — расширение спецификации JSP (конечный результат JSR 52), добавляющее библиотеку JSP тегов для общих нужд, таких как разбор XML данных, условная обработка, создание циклов и поддержка интернационализации.

JSTL является альтернативой такому виду встроенной в JSP логики, как скриплеты (прямые вставки Java кода). Использование стандартизованного множества тегов предпочтительнее, поскольку получаемый код легче поддерживать и проще отделять бизнес-логику от логики отображения.

Для использования JSTL тегов необходимо:

  • подключить зависимости, например в pom.xml:
<dependency>
    <groupId>jstl</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
</dependency>
<dependency>
    <groupId>taglibs</groupId>
    <artifactId>standard</artifactId>
    <version>1.1.2</version>
</dependency>
  • указать пространство имен основных тегов JSTL через указание на JSP странице код:
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/sql" prefix="sql" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/xml" prefix="x" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>

к оглавлению

Из каких групп тегов состоит библиотека JSTL?

Группы тегов JSTL согласно их функциональности:

  • Core Tags предоставляют возможности итерации, обработки исключений, URL, forward, redirect response и т.д.
  • Formatting Tags и Localization Tags предоставляют возможности по форматированию чисел, дат и поддержки i18n локализации и resource bundles.
  • SQL Tags – поддержка работы с базами данных.
  • XML Tags используются для работы с XML документами: парсинга, преобразования данных, выполнения выражений XPath и т.д..
  • JSTL Functions Tags предоставляет набор функций, которые позволяют выполнять различные операции со строками и т.п. Например, по конкатенации или разбиению строк.

к оглавлению

Какая разница между <c:set> и <jsp:useBean>?

Оба тега создают и помещают экземпляры в заданную область видимости, но <jsp:useBean> только создаёт экземпляр конкретного типа, а <c:set>, создав экземпляр, позволяет дополнительно извлекать значение, например, из параметров запроса, сессии и т. д.

к оглавлению

Чем отличается <c:import> от <jsp:include> и директивы <%@include %>?

По сравнению с action-тегом <jsp:include> и директивой <%@include %> тег <c:import> обеспечивает более совершенное включение динамических ресурсов, т.к. получает доступ к источнику, чтение информации из которого происходит непосредственно без буферизации и контент включается в исходную JSP построчно.

к оглавлению

Как можно расширить функциональность JSP?

Что вы знаете о написании пользовательских JSP тегов?

Приведите пример использования собственных тегов.

JSP можно расширить с помощью создания собственных тегов с необходимой функциональностью, которые можно добавить в библиотеку тегов на страницу JSP указав необходимое пространство имен.

/WEB-INF/exampleTag.tld

<?xml version="1.0" encoding="UTF-8"?>
<taglib version="2.1" xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-jsptaglibrary_2_1.xsd">
    <tlib-version>1.0</tlib-version>
    <short-name>example</short-name>
    <uri>/WEB-INF/exampleTag</uri>
    <tag>
        <name>exampleTag</name>
        <tag-class>xyz.company.ExampleTag</tag-class>
        <body-content>empty</body-content>
        <info>The example tag displays Hello World!</info>
    </tag>
</taglib>

xyz.company.ExampleServlet.java

package xyz.company;

import java.io.IOException;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;

public class ExampleTag extends TagSupport{
    private static final long serialVersionUID = 1L;

    @Override
    public int doStartTag() throws JspException {
        try {
            pageContext.getOut().print("Hello World!");
        } catch(IOException ioException) {
            throw new JspException("Error: " + ioException.getMessage());
        }       
        return SKIP_BODY;
    }
}

exampleTag.jsp

<%@ taglib uri="/WEB-INF/exampleTag.tld" prefix="example"%>
<%@ page session="false" pageEncoding="UTF-8"%>
<html>
<head>
<title>Example Tag</title>
</head>
<body>
    <h1>Example Tag</h1>
    <p><example:exampleTage /><p>
</body>
</html>

Также в пользовательских тегах существует возможность задать входные параметры. Например, существует необходимость отформатировать каким-либо стилем очень длинное число. Для этого можно использовать собственный тег по типу:

<mytags:formatNumber number="123456.789" format="#,## #.00"/>

Используя входные параметры, число должно быть преобразовано на JSP странице в таком виде 123,456.79 согласно шаблону. Т.к. JSTL не предоставляет такой функциональности, необходимо создать пользовательский тег для получения необходимого результата.

к оглавлению

Как сделать перенос строки в HTML средствами JSP?

Для переноса строки можно использовать тег c:out и атрибут escapeXml, который отключает обработку HTML элементов. В этом случае браузер получит следующий код в виде строки и обработает элемент <br> как требуется:

<c:out value="<br> creates a new line in HTML" escapeXml="true"></c:out>

к оглавлению

Почему не нужно конфигурировать стандартные JSP теги в web.xml?

Стандартные теги JSP не конфигурируются в web.xml, потому что tld файлы уже находятся внутри каталога /META-INF/ в jar файлах JSTL.

Когда контейнер загружает веб-приложение и находит tld файлы в в jar файле в директории /META-INF/, то он автоматически настраивает их для непосредственного использования на JSP страницах. Остается только задать пространство имен на JSP странице.

к оглавлению

Как можно обработать ошибки JSP страниц?

Для обработки исключений выброшенных на JSP странице достаточно лишь задать страницу ошибки JSP и при её создании установить значение page directive attribute isErrorPage в значение true. Таким образом будет предоставлен доступ к неявным объектам исключений в JSP и появится возможность передавать собственные, более информативные сообщения об ошибках клиенту. При этом настройка дескриптора развертывания выглядит так:

<error-page>
     <error-code>404</error-code>
     <location>/error.jsp</location>
</error-page>
  
<error-page>
     <exception-type>java.lang.Throwable</exception-type>
     <location>/error.jsp</location>
</error-page>

к оглавлению

Как происходит обработка ошибок с помощью JSTL?

Для перехвата и обработки исключений в служебных методах класса служат JSTL Core Tags c:catch и c:if.

Тег c:catch перехватывает исключение и обертывает его в переменную exception, доступную для обработки в теге c:if:

<c:catch var ="exception">
   <% int x = 42/0;%>
</c:catch>  
<c:if test = "${exception ne null}">
   <p>Exception is : ${exception} <br />
   Exception Message: ${exception.message}</p>
</c:if>

Обратите внимание что используется язык выражений JSP EL в теге c:if.

к оглавлению

Как конфигурируется JSP в дескрипторе развертывания.

Для настройки различных параметров JSP страниц используется элемент jsp-config, который отвечает за:

  • управление элементами скриптлетов на странице;
  • управления выполнением в языке выражений;
  • определение шаблона URL для encoding;
  • определение размера буфера, который используется для объектов на странице;
  • обозначение групп ресурсов, соответствующих шаблону URL, которые должны быть обработаны как XML документ.
<jsp-config>
    <taglib>
        <taglib-uri>http://company.xyz/jsp/tlds/customtags</taglib-uri>
        <taglib-location>/WEB-INF/exampleTag.tld</taglib-location>
    </taglib>
</jsp-config>

к оглавлению

Можно ли использовать Javascript на JSP странице?

Да, это возможно. Несмотря на то, что JSP это серверная технология, на выходе она всё равно создает HTML страницу, на которую можно добавлять Javascript и CSS.

к оглавлению

Всегда ли создается объект сессии на JSP странице, можно ли отключить его создание?

Jsp-страница, по умолчанию, всегда создает сессию. Используя директиву page с атрибутом session можно изменить это поведение:

<%@ page session ="false" %>

к оглавлению

Какая разница между JSPWriter и сервлетным PrintWriter?

PrintWriter является объектом отвечающим за запись содержания ответа на запрос. JspWriter использует объект PrintWriter для буферизации. Когда буфер заполняется или сбрасывается, JspWriter использует объект PrintWriter для записи содержания в ответ.

к оглавлению

Опишите общие практические принципы работы с JSP.

Хорошей практикой работы с технологией JSP является соблюдение следующих правил:

  • Следует избегать использования элементов скриптлетов на странице. Если элементы action, JSTL, JSP EL не удовлетворяют потребностям, то желательно написать собственный тег.
  • Рекомендуется использовать разные виды комментариев: так JSP комментарии необходимы для уровня кода и отладки, т.к. они не будут показаны клиенту.
  • Не стоит размещать какой-либо бизнес логики внутри JSP страницы. Страницы должны использоваться только для создания ответов клиенту.
  • Для повышения производительности лучше отключать создание сессии на странице, когда это не требуется.
  • Директивы taglib, page в начале JSP страницы улучшают читабельность кода.
  • Следует правильно использовать директиву include и элемент jsp:include action. Первая используется для статических ресурсов, а второй для динамических ресурсов времени выполнения.
  • Обработку исключений нужно производить с помощью страниц ошибок. Это помогает избегать запуска специальных служебных методов и может повысить производительность.
  • Использующиеся CSS и JavaScript должны быть разнесены в разные файлы и подключаться в начале страницы.
  • В большинстве случаев JSTL должно хватать для всех нужд. Если это не так, то в начале следует проанализировать логику своего приложения, и попробовать перенести выполнения кода в сервлет, а далее с помощью установки атрибутов использовать на JSP странице только результат.

к оглавлению

Источники

  • javastudy.ru
  • java2ee.ru
  • Java-online
  • Codenet
  • JavaTalks Articles

Вопросы для собеседования

Программирование, JAVA, Параллельное программирование, GitHub, Eclipse


Рекомендация: подборка платных и бесплатных курсов Python — https://katalog-kursov.ru/

Цель публикации показать начинающим Java программистам все этапы создания многопоточного сервера. Для полного понимания данной темы основная информация содержится в комментариях моего кода и в выводимых в консоли сообщениях для лучшего понимания что именно происходит и в какой именно последовательности.

В начале будет рассмотрено создание элементарного клиент-сервера, для усвоения базовых знаний, на основе которых будет строиться многопоточная архитектура.

Понятия.

— Потоки: для того чтобы не перепутать что именно подразумевается под потоком я буду использовать существующий в профессиональной литературе синоним — нить, чтобы не путать Stream и Thread, всё-таки более профессионально выражаться — нить, говоря про Thread.

— Сокеты(Sockets): данное понятие тоже не однозначно, поскольку в какой-то момент сервер выполняет — клиентские действия, а клиент — серверные. Поэтому я разделил понятие серверного сокета — (ServerSocket) и сокета (Socket) через который практически осуществляется общение, его будем называть сокет общения, чтобы было понятно о чём речь.

 Кроме того сокетов общения создаётся по одному на каждом из обменивающихся данными приложении, поэтому сокет приложения которое имеет у себя объект - ServerSocket и первоначально открывает порт в ожидании подключения будем называть сокет общения на стороне сервера, а сокет который создаёт подключающееся к порту по известному адресу второе приложение будем называть сокетом общения на стороне клиента.

Спасибо за подсказку про Thread.sleep();!
Конечно в реальном коде Thread.sleep(); устанавливать не нужно — это муветон! В данной публикации я его использую только для того чтобы выполнение программы было нагляднее, что бы успевать разобраться в происходящем.
Так что тестируйте, изучайте и в своём коде никогда не используйте Thread.sleep();!

Оглавление:

1) Однопоточный элементарный сервер.
2) Клиент.
3) Многопоточный сервер – сам по себе этот сервер не участвует в общении напрямую, а лишь является фабрикой однонитевых делегатов(делегированных для ведения диалога с клиентами серверов) для общения с вновь подключившимися клиентами, которые закрываются после окончания общения с клиентом.
4) Имитация множественного обращения клиентов к серверу.

По многочисленным замечаниям выкладываю ссылку на исходники на GitHub:
(https://github.com/merceneryinbox/Clietn-Server_Step-by-step.git)

Итак, начнём с изучения структуры однопоточного сервер, который может принять только одного клиента для диалога. Код приводимый ниже необходимо запускать в своей IDE в этом идея всей статьи. Предлагаю все детали уяснить из подробно задокументированного кода ниже:

  • 1) Однопоточный элементарный сервер.
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class TestAsServer {

/**
 * 
 * @param args
 * @throws InterruptedException
 */
    public static void main(String[] args) throws InterruptedException {
//  стартуем сервер на порту 3345

        try (ServerSocket server= new ServerSocket(3345)){
// становимся в ожидание подключения к сокету под именем - "client" на серверной стороне                                
                Socket client = server.accept();

// после хэндшейкинга сервер ассоциирует подключающегося клиента с этим сокетом-соединением             
                System.out.print("Connection accepted.");

// инициируем каналы для  общения в сокете, для сервера     

// канал записи в сокет
                DataOutputStream out = new DataOutputStream(client.getOutputStream());
                System.out.println("DataOutputStream  created");

                // канал чтения из сокета
                DataInputStream in = new DataInputStream(client.getInputStream());
                System.out.println("DataInputStream created");

// начинаем диалог с подключенным клиентом в цикле, пока сокет не закрыт                
                while(!client.isClosed()){

                System.out.println("Server reading from channel");

// сервер ждёт в канале чтения (inputstream) получения данных клиента               
                String entry = in.readUTF();

// после получения данных считывает их              
                System.out.println("READ from client message - "+entry);

// и выводит в консоль              
                System.out.println("Server try writing to channel");

// инициализация проверки условия продолжения работы с клиентом по этому сокету по кодовому слову       - quit  
                if(entry.equalsIgnoreCase("quit")){
                    System.out.println("Client initialize connections suicide ...");
                    out.writeUTF("Server reply - "+entry + " - OK");                
                    Thread.sleep(3000);
                    break;
                }

// если условие окончания работы не верно - продолжаем работу - отправляем эхо-ответ  обратно клиенту               
                out.writeUTF("Server reply - "+entry + " - OK");                
                System.out.println("Server Wrote message to client.");

// освобождаем буфер сетевых сообщений (по умолчанию сообщение не сразу отправляется в сеть, а сначала накапливается в специальном буфере сообщений, размер которого определяется конкретными настройками в системе, а метод  - flush() отправляет сообщение не дожидаясь наполнения буфера согласно настройкам системы             
                out.flush();    

                }

// если условие выхода - верно выключаем соединения             
                System.out.println("Client disconnected");
                System.out.println("Closing connections & channels.");

                // закрываем сначала каналы сокета !
                in.close();
                out.close();

                // потом закрываем сам сокет общения на стороне сервера!
                client.close();

                // потом закрываем сокет сервера который создаёт сокеты общения
                // хотя при многопоточном применении его закрывать не нужно
                // для возможности поставить этот серверный сокет обратно в ожидание нового подключения

                System.out.println("Closing connections & channels - DONE.");
            } catch (IOException e) {
                e.printStackTrace();
        }
    }
}
  • 2) Клиент.

Сервер запущен и находится в блокирующем ожидании server.accept(); обращения к нему с запросом на подключение. Теперь можно подключаться клиенту, напишем код клиента и запустим его. Клиент работает когда пользователь вводит что-либо в его консоли (внимание! в данном случае сервер и клиент запускаются на одном компьютере с локальным адресом — localhost, поэтому при вводе строк, которые должен отправлять клиент не забудьте убедиться, что вы переключились в рабочую консоль клиента!).
После ввода строки в консоль клиента и нажатия enter строка проверяется не ввёл ли клиент кодовое слово для окончания общения дальше отправляется серверу, где он читает её и то же проверяет на наличие кодового слова выхода. Оба и клиент и сервер получив кодовое слово закрывают ресурсы после предварительных приготовлений и завершают свою работу.
Посмотрим как это выглядит в коде:

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.UnknownHostException;

public class TestASClient {

    /**
     * 
     * @param args
     * @throws InterruptedException
     */
    public static void main(String[] args) throws InterruptedException {

// запускаем подключение сокета по известным координатам и нициализируем приём сообщений с консоли клиента      
        try(Socket socket = new Socket("localhost", 3345);  
                BufferedReader br =new BufferedReader(new InputStreamReader(System.in));
                DataOutputStream oos = new DataOutputStream(socket.getOutputStream());
                DataInputStream ois = new DataInputStream(socket.getInputStream()); )
        {

            System.out.println("Client connected to socket.");
            System.out.println();
            System.out.println("Client writing channel = oos & reading channel = ois initialized.");            

// проверяем живой ли канал и работаем если живой           
                while(!socket.isOutputShutdown()){

// ждём консоли клиента на предмет появления в ней данных                   
                    if(br.ready()){

// данные появились - работаем                      
            System.out.println("Client start writing in channel...");
            Thread.sleep(1000);
            String clientCommand = br.readLine();

// пишем данные с консоли в канал сокета для сервера            
            oos.writeUTF(clientCommand);
            oos.flush();
            System.out.println("Clien sent message " + clientCommand + " to server.");
            Thread.sleep(1000);
// ждём чтобы сервер успел прочесть сообщение из сокета и ответить      

// проверяем условие выхода из соединения           
            if(clientCommand.equalsIgnoreCase("quit")){

// если условие выхода достигнуто разъединяемся             
                System.out.println("Client kill connections");
                Thread.sleep(2000);

// смотрим что нам ответил сервер на последок перед закрытием ресурсов          
                if(ois.available()!=0)      {   
                    System.out.println("reading...");
                    String in = ois.readUTF();
                    System.out.println(in);
                            }

// после предварительных приготовлений выходим из цикла записи чтения               
                break;              
            }

// если условие разъединения не достигнуто продолжаем работу            
            System.out.println("Client sent message & start waiting for data from server...");          
            Thread.sleep(2000);

// проверяем, что нам ответит сервер на сообщение(за предоставленное ему время в паузе он должен был успеть ответить)           
            if(ois.available()!=0)      {   

// если успел забираем ответ из канала сервера в сокете и сохраняем её в ois переменную,  печатаем на свою клиентскую консоль                       
            System.out.println("reading...");
            String in = ois.readUTF();
            System.out.println(in);
                    }           
                }
            }
// на выходе из цикла общения закрываем свои ресурсы
            System.out.println("Closing connections & channels on clentSide - DONE.");

        } catch (UnknownHostException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
  • 3) Многопоточный сервер

А что если к серверу хочет подключиться ещё один клиент!? Ведь описанный выше сервер либо находится в ожидании подключения одного клиента, либо общается с ним до завершения соединения, что делать остальным клиентам? Для такого случая нужно создать фабрику которая будет создавать описанных выше серверов при подключении к сокету новых клиентов и не дожидаясь пока делегированный подсервер закончит диалог с клиентом откроет accept() в ожидании следующего клиента. Но чтобы на серверной машине хватило ресурсов для общения со множеством клиентов нужно ограничить количество возможных подключений. Фабрика будет выдавать немного модифицированный вариант предыдущего сервера(модификация будет касаться того что класс сервера для фабрики будет имплементировать интерфейс — Runnable для возможности его использования в пуле нитей — ExecuteServices). Давайте создадим такую серверную фабрику и ознакомимся с подробным описанием её работы в коде:

  • Фабрика:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author mercenery
 *
 */
public class MultiThreadServer {

    static ExecutorService executeIt = Executors.newFixedThreadPool(2);

    /**
     * @param args
     */
    public static void main(String[] args) {

        // стартуем сервер на порту 3345 и инициализируем переменную для обработки консольных команд с самого сервера
        try (ServerSocket server = new ServerSocket(3345);
                BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) {
            System.out.println("Server socket created, command console reader for listen to server commands");

            // стартуем цикл при условии что серверный сокет не закрыт
            while (!server.isClosed()) {

                // проверяем поступившие комманды из консоли сервера если такие
                // были
                if (br.ready()) {
                    System.out.println("Main Server found any messages in channel, let's look at them.");

                    // если команда - quit то инициализируем закрытие сервера и
                    // выход из цикла раздачии нитей монопоточных серверов
                    String serverCommand = br.readLine();
                    if (serverCommand.equalsIgnoreCase("quit")) {
                        System.out.println("Main Server initiate exiting...");
                        server.close();
                        break;
                    }
                }

                // если комманд от сервера нет то становимся в ожидание
                // подключения к сокету общения под именем - "clientDialog" на
                // серверной стороне
                Socket client = server.accept();

                // после получения запроса на подключение сервер создаёт сокет
                // для общения с клиентом и отправляет его в отдельную нить
                // в Runnable(при необходимости можно создать Callable)
                // монопоточную нить = сервер - MonoThreadClientHandler и тот
                // продолжает общение от лица сервера
                executeIt.execute(new MonoThreadClientHandler(client));
                System.out.print("Connection accepted.");
            }

            // закрытие пула нитей после завершения работы всех нитей
            executeIt.shutdown();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  • Модифицированный Runnable сервер для запуска из предыдущего кода:
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;

public class MonoThreadClientHandler implements Runnable {

    private static Socket clientDialog;

    public MonoThreadClientHandler(Socket client) {
        MonoThreadClientHandler.clientDialog = client;
    }

    @Override
    public void run() {

        try {
            // инициируем каналы общения в сокете, для сервера

            // канал записи в сокет следует инициализировать сначала канал чтения для избежания блокировки выполнения программы на ожидании заголовка в сокете
            DataOutputStream out = new DataOutputStream(clientDialog.getOutputStream());

// канал чтения из сокета
            DataInputStream in = new DataInputStream(clientDialog.getInputStream());
            System.out.println("DataInputStream created");

            System.out.println("DataOutputStream  created");
            ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
            // основная рабочая часть //
            //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

            // начинаем диалог с подключенным клиентом в цикле, пока сокет не
            // закрыт клиентом
            while (!clientDialog.isClosed()) {
                System.out.println("Server reading from channel");

                // серверная нить ждёт в канале чтения (inputstream) получения
                // данных клиента после получения данных считывает их
                String entry = in.readUTF();

                // и выводит в консоль
                System.out.println("READ from clientDialog message - " + entry);

                // инициализация проверки условия продолжения работы с клиентом
                // по этому сокету по кодовому слову - quit в любом регистре
                if (entry.equalsIgnoreCase("quit")) {

                    // если кодовое слово получено то инициализируется закрытие
                    // серверной нити
                    System.out.println("Client initialize connections suicide ...");
                    out.writeUTF("Server reply - " + entry + " - OK");
                    Thread.sleep(3000);
                    break;
                }

                // если условие окончания работы не верно - продолжаем работу -
                // отправляем эхо обратно клиенту

                System.out.println("Server try writing to channel");
                out.writeUTF("Server reply - " + entry + " - OK");
                System.out.println("Server Wrote message to clientDialog.");

                // освобождаем буфер сетевых сообщений
                out.flush();

                // возвращаемся в началло для считывания нового сообщения
            }

            ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
            // основная рабочая часть //
            //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

            // если условие выхода - верно выключаем соединения
            System.out.println("Client disconnected");
            System.out.println("Closing connections & channels.");

            // закрываем сначала каналы сокета !
            in.close();
            out.close();

            // потом закрываем сокет общения с клиентом в нити моносервера
            clientDialog.close();

            System.out.println("Closing connections & channels - DONE.");
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

Для имитации множественного обращения клиентов к серверу, создадим и запустим (после запуска серверной части) фабрику Runnable клиентов которые будут подключаться серверу и писать сообщения в цикле:

  • 4) Имитация множественного обращения клиентов к серверу.
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Main {

    // private static ServerSocket server;

    public static void main(String[] args) throws IOException, InterruptedException {

        // запустим пул нитей в которых колличество возможных нитей ограничено -
        // 10-ю.
        ExecutorService exec = Executors.newFixedThreadPool(10);
        int j = 0;

        // стартуем цикл в котором с паузой в 10 милисекунд стартуем Runnable
        // клиентов,
        // которые пишут какое-то количество сообщений
        while (j < 10) {
            j++;
            exec.execute(new TestRunnableClientTester());
            Thread.sleep(10);
        }

        // закрываем фабрику
        exec.shutdown();
    }
}

Как видно из предыдущего кода фабрика запускает — TestRunnableClientTester() клиентов, напишем для них код и после этого запустим саму фабрику, чтобы ей было кого исполнять в своём пуле:

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;

public class TestRunnableClientTester implements Runnable {

    static Socket socket;

    public TestRunnableClientTester() {
        try {

            // создаём сокет общения на стороне клиента в конструкторе объекта
            socket = new Socket("localhost", 3345);
            System.out.println("Client connected to socket");
            Thread.sleep(2000);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {

        try (

                // создаём объект для записи строк в созданный скокет, для
                // чтения строк из сокета
                // в try-with-resources стиле
                DataOutputStream oos = new DataOutputStream(socket.getOutputStream());
                DataInputStream ois = new DataInputStream(socket.getInputStream())) {
            System.out.println("Client oos & ois initialized");

            int i = 0;
            // создаём рабочий цикл
            while (i < 5) {

                // пишем сообщение автогенерируемое циклом клиента в канал
                // сокета для сервера
                oos.writeUTF("clientCommand " + i);

                // проталкиваем сообщение из буфера сетевых сообщений в канал
                oos.flush();

                // ждём чтобы сервер успел прочесть сообщение из сокета и
                // ответить
                Thread.sleep(10);
                System.out.println("Client wrote & start waiting for data from server...");

                // забираем ответ из канала сервера в сокете
                // клиента и сохраняем её в ois переменную, печатаем на
                // консоль
                System.out.println("reading...");
                String in = ois.readUTF();
                System.out.println(in);
                i++;
                Thread.sleep(5000);

            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

Запускайте, вносите изменения в код, только так на самом деле можно понять работу этой структуры.

Спасибо за внимание.

Improve Article

Save Article

Like Article

  • Read
  • Discuss
  • Improve Article

    Save Article

    Like Article

    Prerequisites: Socket Programming in Java

    Multithreaded Server: A server having more than one thread is known as Multithreaded Server. When a client sends the request, a thread is generated through which a user can communicate with the server. We need to generate multiple threads to accept multiple requests from multiple clients at the same time. 

    Multithreaded Servers

    Advantages of Multithreaded Server:

    • Quick and Efficient: Multithreaded server could respond efficiently and quickly to the increasing client queries quickly.
    • Waiting time for users decreases: In a single-threaded server, other users had to wait until the running process gets completed but in multithreaded servers, all users can get a response at a single time so no user has to wait for other processes to finish.
    • Threads are independent of each other: There is no relation between any two threads. When a client is connected a new thread is generated every time.
    • The issue in one thread does not affect other threads: If any error occurs in any of the threads then no other thread is disturbed, all other processes keep running normally. In a single-threaded server, every other client had to wait if any problem occurs in the thread.

    Disadvantages of Multithreaded Server:

    • Complicated Code: It is difficult to write the code of the multithreaded server. These programs can not be created easily
    • Debugging is difficult: Analyzing the main reason and origin of the error is difficult.

    Quick Overview

    We create two java files, Client.java and Server.java. Client file contains only one class Client (for creating a client). Server file has two classes, Server(creates a server) and ClientHandler(handles clients using multithreading).

    MultiThreaded Server Programming

    Client-Side Program: A client can communicate with a server using this code. This involves 

    1. Establish a Socket Connection
    2. Communication

    Java

    import java.io.*;

    import java.net.*;

    import java.util.*;

    class Client {

        public static void main(String[] args)

        {

            try (Socket socket = new Socket("localhost", 1234)) {

                PrintWriter out = new PrintWriter(

                    socket.getOutputStream(), true);

                BufferedReader in

                    = new BufferedReader(new InputStreamReader(

                        socket.getInputStream()));

                Scanner sc = new Scanner(System.in);

                String line = null;

                while (!"exit".equalsIgnoreCase(line)) {

                    line = sc.nextLine();

                    out.println(line);

                    out.flush();

                    System.out.println("Server replied "

                                       + in.readLine());

                }

                sc.close();

            }

            catch (IOException e) {

                e.printStackTrace();

            }

        }

    }

    Server-Side Program: When a new client is connected, and he sends the message to the server.

    1. Server class: The steps involved on the server side are similar to the article Socket Programming in Java with a slight change to create the thread object after obtaining the streams and port number.

    • Establishing the Connection: Server socket object is initialized and inside a while loop a socket object continuously accepts an incoming connection.
    • Obtaining the Streams: The inputstream object and outputstream object is extracted from the current requests’ socket object.
    • Creating a handler object: After obtaining the streams and port number, a new clientHandler object (the above class) is created with these parameters.
    • Invoking the start() method: The start() method is invoked on this newly created thread object.

    2. ClientHandler class: As we will be using separate threads for each request, let’s understand the working and implementation of the ClientHandler class implementing Runnable. An object of this class acts as a Runnable target for a new thread.

    • First, this class implements Runnable interface so that it can be passed as a Runnable target while creating a new Thread.
    • Secondly, the constructor of this class takes a parameter, which can uniquely identify any incoming request, i.e. a Socket.
    • Inside the run() method of this class, it reads the client’s message and replies.

    Java

    import java.io.*;

    import java.net.*;

    class Server {

        public static void main(String[] args)

        {

            ServerSocket server = null;

            try {

                server = new ServerSocket(1234);

                server.setReuseAddress(true);

                while (true) {

                    Socket client = server.accept();

                    System.out.println("New client connected"

                                       + client.getInetAddress()

                                             .getHostAddress());

                    ClientHandler clientSock

                        = new ClientHandler(client);

                    new Thread(clientSock).start();

                }

            }

            catch (IOException e) {

                e.printStackTrace();

            }

            finally {

                if (server != null) {

                    try {

                        server.close();

                    }

                    catch (IOException e) {

                        e.printStackTrace();

                    }

                }

            }

        }

        private static class ClientHandler implements Runnable {

            private final Socket clientSocket;

            public ClientHandler(Socket socket)

            {

                this.clientSocket = socket;

            }

            public void run()

            {

                PrintWriter out = null;

                BufferedReader in = null;

                try {

                    out = new PrintWriter(

                        clientSocket.getOutputStream(), true);

                    in = new BufferedReader(

                        new InputStreamReader(

                            clientSocket.getInputStream()));

                    String line;

                    while ((line = in.readLine()) != null) {

                        System.out.printf(

                            " Sent from the client: %sn",

                            line);

                        out.println(line);

                    }

                }

                catch (IOException e) {

                    e.printStackTrace();

                }

                finally {

                    try {

                        if (out != null) {

                            out.close();

                        }

                        if (in != null) {

                            in.close();

                            clientSocket.close();

                        }

                    }

                    catch (IOException e) {

                        e.printStackTrace();

                    }

                }

            }

        }

    }

    Steps:

    • Compile both Client and Server programs.
    • Run the server first and then the Client.

    Output

    Last Updated :
    09 Nov, 2020

    Like Article

    Save Article

    В процессе автоматизации деятельности предприятия при помощи 1С: Предприятие часто
    возникают задачи интеграции и обмена с оборудованием и другими сторонними информационными
    системами, например, банками, веб-сайтами, информационными системами партнеров.
    Традиционно 1С: Предприятие выступает в качестве потребителя услуг, и реже – в качестве
    поставщика. До этого момента у разработчиков популярными технологиями при получении
    информации от 1C были COM и веб-сервисы, которые появились только в версии 8.1.

    У обеих технологий (COM и веб-сервисы) есть свои минусы. Главные недостатки заключаются
    в следующем. COM-технология позволяет в каждый момент времени выполнять только один
    запрос. Обращение возможно только внутри своей локальной сети. Обработка одновременных
    запросов для COM возможна, но требует затрат на организацию пула соединений и отладку
    многопоточного приложения. Веб-сервисы же сложны по настройке и негибкие для программирования:
    жестко привязаны к SOAP-стандартам. Подключение устройств, обменивающихся простыми
    пакетами, работающих по http-протоколу, невозможно.

    Новая идея организации веб-сервера внутри 1С, предложенная в статье, опирается на
    проверенную временем богатую функциональность .Net Framework. Решение на основе
    идеи лишены недостатков COM и веб-сервисов 1С. По сравнению с COM http-сервер можно
    использовать вне локальной сети, поддерживается одновременная обработка нескольких
    запросов. По сравнению с веб-сервисами 1С решение на базе http-сервера обладает
    большей гибкостью, так как программист волен сам выбирать формат ответа сервера
    (в том числе HTML, JSON, графические изображения, RSS и т.д.), а также контролировать
    при ответе URL-адрес, идентификацию пользователей, коды ошибок, куки, кодировку,
    осуществлять кеширование. Настройка же http-сервера внутри 1С сводится к простому
    запуску внешней обработки.

    Описание примера

    Пример, приложенный к статье, состоит из двух файлов: http-сервера (внешняя обработка
    1С 8.2 HttpServer82) и тестового приложения эмуляции одновременных запросов к серверу
    (внешняя обработка 1С 8.2 TestHttpServer82). Обе обработки выполнены на основе управляемых
    форм. По умолчанию оба приложения настроены на работу с портом 8082.

    Сервер и тестовое приложение разработаны на 1С: Предприятие 8.2 и используют .Net
    Framework 4.0 и компонент Elisy .Net Bridge 4. Соответственно, для работы примера
    требуется установленный .Net Framework 4.0 и версия библиотеки Elisy .Net Bridge
    v.4.0.2.0 и выше. Elisy .Net Bridge позволяет гармонично использовать классы и технологии
    .Net Framework на 1С, ведущую роль оставляя 1С.

    Для проверки работы достаточно запустить внешнюю обработку HttpServer82.epf из 1С: Предприятие
    8.2. Если у вас Windows с включенным UAC, то запустить 1С: Предприятие необходимо
    под администратором, иначе у приложения не будет достаточно прав на прослушивание
    запросов.

    Внешняя обработка позволяет задать порт, по которому будет осуществляться прослушивание
    и число создаваемых потоков для обработки одновременных запросов. По умолчанию установлен
    порт 8082 и 50 потоков.

    После нажатия на кнопку Старт сервер переходит в рабочее состояние и осуществляет
    обработку запросов на заданный порт. Например, теперь можно обратиться из вашего
    браузера по адресу localhost:8082 и открыть
    страницу, которую вернет сервер. В запросе же можно передавать параметры, например,
    так: localhost:8082/test?x=1

    Для проверки сервера в многопоточном режиме придумана внешняя обработка TestHttpServer82.epf,
    которая осуществляет одновременный запуск запросов в цикле. В основу обработки для
    организации параллельной работы положена замечательная технология PLINQ (Parallel
    LINQ) из .Net framework 4.

    Запускать тестовую обработку TestHttpServer82.epf следует из отдельного от сервера
    сеанса 1С: Предприятие, иначе одновременный запуск в одном сеансе двух обработок
    приведет к зависанию. В качестве параметров тестового примера выступают адрес запроса,
    число одновременных запросов и число циклов. По умолчанию запуск алгоритма приведет
    к 3 циклам обращений по адресу localhost:8082
    с 20 одновременными запросами (надо отметить, что число одновременных запросов ограничено
    числом ядер процессора).

    Принцип работы

    .Net framework предлагает своим разработчикам класс HttpListener, отвечающий за прослушивание http-протокола. Используя HttpListener,
    вы можете создать прослушивание http-трафика, которое отвечает на http-запросы.
    Вы можете использовать этот класс только на операционных системах Windows XP SP2
    или Windows Server 2003 и выше. Попытка использования класса на более ранних системах
    вызовет исключение.

    Ниже приведен пример кода для 1С, который инициализирует объект типа HttpListener,
    настраивая его на прослушивание всех URL по порту 8082. При запуске в 1С работа
    программы приостанавливается, пока не последует запрос на порт, например, из браузера.
    Как только вы пошлете из браузера запрос, например, 127.0.0.1:8082/ 1С вернет описанную в программе html-строку.

    За что ответственен каждый участок кода, можно понять из комментариев, приведенных
    внутри исходного кода.

    ПодключитьВнешнююКомпоненту("Elisy.NetBridge4");
    AddIn = New("AddIn.ElisyNetBridge4");
    net = AddIn.GetNet();
    
    Если НЕ net.GetStatic("System.Net.HttpListener","IsSupported") Тогда
        Сообщить("Для класса HttpListener нужна ОС Windows XP SP2/2003 и выше");
        Возврат;
    КонецЕсли;
    
    listener = net.New("System.Net.HttpListener");
    listener.Prefixes.Add("http://*:8082/");
    listener.Start();
    
    Сообщить("Прослушивание...");
    
    //Метод GetContext блокирует выполнение программы пока ждет запрос. 
    context = listener.GetContext();
    request = context.Request;
    // Получить объект ответа
    response = context.Response;
    // Создать ответ - HTML-строку
    responseString = "Ответ от HttpListener";
    buffer = net.GetStatic("System.Text.Encoding", "UTF8").GetBytes(responseString);
    // Получить поток ответа и записать ответ в него.
    response.ContentLength64 = buffer.Length;
    output = response.OutputStream;
    output.Write(buffer,0,buffer.Length);
    // Необходимо закрыть выходной поток и остановить прослушивание
    output.Close();
    listener.Stop();
    

    Это самый простой пример. Главный смысл простейшего примера в том, что 1С превращается
    в полнофункциональный http-сервер, а 1С-программист получает инструмент для гибкой
    настройки ответа с сервера. Он может вернуть его в любом формате (html, рисунок
    или JSON), затребовать идентификацию пользователя или вообще вернуть ошибку. Но
    вместе с тем есть недостатки: блокирование всего приложения, обработка только одного
    запроса. Пример к статье содержит более сложный код, за счет чего устранены недостатки
    простейшего примера из листинга.

    От простого к сложному

    В готовый пример вошел видоизмененный код работы с HttpListener, в котором вызов
    метода listener.GetContext() заменен вызовом асинхронных аналогов listener.BeginGetContext()
    и listener.EndGetContext(). Кроме этого создаются N отдельных потоков и заложена
    синхронизация между потоками с вызовом кода-обработки запроса на стороне 1С: Предприятие.

    Достоинством предлагаемой реализации http-сервера является возможность одновременной
    обработки N запросов в разных потоках с передачей логики обработки в метод формы
    1С: Предприятие. Пример при каждом запросе возвращает html с описанием источника
    и URL запроса. Решение очень гибкое, так как все доработки можно делать прямо из
    конфигуратора 1С. Никакие дополнительные проекты (например, C# или VB.Net) не задействованы.

    Пример условно можно разделить на 2 части: код, который выполняется на стороне 1С: Предприятие
    и код, который выполняется на стороне .Net framework. При этом .Net framework взял
    на себя все, что нельзя реализовать средствами 1С: Предприятие, например, создание
    и синхронизацию потоков.

    Http-сервер оформлен в виде управляемой формы. При нажатии на кнопку Старт происходит
    создание объектов классов HttpServer и Helper. Оба класса описываются на C# в макете
    обработки ИсходныйКод и компилируются «на лету» в обработчике ПриОткрытии формы.
    Класс Helper отвечает за перенаправление .Net-события в функцию ОбработатьЗапрос
    на форме 1С и формирование сообщение об ошибке.

    Опустив не относящиеся к теме задачи по инициализации формы и вспомогательных классов,
    следует остановиться на методе ОбработатьЗапрос, который отвечает за возврат результата
    http-клиенту. Вызывается обработчик из класса Helper, в качестве параметра принимает
    объект контекста, может возвратить сообщение об ошибке, которое будет возвращено
    клиенту.

    Объект контекста типа HttpListenerContext, передаваемый в метод ОбработатьЗапрос
    содержит два важных свойства: Request и Response, отвечающие за информацию о запросе
    и ответе соответственно.

    Свойство Request позволяет получить параметры и содержимое, переданные клиентом
    при запросе. Информация о запросе, помещенная в свойство Request содержит такие
    основные свойства как:

    AcceptTypes – MIME-типы, поддерживаемые клиентом
    ContentEncoding – информация о кодировке при ответе
    Headers – набор заголовков
    HttpMethod – метод HTTP, определенный клиентом
    InputStream – поток, содержащий данные тела, пришедшие от клиента
    IsAuthenticated – булево значение, показывающее идентифицирован ли пользователь
    IsLocal – значение, показывающее локальный ли запрос (через localhost)
    QueryString – строка запроса из запроса
    RawUrl – информация об URL без хоста и порта
    UrlReferrer – URL ресурса-источник данного перехода
    UserAgent – информация о агенте-браузере пользователя

    Через свойство Response осуществляется возврат контента клиенту, передача информации
    об ошибке или перенаправление. Сопровождаться все может выставлением необходимых
    заголовков. Многие свойства ответа (Response) схожи со свойствами запроса. Значимые
    среди них следующие:

    ContentEncoding – информация о кодировке при ответе
    Headers – набор заголовков
    OutputStream – поток, в который будет записан ответ (например, текст Html, XML или
    байтовый массив изображения)
    RedirectLocation – свойство отвечает за HTTP-заголовок Location и позволяет перенаправить
    вызов
    StatusCode – код статсуса при возврате клиенту, например: 200 (OK), 404 (ресурс не
    найден)
    StatusDescription – описание статуса при возврате клиенту

    Следующий код метода ОбработатьЗапрос преобразует сформированную строку РезультатHTML
    с HTML-кодом в набор байт и записывает этот набор байт в выходной поток, который
    будет возвращен клиенту. Ссылка на выходной поток получена через параметр метода.

    ответСервера = context.Response; 
    массивБайт = net.GetStatic("System.Text.Encoding", "UTF8").GetBytes(РезультатHTML);
    ответСервера.ContentLength64 = массивБайт.Length;
    выходнойПоток = ответСервера.OutputStream;
    выходнойПоток.Write(массивБайт, 0, массивБайт.Length);
    выходнойПоток.Close();
    

    Основой обработки является класс HttpServer, который создает объект HttpListener
    и нужное число потоков для обработки. При вызове метода Start происходит запуск
    всех потоков-обработчиков и отдельного потока для работы HttpListener. Благодаря
    этому, можно продолжать работать с 1С во время работы http-сервера. При поступлении
    запроса HttpListener помещает запрос в очередь, где каждый запрос последовательно
    обрабатывает первый свободный поток. При обработке потока срабатывает цепочка вызовов:
    событие HttpServer.ProcessRequest, обработчик события Helper. HttpServer_ProcessRequest,
    1С-функция Форма. ОбработатьЗапрос. Код C# класса HttpServer при запуске 1C из макета
    ИсходныйКод компилируется «на лету».

    _listener.AuthenticationSchemes = authenticationScheme;
    _listener.Prefixes.Add(String.Format(@"http://+:{0}/", port));
    _listener.Start();
    _listenerThread.Start();
    
    for (int i = 0; i < _workers.Length; i++)
    {
        _workers[i] = new Thread(Worker);
        _workers[i].Start();
     }
    

    Начало получения запросов происходит в методе HandleRequests до тех пор, пока обработка
    не прекратится пользователем. При поступлении запроса, запрос передается в метод
    ContextReady и работа процесса прослушивания продолжается.

    private void HandleRequests()
    {
        while (_listener.IsListening)
        {
             var context = _listener.BeginGetContext(ContextReady, null);
    
             if (0 == WaitHandle.WaitAny(new[] { _stop, context.AsyncWaitHandle }))
             return;
        }
    }
    
    private void Worker()
    {
        WaitHandle[] wait = new[] { _ready, _stop };
        while (0 == WaitHandle.WaitAny(wait))
        {
            HttpListenerContext context;
            lock (_queue)
            {
                if (_queue.Count > 0)
                    context = _queue.Dequeue();
                else
                {
                   _ready.Reset();
                    continue;
                }
            }
    
            try { ProcessRequest(context); }
            catch (Exception e) { Console.Error.WriteLine(e); }
        }
    }
    

    Заключение

    Многопоточный сервер, описанный в статье, по простоте настройки и функциональности
    превосходит традиционные методы доступа извне к информационным базам 1С: Предприятие
    8.х. Это простой по запуску метод, позволяющий одновременно обрабатывать несколько
    запросов. При этом приложение 1С не блокируется, и пользователь может продолжать
    работу после запуска сервера.

    Основное преимущество предложенного подхода – полная подконтрольность программисту
    процесса от получения запроса до формирования ответа. Например, на этапе получения
    запроса может быть выполнен парсинг URL, получена информация о том, как себя идентифицировал
    пользователь, а также полная информация о клиенте (поддерживаемые языки, записанные
    куки, заголовки, метод доступа). Ответ же можно вернуть практически любой, начиная
    от ошибки 404 Not found, заканчивая разными графическими форматами, форматами Word,
    Excel и популярными форматами на основе XML (JSON, HTML, RSS).

    Пример, приложенный к статье, спроектирован так, что его функциональность можно
    легко расширить. Например, для организации кеша применить System.Web.Caching.Cache
    класс из .Net framework. А при парсинге URL попробовать поработать с классом RouteCollection
    из Asp.Net MVC. При создании RSS-ленты вам поможет класс System.ServiceModel.Syndication
    .SyndicationFeed. А при Json-сериализации обратите внимание на класс System.Runtime.Serialization.Json.DataContractJsonSerializer.

    Конкретно для предложенного подхода на данный момент не выявлено явных недостатков.
    Есть вероятность того, что 1С: Предприятие в силу своих ограничений не сможет обеспечить
    на своей стороне должного распараллеливания и значительного увеличения производительности.
    Тем не менее опыты проведенные ранее для 1С: Предприятие 8.2 показали, что в схожем
    применении 1С достигается увеличение производительности, и 1С работает при этом
    стабильно.

    Необходимо обратить внимание разработчиков еще на несколько моментов. Любая публикация
    информации в Интернете связана с риском взлома вне зависимости от способа публикации.
    Но в предложенном способе благодаря гибкости есть больше возможностей противостоять
    угрозам извне. Например, в запросе теперь доступен IP-адрес клиента, который можно
    блокировать по каким-то правилам (осуществляя поиск в черном списке локально или
    в специализированных сервисах). Параметры запроса доступны в виде строк и их можно
    анализировать на стороне 1С или .Net framework и блокировать опасное содержимое.
    Кроме этого есть несколько специализированных .Net-библиотек, доступных для решения
    данной проблемы, которые можно привлечь в 1С, например AntiXSS.

    Второй момент заключается в том, что известные методы, в том числе и этот, предоставления
    услуг со стороны 1С не предназначены для массовых обращений и будут всегда уступать
    с этой точки зрения профессиональным серверам, например, IIS.

    Примеры к статье:

    HttpServer82.epf (11.50 kb)

    TestHttpServer82.epf (8.30 kb)

    Вопросы для собеседования

    Servlets, JSP, JSTL

    • Что такое «сервлет»?
    • В чем заключаются преимущества технологии сервлетов над CGI (Common Gateway Interface)?
    • Какова структура веб-проекта?
    • Что такое «контейнер сервлетов»?
    • Зачем нужны сервера приложений, если есть контейнеры сервлетов?
    • Как контейнер сервлетов управляет жизненным циклом сервлета, когда и какие методы вызываются?
    • Что такое «дескриптор развертывания»?
    • Какие действия необходимо проделать при создании сервлетов?
    • В каком случае требуется переопределять метод service()?
    • Есть ли смысл определять для сервлета конструктор? Каким образом лучше инициализировать данные?
    • Почему необходимо переопределить только init() метод без аргументов?
    • Какие наиболее распространенные задачи выполняются в контейнере сервлетов?
    • Что вы знаете о сервлетных фильтрах?
    • Зачем в сервлетах используются различные listener?
    • Когда стоит использовать фильтры сервлетов, а когда слушателей?
    • Как реализовать запуск сервлета одновременно с запуском приложения?
    • Как обработать в приложении исключения, выброшенные другим сервлетом?
    • Что представляет собой ServletConfig?
    • Что представляет собой ServletContext?
    • В чем отличия ServletContext и ServletConfig?
    • Для чего нужен интерфейс ServletResponse?
    • Для чего нужен интерфейс ServletRequest?
    • Что такое Request Dispatcher?
    • Как из одного сервлета вызвать другой сервлет?
    • Чем отличается sendRedirect() от forward()?
    • Для чего используются атрибуты сервлетов и как происходит работа с ними?
    • Каким образом можно допустить в сервлете deadlock?
    • Как получить реальное расположение сервлета на сервере?
    • Как получить информацию о сервере из сервлета?
    • Как получить IP адрес клиента на сервере?
    • Какие классы-обертки для сервлетов вы знаете?
    • В чем отличия GenericServlet и HttpServlet?
    • Почему HttpServlet класс объявлен как абстрактный?
    • Какие основные методы присутствуют в классе HttpServlet?
    • Стоит ли волноваться о многопоточной безопасности работая с сервлетами?
    • Какой метод HTTP не является неизменяемым?
    • Какие есть методы отправки данных с клиента на сервер?
    • В чем разница между методами GET и POST?
    • В чем разница между PrintWriter и ServletOutputStream?
    • Можно ли одновременно использовать в сервлете PrintWriter и ServletOutputStream?
    • Расскажите об интерфейсе SingleThreadModel.
    • Что означает URL encoding? Как это осуществить в Java?
    • Какие различные методы управления сессией в сервлетах вы знаете?
    • Что такое cookies?
    • Какие методы для работы с cookies предусмотрены в сервлетах?
    • Что такое URL Rewriting?
    • Зачем нужны и чем отличаются методы encodeURL() и encodeRedirectURL()?
    • Что такое «сессия»?
    • Как уведомить объект в сессии, что сессия недействительна или закончилась?
    • Какой существует эффективный способ удостоверится, что все сервлеты доступны только для пользователя с верной сессией?
    • Как мы можем обеспечить transport layer security для нашего веб приложения?
    • Как организовать подключение к базе данных, обеспечить журналирование в сервлете?
    • Какие основные особенности появились в спецификации Servlet 3?
    • Какие способы аутентификации доступны сервлету?
    • Что такое Java Server Pages (JSP)?
    • Зачем нужен JSP?
    • Опишите, как обрабатываются JSP страницы, начиная от запроса к серверу, заканчивая ответом пользователю.
    • Расскажите об этапах (фазах) жизненного цикла JSP.
    • Расскажите о методах жизненного цикла JSP.
    • Какие методы жизненного цикла JSP могут быть переопределены?
    • Как можно предотвратить прямой доступ к JSP странице из браузера?
    • Какая разница между динамическим и статическим содержимым JSP?
    • Как закомментировать код в JSP?
    • Какие существуют основные типы тегов JSP?
    • Что вы знаете о действиях JSP (Action tag и JSP Action Elements).
    • Взаимодействие JSP — сервлет — JSP.
    • Какие области видимости переменных существуют в JSP?
    • Какие неявные, внутренние объекты и методы есть на JSP странице?
    • Какие неявные объекты не доступны в обычной JSP странице?
    • Что вы знаете о PageContext и какие преимущества его использования?
    • Как сконфигурировать параметры инициализации для JSP?
    • Почему не рекомендуется использовать скриплеты (скриптовые элементы) в JSP?
    • Можно ли определить класс внутри JSP страницы?
    • Что вы знаете о Языке выражений JSP (JSP Expression Language – EL)?
    • Какие типы EL операторов вы знаете?
    • Назовите неявные, внутренние объекты JSP EL и их отличия от объектов JSP.
    • Как отключить возможность использования EL в JSP?
    • Как узнать тип HTTP метода используя JSP EL?
    • Что такое JSTL (JSP Standard tag library)?
    • Из каких групп тегов состоит библиотека JSTL?
    • Какая разница между <c:set> и <jsp:useBean>?
    • Чем отличается <c:import> от <jsp:include> и директивы <%@include %>?
    • Как можно расширить функциональность JSP?
    • Что вы знаете о написании пользовательских JSP тегов?
    • Приведите пример использования собственных тегов.
    • Как сделать перенос строки в HTML средствами JSP?
    • Почему не нужно конфигурировать стандартные JSP теги в web.xml?
    • Как можно обработать ошибки JSP страниц?
    • Как происходит обработка ошибок с помощью JSTL?
    • Как конфигурируется JSP в дескрипторе развертывания.
    • Можно ли использовать Javascript на JSP странице?
    • Всегда ли создается объект сессии на JSP странице, можно ли отключить его создание?
    • Какая разница между JSPWriter и сервлетным PrintWriter?
    • Опишите общие практические принципы работы с JSP.

    Что такое «сервлет»?

    Сервлет является интерфейсом, реализация которого расширяет функциональные возможности сервера. Сервлет взаимодействует с клиентами посредством принципа запрос-ответ. Хотя сервлеты могут обслуживать любые запросы, они обычно используются для расширения веб-серверов.

    Большинство необходимых для создания сервлетов классов и интерфейсов содержатся в пакетах javax.servlet и javax.servlet.http.

    Основные методы сервлета:

    • public void init(ServletConfig config) throws ServletException запускается сразу после загрузки сервлета в память;
    • public ServletConfig getServletConfig() возвращает ссылку на объект, который предоставляет доступ к информации о конфигурации сервлета;
    • public String getServletInfo() возвращает строку, содержащую информацию о сервлете, например: автор и версия сервлета;
    • public void service(ServletRequest request, ServletResponse response) throws ServletException, java.io.IOException вызывается для обработки каждого запроса;
    • public void destroy() выполняется перед выгрузкой сервлета из памяти.

    Текущая спецификация — Servlet 3.1 описана в JSR-340 и принята в 2013 году.

    к оглавлению

    В чем заключаются преимущества технологии сервлетов над CGI (Common Gateway Interface)?

    • Сервлеты предоставляют лучшую производительность обработки запросов и более эффективное использование памяти за счет использования преимущество многопоточности (на каждый запрос создается новая нить, что быстрее выделения памяти под новый объект для каждого запроса, как это происходит в CGI).
    • Сервлеты, как платформа и система являются независимыми. Таким образом веб-приложение, написанное с использованием сервлетов может быть запущена в любом контейнере сервлетов, реализующим этот стандарт и в любой операционной системе.
    • Использование сервлетов повышает надежность программы, т.к. контейнер сервлетов самостоятельно заботится о жизненном цикле сервлетов (а значит и за утечками памяти), безопасности и сборщике мусора.
    • Сервлеты относительно легки в изучении и поддержке, таким образом разработчику необходимо заботиться только о бизнес-логике приложения, а не внутренней реализации веб-технологий.

    к оглавлению

    Какова структура веб-проекта?

    src/main/java Исходники приложения/библиотеки

    src/main/resources Ресурсные файлы приложения/библиотеки

    src/main/filters Файлы сервлетных фильтров

    src/main/webapp Исходники веб-приложения

    src/test/java Исходники тестов

    src/test/resources Ресурсные файлы тестов

    src/test/filters Тесты сервлетных фильтров

    src/it Интеграционные тесты

    src/assembly Описание сборки

    src/site Сайт

    LICENSE.txt Лицензия проекта

    NOTICE.txt Замечания и определения библиотек зависимостей.

    README.txt Описание проекта

    к оглавлению

    Что такое «контейнер сервлетов»?

    Контейнер сервлетов — программа, представляющая собой сервер, который занимается системной поддержкой сервлетов и обеспечивает их жизненный цикл в соответствии с правилами, определёнными в спецификациях. Может работать как полноценный самостоятельный веб-сервер, быть поставщиком страниц для другого веб-сервера, или интегрироваться в Java EE сервер приложений.

    Контейнер сервлетов обеспечивает обмен данными между сервлетом и клиентами, берёт на себя выполнение таких функций, как создание программной среды для функционирующего сервлета, идентификацию и авторизацию клиентов, организацию сессии для каждого из них.

    Наиболее известные реализации контейнеров сервлетов:

    • Apache Tomcat
    • Jetty
    • JBoss
    • WildFly
    • GlassFish
    • IBM WebSphere
    • Oracle Weblogic

    к оглавлению

    Зачем нужны сервера приложений, если есть контейнеры сервлетов?

    • Пулы соединений с БД
      • Возможность периодического тестирования доступности СУБД и обновления соединения в случае восстановления после сбоев
      • Замена прав доступа при подключении
      • Балансировка нагрузки между несколькими СУБД, определение доступность или недоступность того или иного узла
      • Защита пула соединений от некорректного кода в приложении, которое по недосмотру не возвращает соединения, просто отбирая его после какого-то таймаута.
    • JMS
      • Доступность сервера очередей сообщений «из-коробки».
      • Возможность кластеризации очередей, т.е. доступность построения распределенных очередей, расположенных сразу на нескольких серверах, что существенно увеличивает масштабируемость и доступность приложения
      • Возможность миграции очередей — в случае падения одного из серверов, его очереди автоматически перемещаются на другой, сохраняя необработанные сообщения.
      • В некоторых серверах приложений поддерживается Unit-of-Order — гарантированный порядок обработки сообщений, удовлетворяющих некоторым критериям.
    • JTA Встроенная поддержка распределенных транзакций для обеспечения согласованности данных в разные СУБД или очереди.
    • Безопасность
      • Наличие множества провайдеров безопасности и аутентификации:
        • во встроенном или внешнем LDAP-сервере
        • в базе данных
        • в различных Internet-directory (специализированных приложениях для управления правами доступа)
      • Доступность Single-Sign-On (возможности разделения пользовательской сессии между приложениями) посредством Security Assertion Markup Language (SAML) 1/2 или Simple and Protected Negotiate (SPNEGO) и Kerberos: один из серверов выступает в роли базы для хранения пользователей, все другие сервера при аутентификации пользователя обращаются к этой базе.
      • Возможность авторизации посредством протокола eXtensible Access Control Markup Language (XACML), позволяющего описывать довольно сложные политики (например, приложение доступно пользователю только в рабочее время).
      • Кластеризация всего вышеперечисленного
    • Масштабируемость и высокая доступность Для контейнера сервлетов обычно так же возможно настроить кластеризацию, но она будет довольно примитивной, так как в случае его использования имеются следующие ограничения:
      • Сложность передачи пользовательской сессии из одного центра обработки данных (ЦоД) в другой через Интернет
      • Отсутствие возможности эффективно настроить репликации сессий на большом (состоящем из 40-50 экземпляров серверов) кластере
      • Невозможность обеспечения миграции экземпляров приложения на другой сервер
      • Недоступность механизмов автоматического мониторинга и реакции на ошибки
    • Управляемость
      • Присутствие единого центра управления, т.н. AdminServer и аналога NodeManager’а, обеспечивающего
        • Возможность одновременного запуска нескольких экземпляров сервера
        • Просмотр состояния запущенных экземпляров сервера, обработчиков той или иной очереди, на том или ином сервере, количества соединений с той или иной БД
    • Административный канал и развертывание в промышленном режиме Некоторые сервера приложений позволяют включить так называемый «административный канал» — отдельный порт, запросы по которому имеют приоритет.
      • Просмотр состояния (выполняющихся транзакций, потоков, очередей) в случае недоступности («зависания») сервера
      • Обновление приложений «на-лету», без простоя:
        • добавление на сервер новой версии приложения в «закрытом» режиме, пока пользователи продолжают работать со предыдущей
        • тестирование корректности развертывания новой версии
        • «скрытый» перевод на использование новой версии всех пользователей

    к оглавлению

    Как контейнер сервлетов управляет жизненным циклом сервлета, когда и какие методы вызываются?

    Контейнер сервлетов управляет четырьмя фазами жизненного цикла сервлета:

    • Загрузка класса сервлета — когда контейнер получает запрос для сервлета, то происходит загрузка класса сервлета в память и вызов его конструктора без параметров.
    • Инициализация класса сервлета — после того как класс загружен контейнер инициализирует объект ServletConfig для этого сервлета и внедряет его через init() метод. Это и есть место где сервлет класс преобразуется из обычного класса в сервлет.
    • Обработка запросов — после инициализации сервлет готов к обработке запросов. Для каждого запроса клиента сервлет контейнер порождает новый поток и вызывает метод service() путем передачи ссылки на объекты ответа и запроса.
    • Удаление — когда контейнер останавливается или останавливается приложение, то контейнер сервлетов уничтожает классы сервлетов путем вызова destroy() метода.

    Таким образом, сервлет создаётся при первом обращении к нему и живёт на протяжении всего времени работы приложения (в отличии от объектов классов, которые уничтожаются сборщиком мусора после того, как они уже не используются) и весь жизненный цикл сервлета можно описать как последовательность вызова методов:

    • public void init(ServletConfig config) – используется контейнером для инициализации сервлета. Вызывается один раз за время жизни сервлета.
    • public void service(ServletRequest request, ServletResponse response) – вызывается для каждого запроса. Метод не может быть вызван раньше выполнения init() метода.
    • public void destroy() – вызывается для уничтожения сервлета (один раз за время жизни сервлета).

    к оглавлению

    Что такое «дескриптор развертывания»?

    Дескриптор развертывания — это конфигурационный файл артефакта, который будет развернут в контейнере сервлетов. В спецификации Java Platform, Enterprise Edition дескриптор развертывания описывает то, как компонент, модуль или приложение (такое, как веб-приложение или приложение предприятия) должно быть развернуто.

    Этот конфигурационный файл указывает параметры развертывания для модуля или приложения с определенными настройками, параметры безопасности и описывает конкретные требования к конфигурации. Для синтаксиса файлов дескриптора развертывания используется язык XML.

    <?xml version="1.0" encoding="UTF-8" ?>
    <web-app xmlns="http://java.sun.com/xml/ns/j2ee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
        version="2.4">
    
        <display-name>Display name.</display-name>
        <description>Description text.</description>
    
        <servlet>
            <servlet-name>ExampleServlet</servlet-name>
            <servlet-class>xyz.company.ExampleServlet</servlet-class>
            <load-on-startup>1</load-on-startup>
            <init-param>
                <param-name>configuration</param-name>
                <param-value>default</param-value>
            </init-param>       
        </servlet>
    
        <servlet-mapping>
            <servlet-name>ExampleServlet</servlet-name>
            <url-pattern>/example</url-pattern>
        </servlet-mapping>
    
        <servlet>
            <servlet-name>ExampleJSP</servlet-name>
            <jsp-file>/sample/Example.jsp</jsp-file>
        </servlet>
    
        <context-param>
            <param-name>myParam</param-name>
            <param-value>the value</param-value>
        </context-param>
    </web-app>

    Для веб-приложений дескриптор развертывания должен называться web.xml и находиться в директории WEB-INF, в корне веб-приложения. Этот файл является стандартным дескриптором развертывания, определенным в спецификации. Также есть и другие типы дескрипторов, такие, как файл дескриптора развертывания sun-web.xml, содержащий специфичные для Sun GlassFish Enterprise Server данные для развертывания именно для этого сервера приложений или файл application.xml в директории META-INF для приложений J2EE.

    к оглавлению

    Какие действия необходимо проделать при создании сервлетов?

    Чтобы создать сервлет ExampleServlet, необходимо описать его в дескрипторе развёртывания:

    <servlet-mapping>
        <servlet-name>ExampleServlet</servlet-name>
        <url-pattern>/example</url-pattern>
    </servlet-mapping>
    <servlet>
        <servlet-name>ExampleServlet</servlet-name>
        <servlet-class>xyz.company.ExampleServlet</servlet-class>
        <init-param>
            <param-name>config</param-name>
            <param-value>default</param-value>
        </init-param>       
    </servlet>

    Затем создать класс xyz.company.ExampleServlet путём наследования от HttpServlet и реализовать логику его работы в методе service() или методах doGet()/doPost().

    к оглавлению

    В каком случае требуется переопределять метод service()?

    Метод service() переопределяется, когда необходимо, чтобы сервлет обрабатывал все запросы (и GET, и POST) в одном методе.

    Когда контейнер сервлетов получает запрос клиента, то происходит вызов метода service(), который в зависимости от поступившего запроса вызывает или метод doGet() или метод doPost().

    к оглавлению

    Есть ли смысл определять для сервлета конструктор? Каким образом лучше инициализировать данные?

    Большого смысла определять для сервлета конструктор нет, т.к. инициализировать данные лучше не в конструкторе, а переопределив метод init(), в котором имеется возможность доступа к параметрам инициализации сервлета через использование объекта ServletConfig.

    к оглавлению

    Почему необходимо переопределить только init() метод без аргументов?

    Метод init() переопределяется, если необходимо инициализировать какие-то данные до того как сервлет начнет обрабатывать запросы.

    При переопределении метода init(ServletConfig config), первым должен быть вызван метод super(config), который обеспечит вызов метода init(ServletConfig config) суперкласса. GenericServlet предоставляет другой метод init() без параметров, который будет вызываться в конце метода init(ServletConfig config).

    Необходимо использовать переопределенный метод init() без параметров для инициализации данных во избежание каких-либо проблем, например ошибку, когда вызов super() не указан в переопределенном init(ServletConfig config).

    к оглавлению

    Какие наиболее распространенные задачи выполняются в контейнере сервлетов?

    • Поддержка обмена данными. Контейнер сервлетов предоставляет легкий способ обмена данными между веб клиентом (браузером) и сервлетом. Благодаря контейнеру нет необходимости создавать слушателя сокета на сервере для отслеживания запросов от клиента, а также разбирать запрос и генерировать ответ. Все эти важные и комплексные задачи решаются с помощью контейнера и разработчик может сосредоточиться на бизнес логике приложения.
    • Управление жизненным циклом сервлетов и ресурсов. Начиная от загрузки сервлета в память, инициализации, внедрения методов и заканчивая уничтожением сервлета. Контейнер так же предоставляет дополнительные утилиты, например JNDI, для управления пулом ресурсов.
    • Поддержка многопоточности. Контейнер самостоятельно создает новую нить для каждого запроса и предоставляет ей запрос и ответ для обработки. Таким образом сервлет не инициализируется заново для каждого запроса и тем самым сохраняет память и уменьшает время до обработки запроса.
    • Поддержка JSP. JSP классы не похожи на стандартные классы джавы, но контейнер сервлетов преобразует каждую JSP в сервлет и далее управляется контейнером как обычным сервлетом.
    • Различные задачи. Контейнер сервлетов управляет пулом ресурсов, памятью приложения, сборщиком мусора. Предоставляются возможности настройки безопасности и многое другое.

    к оглавлению

    Что вы знаете о сервлетных фильтрах?

    Сервлетный фильтр — это Java-код, пригодный для повторного использования и позволяющий преобразовать содержание HTTP-запросов, HTTP-ответов и информацию, содержащуюся в заголовках HTML. Сервлетный фильтр занимается предварительной обработкой запроса, прежде чем тот попадает в сервлет, и/или последующей обработкой ответа, исходящего из сервлета.

    Сервлетные фильтры могут:

    • перехватывать инициацию сервлета прежде, чем сервлет будет инициирован;
    • определить содержание запроса прежде, чем сервлет будет инициирован;
    • модифицировать заголовки и данные запроса, в которые упаковывается поступающий запрос;
    • модифицировать заголовки и данные ответа, в которые упаковывается получаемый ответ;
    • перехватывать инициацию сервлета после обращения к сервлету.

    Сервлетный фильтр может быть конфигурирован так, что он будет работать с одним сервлетом или группой сервлетов. Основой для формирования фильтров служит интерфейс javax.servlet.Filter, который реализует три метода:

    • void init(FilterConfig config) throws ServletException;
    • void destroy();
    • void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException;

    Метод init() вызывается прежде, чем фильтр начинает работать,и настраивает конфигурационный объект фильтра. Метод doFilter() выполняет непосредственно работу фильтра. Таким образом, сервер вызывает init() один раз, чтобы запустить фильтр в работу, а затем вызывает doFilter() столько раз, сколько запросов будет сделано непосредственно к данному фильтру. После того, как фильтр заканчивает свою работу, вызывается метод destroy().

    Интерфейс FilterConfig содержит метод для получения имени фильтра, его параметров инициации и контекста активного в данный момент сервлета. С помощью своего метода doFilter() каждый фильтр получает текущий запрос request и ответ response, а также FilterChain, содержащий список фильтров, предназначенных для обработки. В doFilter() фильтр может делать с запросом и ответом всё, что ему захочется — собирать данные или упаковывать объекты для придания им нового поведения. Затем фильтр вызывает chain.doFilter(), чтобы передать управление следующему фильтру. После возвращения этого вызова фильтр может по окончании работы своего метода doFilter() выполнить дополнительную работу над полученным ответом. К примеру, сохранить регистрационную информацию об этом ответе.

    После того, как класс-фильтр откомпилирован, его необходимо установить в контейнер и «приписать» (map) к одному или нескольким сервлетам. Объявление и подключение фильтра отмечается в дескрипторе развёртывания web.xml внутри элементов <filter> и <filter-mapping>. Для подключение фильтра к сервлету необходимо использовать вложенные элементы <filter-name> и <servlet-name>.

    Объявление класс-фильтра FilterConnect с именем FilterName:

      <filter>
            <filter-name>FilterName</filter-name>
            <filter-class>FilterConnect</filter-class>
            <init-param>
                    <!--- фильтр имеет параметр инициализации `active`, которому присваивается значение `true`. -->
                    <param-name>active</param-name>
                    <param-value>true</param-true>
            </init-param>
      </filter>

    Подключение фильтра FilterName к сервлету ServletName:

      <filter-mapping>
            <filter-name>FilterName</filter-name>
            <servlet-name>ServletName</servlet-name>
      </filter-mapping>

    Для связи фильтра со страницами HTML или группой сервлетов необходимо использовать тег <url-pattern>:

    Подключение фильтра FilterName ко всем вызовам .html страниц

      <filter-mapping>
              <filter-name>FilterName</filter-name>
              <url-pattern>*.html</url-pattern>
      </filter-mapping>

    Порядок, в котором контейнер строит цепочку фильтров для запроса определяется следующими правилами:

    • цепочка, определяемая <url-pattern>, выстраивается в том порядке, в котором встречаются соответствующие описания фильтров в web.xml;
    • последовательность сервлетов, определенных с помощью <servlet-name>, также выполняется в той последовательности, в какой эти элементы встречаются в дескрипторе развёртывания web.xml.

    к оглавлению

    Зачем в сервлетах используются различные listener?

    Listener (слушатель) работает как триггер, выполняя определённые действия при наступлении какого-либо события в жизненном цикле сервлета.

    Слушатели, разделённые по области видимости (scope):

    • Request:
      • ServletRequestListener используется для того, чтобы поймать момент создания и уничтожения запроса;
      • ServletRequestAttributeListener используется для прослушивания событий, происходящих с атрибутами запроса.
    • Context:
      • ServletContextListener позволяет поймать момент, когда контекст инициализируется либо уничтожается;
      • ServletContextAttributeListener используется для прослушивании событий, происходящих с атрибутами в контексте.
    • Session:
      • HttpSessionListener позволяет поймать момент создания и уничтожения сессии;
      • HttpSessionAttributeListener используется при прослушивании событий происходящих с атрибутами в сессии;
      • HttpSessionActivationListener используется в случае, если происходит миграция сессии между различными JVM в распределённых приложениях;
      • HttpSessionBindingListener так же используется для прослушивания событий, происходящих с атрибутами в сессии. Разница между HttpSessionAttributeListener и HttpSessionBindingListener слушателями: первый декларируется в web.xml; экземпляр класса создается контейнером автоматически в единственном числе и применяется ко всем сессиям; второй: экземпляр класса должен быть создан и закреплён за определённой сессией «вручную», количество экземпляров также регулируется самостоятельно.

    Подключение слушателей:

    <web-app>
        ...
        <listener>
            <listener-class>xyz.company.ExampleListener</listener-class>
        </listener>
        ...
    </web-app>

    HttpSessionBindingListener подключается в качестве атрибута непосредственно в сессию, т.е., чтобы его подключить необходимо:

    • создать экземпляр класса, реализующего этот интерфейс;
    • положить созданный экземпляр в сессию при помощи setAttribute(String, Object).

    к оглавлению

    Когда стоит использовать фильтры сервлетов, а когда слушателей?

    Следует использовать фильтры, если необходимо обрабатывать входящие или исходящие данные (например: для аутентификации, преобразования формата, компрессии, шифрования и т.д.), в случае, когда необходимо реагировать на события — лучше применять слушателей.

    к оглавлению

    Как реализовать запуск сервлета одновременно с запуском приложения?

    Контейнер сервлетов обычно загружает сервлет по первому запросу клиента.

    Если необходимо загрузить сервлет прямо на старте приложения (например если загрузка сервлета происходит длительное время) следует использовать элемент <load-on-startup> в дескрипторе или аннотацию @loadOnStartup в коде сервлета, что будет указывать на необходимость загрузки сервлета при запуске.

    Если целочисленное значение этого параметра отрицательно, то сервлет будет загружен при запросе клиента. В противном случае — загрузится на старте приложения, при этом, чем число меньше, тем раньше в очереди на загрузку он окажется.

    <servlet>
        <servlet-name>ExampleServlet</servlet-name>
        <servlet-class>xyz.company.ExampleServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    к оглавлению

    Как обработать в приложении исключения, выброшенные другим сервлетом?

    Когда приложение выбрасывет исключение контейнер сервлетов обрабатывает его и создаёт ответ в формате HTML. Это аналогично тому, что происходит при кодах ошибок вроде 404, 403 и т.д.

    В дополнении к этому существует возможность написания собственных сервлетов для обработки исключений и ошибок с указанием их в дескрипторе развертывания:

    <error-page>
        <error-code>404</error-code>
        <location>/AppExceptionHandler</location>
    </error-page>
    
    <error-page>
        <exception-type>javax.servlet.ServletException</exception-type>
        <location>/AppExceptionHandler</location>
    </error-page>

    Основная задача таких сервлетов — обработать ошибку/исключение и сформировать понятный ответ пользователю. Например, предоставить ссылку на главную страницу или же описание ошибки.

    к оглавлению

    Что представляет собой ServletConfig?

    Интерфейс javax.servlet.ServletConfig используется для передачи сервлету конфигурационной информации. Каждый сервлет имеет свой собственный экземпляр объекта ServletConfig, создаваемый контейнером сервлетов.

    Для установки параметров конфигурации используются параметры init-param в web.xml:

    <servlet>
        <servlet-name>ExampleServlet</servlet-name>
        <servlet-class>xyz.company.ExampleServlet</servlet-class>
        <init-param>
            <param-name>exampleParameter</param-name>
            <param-value>parameterValue</param-value>
        </init-param>
    </servlet>

    или аннотации @WebInitParam:

    @WebServlet(
        urlPatterns = "/example",
        initParams = {
            @WebInitParam(name = "exampleParameter", value = "parameterValue")
        }
    )
    public class ExampleServlet extends HttpServlet {
        //...
    }

    Для получения ServletConfig сервлета используется метод getServletConfig().

    к оглавлению

    Что представляет собой ServletContext?

    Уникальный (в рамках веб-приложения) объект ServletContext реализует интерфейс javax.servlet.ServletContext и предоставляет сервлетам доступ к параметрам этого веб-приложения. Для предоставления доступа используется элемент <context-param> в web.xml:

    <web-app>
        ...
        <context-param>
            <param-name>exampleParameter</param-name>
            <param-value>parameterValue</param-value>
        </context-param>
        ...
    </web-app>

    Объект ServletContext можно получить с помощью метода getServletContext() у интерфейса ServletConfig. Контейнеры сервлетов так же могут предоставлять контекстные объекты, уникальные для группы сервлетов. Каждая из групп будет связана со своим набором URL-путей хоста. В спецификации Servlet 3 ServletContext был расширен и теперь предоставляет возможности программного добавления слушателей и фильтров в приложение. Так же у этого интерфейса имеется множество полезных методов таких как getServerInfo(), getMimeType(), getResourceAsStream() и т.д.

    к оглавлению

    В чем отличия ServletContext и ServletConfig?

    • ServletConfig уникален для сервлета, а ServletContext — для приложения;
    • ServletConfig используется для предоставления параметров инициализации конкретному сервлету, а ServletContext для предоставления параметров инициализации для всех сервлетов приложения;
    • для ServletConfig возможности модифицировать атрибуты отсутствуют, атрибуты в объекте ServletContext можно изменять.

    к оглавлению

    Для чего нужен интерфейс ServletResponse?

    Интерфейс ServletResponse используется для отправки данных клиенту. Все методы данного инструмента служат именно этой цели:

    • String getCharacterEncoding() — возвращает MIME тип кодировки (к примеру — UTF8), в которой будет выдаваться информация;
    • void setLocale(Locale locale)/Locale getLocale() — указывают на язык используемый в документе;
    • ServletOutputStream getOutputStream()/PrintWriter getWriter() — возвращают потоки вывода данных;
    • void setContentLength(int len) — устанавливает значение поля HTTP заголовка Content-Length;
    • void setContentType(String type) — устанавливает значение поля HTTP заголовка Content-Type.
    • void reset() — позволяет сбросить HTTP заголовок к значениям по-умолчанию, если он ещё не был отправлен
    • и др.

    к оглавлению

    Для чего нужен интерфейс ServletRequest?

    Интерфейс ServletRequest используется для получения параметров соединения, запроса, а также заголовков, входящего потока данных и т.д.

    к оглавлению

    Что такое Request Dispatcher?

    Интерфейс RequestDispatcher используется для передачи запроса другому ресурсу, при этом существует возможность добавления данных, полученных из этого ресурса к собственному ответу сервлета. Так же этот интерфейс используется для внутренней коммуникации между сервлетами в одном контексте.

    В интерфейсе объявлено два метода:

    • void forward(ServletRequest var1, ServletResponse var2) — передает запрос из сервлета к другому ресурсу (сервлету, JSP или HTML файлу) на сервере.
    • void include(ServletRequest var1, ServletResponse var2) — включает контент ресурса (сервлет, JSP или HTML страница) в ответ.

    Доступ к интерфейсу можно получить с помощью метода интерфейса ServletContextRequestDispatcher getRequestDispatcher(String path), где путь начинающийся с /, интерпретируется относительно текущего корневого пути контекста.

    к оглавлению

    Как из одного сервлета вызвать другой сервлет?

    Для вызова сервлета из того же приложения необходимо использовать механизм внутренней коммуникации сервлетов (inter-servlet communication mechanisms) через вызовы методов RequestDispatcher:

    • forward() — передаёт выполнение запроса в другой сервлет;
    • include() — предоставляет возможность включить результат работы другого сервлета в возвращаемый ответ.

    Если необходимо вызывать сервлет принадлежащий другому приложению, то использовать RequestDispatcher уже не получится, т.к. он определен только для текущего приложения. Для подобных целей необходимо использовать метод ServletResponsesendRedirect() которому предоставляется полный URL другого сервлета. Для передачи данных между сервлетами можно использовать cookies.

    к оглавлению

    Чем отличается sendRedirect() от forward()?

    forward():

    • Выполняется на стороне сервера;
    • Запрос перенаправляется на другой ресурс в пределах того же сервера;
    • Не зависит от протокола клиентского запроса, так как обеспечивается контейнером сервлетов;
    • Нельзя применять для внедрения сервлета в другой контекст;
    • Клиент не знает о фактически обрабатываемом ресурсе и URL в строке остается прежним;
    • Выполняется быстрее метода sendRedirect();
    • Определён в интерфейсе RequestDispatcher.

    sendRedirect():

    • Выполняется на стороне клиента;
    • Клиенту возвращается ответ 302 (redirect) и запрос перенаправляется на другой сервер;
    • Может использоваться только с клиентами HTTP;
    • Разрешается применять для внедрения сервлета в другой контекст;
    • URL адрес изменяется на адрес нового ресурса;
    • Медленнее forward() т.к. требует создания нового запроса;
    • Определён в интерфейсе HttpServletResponse.

    к оглавлению

    Для чего используются атрибуты сервлетов и как происходит работа с ними?

    Атрибуты сервлетов используются для внутренней коммуникации сервлетов.

    В веб-приложении существует возможность работы с атрибутами используя методы setAttribute(), getAttribute(), removeAttribute(), getAttributeNames(), которые предоставлены интерфейсами ServletRequest, HttpSession и ServletContext (для областей видимости request, session, context соответственно).

    к оглавлению

    Каким образом можно допустить в сервлете deadlock?

    Можно получить блокировку, например, допустив циклические вызовы метода doPost() в методе doGet() и метода doGet() в методе doPost().

    к оглавлению

    Как получить реальное расположение сервлета на сервере?

    Реальный путь к расположению сервлета на сервере можно получить из объекта ServletContext:

    getServletContext().getRealPath(request.getServletPath()).

    к оглавлению

    Как получить информацию о сервере из сервлета?

    Информацию о сервере можно получить из объекта ServletContext:

    getServletContext().getServerInfo().

    к оглавлению

    Как получить IP адрес клиента на сервере?

    IP адрес клиента можно получить вызвав request.getRemoteAddr().

    к оглавлению

    Какие классы-обертки для сервлетов вы знаете?

    Собственные обработчики ServletRequest и ServletResponse можно реализовать, добавив новые или переопределив существующие методы у классов-обёрток ServletRequestWrapper (HttpServletRequestWrapper) и ServletResponseWrapper (HttpServletRequestWrapper).

    к оглавлению

    В чем отличия GenericServlet и HttpServlet?

    Абстрактный класс GenericServlet — независимая от используемого протокола реализация интерфейса Servlet, а абстрактный класс HttpServlet в свою очередь расширяет GenericServlet для протокола HTTP..

    к оглавлению

    Почему HttpServlet класс объявлен как абстрактный?

    Класс HTTPServlet предоставляет лишь общую реализацию сервлета для HTTP протокола. Реализация ключевых методов doGet() и doPost(), содержащих основную бизнес-логику перекладывается на разработчика и по умолчанию возвращает HTTP 405 Method Not Implemented error.

    к оглавлению

    Какие основные методы присутствуют в классе HttpServlet?

    • doGet() — для обработки HTTP запросов GET;
    • doPost() — для обработки HTTP запросов POST;
    • doPut() — для обработки HTTP запросов PUT;
    • doDelete() — для обработки HTTP запросов DELETE;
    • doHead() — для обработки HTTP запросов HEAD;
    • doOptions() — для обработки HTTP запросов OPTIONS;
    • doTrace() — для обработки HTTP запросов TRACE.

    к оглавлению

    Стоит ли волноваться о многопоточной безопасности работая с сервлетами?

    Методы init() и destroy() вызываются один раз за жизненный цикл сервлета — поэтому по поводу них беспокоиться не стоит.

    Методы doGet(), doPost(), service() вызываются на каждый запрос клиента и т.к. сервлеты используют многопоточность, то здесь задумываться о потокобезопасной работе обязательно. При этом правила использования многопоточности остаются теми же: локальные переменные этих методов будут созданы отдельно для каждого потока, а при использовании глобальных разделяемых ресурсов необходимо использовать синхронизацию или другие приёмы многопоточного программирования.

    к оглавлению

    Какой метод HTTP не является неизменяемым?

    HTTP метод называется неизменяемым, если он на один и тот же запрос всегда возвращает одинаковый результат. HTTP методы GET, PUT, DELETE, HEAD и OPTIONS являются неизменяемыми, поэтому необходимо реализовывать приложение так, чтобы эти методы возвращали одинаковый результат постоянно. К изменяемым методам относится метод POST, который и используется для реализации чего-либо, что изменяется при каждом запросе.

    К примеру, для доступа к статической HTML странице используется метод GET, т.к. он всегда возвращает одинаковый результат. При необходимости сохранять какую-либо информацию, например в базе данных, нужно использовать POST метод.

    к оглавлению

    Какие есть методы отправки данных с клиента на сервер?

    • GET — используется для запроса содержимого указанного ресурса, изображения или гипертекстового документа. Вместе с запросом могут передаваться дополнительные параметры как часть URI, значения могут выбираться из полей формы или передаваться непосредственно через URL. При этом запросы кэшируются и имеют ограничения на размер. Этот метод является основным методом взаимодействия браузера клиента и веб-сервера.
    • POST — используется для передачи пользовательских данных в содержимом HTTP-запроса на сервер. Пользовательские данные упакованы в тело запроса согласно полю заголовка Content-Type и/или включены в URI запроса. При использовании метода POST под URI подразумевается ресурс, который будет обрабатывать запрос.

    к оглавлению

    В чем разница между методами GET и POST?

    • GET передает данные серверу используя URL, тогда как POST передает данные, используя тело HTTP запроса. Длина URL ограничена 1024 символами, это и будет верхним ограничением для данных, которые можно отослать через GET. POST может отправлять гораздо большие объемы данных. Лимит устанавливается web-server и составляет обычно около 2 Mb.
    • Передача данных методом POST более безопасна, чем методом GET, так как секретные данные (например пароль) не отображаются напрямую в web-клиенте пользователя, в отличии от URL, который виден почти всегда. Иногда это преимущество превращается в недостаток — вы не сможете послать данные за кого-то другого.
    • GETметод является неизменяемым, тогда как POST — изменяемый.

    к оглавлению

    В чем разница между PrintWriter и ServletOutputStream?

    PrintWriter — класс для работы с символьным потоком, экземпляр которого можно получить через метод ServletResponse getWriter();

    ServletOutputStream — класс для работы байтовым потоком. Для получения его экземпляра используется метод ServletResponse getOutputStream().

    к оглавлению

    Можно ли одновременно использовать в сервлете PrintWriter и ServletOutputStream?

    Так сделать не получится, т.к. при попытке одновременного вызова getWriter() и getOutputStream() будет выброшено исключение java.lang.IllegalStateException с сообщением, что уже был вызван другой метод.

    к оглавлению

    Расскажите об интерфейсе SingleThreadModel.

    Интерфейс SingleThreadModel является маркерным — в нем не объявлен ни один метод, однако, если сервлет реализует этот интерфейс, то метод service() этого сервлета гарантированно не будет одновременно выполняться в двух потоках. Контейнер сервлетов либо синхронизирует обращения к единственному экземпляру, либо обеспечивает поддержку пула экземпляров и перенаправление запроса свободному сервлету.
    Другими словами, контейнер гарантирует отсутствие конфликтов при одновременном обращении к переменным или методам экземпляра сервлета. Однако существуют также и другие разделяемые ресурсы, которые даже при использовании этого интерфейса, остаются всё так же доступны обработчикам запросов в других потоках. Т.о. пользы от использования этого интерфейса немного и в спецификации Servlet 2.4 он был объявлен deprecated.

    к оглавлению

    Что означает URL encoding? Как это осуществить в Java?

    URL Encoding — процесс преобразования данных в форму CGI (Common Gateway Interface), не содержащую пробелов и нестандартных символов, которые заменяются в процессе кодирования на специальные escape-символы. В Java для кодирования строки используется метод java.net.URLEncoder.encode(String str, String unicode). Обратная операция декодирования возможна через использование метода java.net.URLDecoder.decode(String str, String unicode).

    Hello мир! преобразовывается в Hello%20%D0%BC%D0%B8%D1%80!.

    к оглавлению

    Какие различные методы управления сессией в сервлетах вы знаете?

    При посещении клиентом Web-ресурса и выполнении вариантов запросов, контекстная информация о клиенте не хранится. В протоколе HTTP нет возможностей для сохранения и изменения информации о предыдущих посещениях клиента. Сеанс (сессия) – соединение между клиентом и сервером, устанавливаемое на определенное время, за которое клиент может отправить на сервер сколько угодно запросов. Сеанс устанавливается непосредственно между клиентом и Web-сервером. Каждый клиент устанавливает с сервером свой собственный сеанс. Сеансы используются для обеспечения хранения данных во время нескольких запросов Web-страницы или на обработку информации, введенной в пользовательскую форму в результате нескольких HTTP-соединений (например, клиент совершает несколько покупок в интернет-магазине; студент отвечает на несколько тестов в системе дистанционного обучения).

    Существует несколько способов обеспечения уникального идентификатора сессии:

    • User Authentication – Предоставление учетных данных самим пользователем в момент аутентификации. Переданная таким образом информация в дальнейшем используется для поддержания сеанса. Это метод не будет работать, если пользователь вошёл в систему одновременно из нескольких мест.
    • HTML Hidden Field – Присвоение уникального значения скрытому полю HTML страницы, в момент когда пользователь начинает сеанс. Этот метод не может быть использован со ссылками, потому что нуждается в подтверждении формы со скрытым полем каждый раз во время формирования запроса. Кроме того, это не безопасно, т.к. существует возможность простой подмены такого идентификатора.
    • URL Rewriting – Добавление идентификатора сеанса как параметра URL. Достаточно утомительная операция, потому что требует постоянного отслеживания этого идентификатора при каждом запросе или ответе.
    • Cookies – Использование небольших фрагментов данных, отправленных web-сервером и хранимых на устройстве пользователя. Данный метод не будет работать, если клиент отключает использование cookies.
    • Session Management API – Использование специального API для отслеживания сеанса, построенный на основе и на методах, описанных выше и который решает частные проблемы перечисленных способов:
      • Чаще всего недостаточно просто отслеживать сессию, необходимо ещё и сохранять какие-либо дополнительные данные о ней, которые могут потребоваться при обработке последующих запросов. Осуществление такого поведения требует много дополнительных усилий.
      • Все вышеперечисленные методы не являются универсальными: для каждого из них можно подобрать конкретный сценарий, при котором они не будут работать.

    к оглавлению

    Что такое cookies?

    Сookies («куки») — небольшой фрагмент данных, отправленный web-сервером и хранимый на устройстве пользователя. Всякий раз при попытке открыть страницу сайта, web-клиент пересылает соответствующие этому сайту cookies web-серверу в составе HTTP-запроса. Применяется для сохранения данных на стороне пользователя и на практике обычно используется для:

    • аутентификации пользователя;
    • хранения персональных предпочтений и настроек пользователя;
    • отслеживания состояния сеанса доступа пользователя;
    • ведения разнообразной статистики.

    к оглавлению

    Какие методы для работы с cookies предусмотрены в сервлетах?

    Servlet API предоставляет поддержку cookies через класс javax.servlet.http.Cookie:

    • Для получения массива cookies из запроса необходимо воспользоваться методом HttpServletRequest.getCookies(). Методов для добавления cookies в HttpServletRequest не предусмотрено.
    • Для добавления cookie в ответ используется HttpServletResponse.addCookie(Cookie c). Метода получения cookies в HttpServletResponse отсутствует.

    к оглавлению

    Что такое URL Rewriting?

    URL Rewriting — специальная перезапись (перекодирование) оригинального URL. Данный механизм может использоваться для управления сессией в сервлетах, когда cookies отключены.

    к оглавлению

    Зачем нужны и чем отличаются методы encodeURL() и encodeRedirectURL()?

    HttpServletResponse.encodeURL() предоставляет способ преобразования URL в HTML гиперссылку с преобразованием спецсимволов и пробелов, а так же добавления session id к URL. Такое поведение аналогично java.net.URLEncoder.encode(), но с добавлением дополнительного параметра jsessionid в конец URL.

    Метод HttpServletResponse.encodeRedirectURL() преобразует URL для последующего использования в методе sendRedirect().

    Таким образом для HTML гиперссылок при URL rewriting необходимо использовать encodeURL(), а для URL при перенаправлении — encodeRedirectUrl().

    к оглавлению

    Что такое «сессия»?

    Сессия — это сеанс связи между клиентом и сервером, устанавливаемый на определенное время. Сеанс устанавливается непосредственно между клиентом и веб-сервером в момент получения первого запроса к веб-приложению. Каждый клиент устанавливает с сервером свой собственный сеанс, который сохраняется до окончания работы с приложением.

    к оглавлению

    Как уведомить объект в сессии, что сессия недействительна или закончилась?

    Чтобы быть уверенным в том, что объект будет оповещён о прекращении сессии, нужно реализовать интерфейс javax.servlet.http.HttpSessionBindingListener. Два метода этого интерфейса: valueBound() и valueUnbound() используются при добавлении объекта в качестве атрибута к сессии и при уничтожении сессии соответственно.

    к оглавлению

    Какой существует эффективный способ удостоверится, что все сервлеты доступны только для пользователя с верной сессией?

    Сервлет фильтры используются для перехвата всех запросов между контейнером сервлетов и сервлетом. Поэтому логично использовать соответствующий фильтр для проверки необходимой информации (например валидности сессии) в запросе.

    к оглавлению

    Как мы можем обеспечить transport layer security для нашего веб приложения?

    Для обеспечения transport layer security необходимо настроить поддержку SSL сервлет контейнера. Как это сделать зависит от конкретной реализации сервлет-контейнера.

    к оглавлению

    Как организовать подключение к базе данных, обеспечить журналирование в сервлете?

    При работе с большим количеством подключений к базе данных рекомендуется инициализировать их в servlet context listener, а также установить в качестве атрибута контекста для возможности использования другими сервлетами.

    Журналирование подключается к сервлету стандартным для логгера способом (например для log4j это может быть property-файл или XML-конфигурация) , а далее эта информация используется при настройке соответствующего context listener.

    к оглавлению

    Какие основные особенности появились в спецификации Servlet 3?

    • Servlet Annotations. До Servlet 3 вся конфигурация содержалась в web.xml, что приводило к ошибкам и неудобству при работе с большим количестве сервлетов. Примеры аннотаций: @WebServlet, @WebInitParam, @WebFilter, @WebListener.
    • Web Fragments. Одностраничное веб приложение может содержать множество модулей: все модули прописываются в fragment.xml в папке META-INF. Это позволяет разделять веб приложение на отдельные модули, собранные как .jar-файлы в отдельной lib директории.
    • Динамическое добавление веб компонентов. Появилась возможность программно добавлять фильтры и слушатели, используя ServletContext объект. Для этого применяются методы addServlet(), addFilter(), addListener(). Используя это нововведение стало доступным построение динамической системы, в которой необходимый объект будет создан и вызван только по необходимости.
    • Асинхронное выполнение. Поддержка асинхронной обработки позволяет передать выполнение запроса в другой поток без удержания всего сервера занятым.

    к оглавлению

    Какие способы аутентификации доступны сервлету?

    Спецификация сервлетов определяет четыре типа проверки подлинности:

    • HTTP Basic AuthenticationBASIC. При доступе к закрытым ресурсам появится окно, которое попросит ввести данные для аутентификации.
    • Form Based LoginFORM. Используется собственная html форма:
    • HTTP Digest AuthenticationDIGEST. Цифровая аутентификация с шифрованием.
    • HTTPS AuthenticationCLIENT-CERT. Аутентификация с помощью клиентского сертификата.
    <login-config>
        <auth-method>FORM</auth-method>
        <form-login-config>
            <form-login-page>/login.html</form-login-page>
            <form-error-page>/error.html</form-error-page>
        </form-login-config>
    </login-config>

    к оглавлению

    Что такое Java Server Pages (JSP)?

    JSP (JavaServer Pages) — платформонезависимая переносимая и легко расширяемая технология разработки веб-приложений, позволяющая веб-разработчикам создавать содержимое, которое имеет как статические, так и динамические компоненты. Страница JSP содержит текст двух типов: статические исходные данные, которые могут быть оформлены в одном из текстовых форматов HTML, SVG, WML, или XML, и JSP-элементы, которые конструируют динамическое содержимое. Кроме этого могут использоваться библиотеки JSP-тегов, а также EL (Expression Language), для внедрения Java-кода в статичное содержимое JSP-страниц.

    Код JSP-страницы транслируется в Java-код сервлета с помощью компилятора JSP-страниц Jasper, и затем компилируется в байт-код JVM.

    JSP-страницы загружаются на сервере и управляются Java EE Web Application. Обычно такие страницы упакованы в файловые архивы .war и .ear.

    к оглавлению

    Зачем нужен JSP?

    JSP расширяет технологию сервлетов обеспечивая возможность создания динамических страницы с HTML подобным синтаксисом.

    Хотя создание представлений поддерживается и в сервлетах, но большая часть любой веб-страницы является статической, поэтому код сервлета в таком случае получается чересчур перегруженным, замусоренным и поэтому при его написании легко допустить ошибку.

    Еще одним преимуществом JSP является горячее развертывание — возможность заменить одну страницу на другую непосредственно в контейнере без необходимости перекомпилировать весь проект или перезапускать сервер.

    Однако рекомендуется избегать написания серьёзной бизнес-логики в JSP и использовать страницу только в качестве представления.

    к оглавлению

    Опишите, как обрабатываются JSP страницы, начиная от запроса к серверу, заканчивая ответом пользователю.

    Когда пользователь переходит по ссылке на страницу page.jsp, он отправляет http-запрос на сервер GET /page.jsp. Затем, на основе этого запроса и текста самой страницы, сервер генерирует java-класс, компилирует его и выполняет полученный сервлет, формирующий ответ пользователю в виде представления этой страницы, который сервер и перенаправляет обратно пользователю.

    к оглавлению

    Расскажите об этапах (фазах) жизненного цикла JSP.

    Если посмотреть код внутри созданной JSP страницы, то он будет выглядеть как HTML и не будет похож на java класс. Конвертацией JSP страниц в HTML код занимается контейнер, который так же создает и сервлет для использования в веб приложении.

    Жизненный цикл JSP состоит из нескольких фаз, которыми руководит JSP контейнер:

    • Translation – проверка и парсинг кода JSP страницы для создания кода сервлета.
    • Compilation – компиляция исходного кода сервлета.
    • Class Loading – загрузка скомпилированного класса в память.
    • Instantiation – внедрение конструктора без параметра загруженного класса для инициализации в памяти.
    • Initialization – вызов init() метода объекта JSP класса и инициализация конфигурации сервлета с первоначальными параметрами, которые указаны в дескрипторе развертывания (web.xml). После этой фазы JSP способен обрабатывать запросы клиентов. Обычно эти фазы происходят после первого запроса клиента (т.е. ленивая загрузка), но можно настроить загрузку и инициализацию JSP на старте приложения по аналогии с сервлетами.
    • Request Processing – длительный жизненный цикл обработки запросов клиента JSP страницей. Обработка является многопоточной и аналогична сервлетам — для каждого запроса создается новый поток, объекты ServletRequest и ServletResponse, происходит выполнение сервис методов.
    • Destroy – последняя фаза жизненного цикла JSP, на которой её класс удаляется из памяти. Обычно это происходит при выключении сервера или выгрузке приложения.

    к оглавлению

    Расскажите о методах жизненного цикла JSP.

    Контейнер сервлетов (например, Tomcat, GlassFish) создает из JSP-страницы класс сервлета, наследующего свойства интерфейса javax.servlet.jsp.HttpJspBase и включающего следующие методы:

    • jspInit() — метод объявлен в JSP странице и реализуется с помощью контейнера. Этот метод вызывается один раз в жизненном цикле JSP для того, чтобы инициализировать конфигурационные параметры указанные в дескрипторе развертывания. Этот метод можно переопределить с помощью определения элемента JSP scripting и указания необходимых параметров для инициализации;
    • _jspService() — метод переопределяется контейнером автоматически и соответствует непосредственно коду JSP, описанному на странице. Этот метод определен в интерфейсе HttpJspPage, его имя начинается с нижнего подчеркивания и он отличается от других методов жизненного цикла тем, что его невозможно переопределить;
    • jspDestroy() — метод вызывается контейнером для удаления объекта из памяти (на последней фазе жизненного цикла JSP — Destroy). Метод вызывается только один раз и доступен для переопределения, предоставляя возможность освободить ресурсы, которые были созданы в jspInit().

    к оглавлению

    Какие методы жизненного цикла JSP могут быть переопределены?

    Возможно переопределить лишь jspInit() и jspDestroy() методы.

    к оглавлению

    Как можно предотвратить прямой доступ к JSP странице из браузера?

    Прямой доступ к директории /WEB-INF/ из веб-приложения отсутствует. Поэтому JSP-страницы можно расположить внутри этой папки и тем самым запретить доступ к странице из браузера. Однако, по аналогии с описанием сервлетов, будет необходимо настроить дескриптор развертывания:

    <servlet>
        <servlet-name>Example</servlet-name>
        <jsp-file>/WEB-INF/example.jsp</jsp-file>
        <init-param>
            <param-name>exampleParameter</param-name>
            <param-value>parameterValue</param-value>
        </init-param>
    </servlet>
        
    <servlet-mapping>
        <servlet-name>Example</servlet-name>
        <url-pattern>/example.jsp</url-pattern>
    </servlet-mapping>

    к оглавлению

    Какая разница между динамическим и статическим содержимым JSP?

    Статическое содержимое JSP (HTML, код JavaScript, изображения и т.д.) не изменяется в процессе работы веб приложения.

    Динамические ресурсы созданы для того, чтобы отображать свое содержимое в зависимости от пользовательских действий. Обычно они представлены в виде выражений EL (Expression Language), библиотек JSP-тегов и пр.

    к оглавлению

    Как закомментировать код в JSP?

    • <!—- HTML комментарий; отображается на странице JSP —-> такие комментарии будут видны клиенту при просмотре кода страницы.
    • <%—- JSP комментарий; не отображается на странице JSP —-%> такие комментарии описываются в созданном сервлете и не посылаются клиенту. Для любых комментариев по коду или отладочной информации необходимо использовать именно такой тип комментариев.

    к оглавлению

    Какие существуют основные типы тегов JSP?

    • Выражение JSP: <%= expression %> — выражение, которое будет обработано с перенаправлением результата на вывод;
    • Скриплет JSP: <% code %> — код, добавляемый в метод service().
    • Объявление JSP: <%! code %> — код, добавляемый в тело класса сервлета вне метода service().
    • Директива JSP page: <%@ page att="value" %> — директивы для контейнера сервлетов с информацией о параметрах.
    • Директива JSP include: <%@ include file="url" %> — файл в локальной системе, подключаемый при трансляции JSP в сервлет.
    • Комментарий JSP: <%-- comment --%> — комментарий; игнорируется при трансляции JSP страницы в сервлет.

    к оглавлению

    Что вы знаете о действиях JSP (Action tag и JSP Action Elements).

    Action tag и JSP Action Elements предоставляют методы работы с Java Beans, подключения ресурсов, проброса запросов и создания динамических XML элементов. Такие элементы всегда начинаются с записи jsp: и используются непосредственно внутри страницы JSP без необходимости подключения сторонних библиотек или дополнительных настроек.

    Наиболее часто используемыми JSP Action Elements являются:

    • jsp:include: <jsp:include page="относительный URL" flush="true"/> — подключить файл при запросе страницы. Если необходимо, чтобы файл подключался в процессе трансляции страницы, то используется директива page совместно с атрибутом include;
    • jsp:useBean: <jsp:useBean att=значение*/> или <jsp:useBean att=значение*>...</jsp:useBean> — найти или создать Java bean;
    • jsp:setProperty: <jsp:setProperty att=значение*/> — установить свойства Java bean, или явно, или указанием на соответствующее значение, передаваемое при запросе;
    • jsp:forward: <jsp:forward page="относительный URL"/> — передать запрос другой странице;
    • jsp:plugin: <jsp:plugin attribute="значение"*>...</jsp:plugin> — сгенерировать (в зависимости от типа браузера) тэги OBJECT или EMBED для апплета, использующего технологию Java Plugin.

    к оглавлению

    Взаимодействие JSP — сервлет — JSP.

    «JSP — сервлет — JSP» архитектура построения приложений носит название MVC (Model/View/Controller):

    • Model — классы данных и бизнес-логики;

    • View — страницы JSP;

    • Controller — сервлеты.

    к оглавлению

    Какие области видимости переменных существуют в JSP?

    Область видимости объектов определяется тем контекстом, в который помещается данный объект. В зависимости от той или иной области действия так же определяется время существования объекта.

    В JSP предусмотрены следующие области действия переменных (объектов):

    • request область действия запроса — объект будет доступен на текущей JSP странице, странице пересылки (при использовании jsp:forward) или на включаемой странице (при использовании jsp:include);
    • session область действия сессии — объект будет помещен в сеанс пользователя, будет доступен на всех JSP страницах и будет существовать пока существует сессия пользователя, или он не будет из нее принудительно удален.
    • application область действия приложения — объект будет доступен для всех пользователей на всех JSP страницах и будет существовать на протяжении всей работы приложения или пока не будет удален принудительно и контекста приложения.
    • page область действия страницы — объект будет доступен только на той странице, где он определен. На включаемых (jsp:include) и переадресуемых (jsp:forward) страницах данный объект уже не будет доступен.

    Таким образом, чтобы объект был доступен всем JSP страницам, необходимо указать область видимости application или session, в зависимости от того требуется ли доступ к объекту всем пользователям или только текущему.

    Для указания требуемой области действия при определении объекта на JSP странице используется атрибут scope тега jsp:useBean:

    <jsp:useBean id="myBean" class="ru.javacore.MyBean" scope="session"/>
    

    Если не указывать атрибут scope, то по умолчанию задается область видимости страницы page

    к оглавлению

    Какие неявные, внутренние объекты и методы есть на JSP странице?

    JSP implicit objects (неявные объекты) создаются контейнером при конвертации JSP страницы в код сервлета для помощи разработчикам. Эти объекты можно использовать напрямую в скриптлетах для передачи информации в сервис методы, однако мы не можем использовать неявные объекты в JSP Declaration, т.к. такой код пойдет на уровень класса.

    Существует 9 видов неявных объектов, которые можно использовать прямо на JSP странице. Семь из них объявлены как локальные переменные в начале _jspService() метода, а два оставшихся могут быть использованы как аргументы метода _jspService().

    • out Object :
    <strong>Current Time is</strong>: <% out.print(new Date()); %><br>
    
    • request Object :
    <strong>Request User-Agent</strong>: <%=request.getHeader("User-Agent") %><br>
    
    • response Object :
    <strong>Response</strong>: <%response.addCookie(new Cookie("Test","Value")); %>
    
    • config Object :
    <strong>User init param value</strong>: <%=config.getInitParameter("User") %><br>
    
    • application Object :
    <strong>User context param value</strong>: <%=application.getInitParameter("User") %><br>
    
    • session Object :
    <strong>User Session ID</strong>: <%=session.getId() %><br>
    
    • pageContext Object :
    <% pageContext.setAttribute("Test", "Test Value"); %> 
    <strong>PageContext attribute</strong>: {Name="Test",Value="<%=pageContext.getAttribute("Test") %>"}<br>
    
    • page Object :
    <strong>Generated Servlet Name</strong>: <%=page.getClass().getName() %>
    
    • exception Object :
    <strong>Exception occured</strong>: <%=exception %><br>
    

    к оглавлению

    Какие неявные объекты не доступны в обычной JSP странице?

    Неявный объект исключений JSP недоступен в обычных JSP страницах и используется на страницах ошибок JSP (errorpage) только для того, чтобы перехватить исключение, выброшенное JSP страницей и далее предоставить какую-либо полезную информацию клиенту.

    к оглавлению

    Что вы знаете о PageContext и какие преимущества его использования?

    Неявный объект JSP — экземпляр класса javax.servlet.jsp.PageContext предоставляет доступ ко всем пространствам имён, ассоциированным с JSP-страницей, а также к различным её атрибутам.
    Остальные неявные объекты добавляются к pageContext автоматически.

    Класс PageContext это абстрактный класс, а его экземпляр можно получить через вызов метода JspFactory.getPageContext(), и освободить через вызов метода JspFactory.releasePageContext().

    PageContext обладает следующим набором особенностей и возможностей:

    • единый API для обслуживания пространств имён различных областей видимости;
    • несколько удобных API для доступа к различным public-объектам;
    • механизм получения JspWriter для вывода;
    • механизм обслуживания использования сессии страницей;
    • механизм экспонирования («показа») атрибутов директивы page среде скриптинга;
    • механизмы направления или включения текущего запроса в другие компоненты приложения;
    • механизм обработки процессов исключений на страницах ошибок errorpage;

    к оглавлению

    Как сконфигурировать параметры инициализации для JSP?

    Параметры инициализации для JSP задаются в web.xml файле аналогично сервлетам — элементами servlet и servlet-mapping. Единственным отличием будет указание местонахождения JSP страницы:

    <servlet>
        <servlet-name>Example</servlet-name>
        <jsp-file>/WEB-INF/example.jsp</jsp-file>
        <init-param>
            <param-name>exampleParameter</param-name>
            <param-value>parameterValue</param-value>
        </init-param>
    </servlet>

    к оглавлению

    Почему не рекомендуется использовать скриплеты (скриптовые элементы) в JSP?

    JSP страницы используются в основном для целей отображения представления (view), а вся бизнес-логика (controller) и модель (model) должны быть реализованы в сервлетах или классах-моделях. Обязанность JSP страницы — создание HTML ответа из переданных через атрибуты параметров. Большая часть JSP содержит HTML код, а для того, чтобы помочь верстальщикам понять JSP код страницы предоставляется функционал элементов action, JSP EL, JSP Standart Tag Library. Именно их и необходимо использовать вместо скриптлетов для создания моста между (JSP)HTML и (JSP)Java частями.

    к оглавлению

    Можно ли определить класс внутри JSP страницы?

    Определить класс внутри JSP страницы можно, но это считается плохой практикой:

    <%!
    private static class ExampleOne {
      //...
    }
    %>
    
    <%
    private class ExampleTwo {
      //...
    }
    %>

    к оглавлению

    Что вы знаете о Языке выражений JSP (JSP Expression Language – EL)?

    JSP Expression Language (EL) — скриптовый язык выражений, который позволяет получить доступ к Java компонентам (JavaBeans) из JSP. Начиная с JSP 2.0 используется внутри JSP тегов для отделения Java кода от JSP для обеспечения лёгкого доступа к Java компонентам, уменьшая при этом количество кода Java в JSP-страницах, или даже полностью исключая его.

    Развитие EL происходило с целью сделать его более простым для дизайнеров, которые имеют минимальные познания в языке программирования Java. До появления языка выражений, JSP имел несколько специальных тегов таких как скриптлеты (англ.), выражения и т. п. которые позволяли записывать Java код непосредственно на странице. С использованием языка выражений веб-дизайнер должен знать только то, как организовать вызов соответствующих java-методов.

    Язык выражений JSP 2.0 включает:

    • Создание и изменение переменных.
    • Управление потоком выполнения программы: ветвление, выполнение различных типов итераций и т.д.
    • Упрощенное обращение к встроенным JSP-объектам.
    • Возможность создавать собственные функции.

    Язык выражений используется внутри конструкции ${ ... }. Подобная конструкция может размещаться либо отдельно, либо в правой части выражения установки атрибута тега.

    к оглавлению

    Какие типы EL операторов вы знаете?

    Операторы в EL поддерживают наиболее часто используемые манипуляции данными.

    Типы операторов:

    • Стандартные операторы отношения: == (или eq), != (или neq), < (или lt), > (или gt), <= (или le), >= (или ge).
    • Арифметические операторы: +, , *, / (или div), % (или mod).
    • Логические операторы: && (или and), || (или or), ! (или not).
    • Оператор empty – используется для проверки переменной на null, или «пустое значение», который зависит от типа проверяемого объекта. Например, нулевая длина для строки или нулевой размер для коллекции.

    к оглавлению

    Назовите неявные, внутренние объекты JSP EL и их отличия от объектов JSP.

    Язык выражений JSP предоставляет множество неявных объектов, которые можно использовать для получения атрибутов в различных областях видимости (scopes) и для значений параметров. Важно отметить, что они отличаются от неявных объектов JSP и содержат атрибуты в заданной области видимости. Наиболее часто использующийся implicit object в JSP EL и JSP page — это объект pageContext. Ниже представлена таблица неявных объектов JSP EL.

    к оглавлению

    Как отключить возможность использования EL в JSP?

    Для игнорирования выполнения языка выражений на странице существует два способа:

    • использовать директиву <%@ page isELIgnored = «true» %>,
    • настроить web.xml (лучше подходит для отключения EL сразу на нескольких страницах):
    <jsp-config>
        <jsp-property-group>
            <url-pattern>*.jsp</url-pattern>
            <el-ignored>true</el-ignored>
        </jsp-property-group>
    </jsp-config>

    к оглавлению

    Как узнать тип HTTP метода используя JSP EL?

    ${pageContext.request.method}.

    к оглавлению

    Что такое JSTL (JSP Standard tag library)?

    JavaServer Pages Standard Tag Library, JSTL, Стандартная библиотека тегов JSP — расширение спецификации JSP (конечный результат JSR 52), добавляющее библиотеку JSP тегов для общих нужд, таких как разбор XML данных, условная обработка, создание циклов и поддержка интернационализации.

    JSTL является альтернативой такому виду встроенной в JSP логики, как скриплеты (прямые вставки Java кода). Использование стандартизованного множества тегов предпочтительнее, поскольку получаемый код легче поддерживать и проще отделять бизнес-логику от логики отображения.

    Для использования JSTL тегов необходимо:

    • подключить зависимости, например в pom.xml:
    <dependency>
        <groupId>jstl</groupId>
        <artifactId>jstl</artifactId>
        <version>1.2</version>
    </dependency>
    <dependency>
        <groupId>taglibs</groupId>
        <artifactId>standard</artifactId>
        <version>1.1.2</version>
    </dependency>
    • указать пространство имен основных тегов JSTL через указание на JSP странице код:
    <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
    <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
    <%@ taglib uri="http://java.sun.com/jsp/jstl/sql" prefix="sql" %>
    <%@ taglib uri="http://java.sun.com/jsp/jstl/xml" prefix="x" %>
    <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
    

    к оглавлению

    Из каких групп тегов состоит библиотека JSTL?

    Группы тегов JSTL согласно их функциональности:

    • Core Tags предоставляют возможности итерации, обработки исключений, URL, forward, redirect response и т.д.
    • Formatting Tags и Localization Tags предоставляют возможности по форматированию чисел, дат и поддержки i18n локализации и resource bundles.
    • SQL Tags – поддержка работы с базами данных.
    • XML Tags используются для работы с XML документами: парсинга, преобразования данных, выполнения выражений XPath и т.д..
    • JSTL Functions Tags предоставляет набор функций, которые позволяют выполнять различные операции со строками и т.п. Например, по конкатенации или разбиению строк.

    к оглавлению

    Какая разница между <c:set> и <jsp:useBean>?

    Оба тега создают и помещают экземпляры в заданную область видимости, но <jsp:useBean> только создаёт экземпляр конкретного типа, а <c:set>, создав экземпляр, позволяет дополнительно извлекать значение, например, из параметров запроса, сессии и т. д.

    к оглавлению

    Чем отличается <c:import> от <jsp:include> и директивы <%@include %>?

    По сравнению с action-тегом <jsp:include> и директивой <%@include %> тег <c:import> обеспечивает более совершенное включение динамических ресурсов, т.к. получает доступ к источнику, чтение информации из которого происходит непосредственно без буферизации и контент включается в исходную JSP построчно.

    к оглавлению

    Как можно расширить функциональность JSP?

    Что вы знаете о написании пользовательских JSP тегов?

    Приведите пример использования собственных тегов.

    JSP можно расширить с помощью создания собственных тегов с необходимой функциональностью, которые можно добавить в библиотеку тегов на страницу JSP указав необходимое пространство имен.

    /WEB-INF/exampleTag.tld

    <?xml version="1.0" encoding="UTF-8"?>
    <taglib version="2.1" xmlns="http://java.sun.com/xml/ns/j2ee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-jsptaglibrary_2_1.xsd">
        <tlib-version>1.0</tlib-version>
        <short-name>example</short-name>
        <uri>/WEB-INF/exampleTag</uri>
        <tag>
            <name>exampleTag</name>
            <tag-class>xyz.company.ExampleTag</tag-class>
            <body-content>empty</body-content>
            <info>The example tag displays Hello World!</info>
        </tag>
    </taglib>

    xyz.company.ExampleServlet.java

    package xyz.company;
    
    import java.io.IOException;
    
    import javax.servlet.jsp.JspException;
    import javax.servlet.jsp.tagext.TagSupport;
    
    public class ExampleTag extends TagSupport{
        private static final long serialVersionUID = 1L;
    
        @Override
        public int doStartTag() throws JspException {
            try {
                pageContext.getOut().print("Hello World!");
            } catch(IOException ioException) {
                throw new JspException("Error: " + ioException.getMessage());
            }       
            return SKIP_BODY;
        }
    }

    exampleTag.jsp

    <%@ taglib uri="/WEB-INF/exampleTag.tld" prefix="example"%>
    <%@ page session="false" pageEncoding="UTF-8"%>
    <html>
    <head>
    <title>Example Tag</title>
    </head>
    <body>
        <h1>Example Tag</h1>
        <p><example:exampleTage /><p>
    </body>
    </html>
    

    Также в пользовательских тегах существует возможность задать входные параметры. Например, существует необходимость отформатировать каким-либо стилем очень длинное число. Для этого можно использовать собственный тег по типу:

    <mytags:formatNumber number="123456.789" format="#,## #.00"/>

    Используя входные параметры, число должно быть преобразовано на JSP странице в таком виде 123,456.79 согласно шаблону. Т.к. JSTL не предоставляет такой функциональности, необходимо создать пользовательский тег для получения необходимого результата.

    к оглавлению

    Как сделать перенос строки в HTML средствами JSP?

    Для переноса строки можно использовать тег c:out и атрибут escapeXml, который отключает обработку HTML элементов. В этом случае браузер получит следующий код в виде строки и обработает элемент <br> как требуется:

    <c:out value="<br> creates a new line in HTML" escapeXml="true"></c:out>

    к оглавлению

    Почему не нужно конфигурировать стандартные JSP теги в web.xml?

    Стандартные теги JSP не конфигурируются в web.xml, потому что tld файлы уже находятся внутри каталога /META-INF/ в jar файлах JSTL.

    Когда контейнер загружает веб-приложение и находит tld файлы в в jar файле в директории /META-INF/, то он автоматически настраивает их для непосредственного использования на JSP страницах. Остается только задать пространство имен на JSP странице.

    к оглавлению

    Как можно обработать ошибки JSP страниц?

    Для обработки исключений выброшенных на JSP странице достаточно лишь задать страницу ошибки JSP и при её создании установить значение page directive attribute isErrorPage в значение true. Таким образом будет предоставлен доступ к неявным объектам исключений в JSP и появится возможность передавать собственные, более информативные сообщения об ошибках клиенту. При этом настройка дескриптора развертывания выглядит так:

    <error-page>
         <error-code>404</error-code>
         <location>/error.jsp</location>
    </error-page>
      
    <error-page>
         <exception-type>java.lang.Throwable</exception-type>
         <location>/error.jsp</location>
    </error-page>

    к оглавлению

    Как происходит обработка ошибок с помощью JSTL?

    Для перехвата и обработки исключений в служебных методах класса служат JSTL Core Tags c:catch и c:if.

    Тег c:catch перехватывает исключение и обертывает его в переменную exception, доступную для обработки в теге c:if:

    <c:catch var ="exception">
       <% int x = 42/0;%>
    </c:catch>  
    <c:if test = "${exception ne null}">
       <p>Exception is : ${exception} <br />
       Exception Message: ${exception.message}</p>
    </c:if>
    

    Обратите внимание что используется язык выражений JSP EL в теге c:if.

    к оглавлению

    Как конфигурируется JSP в дескрипторе развертывания.

    Для настройки различных параметров JSP страниц используется элемент jsp-config, который отвечает за:

    • управление элементами скриптлетов на странице;
    • управления выполнением в языке выражений;
    • определение шаблона URL для encoding;
    • определение размера буфера, который используется для объектов на странице;
    • обозначение групп ресурсов, соответствующих шаблону URL, которые должны быть обработаны как XML документ.
    <jsp-config>
        <taglib>
            <taglib-uri>http://company.xyz/jsp/tlds/customtags</taglib-uri>
            <taglib-location>/WEB-INF/exampleTag.tld</taglib-location>
        </taglib>
    </jsp-config>

    к оглавлению

    Можно ли использовать Javascript на JSP странице?

    Да, это возможно. Несмотря на то, что JSP это серверная технология, на выходе она всё равно создает HTML страницу, на которую можно добавлять Javascript и CSS.

    к оглавлению

    Всегда ли создается объект сессии на JSP странице, можно ли отключить его создание?

    Jsp-страница, по умолчанию, всегда создает сессию. Используя директиву page с атрибутом session можно изменить это поведение:

    <%@ page session ="false" %>

    к оглавлению

    Какая разница между JSPWriter и сервлетным PrintWriter?

    PrintWriter является объектом отвечающим за запись содержания ответа на запрос. JspWriter использует объект PrintWriter для буферизации. Когда буфер заполняется или сбрасывается, JspWriter использует объект PrintWriter для записи содержания в ответ.

    к оглавлению

    Опишите общие практические принципы работы с JSP.

    Хорошей практикой работы с технологией JSP является соблюдение следующих правил:

    • Следует избегать использования элементов скриптлетов на странице. Если элементы action, JSTL, JSP EL не удовлетворяют потребностям, то желательно написать собственный тег.
    • Рекомендуется использовать разные виды комментариев: так JSP комментарии необходимы для уровня кода и отладки, т.к. они не будут показаны клиенту.
    • Не стоит размещать какой-либо бизнес логики внутри JSP страницы. Страницы должны использоваться только для создания ответов клиенту.
    • Для повышения производительности лучше отключать создание сессии на странице, когда это не требуется.
    • Директивы taglib, page в начале JSP страницы улучшают читабельность кода.
    • Следует правильно использовать директиву include и элемент jsp:include action. Первая используется для статических ресурсов, а второй для динамических ресурсов времени выполнения.
    • Обработку исключений нужно производить с помощью страниц ошибок. Это помогает избегать запуска специальных служебных методов и может повысить производительность.
    • Использующиеся CSS и JavaScript должны быть разнесены в разные файлы и подключаться в начале страницы.
    • В большинстве случаев JSTL должно хватать для всех нужд. Если это не так, то в начале следует проанализировать логику своего приложения, и попробовать перенести выполнения кода в сервлет, а далее с помощью установки атрибутов использовать на JSP странице только результат.

    к оглавлению

    Источники

    • javastudy.ru
    • java2ee.ru
    • Java-online
    • Codenet
    • JavaTalks Articles

    Вопросы для собеседования

    Программирование, JAVA, Параллельное программирование, GitHub, Eclipse


    Рекомендация: подборка платных и бесплатных курсов Python — https://katalog-kursov.ru/

    Цель публикации показать начинающим Java программистам все этапы создания многопоточного сервера. Для полного понимания данной темы основная информация содержится в комментариях моего кода и в выводимых в консоли сообщениях для лучшего понимания что именно происходит и в какой именно последовательности.

    В начале будет рассмотрено создание элементарного клиент-сервера, для усвоения базовых знаний, на основе которых будет строиться многопоточная архитектура.

    Понятия.

    — Потоки: для того чтобы не перепутать что именно подразумевается под потоком я буду использовать существующий в профессиональной литературе синоним — нить, чтобы не путать Stream и Thread, всё-таки более профессионально выражаться — нить, говоря про Thread.

    — Сокеты(Sockets): данное понятие тоже не однозначно, поскольку в какой-то момент сервер выполняет — клиентские действия, а клиент — серверные. Поэтому я разделил понятие серверного сокета — (ServerSocket) и сокета (Socket) через который практически осуществляется общение, его будем называть сокет общения, чтобы было понятно о чём речь.

     Кроме того сокетов общения создаётся по одному на каждом из обменивающихся данными приложении, поэтому сокет приложения которое имеет у себя объект - ServerSocket и первоначально открывает порт в ожидании подключения будем называть сокет общения на стороне сервера, а сокет который создаёт подключающееся к порту по известному адресу второе приложение будем называть сокетом общения на стороне клиента.

    Спасибо за подсказку про Thread.sleep();!
    Конечно в реальном коде Thread.sleep(); устанавливать не нужно — это муветон! В данной публикации я его использую только для того чтобы выполнение программы было нагляднее, что бы успевать разобраться в происходящем.
    Так что тестируйте, изучайте и в своём коде никогда не используйте Thread.sleep();!

    Оглавление:

    1) Однопоточный элементарный сервер.
    2) Клиент.
    3) Многопоточный сервер – сам по себе этот сервер не участвует в общении напрямую, а лишь является фабрикой однонитевых делегатов(делегированных для ведения диалога с клиентами серверов) для общения с вновь подключившимися клиентами, которые закрываются после окончания общения с клиентом.
    4) Имитация множественного обращения клиентов к серверу.

    По многочисленным замечаниям выкладываю ссылку на исходники на GitHub:
    (https://github.com/merceneryinbox/Clietn-Server_Step-by-step.git)

    Итак, начнём с изучения структуры однопоточного сервер, который может принять только одного клиента для диалога. Код приводимый ниже необходимо запускать в своей IDE в этом идея всей статьи. Предлагаю все детали уяснить из подробно задокументированного кода ниже:

    • 1) Однопоточный элементарный сервер.
    import java.io.DataInputStream;
    import java.io.DataOutputStream;
    import java.io.IOException;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    public class TestAsServer {
    
    /**
     * 
     * @param args
     * @throws InterruptedException
     */
        public static void main(String[] args) throws InterruptedException {
    //  стартуем сервер на порту 3345
    
            try (ServerSocket server= new ServerSocket(3345)){
    // становимся в ожидание подключения к сокету под именем - "client" на серверной стороне                                
                    Socket client = server.accept();
    
    // после хэндшейкинга сервер ассоциирует подключающегося клиента с этим сокетом-соединением             
                    System.out.print("Connection accepted.");
    
    // инициируем каналы для  общения в сокете, для сервера     
    
    // канал записи в сокет
                    DataOutputStream out = new DataOutputStream(client.getOutputStream());
                    System.out.println("DataOutputStream  created");
    
                    // канал чтения из сокета
                    DataInputStream in = new DataInputStream(client.getInputStream());
                    System.out.println("DataInputStream created");
    
    // начинаем диалог с подключенным клиентом в цикле, пока сокет не закрыт                
                    while(!client.isClosed()){
    
                    System.out.println("Server reading from channel");
    
    // сервер ждёт в канале чтения (inputstream) получения данных клиента               
                    String entry = in.readUTF();
    
    // после получения данных считывает их              
                    System.out.println("READ from client message - "+entry);
    
    // и выводит в консоль              
                    System.out.println("Server try writing to channel");
    
    // инициализация проверки условия продолжения работы с клиентом по этому сокету по кодовому слову       - quit  
                    if(entry.equalsIgnoreCase("quit")){
                        System.out.println("Client initialize connections suicide ...");
                        out.writeUTF("Server reply - "+entry + " - OK");                
                        Thread.sleep(3000);
                        break;
                    }
    
    // если условие окончания работы не верно - продолжаем работу - отправляем эхо-ответ  обратно клиенту               
                    out.writeUTF("Server reply - "+entry + " - OK");                
                    System.out.println("Server Wrote message to client.");
    
    // освобождаем буфер сетевых сообщений (по умолчанию сообщение не сразу отправляется в сеть, а сначала накапливается в специальном буфере сообщений, размер которого определяется конкретными настройками в системе, а метод  - flush() отправляет сообщение не дожидаясь наполнения буфера согласно настройкам системы             
                    out.flush();    
    
                    }
    
    // если условие выхода - верно выключаем соединения             
                    System.out.println("Client disconnected");
                    System.out.println("Closing connections & channels.");
    
                    // закрываем сначала каналы сокета !
                    in.close();
                    out.close();
    
                    // потом закрываем сам сокет общения на стороне сервера!
                    client.close();
    
                    // потом закрываем сокет сервера который создаёт сокеты общения
                    // хотя при многопоточном применении его закрывать не нужно
                    // для возможности поставить этот серверный сокет обратно в ожидание нового подключения
    
                    System.out.println("Closing connections & channels - DONE.");
                } catch (IOException e) {
                    e.printStackTrace();
            }
        }
    }
    • 2) Клиент.

    Сервер запущен и находится в блокирующем ожидании server.accept(); обращения к нему с запросом на подключение. Теперь можно подключаться клиенту, напишем код клиента и запустим его. Клиент работает когда пользователь вводит что-либо в его консоли (внимание! в данном случае сервер и клиент запускаются на одном компьютере с локальным адресом — localhost, поэтому при вводе строк, которые должен отправлять клиент не забудьте убедиться, что вы переключились в рабочую консоль клиента!).
    После ввода строки в консоль клиента и нажатия enter строка проверяется не ввёл ли клиент кодовое слово для окончания общения дальше отправляется серверу, где он читает её и то же проверяет на наличие кодового слова выхода. Оба и клиент и сервер получив кодовое слово закрывают ресурсы после предварительных приготовлений и завершают свою работу.
    Посмотрим как это выглядит в коде:

    import java.io.BufferedReader;
    import java.io.DataInputStream;
    import java.io.DataOutputStream;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.net.Socket;
    import java.net.UnknownHostException;
    
    public class TestASClient {
    
        /**
         * 
         * @param args
         * @throws InterruptedException
         */
        public static void main(String[] args) throws InterruptedException {
    
    // запускаем подключение сокета по известным координатам и нициализируем приём сообщений с консоли клиента      
            try(Socket socket = new Socket("localhost", 3345);  
                    BufferedReader br =new BufferedReader(new InputStreamReader(System.in));
                    DataOutputStream oos = new DataOutputStream(socket.getOutputStream());
                    DataInputStream ois = new DataInputStream(socket.getInputStream()); )
            {
    
                System.out.println("Client connected to socket.");
                System.out.println();
                System.out.println("Client writing channel = oos & reading channel = ois initialized.");            
    
    // проверяем живой ли канал и работаем если живой           
                    while(!socket.isOutputShutdown()){
    
    // ждём консоли клиента на предмет появления в ней данных                   
                        if(br.ready()){
    
    // данные появились - работаем                      
                System.out.println("Client start writing in channel...");
                Thread.sleep(1000);
                String clientCommand = br.readLine();
    
    // пишем данные с консоли в канал сокета для сервера            
                oos.writeUTF(clientCommand);
                oos.flush();
                System.out.println("Clien sent message " + clientCommand + " to server.");
                Thread.sleep(1000);
    // ждём чтобы сервер успел прочесть сообщение из сокета и ответить      
    
    // проверяем условие выхода из соединения           
                if(clientCommand.equalsIgnoreCase("quit")){
    
    // если условие выхода достигнуто разъединяемся             
                    System.out.println("Client kill connections");
                    Thread.sleep(2000);
    
    // смотрим что нам ответил сервер на последок перед закрытием ресурсов          
                    if(ois.available()!=0)      {   
                        System.out.println("reading...");
                        String in = ois.readUTF();
                        System.out.println(in);
                                }
    
    // после предварительных приготовлений выходим из цикла записи чтения               
                    break;              
                }
    
    // если условие разъединения не достигнуто продолжаем работу            
                System.out.println("Client sent message & start waiting for data from server...");          
                Thread.sleep(2000);
    
    // проверяем, что нам ответит сервер на сообщение(за предоставленное ему время в паузе он должен был успеть ответить)           
                if(ois.available()!=0)      {   
    
    // если успел забираем ответ из канала сервера в сокете и сохраняем её в ois переменную,  печатаем на свою клиентскую консоль                       
                System.out.println("reading...");
                String in = ois.readUTF();
                System.out.println(in);
                        }           
                    }
                }
    // на выходе из цикла общения закрываем свои ресурсы
                System.out.println("Closing connections & channels on clentSide - DONE.");
    
            } catch (UnknownHostException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
    • 3) Многопоточный сервер

    А что если к серверу хочет подключиться ещё один клиент!? Ведь описанный выше сервер либо находится в ожидании подключения одного клиента, либо общается с ним до завершения соединения, что делать остальным клиентам? Для такого случая нужно создать фабрику которая будет создавать описанных выше серверов при подключении к сокету новых клиентов и не дожидаясь пока делегированный подсервер закончит диалог с клиентом откроет accept() в ожидании следующего клиента. Но чтобы на серверной машине хватило ресурсов для общения со множеством клиентов нужно ограничить количество возможных подключений. Фабрика будет выдавать немного модифицированный вариант предыдущего сервера(модификация будет касаться того что класс сервера для фабрики будет имплементировать интерфейс — Runnable для возможности его использования в пуле нитей — ExecuteServices). Давайте создадим такую серверную фабрику и ознакомимся с подробным описанием её работы в коде:

    • Фабрика:
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /**
     * @author mercenery
     *
     */
    public class MultiThreadServer {
    
        static ExecutorService executeIt = Executors.newFixedThreadPool(2);
    
        /**
         * @param args
         */
        public static void main(String[] args) {
    
            // стартуем сервер на порту 3345 и инициализируем переменную для обработки консольных команд с самого сервера
            try (ServerSocket server = new ServerSocket(3345);
                    BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) {
                System.out.println("Server socket created, command console reader for listen to server commands");
    
                // стартуем цикл при условии что серверный сокет не закрыт
                while (!server.isClosed()) {
    
                    // проверяем поступившие комманды из консоли сервера если такие
                    // были
                    if (br.ready()) {
                        System.out.println("Main Server found any messages in channel, let's look at them.");
    
                        // если команда - quit то инициализируем закрытие сервера и
                        // выход из цикла раздачии нитей монопоточных серверов
                        String serverCommand = br.readLine();
                        if (serverCommand.equalsIgnoreCase("quit")) {
                            System.out.println("Main Server initiate exiting...");
                            server.close();
                            break;
                        }
                    }
    
                    // если комманд от сервера нет то становимся в ожидание
                    // подключения к сокету общения под именем - "clientDialog" на
                    // серверной стороне
                    Socket client = server.accept();
    
                    // после получения запроса на подключение сервер создаёт сокет
                    // для общения с клиентом и отправляет его в отдельную нить
                    // в Runnable(при необходимости можно создать Callable)
                    // монопоточную нить = сервер - MonoThreadClientHandler и тот
                    // продолжает общение от лица сервера
                    executeIt.execute(new MonoThreadClientHandler(client));
                    System.out.print("Connection accepted.");
                }
    
                // закрытие пула нитей после завершения работы всех нитей
                executeIt.shutdown();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    • Модифицированный Runnable сервер для запуска из предыдущего кода:
    import java.io.DataInputStream;
    import java.io.DataOutputStream;
    import java.io.IOException;
    import java.net.Socket;
    
    public class MonoThreadClientHandler implements Runnable {
    
        private static Socket clientDialog;
    
        public MonoThreadClientHandler(Socket client) {
            MonoThreadClientHandler.clientDialog = client;
        }
    
        @Override
        public void run() {
    
            try {
                // инициируем каналы общения в сокете, для сервера
    
                // канал записи в сокет следует инициализировать сначала канал чтения для избежания блокировки выполнения программы на ожидании заголовка в сокете
                DataOutputStream out = new DataOutputStream(clientDialog.getOutputStream());
    
    // канал чтения из сокета
                DataInputStream in = new DataInputStream(clientDialog.getInputStream());
                System.out.println("DataInputStream created");
    
                System.out.println("DataOutputStream  created");
                ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                // основная рабочая часть //
                //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    
                // начинаем диалог с подключенным клиентом в цикле, пока сокет не
                // закрыт клиентом
                while (!clientDialog.isClosed()) {
                    System.out.println("Server reading from channel");
    
                    // серверная нить ждёт в канале чтения (inputstream) получения
                    // данных клиента после получения данных считывает их
                    String entry = in.readUTF();
    
                    // и выводит в консоль
                    System.out.println("READ from clientDialog message - " + entry);
    
                    // инициализация проверки условия продолжения работы с клиентом
                    // по этому сокету по кодовому слову - quit в любом регистре
                    if (entry.equalsIgnoreCase("quit")) {
    
                        // если кодовое слово получено то инициализируется закрытие
                        // серверной нити
                        System.out.println("Client initialize connections suicide ...");
                        out.writeUTF("Server reply - " + entry + " - OK");
                        Thread.sleep(3000);
                        break;
                    }
    
                    // если условие окончания работы не верно - продолжаем работу -
                    // отправляем эхо обратно клиенту
    
                    System.out.println("Server try writing to channel");
                    out.writeUTF("Server reply - " + entry + " - OK");
                    System.out.println("Server Wrote message to clientDialog.");
    
                    // освобождаем буфер сетевых сообщений
                    out.flush();
    
                    // возвращаемся в началло для считывания нового сообщения
                }
    
                ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                // основная рабочая часть //
                //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    
                // если условие выхода - верно выключаем соединения
                System.out.println("Client disconnected");
                System.out.println("Closing connections & channels.");
    
                // закрываем сначала каналы сокета !
                in.close();
                out.close();
    
                // потом закрываем сокет общения с клиентом в нити моносервера
                clientDialog.close();
    
                System.out.println("Closing connections & channels - DONE.");
            } catch (IOException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

    Для имитации множественного обращения клиентов к серверу, создадим и запустим (после запуска серверной части) фабрику Runnable клиентов которые будут подключаться серверу и писать сообщения в цикле:

    • 4) Имитация множественного обращения клиентов к серверу.
    import java.io.IOException;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class Main {
    
        // private static ServerSocket server;
    
        public static void main(String[] args) throws IOException, InterruptedException {
    
            // запустим пул нитей в которых колличество возможных нитей ограничено -
            // 10-ю.
            ExecutorService exec = Executors.newFixedThreadPool(10);
            int j = 0;
    
            // стартуем цикл в котором с паузой в 10 милисекунд стартуем Runnable
            // клиентов,
            // которые пишут какое-то количество сообщений
            while (j < 10) {
                j++;
                exec.execute(new TestRunnableClientTester());
                Thread.sleep(10);
            }
    
            // закрываем фабрику
            exec.shutdown();
        }
    }

    Как видно из предыдущего кода фабрика запускает — TestRunnableClientTester() клиентов, напишем для них код и после этого запустим саму фабрику, чтобы ей было кого исполнять в своём пуле:

    import java.io.DataInputStream;
    import java.io.DataOutputStream;
    import java.io.IOException;
    import java.net.Socket;
    
    public class TestRunnableClientTester implements Runnable {
    
        static Socket socket;
    
        public TestRunnableClientTester() {
            try {
    
                // создаём сокет общения на стороне клиента в конструкторе объекта
                socket = new Socket("localhost", 3345);
                System.out.println("Client connected to socket");
                Thread.sleep(2000);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        @Override
        public void run() {
    
            try (
    
                    // создаём объект для записи строк в созданный скокет, для
                    // чтения строк из сокета
                    // в try-with-resources стиле
                    DataOutputStream oos = new DataOutputStream(socket.getOutputStream());
                    DataInputStream ois = new DataInputStream(socket.getInputStream())) {
                System.out.println("Client oos & ois initialized");
    
                int i = 0;
                // создаём рабочий цикл
                while (i < 5) {
    
                    // пишем сообщение автогенерируемое циклом клиента в канал
                    // сокета для сервера
                    oos.writeUTF("clientCommand " + i);
    
                    // проталкиваем сообщение из буфера сетевых сообщений в канал
                    oos.flush();
    
                    // ждём чтобы сервер успел прочесть сообщение из сокета и
                    // ответить
                    Thread.sleep(10);
                    System.out.println("Client wrote & start waiting for data from server...");
    
                    // забираем ответ из канала сервера в сокете
                    // клиента и сохраняем её в ois переменную, печатаем на
                    // консоль
                    System.out.println("reading...");
                    String in = ois.readUTF();
                    System.out.println(in);
                    i++;
                    Thread.sleep(5000);
    
                }
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

    Запускайте, вносите изменения в код, только так на самом деле можно понять работу этой структуры.

    Спасибо за внимание.

    22.06.21

    Все коды состояния или http-статусы состоят из трех цифр. Они помогают понять, чем в итоге обернулся запрос между пользователем и сервером — успехом или ошибкой. Некоторые из рассматриваемых ниже кодов вам хорошо знакомы, но есть и совсем редкие.

    Всего существует пять групп серверных ответов, которые определяют класс состояния по первой цифре. Так можно сразу определить формат ответа от информационного до сообщения об ошибке.

    1xx – Информационные
    2xx – Успешные коды ответа
    3xx – Коды редиректов
    4xx – Ошибки со стороны клиента
    5xx – Ошибки со стороны сервера

    Информационные коды

    Данная группа отвечает за передачу данных. Коды этого типа свидетельствуют о том, что запрос принят сервером и обрабатывается.

    100 — Continue — Временный код ответа, означающий начало принятия запроса к его последующей обработке. 

    101 — Switching Protocols — Сообщает о переключении сервера на протокол, которые был указан в заголовке Upgrade запроса клиента.

    102 — Processing — Информация о том, что запрос принят сервером и находится в обработке, но этот процесс еще не завершен.

    103 — Early Hints — Используется для предварительной загрузки данных пока сервер формирует полный ответ. 


    Успешная обработка запроса

    Коды группы сообщают, что запрос не только принят сервером, но и успешно обработан.

    200 — OK — Один из самых популярных ответов. Он свидетельствует о том, что обмен данными между клиентом и сервером прошли успешно. Именно код ответа 200 ждут от ресурса, чтобы проверить, что все работает как надо: сайт загружен, файл открывается и т.д.

    201 — Created — Информирует об успешном создании нового ресурса в результате выполнения запроса. Например, была создана новая страница. Если сервер по каким-то причинам не смог обработать запрос и ресурс не был создан, то код ответа будет 202.

    202 — Accepted — Сообщает, что сервер принял запрос, но не завершил его обработку.

    203 — Non-Authoritative Information — Отвечает об успешной обработке запроса с оговоркой на то, что передаваемая информация была предоставлена не из исходного сервера, а другого источника (например, резервной копии) и может быть неактуальной. 

    204 — No Content — Сообщает об успешном принятии и обработке запроса, а также о том, что у сервера нет содержимого для отправки пользователю.

    205 — Reset Content — Сервер передает пользователю ответ в виде требований к сбросу введенных данных. Например, о необходимости очистить форму с заполненными до этого данными.

    206 — Partial Content — Свидетельствует о частичном выполнении GET-запроса сервером, возвращая только запрошенную пользователем часть контента. Этот код встречается при использовании кэширования. 


    Коды редиректов

    Данная группа кодов состояния сообщает о перенаправлении пользователя с его согласием или без него. 

    300 — Multiple Choices — Ответ срабатывает при условии, что по указанному запросу существует несколько вариантов URL. При таком варианте пользователь или User-agent должен выбрать альтернативный адрес.

    301 — Moved Permanently — Свидетельствует о перемещении ранее проиндексированного URL на новый адрес. Это команда роботу индексировать новую страницу вместо старой.

    301 редирект также используемся вебмастерами для перенаправления сайта с http://www.mixtelecom.ru на http://mixtelecom.ru. Применяется при переходе на защищенный протокол с HHTP на HTTPS. Используется для защиты доступа к внутренним файлам, например, /index.php переправляется на главный адрес сайта. Некоторые хостинги и CMS автоматически включают данные редиректы при выборе соответствующих опций при настройке сайта.

    302 — Found, 302 Moved Temporarily — Сообщает, что ранее проиндексированный URL был временно перемещен по другому адресу. При этом страница остается в индексе, а в ответе указывается новый адрес запрашиваемого URL.

    303 — See Other — Указывает пользователю, что запрошенная страница находится по другому адресу с запросом GET. 

    304 — Not Modified — Показывает, что запрашиваемая страница или объект не были изменены с момента последнего обновления кэша данного документа.

    305 — Use Proxy — Сообщает пользователю, что запрашиваемый ресурс доступен только через прокси. Данные по прокси указаны в ответе сервера.

    307 — Temporary Redirect — Код схож с 302, сообщая о временном перемещении ресурса на другой адрес. Разница заключается в способе обращения к ресурсу, который должен быть получен тем же методом, что и предыдущий запрос.


    Ошибки клиента

    Коды состояний данной группы сообщают об ошибках клиента, при которых сервер не может вызвать запрашиваемый результат.

    400 — Bad Request — Ошибка свидетельствует от том, что сервер не понял запрос пользователя из-за синтаксической ошибки.

    401 — Unauthorized — Сообщает о необходимости быть авторизованным для получения запрашиваемого доступа. Возникает при неправильном вводе данных пользователем при авторизации. 

    403 — Forbidden — Запрет доступа к запрашиваемой странице. Доступ может быть ограничен настройками индексации или запрещен для определенных IP.

    404 — Пожалуй, самая распространенная ошибка. Сообщает о том, что запрашиваемая страница не найдена. Самая частая причина — ошибка в написании адреса.

    Когда что-то пошло не так и пользователь не попал по запрашиваемому адресу куда хотел, позаботьтесь, чтобы на сайте была соответствующая страница для ошибки 404. На ней должна быть ссылка на главную страницу или меню ресурса, находиться поиск по сайту или контакты для связи. Если запрашиваемый URL был изменен, то настройте 301-редирект со старого адрес страницы на обновленный.

    405 — Method Not Allowed — Сообщает, что в запросе используется метод, который не поддерживается сервером.

    406 — Not Acceptable — Указывает, что запрашиваемый пользователем контент не может быть распознан. Причины могут быть в кодировке, методе сжатия или формате объекта. 

    407 — Proxy Authentication Required — Сообщает, что доступ может быть предоставлен только при авторизации через прокси-сервер. 

    408 — Request Timeout — Сервер прервал соединение с пользователем из-за слишком долгого ожидания. Данный ответ не возвращается если пользователь принудительно отменил запрос или соединение прервалось по иным причинам.

    409 — Conflict — Посылаемый пользователем запрос вызывает конфликт с сервером или другим обращением. 

    410 — Gone — Ответ сервера при запросе к странице или объекту, который был удален и более недоступен.

    411 — Length Required — Отказ сервера на обработку запроса если в нем не указан Content-Length заголовка.

    413 — Request Entity Too Large — Сервер не может обработать обращение из-за слишком большого размера запроса.

    414 — Request-URL Too Long — Сервер не может обработать обращение если в запросе указан слишком длинный URL.

    415 — Unsupported Media Type — Формат запроса пользователя не может быть обработан. Такое встречается при загрузке данных неподходящего формата. 

    416 — Requested Range Not Satisfiable — Отказ сервера на выполнение запроса из-за некорректного значения поля Range. 

    417 — Expectation Failed — Отказ сервера на выполнение запроса из-за некорректного значения поля Expect. 

    422— Unprocessable Entity — Сервер принял и распознал запрос, но не может его выполнить из-за наличия логической ошибки.

    423 — Locked — Запрашиваемая пользователем страница заблокирована. Как правило, это делается для защиты содержимого данной страницы или объекта.

    424 — Failed Dependency — Выполнение текущего запроса зависит от исхода других связанных с ней операций. Если условия не будут соблюдены, то соединение будет разорвано. 

    426 — Upgrade Required — Ошибка указывает на необходимость обновить протокол. Встречается, когда сервер запрашивает https-соединение, которое не может быть предоставлено клиентом.

    429 — Too Many Requests — Возникает при превышении лимита отправляемых пользователем запросов за короткий промежуток времени. Часто используется настройками безопасности.


    Ошибки сервера

    В эту группу входят коды ошибок со стороны сервера, когда по тем или иным причинам он не способен обработать запрос или выполнить требуемую операцию. 

    500 — Internal Server Error — Код оповещает о возникшей внутренней ошибке сервера или его аварийном отказе. 

    501 — Not Implemented — Сервер столкнулся с запросом, который не смог распознать. Либо запрос не поддерживается и не может быть обработан. 

    502 — Bad Gateway — Сообщает о неправильном получении ответа вышестоящего сервера. Частая причина — несогласованные протоколов между шлюзом и сервером (ошибки DNS, прокси, хостинга).

    Если ваш сайт выдает ошибку 502, то вероятнее всего вносились правки или совершались обновления системы. Просмотрите логи для выявления причины. Если на сайте не было никаких работ и апдейтов, то проверьте ресурсы памяти, процессора, php-лимиты сервера. Возможно их недостаточно для корректной обработки запросов. Причинами также могут быть подключенные CDN или anti-DDoS сервисы. Обратитесь в техподдержку.

    503 — Service Unavailable — Указывает на временную недоступность сервера. Причиной может быть его перезагрузка, техническое обслуживание, обращение слишком большого количества пользователей при наличии подобных ограничений. Как правило, сообщение об ошибке содержит параметр Retry-After, информирующий о времени восстановления штатной работы ресурса.

    504 — Gateway Time-out — Истек срок ожидания ответа от вышестоящего сервера. Возможные причины: недостаток ресурсов, неполадки с сетевым соединением, ошибки HTTP протокола, настроен слишком короткий срок ожидания.

    505 — HTTP Version Not Supported — Используемая в запросе версия протокола HTTP не поддерживается сервером. Встречается при использовании устаревшего формата HTTP-протокола.

    506 — Variant Also Negotiates — Сервер не может обработать запрос из-за его неправильной настройки. Сервер зацикливает ответ на себя, выдавая ошибку.

    507 — Insufficient Storage — Означает нехватку места на сервере для обработки запросов пользователя. Нужно освободить или увеличить память, либо обратиться в техническую поддержку.

    508 — Loop Detected — Ошибка возникает в связи с бесконечным перенаправлением. При обработке запроса возникает петля, что приводит к завершению операции.

    509 — Bandwidth Limit Exceeded — Превышен установленный лимит потребления трафика. Ошибка актуальная для интернет-каналов с ограничением по трафику.

    510 — Not Extended — Сервер не поддерживает и не может отработать запрашиваемое пользователем расширение. В теле ошибки может быть приведен список доступных расширений.

    511 — Network Authentication Required — Сообщает о необходимости авторизации для доступа к сети. Например, если пользователь не авторизовался при подключении к Wi-Fi.

    Благодаря приведенным выше кодам ответов намного легче понимать статус сервера при взаимодействии с ним. На практике большинство из них используются в виде указаний роботам по работе с сайтом и порядку его индексации. Сервисы мониторинга и технического анализа сайтов также ориентируются на коды ответов сервера для определения корректной работы ресурса. Вебмастеры могут опираться на статусы состояния в рамках настройки и поддержания работы сайтов. Системные администраторы могут понимать в чем причина неполадок и быстрее устранять возникшие неполадки. Если у вас возникли сложности, которые не получается решить самостоятельно, — воспользуйтесь услугой системного администрирования.

    Уровень сложности
    Простой

    Время на прочтение
    9 мин

    Количество просмотров 6.6K

    Привет! Меня зовут Ивасюта Алексей, я техлид команды Bricks в Авито в кластере Architecture. Я решил написать цикл статей об истории и развитии HTTP, рассмотреть каждую из его версий и проблемы, которые они решали и решают сейчас. 

    Весь современный веб построен на протоколе HTTP. Каждый сайт использует его для общения клиента с сервером. Между собой сервера тоже часто общаются по этому протоколу. На данный момент существует четыре его версии и все они до сих пор используются. Поэтому статьи будут полезны инженерам любых уровней и специализаций, и помогут систематизировать знания об этой важной технологии.

    Что такое HTTP

    HTTP — это гипертекстовый протокол передачи данных прикладного уровня в сетевой модели OSI. Его представил миру Тим Бернерс-Ли в марте 1991 года. Главная особенность HTTP — представление всех данных в нём в виде простого текста. Через HTTP разные узлы в сети общаются между собой. Модель клиент-серверного взаимодействия классическая: клиент посылает запрос серверу, сервер обрабатывает запрос и возвращает ответ клиенту. Полученный ответ клиент обрабатывает и решает: прекратить взаимодействие или продолжить отправлять запросы.

    Ещё одна особенность: протокол не сохраняет состояние между запросами. Каждый запрос от клиента для сервера — отдельная транзакция. Когда поступают два соседних запроса, сервер не понимает, от одного и того же клиента они поступили, или от разных. Такой подход значительно упрощает построение архитектуры веб-серверов.

    Как правило, передача данных по HTTP осуществляется через открытое TCP/IP-соединение1. Серверное программное обеспечение по умолчанию обычно использует TCP-порт 80 для работы веб-сервера, а порт 443 — для HTTPS-соединений.

    HTTPS (HTTP Secure) — это надстройка над протоколом HTTP, которая поддерживает шифрование посредством криптографических протоколов SSL и TLS. Они шифруют отправляемые данные на клиенте и дешифруют их на сервере. Это защищает данные от чтения злоумышленниками, даже если им удастся их перехватить.  

    HTTP/0.9

    В 1991 году была опубликована первая версия протокола с названием HTTP/0.9. Эта реализация была проста, как топор. От интернет-ресурса того времени требовалось только загружать запрашиваемую HTML-страницу и HTTP/0.9 справлялся с этой задачей. Обычный запрос к серверу выглядел так:

    GET /http-spec.html

    В протоколе был определен единственный метод GET и и указывался путь к ресурсу. Так пользователи получали страничку. После этого открытое соединение сразу закрывалось. 

    HTTP/1.0

    Годы шли и интернет менялся. Стало понятно, что нужно не только получать странички от сервера, но и отправлять ему данные. В 1996 году вышла версия протокола 1.0.

    Что изменилось:

    1. В запросе теперь надо было указывать версию протокола. Так сервер мог понимать, как обрабатывать полученные данные.

    2. В ответе от сервера появился статус завершения обработки запроса.

    3. К запросу и ответу добавился новый блок с заголовками.

    4. Добавили поддержку новых методов:

    • HEAD запрашивает ресурс так же, как и метод GET, но без тела ответа. Так можно получить только заголовки, без самого ресурса.

    • POST добавляет сущность к определённому ресурсу. Часто вызывает изменение состояния или побочные эффекты на сервере. Например, так можно отправить запрос на добавление нового поста в блог.

    Структура запроса

    Простой пример запроса:

    GET /path HTTP/1.0
    Content-Type: text/html; charset=utf-8
    Content-Length: 4
    X-Custom-Header: value
    
    test

    В первой строчке указаны метод запроса — GET, путь к ресурсу — /path и версия протокола —  HTTP/1.0.

    Далее идёт блок заголовков. Заголовки — это пары ключ: значение, каждая из которых записывается с новой строки и разделяется двоеточием. Они передают дополнительные данные и настройки от клиента к серверу и обратно. 

    HTTP — это текстовый протокол, поэтому и все данные передаются в виде текста. Заголовки можно отделить друг от друга только переносом строки. Нельзя использовать запятые, точку с запятой, или другие разделители. Всё, что идет после имени заголовка с двоеточием и до переноса строки, будет считаться значением заголовка2.

    В примере серверу передали три заголовка: 

    1. Content-Type — стандартный заголовок. Показывает, в каком формате будут передаваться данные в теле запроса или ответа.

    2. Content-Length — сообщает длину контента в теле запроса в байтах.

    3. X-Custom-Header — пользовательские заголовки, начинающиеся с X- с произвольными именем. Через них реализуется специфическая логика обработки для конкретного сервера. Если веб-сервер не поддерживает такие заголовки, то он проигнорирует их.

    После блока заголовков идёт тело запроса, в котором передается текст test. 

    А так может выглядеть ответ от сервера:

    HTTP/1.1 200 OK
    Date: Thu, 29 Jul 2021 19:20:01 GMT
    Content-Type: text/html; charset=utf-8
    Content-Length: 2
    Connection: close
    Server: gunicorn/19.9.0
    Access-Control-Allow-Origin: *
    Access-Control-Allow-Credentials: true
    
    OK

    В первой строке — версия протокола и статус ответа, например, 200 ОК. Далее идут заголовки ответа. После блока заголовков — тело ответа, в котором записан текст OK

    Статусы ответов

    Клиенту зачастую недостаточно просто отправить запрос на сервер. Во многих случаях надо дождаться ответа и понять, как сервер обработал запрос. Для этого были придуманы статусы ответов. Это трёхзначные числовые коды с небольшими текстовыми обозначениями. Их можно увидеть в терминале или браузере. Сами коды делятся на 5 классов:

    • Информационные ответы: коды 100–199

    • Успешные ответы: коды 200–299

    • Редиректы: коды 300–399

    • Клиентские ошибки: коды 400–499

    • Серверные ошибки: коды 500–599

    Мы рассмотрим основные коды, которые чаще всего встречаются в реальных задачах. С остальными более подробно можно ознакомиться в реестре кодов состояния HTTP.

    Информационные ответы

    100 Continue — промежуточный ответ. Он указывает, что запрос успешно принят.  Клиент может продолжать присылать запросы или проигнорировать этот ответ, если запрос был завершён.

    Примечание

    Этот код ответа доступен с версии HTTP/1.1.

    101 Switching Protocol присылается в ответ на запрос, в котором есть заголовок Upgrade. Это означает, что сервер переключился на протокол, который был указан в заголовке. Такая методика используется, например, для переключения на протокол Websocket.

    102 Processing — запрос получен сервером, но его обработка ещё не завершена.

    Успешные ответы

    200 OK — запрос принят и корректно обработан веб-сервером.

    201 Created — запрос корректно обработан и в результате был создан новый ресурс. Обычно он присылается в ответ на POST запрос.

    Редиректы

    301 Moved Permanently — запрашиваемый ресурс на постоянной основе переехал на новый адрес. Тогда новый путь к ресурсу указывается сервером в заголовке Location ответа.

    Примечание

    Клиент может изменить метод последующего запроса с POST на GET.

    302 Found — указывает, что целевой ресурс временно доступен по другому URl. Адрес перенаправления может быть изменен в любое время, а клиент должен продолжать использовать действующий URI для будущих запросов. Тогда временный путь к ресурсу указывается сервером в заголовке Location ответа.

    Примечание

    Клиент может изменить метод последующего запроса с POST на GET.

    307 Temporary Redirect — имеет то же значение, что и код 302, за исключением того, что клиент не может менять метод последующего запроса.

    308 Permanent Redirect — имеет то же значение, что и код 301, за исключением того, что клиент не может менять метод последующего запроса.

    Клиентские ошибки

    400 Bad Request — запрос от клиента к веб-серверу составлен некорректно. Обычно это происходит, если клиент не передаёт необходимые заголовки или параметры.

    401 Unauthorized — получение запрашиваемого ресурса доступно только аутентифицированным пользователям.

    403 Forbidden — у клиента не хватает прав для получения запрашиваемого ресурса. Например, когда обычный пользователь сайта пытается получить доступ к панели администратора.

    404 Not Found — сервер не смог найти запрашиваемый ресурс.

    405 Method Not Allowed — сервер знает о существовании HTTP-метода, который был указан в запросе, но не поддерживает его. В таком случае сервер должен вернуть список поддерживаемых методов в заголовке Allow ответа.

    Серверные ошибки

    500 Internal Server Error — на сервере произошла непредвиденная ошибка.

    501 Not Implemented — метод запроса не поддерживается сервером и не может быть обработан.

    502 Bad Gateway — сервер, действуя как шлюз или прокси, получил недопустимый ответ от входящего сервера, к которому он обращался при попытке выполнить запрос.

    503 Service Unavailable — сервер не готов обработать запрос (например, из-за технического обслуживания или перегрузки). Обратите внимание, что вместе с этим ответом должна быть отправлена ​​удобная страница с объяснением проблемы. Этот ответ следует использовать для временных условий, а HTTP-заголовок Retry-After по возможности должен содержать расчётное время до восстановления службы.

    504 Gateway Timeout — этот ответ об ошибке выдается, когда сервер действует как шлюз и не может получить ответ за отведенное время.

    505 HTTP Version Not Supported — версия HTTP, используемая в запросе, не поддерживается сервером.

    В HTTP из всего диапазона кодов используется совсем немного. Те коды, которые не используются для задания определенной логики в спецификации, являются неназначенными и могут использоваться веб-серверами для определения своей специфической логики. Это значит, что вы можете, например, придать коду 513 значение «Произошла ошибка обработки видео», или любое другое. Неназначенные коды вы можете посмотреть в реестре кодов состояния HTTP.3

    Тело запроса и ответа

    Тело запроса опционально и всегда отделяется от заголовков пустой строкой. А как понять, где оно заканчивается? Всё кажется очевидным: где кончается строка, там и заканчивается тело. Однако, два символа переноса строки в HTTP означают конец запроса и отправляют его на сервер. Как быть, если мы хотим передать в теле текст, в котором есть несколько абзацев с разрывами в две строки?

    POST /path HTTP/1.1
    Host: localhost
    
    Первая строка
    
    
    Вторая строка после разрыва

    По логике работы HTTP соединение отправится сразу после второй пустой строки и сервер получит в качестве данных только строку Первая строка. Описанную проблему решает специальный заголовок Content-Length. Он указывает на длину контента в байтах. Обычно клиенты (например, браузеры) автоматически считают длину передаваемых данных и добавляют к запросу заголовок с этим значением. Когда сервер получит запрос, он будет ожидать в качестве контента ровно столько байт, сколько указано в заголовке.

    Однако, этого недостаточно для того, чтобы передать данные на сервер. Поведение зависит от реализации сервера, но для большинства из них необходимо также передать заголовок Content-Type. Он указывает на тип передаваемых данных. В качестве значения для этого заголовка используют MIME-типы.4

    MIME (Multipurpose Internet Mail Extensions, многоцелевые расширения интернет-почты) — стандарт, который является частью протокола HTTP. Задача MIME — идентифицировать тип содержимого документа по его заголовку. К примеру, текстовый файл имеет тип text/plain, а HTML-файл — text/html.

    Для передачи данных в формате обычного текста надо указать заголовок Content-Type: text/plain, а для JSON — Content-Type: application/json.

    Можно ли передать тело с GET-запросом?

    Популярный вопрос на некоторых собеседованиях: «Можно ли передать тело с GET-запросом?». Правильный ответ — да. Тело запроса не зависит от метода. В стандарте не описана возможность принимать тело запроса у GET-метода, но это и не запрещено. Технически вы можете отправить данные в теле, но скорее всего веб-сервер будет их игнорировать.

    Представим, что на абстрактном сайте есть форма аутентификации пользователя, в которой есть всего два поля: email и пароль. 

    Если пользователь ввёл данные и нажал на кнопку «Войти», то данные из полей формы должны попасть на сервер. Самым простым и распространенным форматом передачи таких данных будет MIME application/x-www-form-urlencoded. В нем все поля передаются в одной строке в формате ключ=значение и разделяются знаком &

    Запрос на отправку данных будет выглядеть так: 

    POST /login HTTP/1.0
    Host: example.com
    Content-Type: application/x-www-form-urlencoded; charset=utf-8
    Content-Length: 26
    login=user&password=qwerty

    Тут есть небольшая особенность. Как понять, где заканчивается ключ и начинается его значение, если в пароле будет присутствовать знак «=» ?

    POST /login HTTP/1.0
    Host: example.com
    Content-Type: application/x-www-form-urlencoded; charset=utf-8
    Content-Length: 26
    login=user&password=123=45

    В этом случае сервер не сможет понять, как разбить строку на параметры и их значения. На самом деле значения кодируются при помощи механизма url encoding.5 При использовании этого механизма знак «=» будет преобразован в код %3D .

    Тогда наш запрос приобретёт такой вид:

    POST /login HTTP/1.1
    Host: example.com
    Content-Type: application/x-www-form-urlencoded; charset=utf-8
    Content-Length: 28
    
    login=user&password=123%3D45

    Query string

    Данные на сервер можно передавать через тело запроса и через так называемую строку запроса Query String. Это параметры формата ключ=значение, которые располагаются в пути к ресурсу:

    GET /files?key=value&key2=value2 HTTP/1.0

    При этом параметры можно передавать прямо от корня домена:

    GET /?key=value&key2=value2 HTTP/1.0

    Query String имеет такой же формат, как и тело запроса с MIME application/x-www-form-urlencoded, только первая пара значений отделяется от адреса вопросительным знаком.

    Некоторые инженеры ошибочно полагают, что Query String являются параметрами GET-запроса и даже называют их GET-параметрами, но на самом деле это не так. Как и тело запроса, Query String не имеет привязки к HTTP-методам и может передаваться с любым типом запросов.

    Обычно параметры Query String используются в GET-запросах, чтобы конкретизировать получаемый ресурс. Например, можно получить на сервере список файлов, имена которых будут начинаться с переданного значения. 

    GET-запросы по своей идеологии должны быть идемпотентными. Это значит, что множественный вызов метода с одними и теми же параметрами должен приводить к одному и тому же результату. Например, поисковые боты перемещаются по сайту только по ссылкам и делают только GET-запросы, потому что исходя из семантики они не смогут таким образом изменить данные на сайте и повлиять на его работу.

    На этом я закончу говорить про версию протокола 1.0, структуру ответов и запросов. В следующей статье я расскажу, что такое Cookies, для чего нужен CORS и как это всё работает. А пока напоследок оставлю полезные ссылки, которые упомянул в тексте:

    1. Основы TCP/IP

    2. Заголовки HTTP

    3. Реестр кодов состояния HTTP

    4. MIME типы

    5. Алгоритм кодирования URL encoding

    Предыдущая статья: Шесть проблем UX-редакции, которые поможет решить планирование

    Это список кодов состояния ответа протокола передачи гипертекста (HTTP). Коды состояния выдаются сервером в ответ на запрос клиента к серверу. Он включает коды из запроса комментариев IETF (RFC), другие спецификации и некоторые дополнительные коды, используемые в некоторых распространенных приложениях HTTP. Первая цифра кода состояния указывает один из пяти стандартных классов ответов. Показанные фразы сообщений являются типичными, но может быть предоставлена ​​любая удобочитаемая альтернатива. Если не указано иное, код состояния является частью стандарта HTTP / 1.1 (RFC 7231).

    Управление по присвоению номеров в Интернете (IANA) ведет официальный реестр кодов состояния HTTP.

    Все коды состояния ответа HTTP разделены на пять классов или категорий. Первая цифра кода состояния определяет класс ответа, в то время как последние две цифры не имеют никакой роли классификации или категоризации. Стандарт определяет пять классов:

    • Информационный ответ 1xx — запрос получен, процесс продолжается
    • 2xx успешно — запрос был успешно получен, понят и принят
    • Перенаправление 3xx — для выполнения запроса необходимо предпринять дальнейшие действия
    • Ошибка клиента 4xx — запрос содержит неверный синтаксис или не может быть выполнен
    • Ошибка сервера 5xx — серверу не удалось выполнить явно действительный запрос

    Информационный ответ 1xx

    Информационный ответ означает, что запрос был получен и понят. Он выдается на временной основе, пока продолжается обработка запроса. Он предупреждает клиента, чтобы он дождался окончательного ответа. Сообщение состоит только из строки состояния и необязательных полей заголовка и заканчивается пустой строкой. Поскольку в стандарте HTTP / 1.0 не определены коды состояния 1xx, серверы не должны отправлять ответ 1xx клиенту, совместимому с HTTP / 1.0, за исключением экспериментальных условий.

    100 Продолжить
    Сервер получил заголовки запроса, и клиент должен перейти к отправке тела запроса (в случае запроса, для которого необходимо отправить тело; например, запрос POST ). Отправка большого тела запроса на сервер после того, как запрос был отклонен из-за несоответствующих заголовков, будет неэффективен. Чтобы сервер проверил заголовки запроса, клиент должен отправить Expect: 100-continueв качестве заголовка в своем первоначальном запросе и получить 100 Continueкод состояния в ответ перед отправкой тела. Если клиент получает код ошибки, такой как 403 (Запрещено) или 405 (Метод запрещен), он не должен отправлять тело запроса. В ответе 417 Expectation Failedуказано, что запрос следует повторить без Expectзаголовка, поскольку он указывает на то, что сервер не поддерживает ожидания (например, в случае серверов HTTP / 1.0).
    101 протокол переключения
    Запрашивающая сторона попросила сервер переключить протоколы, и сервер дал согласие на это.
    102 Обработка ( WebDAV ; RFC 2518)
    Запрос WebDAV может содержать множество подзапросов, связанных с файловыми операциями, для выполнения которых требуется много времени. Этот код указывает, что сервер получил и обрабатывает запрос, но ответа пока нет. Это предотвращает тайм-аут клиента и предположение, что запрос был потерян.
    103 Ранние подсказки (RFC 8297)
    Используется для возврата некоторых заголовков ответа перед окончательным HTTP-сообщением.

    2хх успех

    Этот класс кодов состояния указывает, что действие, запрошенное клиентом, было получено, понято и принято.

    200 ОК
    Стандартный ответ на успешные HTTP-запросы. Фактический ответ будет зависеть от используемого метода запроса. В запросе GET ответ будет содержать объект, соответствующий запрошенному ресурсу. В запросе POST ответ будет содержать объект, описывающий или содержащий результат действия.
    201 Создано
    Запрос выполнен, в результате чего создан новый ресурс.
    202 Принято
    Запрос принят в обработку, но обработка не завершена. Запрос может или не может быть в конечном итоге обработан, и может быть отклонен, когда происходит обработка.
    203 Неавторизованная информация (начиная с HTTP / 1.1)
    Сервер представляет собой преобразующий прокси (например, веб-ускоритель ), который получил 200 OK от своего источника, но возвращает измененную версию ответа источника.
    204 Нет содержимого
    Сервер успешно обработал запрос и не возвращает никакого содержимого.
    205 Сбросить содержимое
    Сервер успешно обработал запрос, просит, чтобы инициатор запроса сбросил представление документа, и не возвращает никакого содержимого.
    206 Частичное содержимое (RFC 7233)
    Сервер доставляет только часть ресурса ( обслуживание байтов ) из-за заголовка диапазона, отправленного клиентом. Заголовок диапазона используется HTTP-клиентами для возобновления прерванных загрузок или разделения загрузки на несколько одновременных потоков.
    207 Мульти-статус (WebDAV; RFC 4918)
    Нижеследующее тело сообщения по умолчанию является XML- сообщением и может содержать несколько отдельных кодов ответа, в зависимости от того, сколько подзапросов было сделано.
    208 уже сообщено (WebDAV; RFC 5842)
    Члены привязки DAV уже были перечислены в предыдущей части (мультистатусного) ответа и не включаются снова.
    226 IM используется (RFC 3229)
    Сервер выполнил запрос ресурса, и ответ является представлением результата одной или нескольких манипуляций с экземпляром, примененных к текущему экземпляру.

    Перенаправление 3xx

    Этот класс кода состояния указывает, что клиент должен предпринять дополнительные действия для выполнения запроса. Многие из этих кодов состояния используются при перенаправлении URL-адресов .

    Пользовательский агент может выполнить дополнительное действие без взаимодействия с пользователем, только если во втором запросе используется метод GET или HEAD. Пользовательский агент может автоматически перенаправить запрос. Пользовательский агент должен обнаруживать и вмешиваться, чтобы предотвратить циклические перенаправления.

    300 вариантов выбора
    Указывает несколько вариантов ресурса, из которых клиент может выбрать (через согласование содержимого, управляемое агентом ). Например, этот код можно использовать для представления нескольких параметров формата видео, для перечисления файлов с разными расширениями файлов или для устранения неоднозначности в словах .
    301 перемещен навсегда
    Этот и все будущие запросы должны быть направлены на данный URI .
    302 найдено (ранее «перемещено временно»)
    Сообщает клиенту, что нужно посмотреть (перейти) на другой URL-адрес. Спецификация HTTP / 1.0 (RFC 1945) требовала, чтобы клиент выполнял временное перенаправление с помощью того же метода (исходная описывающая фраза была «Перемещено временно»), но популярные браузеры реализовали 302 перенаправления, изменив метод на GET. Поэтому в HTTP / 1.1 добавлены коды состояния 303 и 307, чтобы различать два поведения.
    303 См. Другое (начиная с HTTP / 1.1)
    Ответ на запрос можно найти под другим URI с помощью метода GET. При получении в ответ на POST (или PUT / DELETE) клиент должен предполагать, что сервер получил данные, и должен отправить новый запрос GET для данного URI.
    304 Не изменено (RFC 7232)
    Указывает, что ресурс не был изменен с версии, указанной в заголовках запроса If-Modified-Since или If-None-Match. В таком случае нет необходимости повторно передавать ресурс, поскольку у клиента все еще есть ранее загруженная копия.
    305 Использовать прокси (начиная с HTTP / 1.1)
    Запрошенный ресурс доступен только через прокси, адрес которого указан в ответе. По соображениям безопасности многие HTTP-клиенты (например, Mozilla Firefox и Internet Explorer ) не подчиняются этому коду состояния.
    306 Переключить прокси
    Больше не используется. Изначально означало «Последующие запросы должны использовать указанный прокси».
    307 Временное перенаправление (начиная с HTTP / 1.1)
    В этом случае запрос следует повторить с другим URI; однако в будущих запросах должен по-прежнему использоваться исходный URI. В отличие от того, как 302 был исторически реализован, метод запроса не может быть изменен при повторной выдаче исходного запроса. Например, запрос POST следует повторить, используя другой запрос POST.
    308 постоянное перенаправление (RFC 7538)
    Этот и все будущие запросы должны быть направлены на данный URI . 308 аналогичен поведению 301, но не позволяет изменять метод HTTP . Так, например, отправка формы на постоянно перенаправляемый ресурс может продолжаться гладко.

    4хх клиентских ошибок

    Сообщение Викимедиа 404

    Этот класс кода состояния предназначен для ситуаций, в которых кажется, что ошибка была вызвана клиентом. За исключением ответа на запрос HEAD, сервер должен включать объект, содержащий объяснение ситуации с ошибкой и того, является ли это временным или постоянным состоянием. Эти коды состояния применимы к любому методу запроса. Пользовательские агенты должны отображать пользователю любую включенную сущность.

    ошибка 400, неверный запрос
    Сервер не может или не будет обрабатывать запрос из-за очевидной ошибки клиента (например, неверный синтаксис запроса, слишком большой размер, недопустимое формирование сообщения запроса или обманчивая маршрутизация запроса).
    401 неавторизованный (RFC 7235)
    Подобно 403 Forbidden , но специально для использования, когда аутентификация требуется, но она не удалась или еще не была предоставлена. Ответ должен включать поле заголовка WWW-Authenticate, содержащее запрос, применимый к запрошенному ресурсу. См. Разделы Аутентификация базового доступа и Дайджест-аутентификация доступа . 401 семантически означает «неавторизованный», у пользователя нет действительных учетных данных для аутентификации для целевого ресурса.
    Примечание. Некоторые сайты неправильно выдают HTTP 401, когда IP-адрес запрещен для доступа к веб-сайту (обычно это домен веб-сайта) и этому конкретному адресу отказано в разрешении на доступ к веб-сайту.
    402 Требуется оплата
    Зарезервировано для использования в будущем. Первоначальное намерение состояло в том, чтобы этот код можно было использовать как часть какой-либо формы цифровых денег или схемы микроплатежей , как было предложено, например, GNU Taler , но этого еще не произошло, и этот код широко не используется. Google Developers API использует этот статус, если конкретный разработчик превысил дневной лимит запросов. Sipgate использует этот код, если на счете недостаточно средств для начала звонка. Shopify использует этот код, когда магазин не выплатил комиссию и временно отключен. Stripe использует этот код для неудачных платежей с правильными параметрами, например, заблокированных мошеннических платежей.
    403 Запрещено
    Запрос содержал действительные данные и был понят сервером, но сервер отклоняет действие. Это может быть связано с тем, что пользователь не имеет необходимых разрешений для ресурса или ему нужна учетная запись какого-либо типа, или он пытается выполнить запрещенное действие (например, создает дублирующую запись, где разрешен только один). Этот код также обычно используется, если запрос предоставил аутентификацию путем ответа на запрос поля заголовка WWW-Authenticate, но сервер не принял эту аутентификацию. Запрос не должен повторяться.
    404 Не Найдено
    Запрошенный ресурс не может быть найден, но может быть доступен в будущем. Последующие запросы клиента допустимы.
    405 Метод запрещен
    Метод запроса не поддерживается для запрошенного ресурса; например, запрос GET в форме, которая требует, чтобы данные были представлены через POST , или запрос PUT для ресурса, доступного только для чтения.
    406 неприемлемо
    Запрошенный ресурс может генерировать только контент, неприемлемый в соответствии с заголовками Accept, отправленными в запросе. См. Согласование содержания .
    407 Требуется проверка подлинности прокси (RFC 7235)
    Клиент должен сначала аутентифицироваться с помощью прокси .
    408 Тайм-аут запроса
    Время ожидания запроса на сервере истекло. Согласно спецификациям HTTP: «Клиент не отправил запрос в течение времени, в течение которого сервер был подготовлен к ожиданию. Клиент МОЖЕТ повторить запрос без изменений в любое более позднее время».
    409 Конфликт
    Указывает, что запрос не может быть обработан из-за конфликта в текущем состоянии ресурса, такого как конфликт редактирования между несколькими одновременными обновлениями.
    410 ушел
    Указывает, что запрошенный ресурс больше не доступен и больше не будет доступен. Это следует использовать, когда ресурс был намеренно удален, и ресурс должен быть очищен. После получения кода состояния 410 клиент не должен запрашивать ресурс в будущем. Такие клиенты, как поисковые системы, должны удалить ресурс из своих индексов. В большинстве случаев не требуется, чтобы клиенты и поисковые системы очищали ресурс, и вместо этого можно использовать сообщение «404 Not Found».
    411 Требуемая длина
    В запросе не указана длина его содержимого, необходимая для запрашиваемого ресурса.
    412 Ошибка предварительного условия (RFC 7232)
    Сервер не соответствует одному из предварительных условий, которые запрашивающая сторона поставила в полях заголовка запроса.
    413 Payload Too Large (RFC 7231).
    Запрос больше, чем сервер хочет или может обработать. Ранее назывался «Слишком большой объект запроса».
    414 URI слишком длинный (RFC 7231)
    Предоставленный URI слишком длинный для обработки сервером. Часто это результат того, что слишком много данных кодируется как строка запроса запроса GET, и в этом случае его следует преобразовать в запрос POST. Ранее вызывался «Request-URI Too Long».
    415 Неподдерживаемый тип носителя (RFC 7231)
    Объект запроса имеет тип мультимедиа, который сервер или ресурс не поддерживает. Например, клиент загружает изображение как image / svg + xml , но сервер требует, чтобы изображения использовали другой формат.
    416 Диапазон не соответствует требованиям (RFC 7233)
    Клиент запросил часть файла ( обслуживание байтов ), но сервер не может предоставить эту часть. Например, если клиент запросил часть файла, лежащую за концом файла. Ранее называлась «Запрошенный диапазон не удовлетворяется».
    417 Ожидание не выполнено
    Сервер не может удовлетворить требованиям поля заголовка запроса Expect.
    418 Я чайник (RFC 2324, RFC 7168)
    Этот код был определен в 1998 году как одна из традиционных первоапрельских шуток IETF в RFC 2324, Hyper Text Coffee Pot Control Protocol , и не ожидается, что он будет реализован на реальных HTTP-серверах. RFC указывает, что этот код должен возвращаться чайниками, которых просят заварить кофе. Этот HTTP-статус используется в качестве пасхального яйца на некоторых веб-сайтах, например на Google.com « Я — пасхальное яйцо для чайника» .
    421 неправильно направленный запрос (RFC 7540)
    Запрос был направлен на сервер, который не может дать ответ (например, из-за повторного использования соединения).
    422 Unprocessable Entity (WebDAV; RFC 4918).
    Запрос был правильно сформирован, но его не удалось выполнить из-за семантических ошибок.
    423 заблокировано (WebDAV; RFC 4918)
    Ресурс, к которому осуществляется доступ, заблокирован.
    424 Неудачная зависимость (WebDAV; RFC 4918)
    Запрос не удался, потому что он зависел от другого запроса, и этот запрос не удался (например, PROPPATCH).
    425 Слишком рано (RFC 8470)
    Указывает, что сервер не желает рисковать обработкой запроса, который может быть воспроизведен.
    426 Требуется обновление
    Клиент должен переключиться на другой протокол, такой как TLS / 1.3 , указанный в поле заголовка Upgrade .
    428 Требуется предварительное условие (RFC 6585)
    Исходный сервер требует, чтобы запрос был условным. Предназначен для предотвращения проблемы «потерянного обновления», когда клиент ПОЛУЧАЕТ состояние ресурса, изменяет его и отправляет обратно на сервер, когда тем временем третья сторона изменила состояние на сервере, что привело к конфликту.
    429 Слишком много запросов (RFC 6585)
    Пользователь отправил слишком много запросов за заданный промежуток времени. Предназначен для использования со схемами ограничения скорости .
    431 Слишком большие поля заголовка запроса (RFC 6585)
    Сервер не желает обрабатывать запрос, потому что либо отдельное поле заголовка, либо все поля заголовка в совокупности слишком велики.
    451 недоступен по юридическим причинам (RFC 7725)
    Оператор сервера получил законное требование запретить доступ к ресурсу или набору ресурсов, который включает запрошенный ресурс. Код 451 был выбран в качестве ссылки на роман « 451 градус по Фаренгейту» (см. «Благодарности» в RFC).

    5xx ошибки сервера

    Сервер не смог выполнить запрос.

    Коды состояния ответа, начинающиеся с цифры «5», указывают на случаи, когда сервер знает, что он обнаружил ошибку или иным образом не может выполнить запрос. За исключением ответа на запрос HEAD, сервер должен включать объект, содержащий объяснение ситуации с ошибкой, и указывать, является ли это временным или постоянным состоянием. Аналогичным образом, пользовательские агенты должны отображать пользователю любую включенную сущность. Эти коды ответов применимы к любому методу запроса.

    внутренняя ошибка сервера 500
    Общее сообщение об ошибке, которое выдается, когда возникла непредвиденная ситуация, и более конкретное сообщение не подходит.
    501 Не реализовано
    Сервер либо не распознает метод запроса, либо не может выполнить запрос. Обычно это подразумевает доступность в будущем (например, новую функцию API веб-службы).
    502 Неверный шлюз
    Сервер действовал как шлюз или прокси и получил недопустимый ответ от вышестоящего сервера.
    503 Сервис недоступен
    Сервер не может обработать запрос (потому что он перегружен или отключен для обслуживания). Как правило, это временное состояние.
    Ошибка 504 Время ответа сервера истекло
    Сервер действовал как шлюз или прокси и не получил своевременного ответа от вышестоящего сервера.
    505 Версия HTTP не поддерживается
    Сервер не поддерживает версию протокола HTTP, используемую в запросе.
    506 вариант также согласовывается (RFC 2295)
    Прозрачное согласование содержимого для запроса приводит к циклической ссылке .
    507 Недостаточно места для хранения (WebDAV; RFC 4918)
    Сервер не может сохранить представление, необходимое для выполнения запроса.
    Обнаружен цикл 508 (WebDAV; RFC 5842)
    Сервер обнаружил бесконечный цикл при обработке запроса (отправлено вместо 208 Already Reported ).
    510 не расширенный (RFC 2774)
    Для его выполнения сервером требуются дальнейшие расширения запроса.
    511 Требуется сетевая аутентификация (RFC 6585)
    Клиенту необходимо пройти аутентификацию, чтобы получить доступ к сети. Предназначен для использования путем перехвата прокси-серверов, используемых для управления доступом к сети (например, «перехватывающие порталы », используемые для запроса согласия с Условиями обслуживания перед предоставлением полного доступа в Интернет через точку доступа Wi-Fi ).

    Неофициальные коды

    Следующие ниже коды не определены никаким стандартом.

    218 Это нормально ( веб-сервер Apache )
    Используется как условие универсальной ошибки, позволяющее передавать тела ответов через Apache, когда включен ProxyErrorOverride. Когда ProxyErrorOverride включен в Apache, тела ответов, содержащие код состояния 4xx или 5xx, автоматически отбрасываются Apache в пользу общего ответа или настраиваемого ответа, указанного в директиве ErrorDocument. Фраза «Все в порядке » — это интернет-мем, означающий игнорирование ситуации или бездействие, несмотря на очевидные доказательства продолжающейся катастрофы.
    419 Страница истекла ( Laravel Framework )
    Используется Laravel Framework, когда токен CSRF отсутствует или просрочен.
    420 Ошибка метода ( Spring Framework )
    Устаревший ответ, используемый Spring Framework в случае сбоя метода.
    420 Повышайте свое спокойствие ( Twitter )
    Возвращается версией 1 Twitter Search and Trends API, когда клиент ограничен по скорости; версии 1.1 и более поздние используют код ответа 429 Too Many Requests . Фраза «Укрепи свое спокойствие» взята из фильма 1993 года « Человек-подрывник» , и ее связь с этим числом, вероятно, является отсылкой к каннабису .
    430 Поля заголовка запроса слишком велики ( Shopify )
    Используется Shopify вместо кода ответа 429 Too Many Requests , когда слишком много URL-адресов запрашивается в течение определенного периода времени.
    450 заблокировано родительским контролем Windows (Microsoft)
    Код расширения Microsoft указывает, когда родительский контроль Windows включен и блокирует доступ к запрошенной веб-странице.
    498 Неверный токен (Esri)
    Возвращено ArcGIS for Server . Код 498 указывает на просроченный или недействительный токен по иным причинам.
    Требуется 499 токенов (Esri)
    Возвращено ArcGIS for Server . Код 499 указывает на то, что токен необходим, но не был отправлен.
    509 Превышен предел пропускной способности ( веб-сервер Apache / cPanel )
    Сервер превысил пропускную способность, указанную администратором сервера; это часто используется провайдерами виртуального хостинга для ограничения пропускной способности клиентов.
    529 Сайт перегружен
    Используется Qualys в сервере тестирования API SSLLabs , чтобы сигнализировать о том , что сайт не может обработать запрос.
    530 Сайт заморожен
    Используется веб-платформой Pantheon для обозначения сайта, который был заблокирован из-за бездействия.
    598 (Неофициальное соглашение) Ошибка тайм-аута сетевого чтения.
    Используется некоторыми прокси-серверами HTTP для сигнализации тайм-аута сетевого чтения за прокси-сервером клиенту перед прокси.

    Информационные службы Интернета

    Веб-сервер Microsoft Internet Information Services (IIS) расширяет пространство ошибок 4xx, чтобы сигнализировать об ошибках в запросе клиента.

    440 Тайм-аут входа в систему
    Срок действия сеанса клиента истек, и ему необходимо снова войти в систему.
    449 Повторить с
    Сервер не может выполнить запрос, потому что пользователь не предоставил требуемую информацию.
    451 перенаправление
    Используется в Exchange ActiveSync, когда доступен более эффективный сервер или сервер не может получить доступ к почтовому ящику пользователей. Ожидается, что клиент повторно запустит операцию HTTP AutoDiscover, чтобы найти более подходящий сервер.

    IIS иногда использует дополнительные десятичные субкоды для получения более конкретной информации, однако эти субкоды появляются только в полезных данных ответа и в документации, а не вместо фактического кода состояния HTTP.

    nginx

    Программное обеспечение веб-сервера nginx расширяет область ошибок 4xx, чтобы сигнализировать о проблемах с запросом клиента.

    444 Нет ответа
    Используется внутри, чтобы указать серверу не возвращать информацию клиенту и немедленно закрыть соединение.
    494 Заголовок запроса слишком большой
    Клиент отправил слишком большой запрос или слишком длинную строку заголовка.
    495 Ошибка сертификата SSL
    Расширение кода ответа 400 Bad Request , используемого, когда клиент предоставил недействительный сертификат клиента .
    496 Требуется сертификат SSL
    Расширение кода ответа 400 Bad Request , используемого, когда сертификат клиента требуется, но не предоставляется.
    497 HTTP-запрос отправлен на HTTPS-порт
    Расширение кода ответа 400 Bad Request , используемого, когда клиент отправил HTTP-запрос на порт, который прослушивает HTTPS-запросы.
    499 Клиент закрытый запрос
    Используется, когда клиент закрыл запрос до того, как сервер смог отправить ответ.

    Cloudflare

    Сервис обратного прокси Cloudflare расширяет область ошибок серии 5xx, чтобы сигнализировать о проблемах с исходным сервером.

    520 Веб-сервер возвратил неизвестную ошибку
    Исходный сервер вернул Cloudflare пустой, неизвестный или неожиданный ответ.
    521 Веб-сервер не работает
    Исходный сервер отказал в подключении Cloudflare. Решения безопасности в источнике могут блокировать законные соединения с определенных IP-адресов Cloudflare.
    522 Время ожидания подключения истекло
    Время ожидания Cloudflare для связи с исходным сервером истекло.
    523 Источник недоступен
    Cloudflare не смог связаться с исходным сервером; например, если записи DNS для исходного сервера неверны или отсутствуют.
    524 Истекло время ожидания
    Cloudflare смог установить TCP-соединение с исходным сервером, но не получил своевременного ответа HTTP.
    525 SSL Handshake Failed
    Cloudflare не удалось согласовать рукопожатие SSL / TLS с исходным сервером.
    526 Неверный сертификат SSL
    Cloudflare не удалось проверить сертификат SSL на исходном веб-сервере. Также используется горутером Cloud Foundry .
    527 Ошибка рельсотрона
    Ошибка 527 указывает на прерванное соединение между Cloudflare и сервером Railgun исходного сервера.
    530
    Ошибка 530 возвращается вместе с ошибкой 1xxx.

    AWS Elastic Load Balancer

    Amazon «S Elastic Load Balancing добавляет несколько пользовательских кодов возврата

    460
    Клиент закрыл соединение с балансировщиком нагрузки до истечения периода ожидания простоя. Обычно, когда время ожидания клиента превышает время ожидания Elastic Load Balancer.
    463
    Балансировщик нагрузки получил заголовок запроса X-Forwarded-For с более чем 30 IP-адресами.
    561 Неавторизованный
    Ошибка аутентификации, возвращенная сервером, зарегистрированным в балансировщике нагрузки. Вы настроили правило прослушивателя для аутентификации пользователей, но поставщик удостоверений (IdP) вернул код ошибки при аутентификации пользователя.

    Кэширование кодов предупреждений

    Следующие коды предупреждений, связанные с кешированием , указаны в RFC 7234. В отличие от других кодов состояния, приведенных выше, они отправляются не как статус ответа в протоколе HTTP, а как часть HTTP-заголовка «Предупреждение». Поскольку этот заголовок часто не пересылается серверами и не подтверждается клиентами, он скоро будет отменен Рабочей группой HTTP .

    110 Ответ устарел
    Ответ, предоставленный кешем, устарел (возраст содержимого превышает максимальный возраст, установленный заголовком Cache-Control или эвристически выбранным временем жизни).
    111 Ошибка повторной валидации
    Кэш не смог проверить ответ из-за невозможности связаться с исходным сервером.
    112 Работа в отключенном состоянии
    Кэш намеренно отключен от остальной сети.
    113 Эвристическое истечение
    Кэш эвристически выбрал срок действия обновления более 24 часов, а возраст ответа превышает 24 часа.
    199 Прочие предупреждения
    Произвольное, неспецифическое предупреждение. Текст предупреждения может быть зарегистрирован или предоставлен пользователю.
    214 Преобразование применено
    Добавляется прокси-сервером, если он применяет к представлению какие-либо преобразования, такие как изменение кодировки контента, типа мультимедиа и т.п.
    299 Разное постоянное предупреждение
    То же, что и 199, но с постоянным предупреждением.

    Смотрите также

    • Пользовательские страницы ошибок
    • Список кодов возврата FTP-сервера
    • Список полей заголовка HTTP
    • Список кодов возврата SMTP-сервера
    • Общий формат журнала

    Примечания

    использованная литература

    внешние ссылки

    • RFC 7231 — Протокол передачи гипертекста (HTTP / 1.1): семантика и контент — раздел 6, коды состояния ответа
    • Реестр кода состояния протокола передачи гипертекста (HTTP)
    • База знаний Microsoft: MSKB943891: коды состояния HTTP в IIS 7.0
    • База знаний Microsoft Office: код ошибки 2–11
    Logo Море(!) аналитической информации!

    2005 г.

    ,

    Справочное руководство по MySQL версии 5.0.3-alpha. — 1 Общая информация

    Go to the first, previous, next, last section, table of contents.


    1 Общая информация

    Программное обеспечение MySQL (TM) представляет собой очень быстрый
    многопоточный, многопользовательский надежный SQL-сервер баз данных (SQL —
    язык структурированных запросов). Сервер MySQL предназначен как для
    критических по задачам производственных систем с большой нагрузкой, так и
    для встраивания в программное обеспечение массового распространения.

    MySQL — это торговая марка MySQL АВ.

    Программное обеспечение MySQL имеет двойное лицензирование. Это означает,
    что пользователи могут выбирать, использовать ли ПО MySQL бесплатно по общедоступной лицензии GNU
    General Public License
    (GPL) или приобрести одну из стандартных коммерческих лицензий MySQL AB.
    (http://www.gnu.org/licenses/).

    Для получения самой свежей информации о программном обеспечении MySQL
    обращайтесь на веб-сайт MySQL (http://www.mysql.com/).

    Ниже перечислены наиболее интересные разделы данного руководства.

    • Информация о компании, которая занимается разработкой и
      распространением ПО MySQL, находится в разделе section 1.5 Что представляет собой компания MySQL AB?.

    • Основные возможности сервера MySQL рассматриваются в разделе section 1.4.2 Основные возможности MySQL.

    • Инструкции по инсталляции приведены в разделе section 2 Установка MySQL.

    • Советы по переносу на другие архитектуры и операционные системы вы
      найдете в разделе section D Перенос на другие системы.

    • Информация по апгрейду с версии 3.23 находится в разделе section 2.5.2 Модернизация с версии 3.23 до версии 4.0

    • Информация по апгрейду с версии 3.22 находится в разделе section 2.5.3 Модернизация с версии 3.22 до версии 3.23

    • Введение в обучающий курс по серверу MySQL см. в разделе section 3 Учебное пособие по MySQL.

    • Примеры по SQL и данные по тестам производительности находятся в
      директории тестов производительности (`sql-bench’ в дистрибутиве).

    • Информацию о новых возможностях и об исправлениях ошибок см. в разделе
      section C История изменений и обновлений MySQL.

    • Список известных на сегодняшний день ошибок и конструктивных дефектов
      см. в разделе section 1.9.5 Известные ошибки и недостатки проектирования в MySQL.

    • Планы развития MySQL см. в разделе section 1.10 MySQL и будущее (что предстоит сделать).

    • Полный список тех людей, которые сделали вклад в наш проект, вы
      найдете в разделе section B Благодарности.

    Что важно:

    Отчеты об ошибках (bugs), а также вопросы и комментарии следует посылать
    по адресу mysql@lists.mysql.com. See section 1.8.1.3 Как отправлять отчеты об ошибках или проблемах. Для
    составления отчетов об ошибках следует использовать сценарий mysqlbug. В
    поставках исходного текста сценарий mysqlbug находится в директории
    scripts. Если у вас бинарная поставка, то сценарий mysqlbug следует
    искать в директории `bin’.

    Если вы обнаружите существенную ошибку, относящуюся к безопасности в сервере
    MySQL, следует сообщить об этом по адресу: security@mysql.com.

    1.1 Об этом руководстве

    Это — справочное руководство по MySQL; оно представляет собой документацию по
    MySQL версии 5.0.3-alpha. Функциональные изменения отмечены номером
    версии, в которой они произведены, поэтому это руководство будет полезно
    при освоении также и более старых версий MySQL.

    Поскольку данный материал носит чисто справочный характер, в нем не содержится
    основных положений SQL или сведений по реляционным базам данных.

    Руководство часто обновляется, поскольку ПО СУБД MySQL находится в
    состоянии постоянного развития. Самая последняя версия данного руководства
    доступна по адресу http://www.mysql.com/documentation/ в различных
    форматах, включая HTML, PDF и Windows HLP.

    Исходным документом для всех версий документации является файл Texinfo.
    HTML-версия генерируется автоматически модифицированной версией texi2html.
    Текстовая и Info-версии генерируются при помощи makeinfo,
    PostScript-версия создается texi2dvi и dvips. PDF-версия генерируется с
    помощью pdftex.

    Если найти нужную информацию в руководстве не удается, можно прибегнуть к
    помощи версии руководства с функцией поиска, находящейся по адресу
    http://www.mysql.com/doc/.

    Группа, работающая над документацией, рассмотрит любые предложения по
    дополнению или исправлению данного руководства. Их следует отправлять по
    адресу docs@mysql.com.

    Данное руководство было изначально написано Дэвидом Аксмарком (David
    Axmark) и Майклом (Монти) Видениусом (Michael (Monty) Widenius). Сейчас
    это руководство поддерживается Майклом (Монти) Видениусом, Полом
    Дюбуа (Paul DuBois), Арйен Ленц (Arjen Lentz). Остальные разработчики упомянуты
    в разделе section B Благодарности.

    Авторское право (2003) на данное руководство принадлежит шведской компании
    MySQL AB. See section 1.6.2 Авторские права и лицензии на MySQL.

    1.2 Соглашения, используемые в данном руководстве

    В данном руководстве используются следующие обозначения:

    Моноширинный
    Для имен команд и опций; SQL-операторов; имен баз данных, таблиц,
    столбцов; кода на C и Perl, переменных окружения применяется моноширинный
    шрифт. Например: »Чтобы увидеть, как работает mysqladmin, запустите его с
    опцией --help
    `filename’
    Моноширинный шрифт и кавычки используются для имен файлов и путей. Например: «Дистрибутив устанавливается в каталог `/usr/local/’».
    `c’
    Для представления последовательности символов используется моноширинный
    шрифт, а сама последовательность символов заключается в кавычки. Например:
    «Для задания шаблонного символа используется символ `%’«.
    Курсив
    Курсив используется для выделения текста, подобно данному.
    полужирный шрифт
    Полужирный шрифт используется для заголовков таблиц и для того, чтобы
    особо выделить фрагмент текста.

    Если нужно показать, что приведенная команда должна выполняться
    определенной программой, она предваряется подсказкой с именем этой
    программы. Например, shell> указывает, что команда будет выполняться
    вашей оболочкой, а mysql> означает, что команда будет выполняться
    клиентской программой mysql:

    shell> набирайте команды оболочки здесь
    mysql> набирайте SQL-операторы здесь
    

    Для представления команд оболочки в данном руководстве применяется
    синтаксис оболочки Bourne. Если у вас используется csh-оболочка, синтаксис
    для нее будет несколько отличаться. Например, последовательность команд
    для установки переменной окружения и запуска команды в синтаксисе оболочки
    Bourne выглядит следующим образом:

    shell> ПЕРЕМЕННАЯ=значение некая_команда
    

    В случае csh-оболочки для выполнения тех же действий вам потребуются
    следующие команды:

    shell> setenv ПЕРЕМЕННАЯ значение
    shell> некая_команда
    

    Часто в командах имена баз данных, таблиц, столбцов следует заменять
    конкретными значениями. Чтобы показать необходимость такой замены, в
    данном руководстве используются выражения типа db_name (имя базы данных),
    table_name (имя таблицы) и col_name (имя столбца). Например, в тексте
    руководства может встретиться оператор, подобный следующему:

    mysql> SELECT col_name FROM db_name.table_name;
    

    Это означает, что при вводе такого оператора вам необходимо будет указать
    собственную базу данных, имя таблицы и имя столбца, например:

    mysql> SELECT author_name FROM biblio_db.author_list;
    

    Ключевые слова SQL являются нечувствительными к регистру, поэтому их можно
    записывать, используя как прописные, так и строчные буквы. В данном
    руководстве используется регистр прописных букв.

    Для представления необязательных слов и выражений в синтаксических
    описаниях используются квадратные скобки (`[‘ и `]’). Например, в
    приведенном ниже операторе выражение IF EXISTS является необязательным:

    DROP TABLE [IF EXISTS] имя_таблицы
    

    Если элемент синтаксиса состоит из ряда альтернативных элементов,
    последние отделяются друг от друга вертикальными чертами (`|’ ). В случае,
    когда может быть выбран один элемент из такого ряда, альтернативные
    элементы заключаются в квадратные скобки (`[‘ и `]’):

    TRIM([[BOTH | LEADING | TRAILING] [remstr] FROM] str)
    

    В случае, когда должен быть выбран один элемент из такого ряда,
    альтернативные элементы заключаются в фигурные скобки (`{‘ и `}’):

    {DESCRIBE | DESC } имя_таблицы {имя_столбца | wild}
    

    1.3 О русском переводе руководства

    Русский перевод документации на ПО СУБД MySQL выполнен в 2002-2003гг.
    компанией Ensita.NET (http://www.ensita.net/).

    Переводчики: (в алфавитном порядке) Василюк Елена, Добродеев Сергей, Закиянов Денис, Коротун
    Юрий, Пономарев Алексей, Ченцов Алексей; а также Жданов Сергей (раздел
    «Интерфейс DBI«).

    Научная редакция: Егор Егоров, Людмила Мезенко, Виктория Резниченко.

    Литературный редактор: Людмила Мезенко (the best!)

    Главный редактор перевода: Егор Егоров

    Все замечания по русской документации направляйте по адресу docs-team@ensita.net.

    * * * * *

    Компания Ensita.NET (http://www.ensita.net/), являясь официальным
    партнером MySQL AB с января 2002г. консультирует пользователей ПО СУБД MySQL по
    всему миру, поддерживая список рассылки mysql@lists.mysql.com
    (see section 1.8.1.1 Списки рассылки MySQL).

    Ensita.NET с 1999г. занимается разработкой программного обеспечения для веб-сайтов,
    обслуживанием СУБД и консалтингом.

    1.3.1 Список терминов, принятых в русском переводе

    Редакционная коллегия при подготовке этого перевода старалась как можно адекватнее
    перевести специфические термины с английского языка на русский. Нами была
    поставлена цель создать справочное руководство на живом и понятном языке, но в то
    же время не переходить на чистый сленг. Поэтому терминология, принятая в этом
    переводе, возможно, не полностью соответствует четким терминам, принятым
    в русскоязычной литературе на «около-реляционную» тему.

    Для ясности понимания мы включаем в документацию список принятых в этом
    переводе терминов.

    Термин Значение
    account аккаунт, учетная запись
    ACL списки контроля доступа
    banner баннер
    benchmark page страничка тестов производительности
    Berkeley-style copyright лицензия наподобие Berkeley
    binary release, binary distribution бинарная поставка, двоичная поставка
    cache кэш
    case sensitive чувствительный к регистру, регистро-зависимый
    change history история изменений
    charset набор символов, кодировка
    client/server клиент-серверный
    command-line tool утилита командной строки
    commit принять; commit transaction — принять транзакцию
    communication protocol коммуникационный протокол
    contact form форма. fill in the contact form — заполните форму
    data dir каталог с данными
    datadir, data dir каталог datadir, каталог данных
    date type тип даты
    DBA администратор (физическое лицо)
    dbms см. rdbms
    ddos distributed dos — распределенная атака создания отказа в обслуживании
    denial of service отказ в обслуживании
    deployment распространение
    distribution поставка, дистрибутив
    distributor производитель
    dos см. denial of service
    embedded встраиваемый. Не «встроенный»
    environment variables переменные окружения
    escape chars символы экранирования
    escaped chars экранированные символы
    extended regular expression расширенное регулярное выражение
    fail-safe отказобезопасный
    feature возможность, особенность
    firewall брандмауэр
    flush сброс, очистка, перегрузка
    foreign key constraint ограничения внешних ключей
    full precision полная точность
    full-text полнотекстовый
    grant tables таблицы привилегий
    grants привилегии
    group группа, группировка
    group functions групповые функции, операции группировки данных, операции над групповыми данными
    host удаленный компьютер
    inserting data добавление, вставка данных
    join связь
    join optimizer оптимизатор связей
    key cache кэш ключей
    large-scale широкомасштабный
    license лицензция
    link линкование (в контексте линкования компиляции программ)
    localhost, local host локальный хост, локальный компьютер
    locking блокировка
    log журнал
    log position точка положения в журнале репликации
    login см. account
    login shell первая оболочка входа (login shell)
    master (репликация) головной сервер
    miscellaneous разнообразный
    multi-byte мультибайтный
    multi-layered многоуровневый
    multithread многопоточный
    mysql server MySQL или сервер
    mysql user пользователь MySQL
    non-updating queries запросы, не изменяющие информацию
    null null
    open source система с открытым кодом (open source)
    optional опциональный
    parser синтаксический анализатор
    patch патч
    port listening слушание порта, слушать порт, ожидать соединение на порту
    privacy конфиденциальность
    privilege привилегии
    qualifier определитель
    qualify определять
    rdbms система управления реляционными базами данных
    reference manual справочник, руководство
    regular client обычный клиент
    regular expression регулярное выражение; extended — расширенное р.в.
    released under gpl выпущено под лицензией GPL
    replica (server) сервер с копиями
    robustness надежность, отказоустойчивость
    rollback откат
    rotation ротация
    row-level locking строчная блокировка
    scope контекст
    script сценарий, скрипт
    security безопасность
    security issues вопросы безопасности
    SET множество
    silent молчаливо
    slave (репликация) подчиненный сервер
    snapshop образ, снимок
    socket сокет
    ssh port-forwarding пересылка по SSH (SSH port-forwarding)
    SSL connections SSL-соединения
    sticky прилипчив
    superuser суперпользователь
    symbol table см. charset
    symbol value строкое значение
    symlink символическая ссылка
    table handler обработчик таблиц
    thread поток
    time stamp временная метка
    timestamp тип данных временной метки
    TODO-list список задач к выполнению
    track последовательность, протокол
    transactional транзакционный
    trick трюк, хитрость
    unix socket, unix domain socket unix-сокет
    up-to-date своевременный
    utilities инструментальные программы
    vendor поставщик
    verbose расширенный режим вывода сообщений
    voting algorithm алгоритм голосования
    warning предупреждение
    wildcard шаблон, шаблонные символы
    wrapper оболочка

    1.4 Что представляет собой MySQL?

    Разработку и сопровождение MySQL, самой популярной SQL-базы данных с открытым
    кодом, осуществляет компания MySQL AB. MySQL AB — коммерческая компания,
    основанная разработчиками MySQL, строящая свой бизнес, предоставляя различные
    сервисы для СУБД MySQL. See section 1.5 Что представляет собой компания MySQL AB?.

    На веб-сайте MySQL (http://www.mysql.com/) представлена самая свежая
    информация о программном обеспечении MySQL и о компании MySQL AB.

    MySQL — это система управления базами данных.
    База данных представляет собой структурированную совокупность данных. Эти
    данные могут быть любыми — от простого списка предстоящих покупок до
    перечня экспонатов картинной галереи или огромного количества информации в
    корпоративной сети. Для записи, выборки и обработки данных, хранящихся в
    компьютерной базе данных, необходима система управления базой данных,
    каковой и является ПО MySQL. Поскольку компьютеры замечательно справляются
    с обработкой больших объемов данных, управление базами данных играет
    центральную роль в вычислениях. Реализовано такое управление может быть
    по-разному — как в виде отдельных утилит, так и в виде кода, входящего в
    состав других приложений.
    MySQL — это система управления реляционными базами данных.
    В реляционной базе данных данные хранятся не все скопом, а в отдельных
    таблицах, благодаря чему достигается выигрыш в скорости и гибкости.
    Таблицы связываются между собой при помощи отношений, благодаря чему
    обеспечивается возможность объединять при выполнении запроса данные из
    нескольких таблиц. SQL как часть системы MySQL можно охарактеризовать как
    язык структурированных запросов плюс наиболее распространенный
    стандартный язык, используемый для доступа к базам данных.
    Программное обеспечение MySQL — это ПО с открытым кодом.
    ПО с открытым кодом означает, что применять и модифицировать его может
    любой желающий. Такое ПО можно получать по Internet и использовать
    бесплатно. При этом каждый пользователь может изучить исходный код и
    изменить его в соответствии со своими потребностями. Использование
    программного обеспечения MySQL регламентируется лицензией GPL (GNU General
    Public License
    ), http://www.gnu.org/licenses/, в которой указано, что
    можно и чего нельзя делать с этим программным обеспечением в различных
    ситуациях. Если работа в рамках GPL вас не устраивает или планируется
    встраивание MySQL-кода в коммерческое приложение, есть возможность купить
    коммерческую лицензированную версию у компании MySQL AB. See section 1.6.3 Лицензии на ПО MySQL.
    В каких случаях следует отдавать предпочтение СУБД MySQL?
    MySQL является очень быстрым, надежным и легким в использовании. Если вам
    требуются именно эти качества, попробуйте поработать с данным сервером.
    MySQL обладает также рядом удобных возможностей, разработанных в
    тесном контакте с пользователями. Сравнительные характеристики MySQL и
    других средств управления базами данных приведены на нашей странице тестов
    производительности (see section 5.1.4 Набор тестов MySQL (The MySQL Benchmark Suite)).

    Первоначально сервер MySQL разрабатывался для управления большими базами данных с целью
    обеспечить более высокую скорость работы по сравнению с существующими на
    тот момент аналогами. И вот уже в течение нескольких лет данный сервер
    успешно используется в условиях промышленной эксплуатации с высокими
    требованиями. Несмотря на то что MySQL постоянно совершенствуется, он уже
    сегодня обеспечивает широкий спектр полезных функций. Благодаря своей
    доступности, скорости и безопасности MySQL очень хорошо подходит для
    доступа к базам данных по Internet.

    Технические возможности СУБД MySQL
    Более детальную информацию по техническим возможностям MySQL можно
    получить в разделе section 6 Справочник по языку MySQL. ПО MySQL является системой
    клиент-сервер, которая содержит многопоточный SQL-сервер, обеспечивающий
    поддержку различных вычислительных машин баз данных, а также несколько
    различных клиентских программ и библиотек, средства администрирования и
    широкий спектр программных интерфейсов (API).

    Мы также поставляем сервер MySQL в виде многопоточной библиотеки, которую можно подключить к
    пользовательскому приложению и получить компактный, более быстрый и легкий
    в управлении продукт.

    Доступно также большое количество программного обеспечения MySQL,
    разработанного сторонними разработчиками.

    Вполне возможно, что СУБД MySQL уже поддерживается вашим
    любимым приложением или языком.

    MySQL правильно произносится как »Май Эс Кью Эль» (а не »майсиквел»),
    хотя никто не запрещает вам произносить эту аббревиатуру как «майсиквел»
    или еще каким-либо образом.

    1.4.1 История MySQL

    В один прекрасный день мы решили применить mSQL для доступа к нашим
    таблицам, для которых использовались собственные быстрые (ISAM)
    подпрограммы низкого уровня. Однако после тестирования мы пришли к
    заключению, что для наших целей скорость и гибкость mSQL недостаточны.
    В результате для базы данных был разработан новый SQL-интерфейс, но почти
    с тем же API-интерфейсом, что и mSQL. Этот API мы выбрали, чтобы
    упростить перенос на код сторонних разработчиков.

    Происхождение имени MySQL не совсем ясно. Уже около 10 лет наша основная
    директория и большое количество библиотек и инструментария имеют префикс
    `my’. Более того, дочь Монти (она несколькими годами моложе) тоже
    получила имя My! Что из этих двух факторов повлияло на имя — до сих пор
    остается загадкой, даже для разработчиков.

    1.4.2 Основные возможности MySQL

    Ниже приведено описание важных характеристик программного обеспечения
    MySQL. See section 1.7 Кратко о MySQL 4.x.

    Внутренние характеристики и переносимость
    • Написан на C и C++. Протестирован на множестве различных компиляторов.

    • Работает на различных платформах. See section 2.2.5 Операционные системы, поддерживаемые MySQL.

    • Для обеспечения переносимости используется GNU Automake,
      Autoconf и Libtool.

    • API для C, C++, Eiffel, Java, Perl, PHP, Python, Ruby и Tcl.

      See section 8 Интерфейсы для MySQL.

    • Полностью многопоточный с использованием потоков ядра. Это означает,
      что, если такая возможность обеспечивается, можно легко организовать
      работу с несколькими процессорами.

    • Очень быстрые дисковые таблицы на основе В-деревьев со сжатием
      индексов.

    • Очень быстрая базирующаяся на потоках система распределения памяти.

    • Очень быстрые соединения, использующие оптимизированный метод
      однопроходного мультисоединения (one-sweep multi-join).

    • Хеш-таблицы в памяти, используемые как временные таблицы.

    • SQL-функции реализованы при помощи хорошо оптимизированной библиотеки
      классов, поэтому они выполняются настолько быстро, насколько это
      возможно. Обычно после инициализации запроса распределения памяти не
      происходит вообще.

    • MySQL- код протестирован с использованием Purify (коммерческий
      детектор утечки памяти), а также Valgrind, одного из GPL-инструментов
      (http://developer.kde.org/~sewardj/

    Типы столбцов
    • Большое количество: целочисленные со знаком/беззнаковые, длиной в 1, 2, 3, 4 и
      8 байтов, FLOAT, DOUBLE, CHAR, VARCHAR,
      TEXT, BLOB, DATE, TIME, DATETIME,
      TIMESTAMP, YEAR, SET и ENUM. See section 6.2 Типы данных столбцов.

    • С записями фиксированной и переменной длины.

    • Все столбцы имеют значения по умолчанию. С помощью INSERT можно
      вставить подмножество столбцов таблицы; столбцы, для которых явно не
      заданы значения, устанавливаются в значения по умолчанию.

    Команды и функции
    • Полная поддержка операторов и функций в SELECT— и WHERE— частях

      запросов. Например:

      mysql> SELECT CONCAT(first_name, " ", last_name)
          -> FROM tbl_name
          -> WHERE income/dependents > 10000 AND age > 30;
      
    • Полная поддержка для операторов SQL GROUP BY и ORDER BY с выражениями SQL. Поддержка
      групповых функций (COUNT(), COUNT(DISTINCT ...), AVG(), STD(), SUM(),
      MAX() и MIN()).

    • Поддержка LEFT OUTER JOIN и RIGHT OUTER JOIN с синтаксисом ANSI SQL и
      ODBC.

    • Разрешены псевдонимы для таблиц и столбцов в соответствии со

      стандартом SQL92.

    • DELETE, INSERT, REPLACE, and UPDATE возвращают число строк, которые

      были изменены. Вместо этого можно задать возвращение совпавших строк.
      Для этого следует установить флаг при соединении с сервером.

    • Команду SHOW, которая является специфической для MySQL, можно
      использовать для получения информации о базах данных, таблицах и
      индексах. Чтобы выяснить, как оптимизатор выполняет запрос, можно
      применять команду EXPLAIN.

    • Имена функций не конфликтуют с именами таблиц и столбцов. Например,
      ABS является корректным именем столбца. Для вызова функции существует
      только одно ограничение: между именем функции и следующей за ним
      открывающей скобкой `(‘ не должно быть пробелов. See section 6.1.7 «Придирчив» ли MySQL к зарезервированным словам?.

    • В одном и том же запросе могут указываться таблицы из различных баз
      данных (с версии 3.22).

    Безопасность
    • Система, основанная на привилегиях и паролях, за счет чего
      обеспечивается гибкость и безопасность, и с возможностью верификации с
      удаленного компьютера. Пароли защищены, т.к. они при передаче по сети
      при соединении с сервером шифруются.

    Масштабируемость и ограничения
    • Управляет очень большими базами данных. Компания MySQL AB. использует
      MySQL для работы с несколькими базами данных, которые содержат
      50 миллионов записей, кроме того, нам известны пользователи,
      использующие MySQL для работы с 60000 таблицами, включающими
      около 5000000000 строк.

    • Для каждой таблицы разрешается иметь до 32 индексов. Каждый индекс
      может содержать от 1 до 16 столбцов или частей столбцов. Максимальная
      ширина индекса 500 бит (это значение может быть изменено при
      компиляции MySQL). Для индекса может использоваться префикс
      поля CHAR или VARCHAR.

    Установка соединений
    • Клиенты могут соединяться с MySQL, используя сокеты TCP/IP,
      сокеты Unix или именованные каналы (named pipes, под NT).

    • Поддержка ODBC (Open-DataBase-Connectivity) для Win32 (с исходным
      кодом). Все функции ODBC 2.5 и многие другие. Например, для соединения
      с MySQL можно использовать MS Access. See section 8.3 Поддержка ODBC в MySQL.

    Локализация
    • Сервер может обеспечивать сообщения об ошибках для клиентов на
      различных языках. See section 4.6.2 Сообщения об ошибках на языках, отличных от английского.

    • Полная поддержка нескольких различных кодировок, включая ISO-8859-1
      (Latin1), немецкий, big5, ujis и многие другие. Например,
      скандинавские символы разрешены в именах таблиц и столбцов.

    • Для хранения всех данных используется выбранный набор символов. Все
      сравнения для столбцов с нормальными строками проводятся с учетом
      регистра символов.

    • Сортировка производится согласно выбранному алфавиту (по умолчанию
      используется шведский). Эту установку можно изменить при запуске
      сервера MySQL. Чтобы ознакомиться с примером очень грамотной
      сортировки, можно обратиться к коду сортировки для чешского языка.
      MySQL поддерживает много различных кодировок, которые можно задавать
      во время компиляции и в процессе работы.

    Клиенты и инструментарий
    • Включает myisamchk, очень быструю утилиту для проверки, оптимизации и
      восстановления таблиц. Все функциональные возможности myisamchk также
      доступны через SQL-интерфейс. See section 4 Администрирование баз данных.

    • Все MySQL-программы можно запускать с опциями --help или -? для
      получения помощи.

    1.4.3 Насколько стабильным является MySQL?

    Этот раздел дает ответ на следующие вопросы »Насколько стабильным
    является MySQL?
    » и »Могу ли я положиться на MySQL в своем
    проекте?
    » Мы попытаемся внести ясность в эти проблемы, а также ответить
    на некоторые важные вопросы, которые имеют значение для многих
    потенциальных пользователей. Информация данного раздела базируется на
    данных из списка рассылки, — наши пользователи очень активно сообщают нам
    о выявленных проблемах и о своем опыте использования нашего ПО.

    Самые первые версии кода были созданы в начале 80-х. Это был устойчивый
    код с форматом таблиц ISAM, обеспечивающим обратную совместимость с
    предыдущими версиями. Во времена компании TcX, предшественника MySQL AB, с
    середины 1986 года код MySQL работал в проектах без каких-либо проблем. Но
    когда сервер MySQL был выпущен для широкого использования, оказалось, что
    существует несколько фрагментов «непротестированного кода». Эти
    фрагменты были быстро обнаружены новыми пользователями, которые составляли
    запросы в несколько ином виде, чем мы.

    С каждым новым релизом количество проблем, связанных с переносимостью,
    уменьшалось (несмотря на то, что в каждом выпуске появлялось множество
    новых возможностей).

    Каждый релиз MySQL был рабочим, проблемы возникали только при
    использовании кода из «серых зон». Естественно, что новые пользователи
    не знают о том, где находятся такие «серые зоны»; в данном разделе
    сделана попытка описать те из них, которые известны на данный момент.
    Большая часть описания относится к версии 3.23 MySQL-сервера. В самой
    последней версии все известные ошибки устранены, за исключением тех,
    которые перечислены в разделе ошибок, а также конструктивных дефектов.
    See section 1.9.5 Известные ошибки и недостатки проектирования в MySQL.

    Структура ПО MySQL является многоуровневой с независимыми модулями.
    Некоторые из новейших модулей перечислены ниже, причем по каждому дается
    информация о том, насколько хорошо он протестирован.

    Репликация — Gamma
    Большие серверные кластеры, в которых применяется репликация, находятся в
    промышленной эксплуатации и показывают хорошие результаты. Работа над
    средствами репликации в MySQL 4.x продолжается.
    InnoDB-таблицы — стабильно (в 3.23 с 3.23.49)
    Обработчик транзакционных InnoDB-таблиц объявлен в настоящее время
    стабильными в дереве MySQL 3.23, начиная с версии 3.23.49. InnoDB
    используется в больших промышленных системах с большой нагрузкой.
    BDB-таблицы — Gamma
    Код Berkeley DB очень устойчив, но на настоящий момент продолжается
    усовершенствование интерфейса обработчика транзакционных BDB-таблиц с
    MySQL, поэтому должно пройти некоторое время, пока он будет так же хорошо
    протестирован, как и таблицы других типов.
    Полнотекстовый поиск — Beta
    Полнотекстовый поиск работает, но широко не используется. В версии MySQL
    4.0 реализованы существенные улучшения данной возможности.
    MyODBC 2.50 (использующий ODBC SDK 2.5) — Gamma
    Чрезвычайно широко используется. Как оказалось, некоторые из возникших
    проблем являются зависящими от приложения, а не от ODBC-драйвера или
    сервера баз данных.
    Aвтоматическое восстановление MyISAM-таблиц — Gamma
    Статус Gamma относится только к новому коду в обработчике таблиц, который
    проверяет правильность закрытия таблицы после ее открытия и выполняет
    автоматическую проверку/восстановление незакрытой таблицы.
    Вставка больших объемов данных — Alpha
    Новая возможность в MyISAM-таблицах в MySQL 4.0 для быстрой вставки
    большого количества строк.
    Блокировка — Gamma
    В большой степени зависит от системы. В некоторых системах возникают
    большие проблемы с использованием стандартной для ОС блокировки
    (fcntl()). В таких случаях следует запустить демон mysqld с флагом
    --skip-external-locking. Известно, что проблемы имеют место в некоторых системах
    Linux и в SunOS, когда используются NFS-монтированные файловые системы.

    Несмотря на то, что высококвалифицированную поддержку MySQL AB
    обеспечивает за плату, в списке рассылки MySQL обычно можно получить
    ответы на часто возникающие вопросы. Ошибки обычно ликвидируются сразу же
    при помощи патчей, а серьезные дефекты почти всегда устраняются в новом
    выпуске.

    1.4.4 Насколько большими могут быть таблицы в MySQL?

    MySQL версии 3.22 имеет предел по размеру таблиц 4 Гб. В MySQL версии
    3.23, где используется новый тип таблиц, максимальный размер таблицы
    доведен до 8 миллионов терабайтов (2 ^ 63 bytes).

    Однако следует заметить, что операционные системы имеют свои собственные
    ограничения по размерам файлов. Ниже приведено несколько примеров:

    Операционная система Ограничения на размеры файла
    32-разрядная Linux-Intel 2Гб, 4Гб и более, в зависимости от версии Linux
    Linux-Alpha 8T (?)
    Solaris 2.5.1 2 Гб (с патчем возможно 4Гб)
    Solaris 2.6 4Гб (может быть изменено при помощи указания флага)
    Solaris 2.7 Intel 4 Гб
    Solaris 2.7 UltraSPARC 512 Гб

    В Linux 2.2 существует возможность создавать таблицы с размерами более 2
    Гб, используя патч LFS для файловой системы ext2. Существуют также патчи,
    обеспечивающие поддержку больших файлов для ReiserFS в Linux 2.4.

    Как можно видеть, размер таблицы в базе данных MySQL обычно лимитируется
    операционной системой.

    По умолчанию MySQL-таблицы имеют максимальный размер около 4 Гб. Для любой
    таблицы можно проверить/определить ее максимальный размер с помощью команд
    SHOW TABLE STATUS или myisamchk -dv table_name. See section 4.5.6 Синтаксис команды SHOW.

    Если необходимы таблицы большего размера, чем 4 Гб (и используемая
    операционная система «не возражает»), следует при создании такой таблицы
    задать параметры AVG_ROW_LENGTH и MAX_ROWS (see section 6.5.3 Синтаксис оператора CREATE TABLE). Эти параметры можно задать и позже — с помощью ALTER TABLE (see section 6.5.4 Синтаксис оператора ALTER TABLE).

    Если большая таблица предназначена только для чтения, можно
    воспользоваться myisampack, чтобы слить несколько таблиц в одну и сжать
    ее. Обычно myisampack ужимает таблицу по крайней мере на 50%, поэтому в
    результате можно получить очень большие таблицы (see section 4.7.4 myisampack, MySQL-генератор сжатых таблиц (только для чтения)).

    Есть еще одна возможность обойти ограничения операционной системы на
    размеры файлов данных MyISAM, — это делается при помощи опции RAID (see section 6.5.3 Синтаксис оператора CREATE TABLE).

    Еще одним решением может быть использование функции MERGE, которая
    обеспечивает возможность обрабатывать набор идентичных таблиц как одну
    таблицу (see section 7.2 Таблицы MERGE).

    1.4.5 Вопросы, связанные с Проблемой-2000

    Сам MySQL не имеет проблем, связанных с Проблемой-2000 (Y2K):

    • В MySQL используются функции времени Unix, поэтому проблемы с
      датами, вплоть до 2069, исключены. Принимается, что все двузначные
      значения годов находятся в диапазоне с 1970 по 2069, поэтому число 01 в
      столбце с типом year MySQL обрабатывает как 2001.

    • Все MySQL-функции, обрабатывающие даты, хранятся в одном файле
      `sql/time.cc’. Их код был написан очень тщательно, чтобы
      застраховаться от проблем, связанных с 2000-м годом.

    • В версиях MySQL 3.22 и более поздних в столбцах с новым типом YEAR,
      который обеспечивает хранение нулевого 0 года и значений лет от 1901
      до 2155 в одном байте, а также отображение дат при помощи 2 или 4
      знаков.

    Проблемы, связанные с 2000-м годом, могут возникнуть в приложениях,
    которые используют MySQL так, что это может оказаться небезопасным
    с точки зрения Y2K. Например, во многих старых приложениях для хранения и
    обработки значений годов используются 2-значные величины (которые можно
    трактовать неоднозначно), а не 4-значные. Эта проблема может быть
    урегулирована при помощи приложений, которые используют 00 или 99 как
    «отсутствующие» индикаторы значений.

    К сожалению, такие проблемы бывает сложно устранить, так как разные
    приложения могут быть написаны разными программистами, каждый из которых
    мог применять отличный от других набор соглашений и обрабатывающих
    значения даты функций.

    Приведенный ниже код является наглядной демонстрацией того, что в MySQL
    Server проблемы с датами вплоть до 2030 года отсутствуют.

    mysql> DROP TABLE IF EXISTS y2k;
    Query OK, 0 rows affected (0.01 sec)
    
    mysql> CREATE TABLE y2k (date DATE,
        -> date_time DATETIME,
        -> time_stamp TIMESTAMP);
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> INSERT INTO y2k VALUES
        -> ("1998-12-31","1998-12-31 23:59:59",19981231235959),
        -> ("1999-01-01","1999-01-01 00:00:00",19990101000000),
        -> ("1999-09-09","1999-09-09 23:59:59",19990909235959),
        -> ("2000-01-01","2000-01-01 00:00:00",20000101000000),
        -> ("2000-02-28","2000-02-28 00:00:00",20000228000000),
        -> ("2000-02-29","2000-02-29 00:00:00",20000229000000),
        -> ("2000-03-01","2000-03-01 00:00:00",20000301000000),
        -> ("2000-12-31","2000-12-31 23:59:59",20001231235959),
        -> ("2001-01-01","2001-01-01 00:00:00",20010101000000),
        -> ("2004-12-31","2004-12-31 23:59:59",20041231235959),
        -> ("2005-01-01","2005-01-01 00:00:00",20050101000000),
        -> ("2030-01-01","2030-01-01 00:00:00",20300101000000),
        -> ("2050-01-01","2050-01-01 00:00:00",20500101000000);
    Query OK, 13 rows affected (0.01 sec)
    Records: 13 Duplicates: 0 Warnings: 0
    
    mysql> SELECT * FROM y2k;
    +------------+---------------------+----------------+
    | date | date_time | time_stamp |
    +------------+---------------------+----------------+
    | 1998-12-31 | 1998-12-31 23:59:59 | 19981231235959 |
    | 1999-01-01 | 1999-01-01 00:00:00 | 19990101000000 |
    | 1999-09-09 | 1999-09-09 23:59:59 | 19990909235959 |
    | 2000-01-01 | 2000-01-01 00:00:00 | 20000101000000 |
    | 2000-02-28 | 2000-02-28 00:00:00 | 20000228000000 |
    | 2000-02-29 | 2000-02-29 00:00:00 | 20000229000000 |
    | 2000-03-01 | 2000-03-01 00:00:00 | 20000301000000 |
    | 2000-12-31 | 2000-12-31 23:59:59 | 20001231235959 |
    | 2001-01-01 | 2001-01-01 00:00:00 | 20010101000000 |
    | 2004-12-31 | 2004-12-31 23:59:59 | 20041231235959 |
    | 2005-01-01 | 2005-01-01 00:00:00 | 20050101000000 |
    | 2030-01-01 | 2030-01-01 00:00:00 | 20300101000000 |
    | 2050-01-01 | 2050-01-01 00:00:00 | 00000000000000 |
    +------------+---------------------+----------------+
    13 rows in set (0.00 sec)
    
    

    Можно видеть, что при использовании типов DATE и DATETIME проблем с датами
    будущего не возникнет (эти типы «справляются» с датами вплоть до 9999
    года).

    Тип TIMESTAMP, который используется для сохранения текущего времени, имеет
    диапазон только до 2030-01-01. В 32-разрядных машинах TIMESTAMP тип имеет
    диапазон от 1970 до 2030 (значение со знаком). В 64-разрядных машинах этот
    тип «справляется» со значениями времени до 2106 года (значение без
    знака).

    Таким образом, даже несмотря на то, что MySQL является
    Y2K-совместимым, ответственность за однозначную интерпретацию значений
    даты ложится на плечи пользователя. See section 6.2.2.1 Проблема 2000 года и типы данных,
    где приведены правила по работе MySQL с входными данными, которые
    имеют неоднозначные значения даты (данные, содержащие 2-значные значения
    года).

    1.5 Что представляет собой компания MySQL AB?

    MySQL AB — компания, в состав которой входят основатели MySQL и основные
    разработчики. MySQL AB создана в Швеции Дэвидом Аксмарком (David Axmark),
    Аланом Ларссом (Allan Larsson) и Майклом Монти Видениусом (Michael
    Monty Widenius).

    Все разработчики сервера MySQL — штатные сотрудники компании. MySQL AB —
    виртуальная компания, состоящая из сотрудников, живущих в десятках стран
    по всему миру. Мы каждый день общаемся по Сети друг с другом, а также с
    нашими партнерами и пользователями.

    Наша компания занимается разработкой и распространением СУБД MySQL и
    сопутствующего ПО. MySQL AB владеет всеми правами на исходный код MySQL,
    на логотип и торговую марку MySQL, а также на данное руководство (see section 1.4 Что представляет собой MySQL?).

    Наши основные ценности — это то, что наша деятельность посвящена MySQL и
    идеям Open Source, открытого программного обеспечения.

    Мы хотим, чтобы сервер MySQL соответствовал следующим критериям:

    • Лучший и популярнейший сервер СУБД в мире

    • Доступный всем

    • Простой в эксплуатации

    • Постоянно развивающийся не в ущерб скорости и безопасности

    • Работающий надежно

    • Удобный для использования и усовершенствования

    MySQL AB и люди, работающие в MySQL AB:

    • Продвигают философию Open Source и поддерживают пользователей Open
      Source

    • Хорошие граждане

    • С удовольствием работают с партнерами, разделяющими нашу точку зрения
      и ценности

    • Отвечают на электронную почту и предоставляют поддержку пользователям

    • Является виртуальной командой, взаимодействующей через Internet

    • Работают «против» патентованного ПО

    Свежая информация о MySQL и MySQL AB находится на сайте MySQL
    (http://www.mysql.com/).

    1.5.1 Бизнес-модель и услуги, оказываемые компанией MySQL AB

    Очень часто нам задают такой вопрос: »Как вам удается зарабатывать на
    жизнь, ведь вы все раздаете бесплатно?
    »

    Компания MySQL AB получает плату за поддержку, услуги, коммерческие
    лицензии и лицензионные платежи. Эти доходы вкладываются в разработку
    продукта и расширение бизнеса нашей компании.

    Компания является прибыльной с момента своего основания. В октябре 2001
    ряд ведущих скандинавских инвесторов и небольшая группа меценатов
    предоставили нам венчурный кредит. Эти капиталовложения идут на укрепление
    нашей бизнес-модели и создают основу устойчивого роста бизнеса компании.

    1.5.1.1 Поддержка

    Владельцами и руководителями компании MySQL AB являются ее основатели и
    ведущие разработчики ПО баз данных MySQL. В задачи разработчиков входит
    предоставление поддержки клиентам и другим пользователям, что обеспечивает
    нам возможность всегда быть в курсе их потребностей и проблем. Вся наша
    поддержка осуществляется квалифицированными разработчиками. Ответы на
    самые каверзные вопросы дает

    Майкл Монти Вайдиниус (Michael Monty Widenius), главный автор MySQL
    Server. See section 1.6.1 Поддержка, предлагаемая компанией MySQL AB.

    Для более подробной информации и заказа различных уровней поддержки обратитесь на
    веб-сайт https://order.mysql.com/ или свяжитесь с нашим
    отделом сбыта по адресу sales@mysql.com.

    1.5.1.2 Обучение и сертификация

    Компания MySQL AB проводит обучение по MySQL и другим смежным продуктам по
    всему миру. Мы предлагаем как общедоступные, так и внутрифирменные курсы,
    подготовленные в соответствии с конкретными потребностями вашей компании.
    Обучение по MySQL проводят также наши партнеры — авторизованные центры
    обучения по MySQL.

    В наших учебных материалах в качестве примеров используются те же базы
    данных, что и в нашей документации и типовых приложениях. Эти материалы
    постоянно дополняются, чтобы соответствовать последней версии MySQL. Наши
    инструкторы опираются на поддержку коллектива разработчиков, что
    гарантирует качество обучения и постоянное совершенствование учебного
    материала. Благодаря тесной связи с разработчиками вы можете быть уверены
    также и в том, что какой бы вопрос ни возник у вас в процессе обучения, на
    него всегда будет найден ответ.

    Обучение на наших курсах позволит вам достичь целей, которые вы ставите
    перед собой при создании своих MySQL-приложений. Помимо этого вы:

    • сэкономите время

    • повысите эффективность работы своих приложений

    • уменьшите или устраните необходимость в дополнительном оборудовании,
      сократив таким образом издержки

    • усилите защищенность своих приложений

    • полнее удовлетворите потребности ваших заказчиков и коллег.

    • подготовитесь к сертификации по MySQL.

    Если предлагаемое нами обучение интересует вас как потенциального
    участника нашего проекта или партнера в реализации учебных курсов,
    посетите учебный раздел на нашем веб-сайте http://www.mysql.com/training/
    или свяжитесь с нами по адресу: training@mysql.com.

    Для более подробной информации о программе сертификации MySQL, см.
    http://www.mysql.com/certification/.

    1.5.1.3 Консультации

    Компания MySQL AB и ее авторизованные партнеры предлагают по всему миру
    консультационные услуги для пользователей MySQL а также для
    разработчиков, встраивающих MySQL в свое программное обеспечение.

    Наши консультанты окажут вам помощь в таких вопросах, как проектирование и
    настройка баз данных, создание эффективных запросов, настройка
    используемой вами платформы для достижения оптимальной эффективности
    работы, решение проблем миграции, реализация тиражирования, создание
    устойчивых приложений диалоговой обработки запросов, а также во многих
    других. Кроме того, мы помогаем нашим клиентам встраивать MySQL в
    их продукты и приложения, предназначенные для широкомасштабного
    распространения.

    Наши консультанты работают в тесном сотрудничестве с коллективом
    разработчиков компании, что обеспечивает высокий технический уровень
    предоставляемых ими профессиональных услуг. Мы предлагаем широкий диапазон
    форм консультационной поддержки — от двухдневных интенсивных курсов до
    проектов продолжительностью в недели и месяцы. Наши консультации
    охватывают не только MySQL, но и языки программирования и
    сценариев, например PHP, Perl и многое другое.

    Заинтересованных в наших консультационных услугах, а также тех, кто хотели
    бы стать нашими партнерами в оказании таких услуг, приглашаем посетить
    консультационный раздел на нашем веб-сайте
    http://www.mysql.com/consulting/ или связаться с нашим консультационным
    отделом по адресу consulting@mysql.com.

    1.5.1.4 Коммерческие лицензии

    ПО баз данных MySQL выпускается по общедоступной лицензии GNU General
    Public License
    (GPL). Это означает, что при соблюдении условий GPL ПО
    MySQL можно пользоваться бесплатно. Если вы не хотите связывать себя
    лицензионными ограничениями GPL (например, вас не устраивает условие, что
    ваше собственное приложение также подпадает под действие GPL), есть
    возможность приобрести у компании MySQL AB на этот же продукт коммерческую
    лицензию.

    См. https://order.mysql.com/).

    Компания MySQL AB владеет авторскими правами на
    исходный код MySQL, поэтому мы вправе использовать двойное лицензирование,
    в соответствии с которым один и тот же продукт доступен как по лицензии
    GPL, так и по коммерческой лицензии, и это никоим образом не нарушает
    обязательств компании MySQL AB по предоставлению исходного кода. Более
    подробную информацию о том, в каких случаях необходимо приобретение
    коммерческой лицензии, вы найдете в разделе section 1.6.3 Лицензии на ПО MySQL.

    Компания MySQL AB занимается также продажей коммерческих лицензий на ПО
    сторонних разработчиков, предоставляемое с открытым исходным кодом на
    условиях GPL. Это ПО расширяет возможности MySQL. Хороший пример —
    транзакционный обработчик таблиц InnoDB, обеспечивающий поддержку
    технологии ACID, строковую блокировку, восстановление системы после аварии,
    управление версиями, поддержку внешних ключей и многое другое (see section 7.5 Таблицы InnoDB).

    1.5.1.5 О нашей программе партнерства

    Компания MySQL AB реализует глобальную программу партнерства, которая
    охватывает учебные курсы, консультативные услуги и поддержку продукта,
    издательскую деятельность, а также продажу и распространение MySQL и
    смежных продуктов. Партнеры компании MySQL AB получают право быть
    представленными на веб-сайте http://www.mysql.com/ и использовать в
    маркировке своих продуктов специальные варианты торговой марки MySQL — с
    целью идентификации и продвижения этих продуктов на рынке.

    Тех, кто заинтересован в получении статуса партнера MySQL, просим
    обращаться по адресу partner@mysql.com.

    Название MySQL и логотип MySQL в виде дельфина являются торговыми марками
    компании MySQL AB (see section 1.6.4 Логотипы и торговые марки MySQL AB),
    принадлежащими компании MySQL AB. Узнаваемость этих торговых марок
    свидетельствует о том, что за годы своей работы основатели компании MySQL
    AB сумели добиться для своей компании заметного положения и признания в
    мире.

    1.5.1.6 О рекламе

    Веб-сайт компании MySQL (http://www.mysql.com/) пользуется популярностью
    среди разработчиков и пользователей. Например, в октябре 2001 г. мы
    обслужили 10 миллионов запросов на просмотр размещенных на нем страниц.
    Наши посетители относятся к категории лиц, принимающих решения и дающих
    рекомендации о покупке как программного, так и аппаратного обеспечения.
    12% наших посетителей утверждают решения о приобретении, и только 9% наших
    посетителей совсем не имеют отношения к принятию подобных решений. Более
    65% наших посетителей сделали в деловых целях как минимум одну покупку в
    Сети за последние полгода, а 70% — планируют совершить ее в ближайшие
    месяцы.

    1.5.2 Как с нами связаться

    Самая свежая информация о MySQL и нашей компании представлена на веб-сайте
    MySQL (http://www.mysql.com/).

    По вопросам связи с прессой и темам, не затронутым в наших сообщениях для
    печати (http://www.mysql.com/news/), обращайтесь по адресу
    press@mysql.com.

    Для получения своевременных и точных ответов на технические вопросы,
    касающиеся ПО MySQL, необходимо иметь действующий контракт с компанией
    MySQL AB по поддержке (за дополнительной информацией обращайтесь к разделу
    section 1.6.1 Поддержка, предлагаемая компанией MySQL AB). Чтобы заказать контракт по поддержке,
    следует обратиться на сайт https://order.mysql.com/ или направить
    сообщение по адресу sales@mysql.com.

    Для получения информации об учебных курсах, которые проводит компания
    MySQL AB, посетите раздел по обучению на веб-сайте
    http://www.mysql.com/training/. Тех, кто имеет ограниченный доступ в
    Internet, просим связаться с учебным отделом компании MySQL AB по адресу
    training@mysql.com (see section 1.5.1.2 Обучение и сертификация).

    Информацию о программе сертификации компании MySQL AB вы найдете на
    странице http://www.mysql.com/certification/index.html нашего
    веб-сайта. Если вы желаете быть в курсе текущего состояния программы
    сертификации по MySQL, просим сообщить об этом по адресу
    certification@mysql.com. See section 1.5.1.2 Обучение и сертификация.

    Заинтересованных в получении консультаций приглашаем посетить раздел
    консультаций на веб-сайте http://www.mysql.com/consulting/. Тех, у кого
    имеется ограниченный доступ в Internet, просим связаться с
    консультационным отделом компании MySQL AB по адресу consulting@mysql.com.
    Обращайтесь к разделу section 1.5.1.3 Консультации.

    Коммерческие лицензии можно приобрести по Сети, на веб-сайте
    https://order.mysql.com/. Здесь вы найдете также информацию о том, как
    передать в компанию MySQL AB свой заказ на покупку по факсу. Более подробная информация
    о лицензировании доступна на http://www.mysql.com/products/pricing.html.

    Если у вас
    имеются вопросы, касающиеся лицензирования, или вы хотите знать расценки
    на лицензии в случае массового выпуска продукта, заполните форму на нашем
    веб-сайте (http://www.mysql.com/) или пошлите сообщение по электронной
    почте: вопросы, касающиеся лицензирования, направляйте по адресу
    licensing@mysql.com, а запросы на покупку — по адресу sales@mysql.com.
    Обращайтесь также к разделу section 1.6.3 Лицензии на ПО MySQL.

    Если вы представляете деловые круги, заинтересованные в партнерских
    отношениях с компанией MySQL AB, напишите нам по адресу partner@mysql.com.
    Обращайтесь к разделу section 1.5.1.5 О нашей программе партнерства.

    Для получения дополнительной информации о политике компании MySQL в
    отношении торговых марок обращайтесь на наш веб-сайт
    http://www.mysql.com/company/trademark.html или напишите письмо по адресу
    trademark@mysql.com. Обратитесь к разделу section 1.6.4 Логотипы и торговые марки MySQL AB.

    Если вас заинтересовало какое-либо из предложений, перечисленных в нашем
    разделе предложений работы (http://www.mysql.com/company/jobs/),
    направляйте свои письма по адресу jobs@mysql.com. Просьба не оформлять
    свои личные данные в виде вложения в письмо: лучше добавьте эту информацию
    в виде обычного текста в конце своего сообщения.

    Если вы желаете принять участие в общей дискуссии с нашими многочисленными
    пользователями, обращайтесь на соответствующий список рассылки (see section 1.8.1 Списки рассылки MySQL).

    Сообщения об ошибках (или bugs), а также вопросы и комментарии следует
    направлять в список рассылки по адресу mysql@lists.mysql.com. При
    обнаружении в MySQL ошибок, влияющих на безопасность баз данных,
    просим сообщать об этом по адресу security@mysql.com. Обратитесь также к
    разделу section 1.8.1.3 Как отправлять отчеты об ошибках или проблемах.

    Если у вас имеются сравнительные результаты тестирования, которые мы можем
    опубликовать, свяжитесь с нами по адресу benchmarks@mysql.com.

    Предложения по внесению дополнений или исправлений в данное руководство
    пользователя следует направлять коллективу разработчиков руководства по
    адресу docs@mysql.com.

    Вопросы и замечания по работе или содержанию веб-сайта MySQL
    (http://www.mysql.com/) направляйте по адресу webmaster@mysql.com.

    Компания MySQL AB придерживается определенной политики относительно
    конфиденциальности информации. Об этом вы можете прочитать на странице
    http://www.mysql.com/company/privacy.html нашего веб-сайта. По вопросам
    этой политики просим обращаться по адресу privacy@mysql.com.

    По всем другим вопросам обращайтесь по адресу info@mysql.com.

    1.6 Лицензии и поддержка MySQL

    В этом разделе описаны условия предоставления компанией MySQL AB лицензий
    и поддержки.

    1.6.1 Поддержка, предлагаемая компанией MySQL AB

    Что подразумевается под технической поддержкой компании MySQL AB? Это
    означает, что на каждый свой вопрос вы получите адресованный лично вам
    ответ непосредственно от программистов, пишущих программы баз данных
    MySQL.

    Мы стараемся, чтобы наша техническая поддержка носила широкий и
    содержательный характер. Если вопрос, который вы задаете по MySQL, важен
    для вас, то, следовательно, он должен быть важен и для нас. Как правило,
    клиенты просят помочь разобраться в том, как работают те или иные команды
    или утилиты, как устранить «узкие места», мешающие эффективной работе
    системы, как восстановить систему в случае аварии, как влияет на работу
    MySQL та или иная операционная система или локальная сеть, какие
    технологии резервного копирования и восстановления данных лучше применять,
    как использовать API-интерфейсы и т.д. Наша поддержка охватывает вопросы,
    относящиеся только к серверу MySQL и нашим собственным утилитам, но не к
    продуктам сторонних разработчиков, которые обеспечивают доступ к серверу
    MySQL, хотя мы стараемся и в этих случаях оказывать посильную помощь.

    Подробная информация о различных видах поддержки, которую предлагает
    компания, приведена на веб-сайте http://www.mysql.com/support/. Там же вы
    можете заказать по Сети контракты по поддержке. Те, кто имеет ограниченный
    доступ в Internet, могут связаться с нашим отделом сбыта по адресу
    sales@mysql.com.

    Техническая поддержка — это своего рода страхование жизни. Без такой
    страховки можно успешно обходиться долгие годы, но наступит критический
    момент — и придется сожалеть о своей беспечности! Если вы используете
    сервер MySQL для важных приложений и внезапно сталкиваетесь с неполадками
    в их работе, может оказаться, что на самостоятельное выяснение ответов на
    все вопросы потребуется слишком много времени. В таких случаях не обойтись
    без срочной помощи самых опытных специалистов по устранению аварийных
    ситуаций в MySQL, а они работают именно в компании MySQL AB.

    1.6.2 Авторские права и лицензии на MySQL

    Компания MySQL AB является владельцем авторских прав на исходный код ПО
    MySQL, логотипы и торговые марки MySQL, а также на данное руководство
    пользователя. Обратитесь к разделу section 1.5 Что представляет собой компания MySQL AB? Распространение
    MySQL подпадает под действие нескольких различных лицензий:

    1. Весь код ПО сервера, специфичный только для MySQL, библиотека
      mysqlclient и клиентское ПО, а также библиотека GNU readline подпадают под
      действие общедоступной лицензиии GNU General Public License
      (see section G GNU General Public License). Текст этой лицензии имеется также в
      составе дистрибутива ПО, в файле `COPYING’.

    2. Библиотека GNU getopt подпадает под действие GNU Lesser General
      Public License
      (see section H GNU Lesser General Public License).

    3. Некоторые фрагменты исходного кода (библиотека regexp) подпадают под
      действие Berkeley-подобной лицензии.

    4. Старые версии MySQL (3.22 и более ранние) подпадают под действие более
      строгой лицензии (http://www.mysql.com/products/mypl.html).
      Информация об условиях лицензии имеется в документации на конкретную
      версию.

    5. Распространение руководства пользователя в данное время не подпадает
      под действие лицензии типа GPL. Его использование допускается на следующих
      условиях:

      • Допускается конвертирование в другие форматы, но внесение при этом
        каких-либо изменений или редактирование содержания не допускается.

      • Разрешается выпуск печатных копий руководства для личных целей.

      • Во всех остальных случаях, таких как продажа печатных изданий
        руководства или использование руководства (или его части) в других
        публикациях, требуется предварительно получить письменное согласие
        компании MySQL AB.

      Для получения дополнительной информации или в случае, если вы хотели бы
      принять участие в переводе руководства, обращайтесь по адресу
      docs@mysql.com.

    Дополнительная информация о том, как практически осуществляется
    лицензирование MySQL, находится в разделе section 1.6.3 Лицензии на ПО MySQL. Обращайтесь
    также к разделу section 1.6.4 Логотипы и торговые марки MySQL AB.

    1.6.3 Лицензии на ПО MySQL

    ПО MySQL распространяется в соответствии с условиями общедоступной
    лицензии GNU General Public License (GPL), которая является одной из
    наиболее широко распространенных лицензий на ПО с открытым исходным кодом.
    Официальные условия лицензии GPL вы найдете на веб-сайте
    http://www.gnu.org/licenses/. Обратитесь также к
    http://www.gnu.org/licenses/gpl-faq.html и
    http://www.gnu.org/philosophy/enforcing-gpl.html.

    Так как ПО MySQL выпускается по лицензии GPL, зачастую им можно
    пользоваться бесплатно, но в некоторых случаях желательно или необходимо
    приобрести коммерческую лицензию у компании MySQL AB (это можно сделать на
    веб-сайте https://order.mysql.com/).

    См. http://www.mysql.com/products/licensing.html для получения более
    подробной информации.

    Старые версии MySQL (3.22 и более ранние) подпадают под действие более
    строгой лицензии (http://www.mysql.com/products/mypl.html).
    Информацию об условиях лицензии вы найдете в документации на конкретную
    версию.

    Обращаем ваше внимание на то, что использование ПО MySQL, подпадающего под
    коммерческую лицензию, лицензию GPL или старую лицензию MySQL, не
    означает, что вы автоматически получаете право на использование торговых марок,
    принадлежащих компании MySQL AB. Об этом читайте в разделе section 1.6.4 Логотипы и торговые марки MySQL AB.

    1.6.3.1 Использование ПО MySQL под коммерческой лицензией

    Лицензия GPL — в хорошем смысле — носит «заразный» характер. Это
    означает, что в случае линкования какой-либо программы с программой, выпущенной
    по данной лицензии, все части исходного кода получившегося продукта должны
    также выпускаться по лицензии GPL. В противном случае будут нарушены
    условия лицензии и вы вообще лишитесь права использовать программу, подпадающую
    под ее действие.

    Коммерческая лицензия является необходимой в следующих случаях:

    • При линковании программы с любым GPL кодом из ПО MySQL, в тех случаях, когда вы не
      хотите, чтобы готовый продукт подпадал под действие GPL (например,
      продукт разрабатывается как коммерческий или существуют какие-либо
      другие причины не открывать добавленный программный код, который не
      подпадает под действие GPL). При покупке коммерческой лицензии
      вы не используете ПО MySQL под лицензией GPL, даже несмотря на то,
      что это один и тот же код.

    • В случае распространения приложения, не защищенного лицензией GPL,
      которое предназначено для работы исключительно с ПО MySQL и
      поставляется вместе с ним. Такой вариант решения в действительности
      считается связыванием, даже если оно осуществляется по сети.

    • В случае, когда вам требуется распространять ПО MySQL без
      предоставления исходного кода, как того требует лицензия GPL.

    • Если вы хотите сделать вклад в дальнейшее развитие технологии баз
      данных MySQL — в таких случаях коммерческая лицензия формально может и
      не требоваться. Еще одним хорошим способом оказать содействие развитию
      ПО MySQL является приобретение контракта по поддержке непосредственно
      у компании MySQL AB — это сразу же принесет пользу и вам (see section 1.6.1 Поддержка, предлагаемая компанией MySQL AB).

    Для каждой инсталляции ПО MySQL вам понадобится отдельная лицензия. Ее
    действие распространяется на любое число процессоров в машине и при этом
    не накладывается никаких юридических ограничений на число клиентских
    машин, подключенных к серверу.

    По поводу коммерческих лицензий, см.
    http://www.mysql.com/products/licensing.html. Для контрактов на
    поддержку, см. http://www.mysql.com/support/. Тех, для кого требуются
    особые условия лицензирования, а также тех, у кого имеется ограниченный доступ
    в Internet, просим связаться с нашим отделом сбыта по адресу
    sales@mysql.com.

    1.6.3.2 Бесплатное использование ПО MySQL по лицензии GPL

    По лицензии GPL допускается бесплатное использование ПО MySQL если вы
    согласны с условиями GPL. Подробнее по поводу лицензии GPL и
    освещение наиболее популярных вопросов вы найдете по адресу
    http://www.gnu.org/licenses/gpl-faq.html.

    Некоторые общие примеры GPL-использования MySQL:

    • Если вы распространяете код вашего приложения и код MySQL под
      лицензией GPL и в исходных текстах.

    • При распространении исходного кода MySQL в комплекте с другими
      программами, не связанными с MySQL или не зависящими от него по
      своему функциональному назначению, даже в том случае, если они
      распространяются на коммерческой основе.
      Это называется mere aggregation в лицензии GPL.

    • Если вы не распространяете никаких частей кода MySQL — вы можете
      использовать MySQL бесплатно.

    • При использовании ПО MySQL сервис-провайдерами Internet (ISP),
      предлагающими своим клиентам веб-хостинг с серверами баз данных MySQL.
      С другой стороны, мы настоятельно рекомендуем пользователям обращаться
      только к тем сервис-провайдерам, которые имеют контракт на поддержку
      компании MySQL: только при наличии такого контракта можно иметь
      уверенность в том, что при возникновении проблем с инсталляцией MySQL
      сервис-провайдер будет способен оказать своим клиентам действенную
      помощь.

      Заметим, что даже те сервис-провайдеры, которые
      не имеют коммерческой лицензии на MySQL, должны, по крайней
      мере, предоставить своим клиентам доступ к чтению исходного кода
      инсталляции MySQL, чтобы они могли самостоятельно проверить
      корректность всех сделанных ими дополнений или изменений.

    • При использовании ПО баз данных MySQL совместно с веб-сервером
      коммерческой лицензии не требуется (если, конечно, это не есть
      продукт, который вы распространяете). Это разрешение остается в силе
      даже в том случае, если веб-сервер, использующий MySQL, —
      коммерческий, так как при этом продажи версии MySQL, заложенной в
      него, как таковой не происходит. Однако в таком случае желательно,
      чтобы владелец веб-сервера приобрел контракт на поддержку MySQL,
      поскольку ПО MySQL способствует успеху его предприятия.

    В общем случае мы рекомендуем приобретать контракт на поддержку компании
    MySQL AB и тем, кому для использования ПО баз данных MySQL не требуется
    коммерческой лицензии: этим вы будете способствовать развитию технологии
    MySQL и заодно немедленно получите для себя дополнительные преимущества
    (see section 1.6.1 Поддержка, предлагаемая компанией MySQL AB).

    В случае использования ПО баз данных MySQL в коммерческих целях,
    предполагающего получение прибыли, мы предлагаем приобретение поддержки
    того или иного уровня, что будет способствовать дальнейшему развитию ПО
    MySQL. Мы считаем, что, если база данных MySQL способствует вашему
    бизнесу, то резонно предложить и вам оказать содействие компании MySQL AB
    (иначе получается так, что, обращаясь в нашу службу поддержки с вопросом,
    вы не только бесплатно пользуетесь тем, во что мы вложили большое
    количество усилий, но к тому же и требуете от нас еще и бесплатной
    поддержки)

    1.6.4 Логотипы и торговые марки MySQL AB

    Многие пользователи СУБД MySQL выражают желание расположить логотип MySQL
    AB с изображением дельфина на своих веб-сайтах, книгах или коробках со
    своими программными продуктами. Мы приветствуем это желание, хотя и
    обязаны напомнить, что MySQL и логотип MySQL с изображением дельфина
    являются торговыми марками компании MySQL AB и могут применяться только в
    соответствии с нашими правилами использования торговых знаков, с которыми
    вы можете ознакомиться по адресу
    http://www.mysql.com/company/trademark.html.

    1.6.4.1 Оригинальный логотип MySQL

    Логотип MySQL с изображением дельфина был создан финским рекламным
    агентством Priority в 2001 году. Мы решили сделать эмблемой СУБД MySQL
    дельфина — умное, проворное и изящное животное, с удивительной легкостью
    плавающее в океане, так же как и наша СУБД — в океане данных. К тому же
    дельфины нам просто нравятся.

    Оригинальный логотип MySQL может использоваться только представителями
    MySQL AB, а также лицами, получившими на то письменное разрешение.

    1.6.4.2 Логотипы MySQL, которые могут использоваться без письменного разрешения

    Мы разработали несколько специальных логотипов для договорного
    использования, которые можно загрузить с нашего сайта, расположенного по
    адресу http://www.mysql.com/press/logos.html и применять на сайтах
    третьих сторон без письменного разрешения MySQL AB. Возможности
    использования этих логотипов, как и следует из их названия, определенным
    образом ограничены: они регламентируются правилами применения наших
    торговых знаков (которые также приведены у нас на сайте). Если вы
    планируете использовать данные логотипы, необходимо ознакомиться с
    указанными правилами. Они в основном сводятся к следующим:

    • Нужный вам логотип вы можете использовать в том виде, в котором он
      приведен на сайте http://www.mysql.com/. Вы имеете право задать
      необходимые размеры логотипа, но не можете менять его цвета или
      вносить в изображение какие-либо другие изменения.

    • Должно быть явно указано, что именно вы, а не MySQL AB, являетесь
      создателем и владельцем сайта, на котором присутствует торговый знак
      MySQL.

    • Не разрешается использовать торговый знак в целях, которые могли бы
      принести вред MySQL AB или понизить ценность торговых знаков MySQL AB.
      Мы оставляем за собой право запретить использование торгового знака
      MySQL AB.

    • Помещая торговый знак на своем сайте, сделайте его гиперссылкой на
      сайт http://www.mysql.com/.

    • Если вы используете СУБД MySQL в своем приложении на условиях лицензии
      GPL, оно должно быть создано в соответствии с идеологией Open Source и
      снабжено способностью подсоединяться к серверу MySQL.

    Связаться с нами с целью заключения соответствующих вашим потребностям
    соглашений можно по адресу trademark@mysql.com.

    1.6.4.3 В каком случае для использования логотипов необходимо письменное разрешение?

    Письменное разрешение MySQL AB для использования логотипов MySQL
    необходимо в следующих случаях:

    • При использовании логотипа MySQL AB где бы то ни было, кроме вашего
      веб-сайта.

    • При использовании любого логотипа MySQL AB, кроме вышеупомянутых
      специальных логотипов для договорного использования, на веб-сайтах или
      в любых других местах.

    Исходя из юридических и коммерческих соображений, мы следим за
    использованием торговых знаков MySQL на различных продуктах, книгах и т.п.
    Обычно мы взимаем плату за помещение логотипов MySQL AB на коммерческих
    продуктах, так как считаем, что вполне справедливо, если часть полученных
    производителем прибылей идет таким образом на финансирование дальнейшего
    усовершенствования СУБД MySQL.

    1.6.4.4 Партнерские логотипы MySQL AB

    Партнерские логотипы MySQL могут использоваться только теми компаниями и
    частными лицами, которые подписали письменное соглашение о партнерстве с
    MySQL AB. В условия подписания такового соглашения входит сертификация в
    качестве преподавателя или консультанта MySQL. See section 1.5.1.5 О нашей программе партнерства.

    1.6.4.5 Использование слова MySQL в текстовых документах и презентациях

    MySQL AB приветствует упоминания о СУБД MySQL, но не следует забывать о
    том, что слово MySQL — торговая марка MySQL AB. Поэтому первое
    встречающееся в тексте слово MySQL следует снабдить символом,
    обозначающим торговую марку (TM), а также (по возможности) упомянуть о
    том, что MySQL является зарегистрированной торговой маркой компании MySQL
    AB. Дополнительную информацию вы сможете получить, ознакомившись с нашими
    правилами использования торговых знаков, расположенными по адресу
    http://www.mysql.com/company/trademark.html.

    1.6.4.6 Использование слова MySQL в названиях компаний и продуктов

    Использовать слово MySQL в названиях компаний, продуктов или в именах
    доменов без письменного разрешения MySQL AB запрещено.

    1.7 Кратко о MySQL 4.x

    Наконец-то появилась давно обещанная компанией MySQL AB бета-версия MySQL
    Server 4.0, которую так долго ждали пользователи. Ее можно загрузить с
    веб-сайта http://www.mysql.com/ или с наших зеркал.

    Большинство новых функций MySQL 4.0 ориентированы на уже
    существующих пользователей MySQL уже существующих пользователей в
    общественной и деловой сфере. Эти функции позволяют усовершенствовать
    программное обеспечение базы данных MySQL, предоставляя решения для
    критически важных систем управления базами данных, работающих с большими
    объемами информации. Остальные новые функции предназначены для
    пользователей встроенных баз данных.

    1.7.1 Поэтапный выпуск

    Начиная с 4.0.6, MySQL имеет статус gamma, что означает что версии 4.0.x в
    течении более чем 2 месяцев (сначала в alpha-, затем и в beta-статусе)
    используются без каких-либо известных серьезных и сложных для исправления
    ошибок и готовы для промышленного использования.

    Мы снимем префикс gamma, когда MySQL 4.0 будет в эксплуатации более чем один
    месяц без обнаруженных серьезных ошибок.

    Все новые функции будут добавляться в версию MySQL 4.1, доступную сейчас
    из нашего репозитория bk, выпуск alpha-версии которой запланирован на
    первый квартал 2003. See section 2.3.4 Установка из экспериментального набора исходных кодов.

    1.7.2 Можно использовать уже прямо сейчас

    Все бинарные поставки проходят наши сложные тесты без каких-либо ошибок на всех
    платформах, на которых мы тестируем MySQL. MySQL 4.0 протестирован в реальных
    условиях огромным количеством пользователей и находится в промышленной
    эксплуатациями на нескольких крупных сайтах.

    1.7.3 Встроенный MySQL

    Библиотека libmysqld обеспечивает для MySQL возможность не
    отставать от прогресса в стремительно развивающемся мире приложений.
    Вариант MySQL в виде встроенной библиотеки позволяет встраивать
    MySQL в различные приложения и электронные устройства так, что
    конечный пользователь даже не будет знать о «заложенной в их фундаменте»
    базе данных. Встроенный MySQL идеально подходит для использования в
    интернет-приложениях, публичных киосках, в устройствах с сочетанием
    аппаратного и программного обеспечения, высокопроизводительных
    интернет-серверах, автономных базах данных, распространяемых на
    компакт-дисках, и так далее.

    Большинство пользователей libmysqld оценят преимущество Двойной лицензии
    MySQL. Для тех, кто не хочет связывать себя условиями GPL лицензии, программное обеспечение доступно также на условиях коммерческой
    лицензии. Для встроенной библиотеки MySQL используется такой же интерфейс,
    как и для обычной клиентской библиотеки, поэтому ею удобно и легко
    пользоваться. See section 8.4.9 libmysqld, встраиваемая библиотека сервера MySQL.

    1.7.4 Другие функции, доступные в MySQL 4.0

    • В версии 4.0 еще больше возросла скорость работы MySQL в нескольких
      областях, таких как множественные вставки (bulk INSERT) для большого
      количества данных, поиск в сжатых индексах, создание полнотекстовых
      индексов (FULLTEXT), а также COUNT(DISTINCT).

    • Обработчик таблиц InnoDB теперь входит в стандартный набор сервера
      MySQL, включая полную поддержку транзакций и блокировок уровня строки.

    • Немецкие, австрийские и швейцарские пользователи нашей программы
      обратят внимание, что мы добавили новый набор символов, latin1_de,
      который позволяет исправить порядок сортировки немецких символов,
      размещая немецкие умляуты в соответствии с телефонными книгами,
      используемыми в Германии.

    • Функции для упрощения преобразования из других систем баз данных в
      MySQL, включают TRUNCATE TABLE (как в Oracle) и IDENTITY, как
      синоним автоматически инкрементируемых ключей (как в Sybase). Многим
      пользователям также будет приятно узнать, что MySQL теперь
      поддерживает оператор UNION, долгожданную стандартную функцию SQL.

    • Создавая новые функции для новых пользователей, мы не забыли о запросах наших
      постоянных пользователей. У нас есть многотабличные операторы DELETE и
      UPDATE Добавив поддержку символических ссылок к MyISAM на уровне
      таблицы (а не только на уровне базы данных, как это было раньше), а также
      включив обработку таких ссылок как функцию, используемую в Windows по
      умолчанию, мы надеемся продемонстрировать, что серьезно относимся к
      предложениям по усовершенствованиям. Такие функции как
      SQL_CALC_FOUND_ROWS и FOUND_ROWS() позволяют узнать, сколько
      строк возвратит запрос без оператора LIMIT.

    1.7.5 Функции MySQL 4.x, которые будут добавлены в будущем

    В последующих версиях MySQL 4.x будут добавлены следующие функции,
    которые на данный момент находятся в стадии разработки:

    • Пользователи MySQL, работающие с критически важными системами и
      большими объемами данных, оценят дополнения к нашей системе репликации
      и удаленного резервного копирования. В более поздние версии 4.x будет
      включена отказобезопасная репликация; а к функциям уже существующей в
      версии 4.0 команды LOAD DATA FROM MASTER в скором времени будет
      добавлена автоматизация настройки подчиненных серверов. Удаленное
      резервное копирование обеспечит возможность легко добавлять новые
      подчиненные серверы, не отключая головной сервер, — это позволит
      практически избежать потерь в производительности при обновлении
      объемных систем.

    • Для администраторов баз данных удобным окажется еще одно новшество: в
      скором времени параметры mysqld (настройки запуска) можно будет
      изменять без выключения серверов.

    • Новые свойства поиска FULLTEXT в MySQL 4.0 позволяют
      использовать FULLTEXT-индексацию больших объемов текста при помощи как
      бинарной логики поиска, так и логики поиска естественного языка.
      Пользователи могут производить настройку минимальной длины слова и
      задавать свои списки недопустимых слов на любом естественном языке,
      благодаря чему появляется возможность создания новой группы программ
      на основе MySQL.

    • Производительность многих «тяжеловесных» приложений повысится
      благодаря еще более возросшей скорости заново переписанного ключевого
      кэша.

    • Большинству разработчиков также понравится встроенная в MySQL справка,
      которая вызывается из командной строки на клиенте.

    1.7.6 MySQL 4.1, следующая ветка в разработке

    MySQL 4.0 готовит базу для реализации новых возможностей в сервере MySQL
    4.1 и более новых версиях. Имеются в виду такие возможности, как вложенные
    подзапросы (nested subqueries) (4.1), хранимые процедуры (5.0) и правила
    целостности ссылок (foreign key integrity rules) для MyISAM-таблиц (5.0).

    Эти возможности возглавляют список наиболее востребованных нашими потребителями
    функций.

    После реализации этих изменений критикам СУБД MySQL придется проявить
    больше изобретательности и придумать более убедительные аргументы, чем
    просто указание на недостающие функциональные возможности. Будучи давно
    известной как быстродействующая, надежная и легкая в использовании, СУБД
    MySQL теперь станет соответствовать ожиданиям самых требовательных
    потребителей.

    1.8 Источники информации по MySQL

    1.8.1 Списки рассылки MySQL

    В этом разделе представлены списки рассылки MySQL, а также даются
    некоторые указания по их использованию. Подписавшись на список рассылки,
    вы будете получать по электронной почте информацию из данного списка и
    сможете отправлять туда свои собственные вопросы и ответы.

    1.8.1.1 Списки рассылки MySQL

    Чтобы подписаться на главный список рассылки MySQL, следует отправить
    сообщение на адрес электронной почты mysql-subscribe@lists.mysql.com.

    Чтобы отказаться от подписки на главный список рассылки MySQL, следует
    отправить сообщение на адрес электронной почты
    mysql-unsubscribe@lists.mysql.com.

    В посылаемом сообщении роль играет только адрес, на который это сообщение
    отправляется. Тема и текст сообщения игнорируются.

    Если адрес, с которого было отправлено ваше сообщение, не действителен,
    можно точно указать адрес для подписки или адрес, подписку для которого
    следует аннулировать. Для этого в указанных выше адресах электронной почты
    следует добавить дефис в конце командного слова, обозначающего подписку
    (subscribe) или отказ от нее (unsubscribe), а за ним — нужный адрес
    электронной почты, заменив в нем символ `@’ на символ `=’. Например, чтобы
    подписать адрес your_name@host.domain, необходимо отправить сообщение на
    mysql-subscribe-your_name=host.domain@lists.mysql.com.

    Сообщения, посланные по адресам mysql-subscribe@lists.mysql.com или
    mysql-unsubscribe@lists.mysql.com, обрабатываются автоматически программой
    обслуживания списка рассылки ezmlm. Информация по программе ezmlm доступна
    на веб-узле ezmlm (http://www.ezmlm.org/).

    Чтобы отправить сообщение в список рассылки, следует послать сообщение по
    адресу mysql@lists.mysql.com. Пожалуйста, не отправляйте сообщения о
    подписке или отказе от подписки на адрес mysql@lists.mysql.com, поскольку
    все сообщения, направленные на этот адрес, автоматически распространяются
    среди тысяч других подписчиков.

    Если на вашем локальном веб-узле уже есть подписчики на
    mysql@lists.mysql.com, то на нем может существовать локальный список
    рассылки, поэтому сообщения, отправленные из lists.mysql.com на ваш
    веб-узел, будут также дублироваться в местный список. В таких случаях
    необходимо связаться со своим системным администратором, чтобы он добавил
    вас в местный список рассылки MySQL или исключил из него.

    Если необходимо, чтобы сообщения, поступающие со списка рассылки,
    направлялись в отдельный почтовый ящик вашего почтового клиента,
    установите фильтр по заголовкам сообщений. Чтобы выделить сообщения из
    списка рассылки, можно использовать заголовки List-ID: или Delivered-To:.

    Существуют следующие списки рассылки MySQL:

    announce-subscribe@lists.mysql.com announce
    Список для объявлений о выходах новых версий MySQL и относящихся к нему
    программ. Количество сообщений здесь небольшое, и на этот список
    желательно подписаться всем пользователям MySQL.
    mysql-subscribe@lists.mysql.com mysql
    Главный список для обсуждения общих вопросов по MySQL. Обратите внимание:
    некоторые темы лучше обсуждать в более специализированных списках. Если
    отправить сообщение не в тот список, то можно не получить ответа!
    mysql-digest-subscribe@lists.mysql.com mysql-digest
    Список mysql в виде сборника. Это означает, что вы получите все сообщения,
    отправленные за день, в виде одного большого почтового сообщения, которое
    отправляется раз в день.
    bugs-subscribe@lists.mysql.com bugs
    В этот список можно отправлять только подробные отчеты о повторяющихся
    ошибках, используя макропрограмму mysqlbug (если вы работаете в Windows,
    необходимо включить описание операционной системы и указать версию MySQL).
    Прежде чем отправлять отчет об ошибке, желательно проверить, при
    использовании какой версии MySQL данная ошибка возникает —
    последней окончательной или находящейся на стадии разработки! Чтобы любой
    желающий мог воспроизвести эту ошибку, желательно также включить в отчет
    контрольный тестовый пример, который можно было бы запустить при помощи
    mysql test < script. Все ошибки, сообщения о которых будут направлены в
    список рассылки, будут либо исправлены, либо включены в список ошибок в
    следующей версии MySQL! Если необходимо только небольшое изменение кода,
    мы также отправим исправляющую эту ошибку заплатку для программы.
    bugs-digest-subscribe@lists.mysql.com bugs-digest
    Список bugs в виде сборника.
    internals-subscribe@lists.mysql.com internals
    Список для тех, кто работает над кодом MySQL. В этом списке также можно
    обсуждать разработку MySQL и отправлять в него вставки в программу.
    internals-digest-subscribe@lists.mysql.com internals-digest
    Версия в виде сборника для списка internals.
    java-subscribe@lists.mysql.com java
    Обсуждение вопросов, связанных с MySQL и Java. В основном
    обсуждение по драйверам JDBC включая MySQL Connector/J.
    java-digest-subscribe@lists.mysql.com java-digest
    Версия в виде сборника для списка java.
    win32-subscribe@lists.mysql.com win32
    Все вопросы, касающиеся программного обеспечения MySQL в операционных
    системах Microsoft, таких как Windows 9x/Me/NT/2000/XP.
    win32-digest-subscribe@lists.mysql.com win32-digest
    Версия в виде сборника для списка win32.
    myodbc-subscribe@lists.mysql.com myodbc
    Все вопросы, касающиеся соединения MySQL через ODBC.
    myodbc-digest-subscribe@lists.mysql.com myodbc-digest
    Версия в виде сборника для списка myodbc.
    mysqlcc-subscribe@lists.mysql.com mysqlcc
    Все вопросы, касающиеся графического клиента MySQL Control Center (MyCC).
    mysqlcc-digest-subscribe@lists.mysql.com mysqlcc-digest
    Версия в виде сборника для списка mysqlcc.
    plusplus-subscribe@lists.mysql.com plusplus
    Все вопросы, касающиеся программирования на C++ API для MySQL.
    plusplus-digest-subscribe@lists.mysql.com plusplus-digest
    Версия в виде сборника для списка plusplus.
    msql-mysql-modules-subscribe@lists.mysql.com msql-mysql-modules
    Список по поддержке Perl для MySQL при помощи msql-mysql-modules.
    msql-mysql-modules-digest-subscribe@lists.mysql.com msql-mysql-modules-digest
    Версия в виде сборника для списка msql-mysql-modules.

    Подписаться или отказаться от подписки на все списки рассылки можно
    способом, указанным выше. В своем сообщении о подписке или отказе от
    подписки вместо mysql просто укажите соответствующее название списка
    рассылки. Например, чтобы подписаться на список рассылки myodbc или
    отказаться от подписки на него, следует отправить сообщение по адресу
    myodbc-subscribe@lists.mysql.com или myodbc-unsubscribe@lists.mysql.com.

    Если получить ответы на свои вопросы в списке рассылки не удалось, можно
    оплатить поддержку от MySQL AB — это позволит вам напрямую общаться с
    разработчиками MySQL. See section 1.6.1 Поддержка, предлагаемая компанией MySQL AB.

    В приведенной ниже таблице указаны некоторые списки рассылки MySQL на
    языках, отличных от английского. Обратите внимание на то, что компания
    MySQL AB эти списки не контролирует, поэтому мы не можем гарантировать их
    качество.

    mysql-france-subscribe@yahoogroups.com Французский список рассылки
    list@tinc.net Корейский список рассылки
    Чтобы подписаться на этот список рассылки, отправьте сообщение subscribe
    mysql your@e-mail.address
    .
    mysql-de-request@lists.4t2.com Немецкий список рассылки
    Чтобы подписаться на этот список рассылки, отправьте сообщение subscribe
    mysql-de your@e-mail.address
    . Информацию по этому списку рассылки можно
    найти на http://www.4t2.com/mysql/.
    mysql-br-request@listas.linkway.com.br Португальский список рассылки
    Чтобы подписаться на этот список рассылки, отправьте сообщение subscribe
    mysql-br your@e-mail.address
    .
    mysql-alta@elistas.net Испанский список рассылки
    Чтобы подписаться на этот список рассылки, отправьте сообщение subscribe
    mysql your@e-mail.address
    .

    1.8.1.2 Как задавать вопросы и направлять сообщения об ошибках

    Прежде чем отправлять отчет об ошибке, необходимо выполнить следующие
    действия:

    • Сначала проведите поиск в интерактивном руководстве по MySQL на:
      http://www.mysql.com/doc/

      Мы стараемся постоянно обновлять данное руководство, добавляя информацию об
      исправлениях последних обнаруженных ошибок! Приложение, описывающее историю
      изменений (http://www.mysql.com/doc/en/News.html) также может быть
      полезным, т.к. вполне возможно, что именно в новой версии уже есть решение
      вашей проблемы.

    • Посмотрите в базе данных ошибок по адресу http://bugs.mysql.com. Может,
      известная ошибка уже сообщена и исправлена.

    • Просмотрите архивы списка рассылки MySQL:
      http://lists.mysql.com/

    • Можно также воспользоваться ссылкой http://www.mysql.com/search/,
      чтобы произвести поиск по всем веб-страницам (включая руководство),
      расположенным на веб-узле http://www.mysql.com/.

    Если в руководстве или архивах не удалось найти ответ, обратитесь к
    локальному эксперту по MySQL. Если же и таким образом не удалось получить
    ответы на вопросы, переходите к следующему разделу, в котором описано, как
    отправлять почту на mysql@lists.mysql.com.

    1.8.1.3 Как отправлять отчеты об ошибках или проблемах

    Чтобы написать хороший отчет об ошибке, потребуется немало терпения.
    Однако лучше сделать все правильно с первой попытки — это сбережет и ваше,
    и наше время. Грамотно составленный отчет об ошибке, содержащий ее
    подробное описание, позволит нам исправить эту ошибку уже в следующей
    версии программы. Включенные в данный раздел рекомендации помогут вам
    правильно написать свой отчет, не тратя времени на описание того, что мало
    чем сможет нам помочь или не потребуется вовсе.

    Мы рекомендуем для создания отчетов об ошибках (или отчетов о любых
    проблемах), всегда, если это возможно, использовать сценарий mysqlbug.
    mysqlbug можно найти в каталоге `scripts’ раздела распространения исходных
    текстов, или в разделе распространения исполняемых программ, в каталоге
    `bin’ инсталляционного каталога MySQL. Если же не удается воспользоваться
    mysqlbug, то все равно необходимо указать в своем отчете все данные,
    перечисленные ниже в этом разделе.

    Сценарий mysqlbug помогает сгенерировать отчет путем автоматического
    определения большей части приведенной ниже информации, но если окажется,
    что в сгенерированном отчете отсутствует что-либо важное, обязательно
    включите это в свое сообщение! Внимательно прочитайте данный раздел и
    убедитесь, что в отчет вошли все описанные здесь сведения.

    Обычно отчеты об ошибках и проблемах направляются в mysql@lists.mysql.com.
    Если же вы можете создать подробное описание, четко определяющее ошибку,
    его можно направить в список рассылки bugs@lists.mysql.com. Обратите
    внимание: в этот список рассылки можно посылать только полный отчет о
    повторяющейся ошибке, составленный при помощи сценария mysqlbug. Если вы
    работаете в Windows, необходимо включить описание операционной системы и
    версии MySQL. Прежде чем направлять отчет, желательно проверить,
    проявляется ли данная проблема при использовании последней окончательной
    или находящейся в стадии разработки версии MySQL! Чтобы любой
    желающий мог воспроизвести эту ошибку, желательно также включить в отчет
    контрольный тестовый пример, который можно было бы запустить при помощи
    «mysql test < script», либо Perl-сценарий или сценарий оболочки, которые
    можно запустить непосредственно. Все ошибки, сообщения о которых будут
    направлены в список рассылки, будут либо исправлены, либо включены в
    список ошибок в следующей версии MySQL! Если необходимо только небольшое
    изменение кода, мы также отправим исправляющий эту ошибку патч для
    программы.

    Если вы нашли ошибку в системе безопасности MySQL, необходимо отправить
    сообщение по адресу security@mysql.com.

    Не забывайте о том, что можно ответить на сообщение, в котором содержится
    слишком много информации, но нельзя ответить на сообщение, в котором
    информации недостаточно. Часто те, кто нам пишет, опускают некоторые
    факты: они считают, что им известна причина возникшей проблемы и поэтому,
    по их мнению, некоторые детали не имеют значения. Необходимо
    придерживаться следующего принципа: если возникают сомнения в отношении
    того, следует или нет приводить в отчете те или иные сведения, — включите
    их в отчет обязательно! Намного быстрее и проще написать несколько
    дополнительных строк в отчете, чем получать уточняющие вопросы и снова
    ждать ответа только потому, что в первый раз были указаны не все данные.

    Чаще всего наши корреспонденты не указывают используемую версию MySQL или
    платформу, на которой установлен сервер MySQL (включая версию платформы).
    Это довольно существенная информация, и в 99 случаях из 100 отчет об
    ошибке без нее будет совершенно бесполезным! Очень часто бывает и так: мы
    получаем вопрос типа: »Почему это у меня не работает?», а потом
    оказывается, что указанная функция в данной версии MySQL отсутствует или
    что ошибка, описанная в отчете, уже была исправлена в более новой версии
    MySQL. Иногда ошибка зависит от используемой платформы. В таких случаях
    практически невозможно ничего исправить, не имея информации об
    операционной системе и о версии платформы.

    Не забывайте указывать информацию о своем компиляторе — в тех случаях,
    когда это имеет отношение к возникшей проблеме. Ведь бывает и так:
    пользователь полагает, что проблема связана с MySQL, а на самом деле он
    нашел ошибку в компиляторе. Большинство компиляторов постоянно находятся в
    состоянии разработки и становятся лучше от версии к версии. Чтобы
    определить, зависит ли ваша проблема от компилятора, мы должны знать,
    какой именно используется компилятор. Обратите внимание на то, что все
    проблемы с компиляторами должны рассматриваться как ошибки и по ним должен
    составляться соответствующий отчет.

    Вы окажете нам значительную помощь, включив в отчет об ошибке подробное
    описание проблемы. В качестве хорошего примера подобной информации можно
    привести описание всех действий, которые привели к возникновению проблемы
    и описание самой проблемы. Лучшие отчеты содержат подробные примеры, в
    которых показано, как можно воспроизвести ошибку или проблему.
    section D.1.6 Создание контрольного примера при повреждении таблиц.

    Если программа выдает сообщение об ошибке, очень важно точно воспроизвести
    это сообщение в своем отчете! Возможно, нам придется производить поиск в
    архивах — лучше, чтобы указанное в отчете сообщение об ошибке точно
    совпадало с тем, которое выдает программа. Не следует пытаться запомнить
    сообщение об ошибке, имеет смысл просто скопировать его полностью и
    вставить в отчет!

    Если возникли проблемы с MyODBC, необходимо попытаться создать файл
    трассировки MyODBC. See section 8.3.7 Составление отчетов о проблемах с MyODBC.

    Не забывайте, что у большинства людей, которые будут читать ваш отчет,
    экраны дисплеев имеют ширину в 80 символов. При создании отчетов или
    примеров при помощи средств командной строки mysql необходимо использовать
    параметр --vertical (или терминатор оператора G) для выходных данных,
    которые будут превышать ширину для таких дисплеев (пример для оператора
    EXPLAIN SELECT приведен ниже в данном разделе).

    В свой отчет вам необходимо включить следующую информацию:

    • Версию используемого дистрибутива MySQL (например MySQL Version
      3.22.22). Чтобы узнать, какая версия запущена, следует выполнить
      команду mysqladmin version. Программу mysqladmin можно найти в
      каталоге `bin’ инсталляционного каталога MySQL.

    • Производителя и модель компьютера, на котором вы работаете.

    • Название и версию операционной системы. Для большинства операционных
      систем эту информацию можно получить, выполнив команду Unix uname -a.

    • Иногда важную роль играет количество памяти (реальной и виртуальной).
      Если у вас есть основания считать, что такая информация может
      оказаться полезной, включите в отчет эти значения.

    • Если используется дистрибутив в виде исходных текстов программного
      обеспечения MySQL, необходимо указать версию используемого
      компилятора. Если используется бинарный дистрибутив, необходимо
      указать имя дистрибутива.

    • Если проблема возникает во время компиляции, включите в отчет точное
      сообщение об ошибке (или ошибках), а также несколько строк до и после
      вызывающего ошибку кода в файле, вызвавшем проблему.

    • Если произошло аварийное завершение работы mysqld, необходимо сообщить
      о запросе, который привел к такому завершению. Это можно выяснить,
      запустив mysqld с включенной функцией ведения журнала.
      See section D.1.5 Использование журналов для определения причин ошибок в mysqld.

    • Если с программой связана какая-либо таблица базы данных, включите в
      отчет выходную информацию команды mysqldump --no-data db_name
      tbl_name1 tbl_name2 ...
      . Выполняется это очень легко. Таким способом
      можно получить подробную информацию о таблице в базе данных, что
      поможет нам создать ситуацию, соответствующую той, в которой оказались
      вы.

    • Для ошибок, связанных со скоростью выполнения или проблемами с операторами
      SELECT, всегда необходимо включать в отчет выходную информацию команды
      EXPLAIN SELECT ... и, по крайней мере, количество строк, которые выдает
      оператор SELECT. Вы также должны включить вывода SHOW CREATE TABLE
      ...
      для каждой таблицы, задействованной в запросе. Чем больше информации
      будет предоставлено о сложившейся ситуации, тем больше шансов, что будет
      оказана надлежащая помощь! Например, ниже приведен образец очень хорошего
      отчета об ошибке (он, конечно, должен быть отправлен при помощи сценария
      mysqlbug):

      Пример запускается при помощи командной строки mysql (обратите внимание
      на применение терминатора операторов G, который используется для
      операторов, если ширина выводимой ими информации превышает ширину строки
      80-символьного дисплея):

      mysql> SHOW VARIABLES;
      mysql> SHOW COLUMNS FROM ...G
             <вывод SHOW COLUMNS>
      mysql> EXPLAIN SELECT ...G
             <вывод EXPLAIN>
      mysql> FLUSH STATUS;
      mysql> SELECT ...;
             <Корокая версия вывода SELECT, включая время,
              затраченное на обработку запроса>
      mysql> SHOW STATUS;
             <вывод SHOW STATUS>
      
    • Если ошибка или проблема возникли во время работы mysqld, постарайтесь
      предоставить сценарий, который воспроизведет аномальное поведение
      программы. Сценарий должен включать все необходимые файлы данных. Чем
      точнее сценарий может воспроизвести сложившуюся ситуацию, тем лучше.

      Если вы можете создать воспроизводимый контрольный пример, его
      необходимо отправить на bugs@lists.mysql.com для немедленного
      рассмотрения! Если сценарий обеспечить нельзя, необходимо, по крайней
      мере, включить в свое сообщение выходную информацию команды mysqladmin
      variables extended-status processlist
      , чтобы предоставить данные о
      работе системы!

    • Если не удается создать контрольный пример в несколько строк, или если
      таблица тестирования слишком велика для отправления в список рассылки
      (более 10 строк), необходимо вывести содержимое таблиц при помощи
      команды mysqldump и создать файл `README’ с описанием вашей проблемы.

      Запакуйте файлы при помощи tar и gzip или zip, и по ftp загрузите
      архив на ftp://support.mysql.com/pub/mysql/secret/. Затем отправьте
      краткое описание проблемы на bugs@lists.mysql.com.

    • Если MySQL на ваш запрос выдает странный, на ваш взгляд,
      результат, приведите в отчете не только сам результат, но также и ваше
      мнение о том, каким должен быть результат, и расчеты, подтверждающие
      это мнение.

    • Когда вы приводите пример ситуации, при которой возникает проблема,
      лучше сохранить действительные имена переменных, таблиц и т.п., вместо
      того, чтобы давать им новые имена. Иногда проблема может быть вызвана
      именем переменной или таблицы! Это довольно редкие случаи, но лучше,
      чтобы не терять времени, такую информацию отправить нам сразу. В конце
      концов, вам будет даже легче использовать данные, соответствующие
      реальной ситуации, и это во всех отношениях лучше для нас. Те же, кто
      не хотел бы показывать свои данные другим пользователям, могут
      загрузить файл по ftp на
      ftp://support.mysql.com/pub/mysql/secret/. Если данные действительно
      представляют собой секретную информацию, которую нельзя показывать
      даже нам, тогда можно привести пример, используя другие имена, но этот
      вариант следует оставить на крайний случай.

    • Включите в отчет все параметры, указанные для важных программ, если
      это возможно. Например, приведите параметры, которые вы используете
      как для запуска сервера mysqld, так и для запуска клиентских программ
      MySQL. Параметры таких программ как mysqld и mysql, а также сценарий
      configure часто содержат ответы на многие вопросы и очень важны!
      Включить их в отчет не помешает в любом случае! Если используются
      какие-либо модули, такие как Perl или PHP, также укажите их версии.

    • Если ваш вопрос относится к системе привилегий, укажите выходные
      данные команды mysqlaccess, выходные данные команды mysqladmin reload
      и все сообщения об ошибках, которые выдаются при попытке соединения!
      Во время проверки своих привилегий сначала необходимо выполнить
      команду mysqlaccess. После этого запустите mysqladmin reload version и
      попытайтесь соединиться с программой, которая вызывала проблемы.
      Программу mysqlaccess можно найти в каталоге `bin’ установочного
      каталога MySQL.

    • Если у вас есть патч, который исправляет ошибку, — это хорошо. Но не
      думайте, что одного лишь патча для нас будет достаточно, или что мы
      будем ее использовать, если вы не предоставите необходимую информацию,
      такую как описание ошибки, которую исправляет ваш патч. Ведь мы можем
      найти проблемы в самом патче или попросту в нем не разобраться, что не
      даст нам возможности его применять. Если нам не удастся проверить, для
      чего именно служит этот патч, мы не станем его использовать. В таких
      случаях нам помогут контрольные примеры. Продемонстрируйте, что патч
      исправляет все проблемы, которые могут возникнуть в связи с этой
      ошибкой. Если мы найдем хотя бы один вариант, в котором патч не
      работает, то он может оказаться бесполезным.

    • Попытки угадать, что представляет собой ошибка, почему она возникает
      или от чего зависит, обычно не дают положительного результата. Даже
      члены команды MySQL не могут определить реальную причину ошибки без
      программы отладки.

    • Укажите в своем почтовом сообщении, что вы просмотрели справочное
      руководство и почтовый архив — тогда будет видно, как вы пытались
      решить эту проблему самостоятельно.

    • Если возникла синтаксическая ошибка, внимательно просмотрите свою
      программу! Если не удается найти никаких ошибок, может оказаться, что
      ваша версия MySQL не поддерживает используемый запрос. Если
      используется текущая версия в руководстве на http://www.mysql.com/doc/
      не описан синтаксис, который вы используете, MySQL не
      поддерживает ваш запрос. В этом случае вы можете либо самостоятельно
      реализовать такой синтаксис, либо направить сообщение по адресу
      licensing@mysql.com и попросить либо предложить реализовать его! Если
      в руководстве описан используемый синтаксис, но у вас установлена
      более старая версия MySQL, необходимо проверить журнал
      изменений MySQL, чтобы узнать, когда был реализован данный синтаксис.
      В этом случае вы можете произвести обновление до более новой версии
      MySQL. See section C История изменений и обновлений MySQL.

    • Если в результате ошибки ваши данные оказываются поврежденными, или
      возникают ошибки при обращении к какой-либо определенной таблице,
      сначала необходимо проверить, а потом попытаться восстановить таблицы
      при помощи команды myisamchk или CHECK TABLE и REPAIR TABLE.
      See section 4 Администрирование баз данных.

    • Если повреждения ваших таблиц случаются часто, необходимо выяснить,
      когда и почему это происходит. В этом случае файл
      `mysql-data-directory/`hostname`.err’ может содержать некоторую
      информацию о происходящем. See section 4.9.1 Журнал ошибок. Включите в отчет
      об ошибке всю важную информацию из этого файла. Обычно mysqld никогда
      не должна повреждать данные в таблицах, если не произошло никакого
      сбоя во время обновления! Если вам удалось найти причину ошибки в
      mysqld, нам будет гораздо проще оказать вам помощь в решении этой
      проблемы. See section A.1 Как определить, чем вызваны проблемы.

    • Если это возможно, загрузите и установите последнюю версию MySQL
      Server и проверьте, не решена ли в ней ваша проблема. Все версии
      программного обеспечения MySQL проходят тщательное тестирование и
      должны работать без проблем. Мы делаем все возможное, чтобы сохранить
      обратную совместимость, поэтому при переходе с одной версии MySQL на
      другую никаких проблем не должно возникать. See section 2.2.6 Какую версию MySQL использовать.

    Если вы пользователь, пользующийся официальной поддержкой, направьте отчет
    об ошибке на mysql-support@mysql.com, чтобы его рассмотрели в первую
    очередь, а также в соответствующий список рассылки, чтобы узнать,
    сталкивался ли кто-нибудь еще с этой проблемой (и, возможно, нашел
    решение).

    Чтобы получить информацию по отчетам об ошибках в MyODBC, See section 8.3.4 Как сообщать о проблемах с MyODBC.

    Решения для наиболее часто встречающихся проблем можно найти в разделе
    section A Проблемы и распространенные ошибки.

    Если ответы направляются к вам индивидуально, не попадая в список
    рассылки, хорошим тоном считается составить отчет по полученным ответам и
    отправить его в список рассылки, чтобы другие пользователи смогли получить
    информацию, которая помогла решить вашу проблему!

    1.8.1.4 Рекомендации по ответам на вопросы, направляемые в список рассылки

    Если вы считаете, что ваш ответ представляет интерес для большинства
    пользователей, то можете направить его прямо в список рассылки, вместо
    того, чтобы отвечать напрямую человеку, задавшему вопрос. Постарайтесь
    обобщить свой ответ таким образом, чтобы его смысл был понятен всем, а не
    только человеку, задавшему вопрос. При отправке сообщения в список
    рассылки убедитесь, что ваш ответ не является повторением предыдущего
    ответа.

    Постарайтесь оставить главную часть вопроса в своем ответе, не стесняйтесь
    оставить все исходное сообщение в своем письме.

    Не отправляйте сообщения из своего браузера с включенным режимом HTML!
    Многие пользователи не используют браузер для чтения почты!

    1.8.2 Пользователи MySQL на IRC

    В дополнение к спискам рассылки MySQL, вы можете найти поддержку у
    опытных пользователей MySQL в IRC.

    Вот сети/каналы, известные нам на данный момент:

    • freenode (см. список серверов на http://www.freenode.net/ )
      • #mysql

        В основном обсуждение MySQL, но вопросы по другим СУБД приветствуются.

      • #mysqlphp

        Вопросы про популярную связку MySQL+PHP

    • EFnet (см. список серверов на http://www.efnet.org/)
      • #mysql

        Вопросы по MySQL.

    Мы можем рекомендовать вам X-Chat для подключения к IRC-сети. X-Chat доступен
    как для Unix, так и для Windows по адресу: http://www.xchat.org/.

    1.9 Насколько MySQL соответствует стандартам?

    В этом разделе рассматривается соотношение между MySQL и стандартами ANSI
    SQL. Сервер MySQL имеет много расширений стандарта ANSI SQL; здесь вы
    найдете информацию о том, что представляют собой эти расширения и как их
    использовать. Помимо этого, в данном разделе содержится информация о том,
    какие функциональные возможности отсутствуют в сервере MySQL, а также
    дается описание способов обхода некоторых трудностей.

    Мы не ставили перед собой цель ограничивать какую бы то ни было область
    применения сервера MySQL, если на то нет веских причин. И несмотря на то
    что у нас не хватает ресурсов выполнять разработку для каждого возможного
    применения сервера, мы всегда охотно окажем помощь и предложим советы тем,
    кто пытается расширять сферу использования MySQL.

    Одним из главных направлений разработки данного продукта является
    продолжение работы в области соответствия его стандартам ANSI 99, но не за
    счет ущерба для скорости или надежности. Мы без опаски добавляем к серверу
    MySQL расширения к SQL или поддержку не предусмотренных в SQL
    возможностей, если это заметно увеличивает удобство его использования для
    значительной части наших пользователей (одним из примеров такой стратегии
    является новый интерфейс HANDLER в версии сервера MySQL 4.0;
    see section 6.4.2 Синтаксис оператора HANDLER).

    В наших планах — продолжение поддержки баз данных с транзакциями и без
    транзакций, чтобы обеспечить как интенсивное применение для
    веб-регистрации, так и зависящее от целевого назначения использование в
    круглосуточном режиме 24/7.

    С самого начала сервер MySQL был спроектирован для работы с базами данных
    среднего размера (10-100 миллионов строк или около 100 MB на таблицу) на
    малых вычислительных системах. Мы будем продолжать расширять сервер MySQL
    для работы с базами данных с размерами даже больше терабайта, и наряду с
    этим — предоставлять возможность компиляции упрощенной версии MySQL,
    которая больше подходит для портативных устройств и встраивания. Благодаря
    компоновочной схеме сервера MySQL оба эти направления возможны без
    каких-либо конфликтов в дереве исходных кодов.

    В настоящее время мы не ставим перед собой задач поддержки работы в режиме
    реального времени или кластеризованных баз данных (хотя наши сервисы
    репликации уже обеспечивают многие из таких возможностей).

    Мы не считаем, что для базы данных необходима поддержка чистого XML, но
    при этом будем добавлять на клиентской стороне поддержку XML-запросов
    наших пользователей. По нашему мнению, основной код сервера должен
    оставаться настолько «скудным и чистым», насколько возможно, а взамен
    следует разрабатывать библиотеки, которые «взвалят на свои плечи» все
    сложности на клиентской стороне. Эта концепция полностью соответствует
    упомянутой выше стратегии — не жертвовать скоростью или надежностью
    сервера.

    1.9.1 Каким стандартам соответствует MySQL ?

    Начальный уровень SQL92. Для ODBC уровни 0-3.51.

    Мы стремимся к полной поддержке стандарта ANSI SQL99, но без ущерба для
    скорости и качества кода.

    1.9.2 Запуск MySQL в режиме ANSI

    При запуске mysqld с опцией --ansi поведение сервера MySQL изменяется
    следующим образом:

    • || представляет собой конкатенацию строк вместо ИЛИ (OR).

    • Допускается любое количество пробелов между именем функции и скобкой
      `(‘. Это заставляет MySQL интерпретировать все имена функций как
      зарезервированные слова.

    • `»‘ будет интерпретироваться как символ кавычки идентификатора (как
      символ кавычки «’ сервера MySQL), а не как символ кавычки строки.

    • REAL будет синонимом для FLOAT, а не для DOUBLE.

    • Уровнем изоляции транзакций по умолчанию является SERIALIZABLE (see section 6.7.3 Синтаксис команды SET TRANSACTION).

    • Вы можете использовать столбец/выражение в GROUP BY, которое не перечислено в списке столбцов.

    Использование данной опции равносильно применению
    --sql-mode=REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,
    IGNORE_SPACE,SERIALIZE,ONLY_FULL_GROUP_BY
    .

    1.9.3 Расширения MySQL к ANSI SQL92

    Сервер MySQL включает в себя ряд расширений, которые могут отсутствовать в
    других базах данных SQL. Если вы их используете, то следует иметь в виду,
    что такой код не будет переносимым на другие SQL-серверы. В некоторых
    случаях можно написать код, включающий расширения MySQL, но, тем не менее,
    являющийся переносимым, воспользовавшись комментариями вида /*! ... */. В
    этом случае сервер MySQL будет анализировать и выполнять данный код внутри
    этого комментария как обычную команду MySQL, в то время как другие
    SQL-серверы будут игнорировать данное расширение. Например:

    SELECT /*! STRAIGHT_JOIN */ col_name FROM table1,table2 WHERE ...
    

    При добавлении номера версии после '!' это выражение будет исполняться
    только в случае, если номер данной версии MySQL равен указанному номеру
    или больше:

    CREATE /*!32302 TEMPORARY */ TABLE t (a int);
    

    Это означает, что при наличии версии 3.23.02 или выше сервер MySQL будет
    использовать ключевое слово TEMPORARY.

    Ниже приводится перечень расширений MySQL:

    • Типы полей MEDIUMINT, SET, ENUM и различные типы BLOB и TEXT.

    • Атрибуты полей AUTO_INCREMENT, BINARY, NULL, UNSIGNED и ZEROFILL.

    • Все сравнения строк по умолчанию являются независимыми от регистра
      символов с порядком сортировки, заданным текущей кодировкой
      (ISO-8859-1 Latin1 по умолчанию). Если вас это не устраивает, то можно
      объявить столбцы с атрибутом BINARY или использовать явное приведение
      типов BINARY, в результате чего сравнение будет выполняться в
      соответствии с порядком ASCII, используемом на хосте сервера MySQL.

    • Сервер MySQL сопоставляет каждую базу данных с подкаталогом в каталоге
      данных MySQL, а таблицы внутри базы данных — с именами файлов в этом
      подкаталоге базы данных.

      Это правило имеет несколько следствий:

      • В сервере MySQL, работающем под операционными системами с
        зависимыми от регистра символов именами файлов (таковыми являются
        большинство Unix-систем), имена баз данных и имена таблиц
        являются зависимыми от регистра символов (see section 6.1.3 Чувствительность имен к регистру).

      • Имена базы данных, таблицы, индекса, столбца или псевдонимы могут
        начинаться с цифры (но не должны содержать только цифры).

      • Можно использовать стандартную систему команд выполнения
        резервного копирования, переименования, перемещения, удаления и
        копирования таблиц. Например, для переименования таблицы
        необходимо переименовать соответствующие этой таблице файлы `.MYD’,
        `.MYI’ и `.frm’.

    • В командах SQL можно обращаться к таблицам из разных баз данных с
      помощью выражения db_name.tbl_name. В некоторых SQL-серверах
      обеспечивается точно такая же функциональная возможность, но она
      называется User space. Сервер MySQL не поддерживает табличные
      пространства (как в выражении: CREATE TABLE ralph.my_table...IN
      my_tablespace
      ).

    • LIKE разрешается на числовых столбцах.

    • Использование INTO OUTFILE и STRAIGHT_JOIN в команде SELECT (see section 6.4.1 Синтаксис оператора SELECT).

    • Опция SQL_SMALL_RESULT в команде SELECT.

    • Использование EXPLAIN SELECT для получения описаний объединения
      таблиц.

    • Использование индексных имен, индексов на префиксах полей, а также
      INDEX или KEY в команде CREATE TABLE (see section 6.5.3 Синтаксис оператора CREATE TABLE).

    • Использование TEMPORARY или IF NOT EXISTS с CREATE TABLE.

    • Использование COUNT(DISTINCT list), где list представляет собой более
      чем один элемент.

    • Использование CHANGE col_name, DROP col_name или DROP INDEX, IGNORE
      или RENAME в команде ALTER TABLE (see section 6.5.4 Синтаксис оператора ALTER TABLE).

    • Использование RENAME TABLE. See section 6.5.5 Синтаксис оператора RENAME TABLE.

    • Использование нескольких выражений ADD, ALTER, DROP или CHANGE в
      команде ALTER TABLE.

    • Использование DROP TABLE с ключевыми словами IF EXISTS.

    • Возможность удаления нескольких таблиц одной командой DROP TABLE.

    • Условие LIMIT в команде DELETE.

    • Условие DELAYED в командах INSERT и REPLACE.

    • Условие LOW_PRIORITY в командах INSERT, REPLACE, DELETE и UPDATE.

    • Использование LOAD DATA INFILE. Во многих случаях этот синтаксис
      совместим с применяющимся в Oracle LOAD DATA INFILE (see section 6.4.9 Синтаксис оператора LOAD DATA INFILE).

    • Команды ANALYZE TABLE, CHECK TABLE, OPTIMIZE TABLE и REPAIR TABLE.

    • Команда SHOW (see section 4.5.6 Синтаксис команды SHOW).

    • Строки могут быть заключены в кавычки с помощью или `»‘, или , но
      не просто .

    • Использование символа экранирования `’.

    • Команда SET (see section 5.5.6 Синтаксис команды SET).

    • Нет необходимости называть имена всех выбранных столбцов в части GROUP
      BY
      . Это дает лучшую производительность для некоторых очень
      специфических, но вполне нормальных запросов (see section 6.3.7 Функции, используемые в операторах GROUP BY).

    • Можно указывать ASC и DESC с GROUP BY.

    • Чтобы упростить работу для пользователей, привыкших к иным условиям
      среды SQL, в сервере MySQL поддерживаются псевдонимы для многих
      функций. Например, для всех строковых функций поддерживается синтаксис
      как ANSI SQL, так и ODBC.

    • Сервер MySQL понимает операторы || и && для обозначения логических ИЛИ
      (OR) и И (AND), как это принято в языке программирования C. В сервере
      MySQL || и ИЛИ (OR) являются синонимами, так же, как && и И (AND).
      Благодаря этому удобному синтаксису, в сервере MySQL не поддерживается
      оператор ANSI SQL || для конкатенации строк: вместо него используется
      функция CONCAT(). Поскольку функция CONCAT() принимает любое
      количество аргументов, то в сервере MySQL можно легко модифицировать
      использование оператора ||.

    • CREATE DATABASE или DROP DATABASE (see section 6.5.1 Синтаксис оператора CREATE DATABASE).

    • Оператор % является синонимом для MOD(). Т.е. N % M эквивалентно
      MOD(N,M). Оператор % поддерживается для программистов на C и для
      совместимости с PostgreSQL.

    • Операторы =, <>, <=,<, >=,>, <<, >>, <=>, AND, OR или LIKE могут
      использоваться при сравнении столбцов слева от FROM в командах SELECT.
      Например:

      mysql> SELECT col1=1 AND col2=2 FROM tbl_name;
      
    • Функция LAST_INSERT_ID() (see section 8.4.3.31 mysql_insert_id()).

    • Операторы REGEXP и NOT REGEXP расширенных регулярных выражений.

    • CONCAT() или CHAR() с одним аргументом или более чем с двумя
      аргументами (в сервере MySQL эти функции могут принимать любое
      количество аргументов).

    • Функции BIT_COUNT(), CASE, ELT(), FROM_DAYS(), FORMAT(), IF(),
      PASSWORD(), ENCRYPT(), MD5(), ENCODE(), DECODE(), PERIOD_ADD(),
      PERIOD_DIFF(), TO_DAYS() или WEEKDAY().

    • Использование функции TRIM() для усечения подстрок. В ANSI SQL
      поддерживается только удаление единичных символов.

    • Операция GROUP BY для функций STD(), BIT_OR() и BIT_AND().

    • Использование REPLACE вместо DELETE + INSERT (see section 6.4.8 Синтаксис оператора REPLACE).

    • Команды FLUSH, RESET и DO.

    • Возможность устанавливать переменные в команде с помощью :=:

      SELECT @a:=SUM(total),@b=COUNT(*),@a/@b AS avg FROM test_table;
      SELECT @t1:=(@t2:=1)+@t3:=4,@t1,@t2,@t3;
      

    1.9.4 Отличия MySQL от ANSI SQL92

    Наши усилия направлены на то, чтобы сервер MySQL соответствовал стандартам
    ANSI SQL и ODBC SQL, но в некоторых случаях сервер MySQL функционирует
    по-другому. Ниже приведен перечень таких отличий:

    • Для столбцов VARCHAR при хранении величины концевые пробелы удаляются
      (see section 1.9.5 Известные ошибки и недостатки проектирования в MySQL).

    • В некоторых случаях столбцы CHAR без уведомления изменяются на столбцы
      VARCHAR (see section 6.5.3.1 Молчаливые изменения определений столбцов).

    • Привилегии для таблицы не аннулируются автоматически при удалении
      таблицы; чтобы удалить привилегии для таблицы, необходимо явно вызвать
      REVOKE (see section 4.3.1 Синтаксис команд GRANT и REVOKE).

    • NULL AND FALSE будет трактоваться как NULL, а не как FALSE. Причина
      здесь в том, что мы не считаем необходимым оценивать множество
      дополнительных условий для этого случая.

    Если вас интересует, когда к серверу MySQL будут добавляться новые
    расширения, необходимо обратиться к онлайновому списку перспективных задач
    к выполнению, в котором дан их перечень в порядке приоритетности. Он
    находится по адресу
    http://www.mysql.com/doc/en/TODO.html. Это самая
    последняя версия списка задач к выполнению (TODO list) в данном
    руководстве (see section 1.10 MySQL и будущее (что предстоит сделать)).

    1.9.4.1 Вложенные SELECTы

    В сервер MySQL поддерживает вложенные запросы вида INSERT ... SELECT ...
    и REPLACE ... SELECT .... В других контекстах можно использовать
    и функцию IN().

    Вложенные операции выборки реализованы в версии 4.1.

    Между тем, во многих случаях можно переписать запрос, чтобы не
    использовать вложенную выборку. Например, запрос:

    SELECT * FROM table1 WHERE id IN (SELECT id FROM table2);
    

    можно переписать следующим образом:

    SELECT table1.* FROM table1,table2 WHERE table1.id=table2.id;
    

    Запросы:

    SELECT * FROM table1 WHERE id NOT IN (SELECT id FROM table2);
    SELECT * FROM table1 WHERE NOT EXISTS (SELECT id FROM table2
             WHERE table1.id=table2.id);
    

    эквивалентны следующему:

    SELECT table1.* FROM table1 LEFT JOIN table2 ON table1.id=table2.id
                    WHERE table2.id IS NULL;
    

    Для более сложных подзапросов часто можно создать временные таблицы,
    содержащие данный подзапрос. Иногда, однако, этот способ не годится, чаще
    всего для команд DELETE, для которых в стандарте SQL не поддерживаются
    объединения (за исключением вложенных выборок). В этой ситуации возможны
    два временных (пока вложенные запросы не поддерживаются сервером MySQL)
    варианта решения проблемы.

    Первый вариант следующий: при помощи какого-либо
    процедурно-ориентированного языка программирования (такого как Perl или
    PHP) делается запрос SELECT для получения первичных ключей тех записей,
    которые должны быть удалены, а затем полученные величины используются для
    составления команды DELETE (DELETE FROM ... WHERE ... IN (key1, key2,
    ...))
    .

    Второй вариант предполагает применение диалогового SQL для автоматического
    создания набора команд DELETE с использованием расширения MySQL CONCAT()
    (вместо стандартного оператора ||). Например:

    SELECT CONCAT('DELETE FROM tab1 WHERE pkid = ', "'", tab1.pkid, "'", ';')
           FROM tab1, tab2
           WHERE tab1.col1 = tab2.col2;
    

    Можно поместить этот запрос в файл скрипта, перенаправить стандартный
    вход клиента командной строки с этого файла, а стандартный выход — на
    еще один экземпляр клиента командной строки:

    shell> mysql --skip-column-names mydb < myscript.sql | mysql mydb
    

    Сервер версии MySQL 4.0 поддерживает многотабличные удаления — эту функцию
    можно использовать для эффективного удаления строк как из одной таблицы,
    так и из нескольких одновременно

    1.9.4.2 Оператор SELECT INTO TABLE

    Для сервера MySQL пока не реализована поддержка расширения Oracle SQL:
    SELECT ... INTO TABLE .... Вместо этого сервер MySQL поддерживает
    синтаксис ANSI SQL INSERT INTO ... SELECT ..., который, по существу,
    представляет собой то же самое (see section 6.4.3.1 Синтаксис оператора INSERT ... SELECT).

    INSERT INTO tblTemp2 (fldID) SELECT tblTemp1.fldOrder_ID
           FROM tblTemp1 WHERE tblTemp1.fldOrder_ID > 100;
    

    Можно также использовать выражения SELECT INTO OUTFILE... или CREATE
    TABLE ... SELECT
    .

    1.9.4.3 Транзакции и атомарные операции

    Поддержка транзакций в сервере MySQL реализуется при помощи обработчиков
    транзакционных таблиц типов InnoDB и BDB (see section 7 Типы таблиц MySQL).
    Таблицы InnoDB обеспечивают соответствие требованиям ACID.

    Однако для таблиц нетранзакционных типов, таких как MyISAM, в MySQL
    используется иная парадигма обеспечения целостности данных, получившая
    название «атомарные операции». Атомарные операции в сравнении с
    транзакциями часто обеспечивают такую же или даже лучшую целостность при
    более высокой производительности. Поскольку сервер MySQL поддерживает обе
    парадигмы, пользователь может выбирать между скоростью, которую
    обеспечивают атомарные операции, и транзакционными возможностями для своих
    приложений. Такой выбор может быть сделан для каждой таблицы отдельно.

    Рассмотрим, как используются возможности сервера MySQL для обеспечения
    строгой целостности и каковы эти возможности в сравнении с транзакционной
    парадигмой.

    1. Транзакционная парадигма обеспечивает следующие возможности: если
      приложения написаны таким образом, что в критических ситуациях зависят
      от вызова ROLLBACK вместо COMMIT, то транзакции предпочтительней
      атомарных операций. Транзакции также обеспечивают гарантию того, что
      незаконченные обновления или искаженные действия не будут
      фиксироваться в базе данных; серверу предоставляется возможность
      выполнить автоматический откат, и база данных будет сохранена. Почти
      во всех случаях при работе с сервером MySQL решить возможные проблемы
      можно путем включения простых проверок перед обновлениями и запуска
      простых скриптов, которые выполняют проверку баз данных на нарушение
      целостности с автоматическим исправлением повреждений или выдачей
      предупреждения, если такое нарушение возникает. Отметим, что
      полноценное выявление и устранение ошибок в таблицах без потери
      целостности данных можно обеспечить, просто используя системный журнал
      MySQL или добавив еще один дополнительный журнал.

    2. Во многих случаях транзакционные обновления можно переписать как
      атомарные. В общем случае все проблемы, которые решаются с помощью
      транзакций, можно решить с помощью LOCK TABLES или атомарных UPDATE,
      при гарантии того, что в базе данных никогда не произойдет
      автоматического прерывания (что является часто встречающейся проблемой
      для транзакционных баз данных).

    3. Даже в транзакционной системе возможна потеря данных в случае
      внезапной остановки сервера (если сервер «упадет»). Разница между
      различными системами состоит только в том, насколько мал промежуток
      времени, в течение которого данные могут быть потеряны. Ни одна
      система не является надежной на 100%, только «достаточно надежной».
      Даже для сервера Oracle (эта база данных считается наиболее надежной
      транзакционной базой данных), по сообщениям, в подобных ситуациях
      иногда возможна потеря данных. Что же касается использования сервера
      MySQL, то в любом случае, независимо от того, применяются или нет
      транзакционные таблицы, для обеспечения безопасности необходимо только
      иметь резервные копии и включенную регистрацию обновлений. Благодаря
      этим мерам в MySQL, так же как и в других транзакционных базах
      данных, можно восстановить информацию в любой ситуации. Резервные
      копии вообще хорошо иметь всегда, независимо от того, какая база
      данных используется.

    Транзакционная парадигма имеет свои достоинства и свои недостатки. Для
    многих пользователей и разработчиков приложений решающее значение имеет
    простота кодирования в проблемных ситуациях, в которых может произойти или
    неизбежно аварийное прерывание. Однако даже если парадигма атомарных
    операций для вас нова или вы привыкли к транзакциям, все же следует
    принимать во внимание выигрыш в скорости, который могут обеспечить
    нетранзакционные таблицы (порядка от трех до пяти раз по сравнению со
    скоростью наиболее быстрых и оптимально настроенных транзакционных
    таблиц).

    В ситуациях, где целостность данных чрезвычайно важна, сервер MySQL
    обеспечивает даже для нетранзакционных таблиц надежность и целостность
    данных уровня транзакций или лучше. При блокировании таблиц с помощью LOCK
    TABLES
    все обновления останавливаются до тех пор, пока не будут выполнены
    все проверки на целостность. При наличии только блокировки чтения (в
    противоположность блокировке записи) операции чтения и вставки, тем не
    менее, производятся. Новые внесенные записи не будут видны никому из
    имеющих блокировку чтения клиентов до освобождения этих блокировок. С
    iомощью INSERT DELAYED вставки становятся в очередь и находятся там до тех
    пор, пока не будут сняты все блокировки. При этом клиент не вынужден
    ждать, пока отработает INSERT (see section 6.4.4 Синтаксис оператора INSERT DELAYED).

    То, что мы подразумеваем под термином «атомарные», не означает ничего
    сверхъестественного. Имеется в виду лишь следующее: гарантируется, что при
    выполнении каждого конкретного обновления никакой другой пользователь не
    может повлиять на него и никогда не произойдет автоматического отката
    (который возможен на транзакционных таблицах, если не приняты должные меры
    предосторожности). Сервер MySQL также гарантирует, что не случится
    грязного чтения (dirty read)».

    Ниже описаны некоторые технические приемы работы с нетранзакционными
    таблицами:

    • Циклы, для которых требуются транзакции, обычно могут кодироваться с
      помощью LOCK TABLES, причем нет необходимости в указателях при
      динамическом обновлении записей.

    • Чтобы избежать применения ROLLBACK, можно использовать следующую
      стратегию:

      • Применить LOCK TABLES ... для блокирования всех таблиц, к которым
        необходим доступ.

      • Проверить условия.

      • Обновить, если все в порядке.

      • Использовать UNLOCK TABLES для освобождения произведенных
        блокировок.

      Обычно этот метод обеспечивает намного более высокую скорость, чем
      использование транзакций с возможными откатами, хотя и не всегда. Это
      решение не годится только для одной ситуации — когда кто-либо уничтожает
      потоки посреди обновления. В этом случае все блокировки будут сняты, но
      некоторые обновления могут не выполниться.

    • Для обновления записей в рамках одиночной операции можно также
      использовать функции. Применяя приведенные ниже технические приемы, вы
      получите очень эффективное приложение:

      • Поля модифицируются относительно их текущей величины.
      • Обновляются только те поля, которые действительно изменились.

      Например, при выполнении обновлений информации некоторого заказчика мы
      обновляем только те данные этого заказчика, которые изменялись, и делаем
      проверку только на предмет того, модифицировались ли изменяемые данные или
      зависящие от них по сравнению с исходной строкой. Проверка на то,
      изменялись или нет данные, выполняется с помощью выражения WHERE в команде
      UPDATE. Если данную запись обновить не удалось, то клиент получает
      сообщение: «Некоторые данные, которые вы изменяли, были модифицированы
      другим пользователем». После этого в окне выводится старая версия, чтобы
      пользователь мог решить, какую версию записи заказчика он должен
      использовать. Такой алгоритм обеспечивает нечто похожее на блокирование
      столбцов, но реально он даже лучше, поскольку мы обновляем только часть
      столбцов, используя величины, соответствующие их текущим значениям. Это
      означает, что типичные команды UPDATE выглядят примерно как приведенные
      ниже:

      UPDATE tablename SET pay_back=pay_back+'relative change';
      UPDATE customer
        SET
          customer_date='current_date',
          address='new address',
          phone='new phone',
          money_he_owes_us=money_he_owes_us+'new_money'
        WHERE
          customer_id=id AND address='old address' AND phone='old phone';
      

      Как можно видеть, этот способ очень эффективно и работает, даже если
      другой клиент изменит величины в столбцах pay_back или money_he_owes_us.

    • Во многих случаях пользователи хотят применять ROLLBACK и/или LOCK
      TABLES
      для управления уникальными идентификаторами для разных таблиц.
      Того же результата можно добиться намного более эффективно, используя
      столбец AUTO_INCREMENT и либо SQL-функцию LAST_INSERT_ID(), либо
      функцию C API mysql_insert_id() (see section 8.4.3.31 mysql_insert_id()).

      В общем случае можно написать код и для блокирования на уровне строк. Для
      некоторых ситуаций это действительно необходимо, но таких случаев очень мало.
      Блокировка на уровне строк поддерживается в таблицах InnoDB. Для типа
      MyISAM можно использовать флаговые столбцы в таблице и выполнять
      запросы, подобные следующему:

      UPDATE tbl_name SET row_flag=1 WHERE id=ID;
      

      MySQL возвращает 1 в качестве количества подвергнутых воздействию строк,
      если данная строка была найдена, а row_flag в исходной строке не был уже
      равен 1. Это можно себе представить так, как будто сервер MySQL изменяет
      предшествующий запрос на:

      UPDATE tbl_name SET row_flag=1 WHERE id=ID AND row_flag <> 1;
      

    1.9.4.4 Хранимые процедуры и триггеры

    Хранимые процедуры представляют собой набор команд SQL, которые могут
    компилироваться и храниться на сервере. Таким образом, вместо того, чтобы
    хранить часто используемый запрос, клиенты могут ссылаться на
    соответствующую хранимую процедуру. Это обеспечивает лучшую
    производительность, поскольку данный запрос должен анализироваться только
    однажды и уменьшается трафик между сервером и клиентом. Концептуальный
    уровень можно также повысить за счет создания на сервере библиотеки
    функций.

    Триггер представляет собой хранимую процедуру, которая активизируется при
    наступлении определенного события. Например, можно задать хранимую
    процедуру, которая срабатывает каждый раз при удалении записи из
    транзакционной таблицы — таким образом обеспечивается автоматическое
    удаление соответствующего заказчика из таблицы заказчиков, когда все его
    транзакции удаляются.

    Возможность работы с хранимыми процедурами будет обеспечивать
    планируемый язык обновлений. Наша цель — ввести хранимые процедуры
    приблизительно в версию сервера MySQL 5.0. Мы работаем также и над
    триггерами.

    1.9.4.5 Внешние ключи

    Следует учитывать, что в SQL внешние ключи используются не для объединения
    таблиц, а главным образом для проверки целостности ссылочных данных
    (ограничения внешних ключей). Если необходимо получить результаты из
    большого количества таблиц от команды SELECT, следует делать это через
    объединение таблиц:

    SELECT * FROM table1,table2 WHERE table1.id = table2.id;
    

    См. разделы section 6.4.1.1 Синтаксис оператора JOIN и See section 3.5.6 Использование внешних ключей.

    В версии сервера MySQL 3.23.44 и выше таблицы InnoDB поддерживают проверку
    ограничений внешних ключей (see section 7.5 Таблицы InnoDB). Для таблиц других
    типов сервер MySQL производит анализ синтаксиса FOREIGN KEY в командах
    CREATE TABLE, но без выполнения дальнейших действий.

    Синтаксис FOREIGN KEY без ON DELETE ... главным образом применяется для
    целей документирования. В некоторых ODBC-приложениях его можно
    использовать для автоматического создания выражений WHERE, но обычно это
    легко сделать вручную. FOREIGN KEY иногда используется в качестве
    проверки ограничений, но на практике такая проверка не является
    необходимой, если строки вносятся в таблицу в правильном порядке.

    В сервере MySQL можно обойти проблему отсутствия реализации ON DELETE ...
    добавлением соответствующей команды DELETE в приложение, когда удаляются
    записи из таблицы, имеющей внешний ключ. На практике при этом достигается
    почти такая же скорость (в некоторых случаях еще быстрее), как и при
    использование внешних ключей, и намного большая переносимость.

    В версии сервера MySQL 4.0 можно использовать многотабличное удаление,
    чтобы удалить строки из многих таблиц одной командой (see section 6.4.6 Синтаксис оператора DELETE).

    В ближайшем будущем мы расширим реализацию FOREIGN KEY таким образом, что
    информация будет сохраняться в специальном файле таблицы и ее можно будет
    извлечь с помощью mysqldump и ODBC. На следующем этапе мы внедрим
    ограничения внешних ключей для приложений, в которых не так просто
    обойтись без них.

    Следует иметь в виду, что внешние ключи часто применяются неправильно, что
    может вызывать большие проблемы. Даже если они использованы
    соответствующим образом, то не являются магическим решением для проблемы
    целостности ссылочных данных, хотя в некоторых случаях действительно
    упрощают ситуацию.

    Некоторые преимущества внедрения внешних ключей:

    • При условии, что связи спроектированы правильно, ограничения внешних
      ключей сделают более сложным для программиста введение
      противоречивости в базу данных.

    • Использование каскадных обновлений и удалений может упростить код
      клиента.

    • Должным образом разработанные правила внешних ключей помогают в
      документировании отношений между таблицами.

    Недостатки:

    • Ошибки, которые легко сделать в проектировании отношений ключей, могут
      вызывать сложные проблемы: например, зацикленные правила или ложные
      комбинации каскадных удалений.

    • Правильно написанное приложение будет само по себе обеспечивать
      отсутствие нарушения целостности ссылочных данных перед началом работы
      запроса. Таким образом, дополнительные проверки на уровне базы данных
      только понизят производительность для такого приложения.

    • Администраторы баз данных часто создают такую сложную топологию
      связей, при которой затруднительно, а зачастую и вовсе невозможно
      получить резервную копию или восстановить единичные таблицы.

    1.9.4.6 Представления

    Представления планируется реализовать примерно в версии сервера MySQL 5.0.

    Представления полезны в основном для случая, когда требуется предоставлять
    пользователям доступ к набору связей как к одной таблице (только в режиме
    чтения). Во многих базах данных SQL не обеспечивается возможность
    обновлять какие-либо строки в представлении — такие обновления необходимо
    выполнять в отдельных таблицах.

    Поскольку сервер MySQL применяется в основном в приложениях и
    веб-системах, где разработчик приложения имеет полный контроль над
    использованием базы данных, большинство из наших пользователей не считают
    представления достаточно важной функциональной возможностью (по крайней
    мере, никто не заинтересовался ими настолько, чтобы выразить готовность
    финансировать реализацию представлений).

    Для сервера MySQL нет необходимости в применении представлений для
    ограничения доступа к столбцам, так как в нем реализована хорошо
    продуманная система привилегий (see section 4.2 Общие проблемы безопасности и система привилегий доступа MySQL).

    1.9.4.7 Символы `—‘ как начало комментария

    В некоторых отличных от MySQL базах данных SQL символы `—‘ используются
    как начальные символы комментариев. В сервере MySQL символом начала
    комментариев является `#’. Для сервера MySQL можно также использовать
    стиль комментирования из C: /* this is a comment */ (see section 6.1.6 Синтаксис комментариев).

    В версии сервера MySQL 3.23.3 и выше поддерживается комментирование с
    помощью символов `—‘ — при условии, что за комментарием следует пробел.
    Это объясняется тем, что данный стиль комментирования вызвал много проблем
    при автоматической генерации SQL-запросов, в которых присутствовал код,
    подобный приведенному ниже (величина платежа вставляется в выражение
    !payment! автоматически):

    UPDATE tbl_name SET credit=credit-!payment!
    

    Давайте представим себе, что произойдет в случае, если величина payment
    окажется отрицательной. Поскольку выражение 1--1 в SQL является
    допустимым, то просто страшно себе вообразить последствия в случае, если
    будут разрешены комментарии, начинающиеся с `—‘,

    Использование нашей реализации этого метода комментирования в версии
    сервера MySQL 3.23.3 и выше — в форме 1-- This is a comment — является
    действительно безопасным.

    Существует еще один безопасный способ решения этой проблемы. Он
    заключается в том, что клиент командной строки mysql удаляет все строки,
    начинающиеся с `—‘.

    Приведенная ниже информация относится только к работе более ранних, чем
    3.23.3, версий MySQL.

    Если ваша SQL-программа представлена в виде текстового файла, содержащего
    комментарии `—‘, необходимо использовать:

    shell> replace " --" " #" < text-file-with-funny-comments.sql 
    	| mysql database
    

    вместо обычного:

    shell> mysql database < text-file-with-funny-comments.sql
    

    Можно также отредактировать сам командный файл, заменив комментарии `—‘
    комментариями `#’:

    shell> replace " --" " #" -- text-file-with-funny-comments.sql
    

    Привести эти комментарии к первоначальному виду можно с помощью следующей
    команды:

    shell> replace " #" " --" -- text-file-with-funny-comments.sql
    

    1.9.5 Известные ошибки и недостатки проектирования в MySQL

    1.9.5.1 Ошибки, известные в 3.23 и исправленные в более поздних версиях MySQL

    Следующие ошибки не исправлены в MySQL 3.23, поскольку исправление их
    требует слишком значительных изменений кода, которые могут повлечь
    создание еще большего количества ошибок. Эти ошибки классифицированы
    как «не фатальные» и «переносимые».

    • Можно получить взаимоблокировку при помощи LOCK TABLE на множестве таблиц,
      а затем выполнив в том же соединениии DROP TABLE одной из таблиц, пока другой поток
      пытается получить блокировку на таблицу. Однако можно уничтожить (KILL) эти
      потоки с тем, чтобы ситуация была исправлена. Исправлено в 4.0.12.

    • SELECT MAX(key_column) FROM t1,t2,t3... где одна из таблиц является
      пустой не вернет NULL, но вместо этого вернет максимальное значение для столбца.
      Исправлено в 4.0.11.

    1.9.5.2 Открытые ошибки / особенности строения MySQL

    Устранение следующих из выявленных проблем относится к числу
    первоочередных задач:

    • ANALYZE TABLE на таблицах BDB в некоторых случаях может сделать
      таблицу недоступной для использования, пока не произойдет перезапуск
      mysqld. При этом в файле ошибок MySQL можно будет увидеть ошибки,
      подобные следующим:

      001207 22:07:56 bdb: log_flush: LSN past current end-of-log
      
    • Не следует выполнять ALTER TABLE на таблицах BDB, на которых
      осуществляются многокомандные транзакции, пока все эти транзакции не
      завершатся (возможно, данные транзакции будут проигнорированы).

    • ANALYZE TABLE, OPTIMIZE TABLE и REPAIR TABLE могут вызвать проблемы на
      таблицах, для которых используется INSERT DELAYED.

    • Выполнение LOCK TABLE ... и FLUSH TABLES ... не гарантирует, что на
      данной таблице нет исполняемых в текущий момент незаконченных
      транзакций.

    • Открытие таблиц BDB происходит несколько медленно. Если база данных
      содержит много таблиц BDB, то потребуется значительное время для
      использования клиента mysql на этой базе данных, если не применять
      опцию -A или если использовать rehash. Это особенно заметно при
      наличии большого табличного кэша.

    Следующие проблемы также известны и будут устранены в свое время:

    • При использовании функции RPAD, или любой другой строковой функции,
      добавляющией пробелы в правую часть строки, в запросе, для которого MySQL
      будет создавать временные таблицы для его выполнения, все результирующие
      строки будут в результате обработаны RTRIM’ом (RTRIM’ed). Вот пример запроса:

      SELECT RPAD(t1.field1, 50, ' ') AS f2, RPAD(t2.field2, 50, '
      ') AS f1 FROM table1 as t1 LEFT JOIN table2 AS t2 ON
      t1.record=t2.joinID ORDER BY t2.record;

      В результате невозможно получить пробелы в правой части результирующего столбца.

      Такое поведение присутствует во всех версиях MySQL.

      Причина заключается в том, что HEAP-таблицы, которые в первую очередь используются для временных
      таблиц, неспособны работать с VARCHAR-столбцами.

      Такое поведение будет исправлено в одной из версий 4.1.

    • При использовании SET CHARACTER SET нельзя использовать
      преобразованные символы в именах базы данных, таблицы и столбца.

    • Нельзя использовать _ или % с ESCAPE в LIKE
      ... ESCAPE
      .

    • Если в столбце DECIMAL число хранится в различных форматах (+01.00,
      1.00, 01.00), то GROUP BY может рассматривать каждую величину как
      самостоятельную.

    • DELETE FROM merge_table при использовании без WHERE будет очищать
      только отображение для этой таблицы, а не удалять все данные в
      отображенных таблицах.

    • При использовании потоков MIT-pthreads нельзя расположить сервер в
      ином каталоге. Поскольку для решения данной проблемы требуются
      изменения для потоков MIT-pthreads, маловероятно, что мы ее устраним
      (see section 2.3.6 Замечания по потокам MIT-pthreads).

    • Не обеспечивается «надежное» применение величин BLOB в GROUP BY или
      ORDER BY или DISTINCT. В этих случаях при сравнении величин BLOB
      используются только первые max_sort_length байтов (по умолчанию 1024).
      Это можно изменить с помощью опции -O max_sort_length для mysqld.
      Обходной путь для большинства случаев заключается в использовании
      подстроки: SELECT DISTINCT LEFT(blob,2048) FROM tbl_name.

    • В сервере MySQL вычисления производятся в форматах типов BIGINT или
      DOUBLE (оба типа обычно имеют длину 64 бита). Это зависит от того,
      какую точность обеспечивает применяемая функция. Общее правило состоит
      в том, что битовые функции работают с точностью BIGINT, функции IF и
      ELT() — с точностью BIGINT или DOUBLE, а остальные — с точностью
      DOUBLE. Следует избегать применения беззнаковых величин двойной
      точности, если они могут превысить 63 бита (9223372036854775807), для
      каких-либо величин, кроме битовых полей! В версии сервера MySQL 4.0
      реализована лучшая обработка BIGINT, чем в 3.23.

    • Во всех строковых столбцах, исключая BLOB и TEXT при извлечении
      данных автоматически удаляются все концевые пробелы. Для типов CHAR
      это хорошо и может рассматриваться как свойство, соответствующее ANSI
      SQL92. Ошибка заключается в том, что в сервере MySQL столбцы VARCHAR
      трактуются тем же самым образом.

    • В одной таблице может быть только до 255 столбцов типа ENUM и SET.

    • В MIN(), MAX() и других групповых функциях, MySQL сейчас
      сравнивает ENUM и SET-столбцы по их строковому значению, а не по
      относительной позиции строки в множестве.

    • При указании параметра safe_mysqld все сообщения из mysqld
      направляются в журнал данного потока mysqld. Одна из проблем
      заключается в том, что если вы исполняете mysqladmin refresh для того,
      чтобы закрыть и заново открыть журнал, то stdout и stderr будут все
      еще по-прежнему направлены в старый журнал. При активном использовании
      --log следует отредактировать safe_mysqld, чтобы записи велись в
      «hostname`.err’, а не в «hostname`.log’, с тем, чтобы вы могли очищать
      пространство, удаляя старые журналы и вызывая mysqladmin refresh.

    • При выполнении команды UPDATE столбцы обновляются слева направо. При
      ссылке на обновленный столбец вместо исходной величины будет получена
      обновленная величина. Например:

      mysql> UPDATE tbl_name SET KEY=KEY+1,KEY=KEY+1;
      

      Эта команда обновит KEY со значением 2 вместо значения 1.

    • В одном и том же запросе нельзя использовать временные таблицы более
      чем один раз. Например, следующая команда выполнена не будет:

      mysql> SELECT * FROM temporary_table, temporary_table AS t2;
      
    • RENAME не работает с временными таблицами или при использовании
      таблицы MERGE.

    • Оптимизатор может обрабатывать DISTINCT по-разному, в зависимости от
      того, используются или нет в объединении «скрытые» столбцы. В
      объединении скрытые столбцы считаются частью результата (даже если они
      не показываются), в то время как в обычных запросах скрытые столбцы не
      участвуют в сравнении DISTINCT. Возможно, в будущем мы изменим это
      правило таким образом, чтобы при выполнении DISTINCT скрытые столбцы
      никогда не сравнивались. Например:

      SELECT DISTINCT mp3id FROM band_downloads
             WHERE userid = 9 ORDER BY id DESC;
      

      и

      SELECT DISTINCT band_downloads.mp3id
             FROM band_downloads,band_mp3
             WHERE band_downloads.userid = 9
             AND band_mp3.id = band_downloads.mp3id
             ORDER BY band_downloads.id DESC;
      

      Во втором случае в версии сервера MySQL 3.23.x можно получить две
      идентичных строки в результирующем наборе данных (поскольку скрытый
      столбец id может варьироваться). Заметьте, что это случается только с
      запросами, где в результате отсутствуют столбцы ORDER BY, что не
      разрешается делать в ANSI SQL.

    • Поскольку сервер MySQL обеспечивает возможность работать с типами
      таблиц, которые не поддерживают транзакции и, следовательно, не
      допускают отката данных, то поведение сервера MySQL в некоторых
      аспектах отличается от других серверов SQL. Это делается, чтобы
      гарантировать, что сервер MySQL никогда не будет нуждаться в откате
      SQL-команд. Такое поведение временами может быть несколько неудобным,
      так как вследствие этого величины столбцов должны проверяться в
      приложении. Однако благодаря такому решению вы получаете действительно
      хорошее увеличение скорости, поскольку для сервера MySQL
      обеспечивается возможность производить определенные оптимизации, что в
      противном случае было бы очень трудно сделать. При попытке установить
      столбец в некорректную величину сервер MySQL вместо выполнения отката
      будет сохранять в данном столбце «наиболее вероятную величину»:

      • При попытке сохранить в числовом столбце величину, выходящую за
        допустимые пределы, сервер MySQL вместо нее будет сохранять в
        этом столбце наименьшую или наибольшую допустимую величину.

      • При попытке сохранить в числовом столбце строку, которая
        начинается не с числа, сервер MySQL будет сохранять в нем 0.

      • При попытке сохранить NULL в числовом столбце, который не
        принимает значения величины NULL, сервер MySQL вместо NULL будет
        сохранять 0 или '' (пустая строка) (однако это поведение можно
        изменить с помощью опции компиляции -DDONT_USE_DEFAULT_FIELDS).

      • MySQL обеспечивает возможность сохранять некоторые ошибочные величины даты в
        столбцах DATE и DATETIME (такие как 2000-02-31 или 2000-02-00).
        Идея заключается в том, что задача проверки валидности даты не входит в круг
        обязанностей сервера баз данных. Если MySQL может сохранить и корректно
        воспроизвести какие-либо значение даты, пусть и неправильное — MySQL будет
        сохранять такое значение. Если дата полностью неправильна, сервер MySQL будет
        сохранять в этом столбце специальную величину даты 0000-00-00.

      • Если устанавливать столбец ENUM в неподдерживаемую величину, то
        он будет устанавливаться в значение ошибки empty string с
        числовым значением 0.

      • Если устанавливать столбец SET в неподдерживаемую величину, то
        эта величина будет игнорироваться.

    • При выполнении PROCEDURE на запросе, возвращающем пустой набор, в
      некоторых случаях PROCEDURE не будет преобразовывать столбцы.

    • При создании таблицы типа MERGE не происходит проверки, если лежащие в
      ее основе таблицы имеют совместимые типы.

    • Сервер MySQL пока еще не может обрабатывать величины NaN, -Inf и Inf с
      двойной точностью. Их использование вызовет проблемы при попытке
      экспорта или импорта данных. В качестве промежуточного решения мы
      должны изменить NaN на NULL (если возможно) и -Inf и Inf на
      минимальную или, соответственно, максимально возможную величину
      двойной точности.

    • LIMIT на отрицательных числах трактуются как большие положительные
      числа.

    • Если ALTER TABLE используется для того, чтобы сначала добавить индекс
      UNIQUE к таблице, которая входит в таблицу MERGE, а затем — чтобы
      добавить обычный индекс к таблице MERGE, то в случае, если существовал
      старый ключ, который не был уникальным в данной таблице, порядок
      ключей для таблиц будет различным. Это обусловлено тем, что ALTER
      TABLE
      помещает уникальные ключи перед обычными ключами, чтобы
      обеспечить возможность определять дублирующиеся ключи как можно
      раньше.

    В более ранних версиях MySQL известны следующие ошибки:

    • Можно получить зависший поток при выполнении DROP TABLE на таблице,
      которая является одной из числа многих таблиц, заблокированных с
      помощью LOCK TABLES.

    • В следующем случае может произойти аварийное завершение процесса:

      • Обработчик задержанных вставок имеет незаконченные вставки в
        таблицу.

      • LOCK table с помощью WRITE.

      • FLUSH TABLES.

    • В версиях сервера MySQL до 3.23.2 команда UPDATE, которая обновляла
      ключ с помощью WHERE на тот же самый ключ, может оказаться неудачной,
      поскольку данный ключ использовался для поиска записей и одна и та же
      строка может быть найдена много раз:

      UPDATE tbl_name SET KEY=KEY+1 WHERE KEY > 100;
      

      Обходным решением является использование:

      mysql> UPDATE tbl_name SET KEY=KEY+1 WHERE KEY+0 > 100;
      

      Эта команда будет работать, поскольку сервер MySQL не будет
      использовать индекс на выражениях в утверждении WHERE.

    • До версии сервера MySQL 3.23 все числовые типы трактовались как поля с
      фиксированной точкой. Это означает, что необходимо было указывать,
      сколько десятичных знаков должно содержать поле с плавающей точкой.
      Все результаты возвращались с правильным количеством десятичных
      знаков.

    В отношении ошибок, связанных со спецификой различных платформ, см. разделы
    о компилировании и переносе.

    1.10 MySQL и будущее (что предстоит сделать)

    В этом разделе приведен список возможностей, которые планируется
    реализовать в MySQL.

    Разработка приведенных в списке пунктов будет проходить примерно в порядке
    их перечисления. Если вы считаете, что это порядок следует изменить, то,
    пожалуйста, зарегистрируйте лицензию или окажите поддержку MySQL АВ и
    сообщите нам, что желательно было бы разработать побыстрее.
    See section 1.6 Лицензии и поддержка MySQL.

    Планируется, что в будущем стандарт ANSI SQL99 будет поддерживаться
    полностью, но с большим числом полезных расширений. Проблема заключается в
    том, чтобы сделать все намеченное, не жертвуя скоростью и без компромиссов
    при кодировании.

    1.10.1 Что планируется реализовать в версии в 4.0

    Уже все сделано. Сейчас мы только исправляем обнаруженные ошибки в MySQL 4.0. See section C.2 Изменения в версии 4.0.x (В разработке; Альфа).
    Сейчас ведется разработка версий 4.1 и 5.0.

    1.10.2 Что планируется реализовать в версии 4.1

    Следующие возможности планируются для реализации в MySQL 4.1.
    Список того, что уже сделано, можно найти в See section C.1 Изменения в версии 4.1.x (Alpha).

    • Стабильная поддержка OpenSSL (MySQL 4.0 поддерживает базовую, не 100%-протестированную
      поддержку OpenSSL).

    • Схема перекодировки и синтаксис для поддержки множества разных кодировок.

    • Встроенная помощь для всех команд из клиента командной строки.

    • Больше тестирования подготовленных выражений и множественных кодировок
      для одной таблицы.

    1.10.3 Что планируется реализовать в версии 5.0

    Перечисленные ниже функции планируется реализовать в MySQL 5.0.

    Отметим также, что поскольку у нас над новыми проектами работает большое
    количество разработчиков, появятся и дополнительные возможности.
    Существует — хотя и очень небольшая — вероятность, что эти возможности
    будут введены уже в MySQL 4.1.
    Список того, что уже сделано в 4.1, можно найти в See section C.1 Изменения в версии 4.1.x (Alpha).

    • Хранимые процедуры

    • Поддержка внешних ключей для всех типов таблиц.

    • Новый текстовый формат файлов определения структуры таблиц (`.frm’-файлы) и
      табличный кэш для определений таблиц. Это позволит нам быстрее получать информацию
      о структуре таблиц и более качественно поддерживать внешние ключи.

    • SHOW COLUMNS FROM table_name (используемый клиентом mysql для
      получения информации о столбцах) не должен открывать таблицы, а только
      файл определения структуры. Это занимает меньше памяти и будет работать быстрее.

    • Отказобезопасная репликация.

    • Резервное копирование без остановки работы системы с очень малыми
      затратами на выполнение. Резервное копирование без остановки работы
      системы позволит добавлять новый подчиненный сервер репликации без
      демонтажа головного сервера.

    • ROLLUP и CUBE OLAP (Online Analytical Processing) опции группировки для
      приложения хранения данных (Data warehousing).

    • Обеспечить возможность для DELETE, работающей с MyISAM-таблицами,
      использовать кэш записей. Чтобы осуществить это, нам необходимо
      обновлять кэш записей потока при обновлении `.MYD’-файла.

    • При использовании SET CHARACTER SET мы должны преобразовать весь запрос целиком, а не
      только строки. Это позволит пользователям использовать сконвертированные символы в
      именах баз данных, таблиц и столбцов.

    • Решить проблему, заключающуюся в том, что оператор RENAME TABLE,
      использующийся для активной MERGE-таблицы, может повредить таблицу.

    • Добавить к клиент-серверному протоколу возможность получения
      информации о прогрессе выполнения для сложных и длительных запросов.

    • Реализовать RENAME DATABASE. Чтобы сделать эту команду безопасной для
      всех обработчиков таблиц, она будет работать следующим образом:

      • Создавать новую базу данных.

      • Переименовывать каждую таблицу, помещаемую в новую базу данных, как
        это обычно делается командой RENAME.

      • Удалять старую базу данных.

      • Добавить корректную поддержку VARCHAR (такая поддержка уже имеется для
        MyISAM).

    • Оптимизировать тип BIT, чтобы он занимал 1 бит (сейчас 1 символ).

    • Новое изменение внутреннего файлового интерфейса. Это позволит сделать
      обработку всех файлов более однотипной и упростит добавление такой
      функциональности, как RAID (текущая реализация — это просто хак).

    • Улучшенные таблицы в памяти (HEAP):

      • Записи динамического размера

      • Более быстрая обработка строк (сокращение операций копирования)

    1.10.4 Что должно быть сделано в ближайшем будущем

    • Не разрешать более чем определенному количеству потоков одновременно
      заниматься восстановлением MyISAM-таблиц.

    • Изменение INSERT ... SELECT с целью оптимального использования
      одновременных вставок.

    • Возвращать истинные типы полей при выполнении SELECT MIN(столбец)
      GROUP BY
      .

    • Множественные результаты.

    • Сделать возможным задание long_query_time с градацией в микросекундах.

    • Cлинковать код myisampack прямо в сервер.

    • Перенос кода MySQL на QNX.

    • Перенос кода MySQL на BeOS.

    • Перенос MySQL-клиентов на LynxOS.

    • Добавление временного буферного кэша ключей во время выполнения
      INSERT/DELETE/UPDATE, чтобы обеспечить изящное восстановление в
      случае, если индексный файл окажется полностью заполненным.

    • Если выполняется работа ALTER TABLE над таблицей, которая имеет
      символическую ссылку на другой диск, создавать временные таблицы на
      этом диске.

    • Реализация типа DATE/DATETIME с корректной обработкой информации о
      временных зонах, чтобы упростить работу с форматом даты для различных
      временных зон.

    • FreeBSD и MIT-pthreads; отнимают ли спящие потоки время процессора?

    • Проверить, занимают ли блокированные потоки время процессора.

    • Исправить configure так, чтобы можно было компилировать все библиотеки
      (подобно MyISAM) без потоков.

    • Добавить опцию периодического сброса на диск страниц ключей для таблиц
      с запрещенными, в случае, если они некоторое время не использовались.

    • Возможность связывания по частям ключа (проблема оптимизации).

    • INSERT SQL_CONCURRENT и mysqld --concurrent-insert для выполнения
      одновременной вставки в конец файла, если файл закрыт для чтения.

    • Серверные курсоры.

    • Проверить, работает ли lockd с современными ядрами Linux; если нет, то
      внести исправления в lockd! Чтобы это протестировать, необходимо
      запустить mysqld с --enable-locking и выполнить различные наборы
      тестов на fork*. Они не должны выявить никаких ошибок, если lockd
      работает.

    • Возможность использования SQL-переменных в LIMIT, как, например, в
      LIMIT @a,@b.

    • Возможность обновления переменных в операторах UPDATE. Например:
      UPDATE TABLE foo SET @a=a+b,a=@a, b=@a+c.

    • Изменение: если пользовательские переменные обновляются, то сделать
      так, чтобы их можно было использовать с GROUP BY, как в следующем
      примере: SELECT id, @a:=COUNT(*), SUM(sum_col)/@a FROM table_name
      GROUP BY id
      .

    • Запретить автоматическое внесение DEFAULT-значений (значений по
      умолчанию) в столбцы. Выдавать ошибку в случае использования INSERT,
      не содержащего столбца, для которого не определено значение по
      умолчанию.

    • Исправить libmysql.c так, чтобы две команды mysql_query(), идущие
      подряд, могли работать без чтения результатов или с выдачей хорошего
      сообщения об ошибке, если это все-таки происходит.

    • Проверка, почему функция ctime() потоков MIT-pthreads не работает на
      некоторых FreeBSD системах.

    • Добавление опции IMAGE опции к LOAD DATA INFILE, чтобы не обновлять
      поля TIMESTAMP и AUTO_INCREMENT.

    • Добавляемый синтаксис LOAD DATE INFILE ... UPDATE.

    • Для таблиц с первичными ключами: если данные содержат первичный ключ,
      строки, соответствующие этому первичному ключу, обновляются остальными
      столбцами. Столбцы, которых нет во входных данных, остаются без
      изменений.

    • Для таблиц с первичными ключами, для которых отсутствует некоторая
      часть ключа во входном потоке данных или которые не имеют первичного
      ключа, входные данные интерпретируются сейчас как LOAD DATA INFILE ...
      REPLACE INTO
      .

    • Сделать понятный синтаксис LOAD DATA INFILE, подобно следующему:

      LOAD DATA INFILE 'file_name.txt' INTO TABLE tbl_name
           TEXT_FIELDS (text_field1, text_field2, text_field3)
           SET table_field1=CONCAT(text_field1, text_field2),
               table_field3=23
           IGNORE text_field3
      

      Такой синтаксис может быть использован для пропуска лишних столбцов в
      текстовом файле или для обновления столбцов на основе выражений,
      построенных по прочитанным данным.

    • LOAD DATA INFILE 'file_name' INTO TABLE 'table_name' ERRORS TO
      err_table_name
      . Этот оператор задает запись всех ошибок и
      предупреждений в таблицу err_table_name, которая будет иметь
      структуру, подобную следующей:

      line_number   - номер строки в файле данных
      error_message - сообщение об ошибке/предупреждение
        и, возможно
      data_line     - строка из файла данных
      
    • Автоматический вывод из mysql в Netscape.

    • LOCK DATABASES (с различными опциями.)

    • Функции: ADD_TO_SET(value,set) и REMOVE_FROM_SET(value,set).

    • Добавить использование t1 JOIN t2 ON ... и t1 JOIN t2 USING ... В
      данное время можно использовать этот синтаксис только с LEFT JOIN.

    • Намного большее количество переменных для SHOW STATUS. Фиксирование
      операций чтения и обновления. Выборки по 1 таблице и выборки по
      связям. Среднее число таблиц в выборке. Большое число запросов ORDER
      BY
      и GROUP BY.

    • Если вы прерываете выполнение запроса в середине, необходимо открыть
      другое соединение и убить старый выполнявшийся запрос. В свою очередь,
      сервер должен распознавать это на своей стороне.

    • Добавить интерфейс обработчика для табличных данных таким образом,
      чтобы была возможность использовать его как системную таблицу. Это
      будет работать несколько медленно в случае запрашивания информации обо
      всех таблицах, но очень гибко. Должен быть реализован SHOW INFO FROM
      tbl_name
      для основных данных о таблицах.

    • Сделать возможным SELECT a FROM crash_me LEFT JOIN crash_me2 USING
      (a)
      ; в данном случае подразумевается, что a будет браться из
      таблицы crash_me.

    • Oracle-подобный CONNECT BY PRIOR ... для изучения иерархических
      структур.

    • mysqladmin copy database new-database; требуется добавить команду COPY
      в mysqld.

    • Список процессов должен показывать количество запросов/потоков.

    • SHOW HOSTS для распечатки информации о кэше имен хостов.

    • Опции DELETE и REPLACE для оператора UPDATE (оператор с этими опциями
      будет удалять строки при получении ошибки дублирующихся ключей во
      время обновления).

    • Изменить формат DATETIME, чтобы сохранять порции в секундах.

    • Добавить все недостающие типы ANSI92 и ODBC 3.0.

    • Изменить имена таблиц с пустых строк на NULL для вычисляемых столбцов.

    • Не использовать Item_copy_string для числовых значений во избежание
      преобразований число->строка->число в случае: SELECT COUNT(*)*(id+0)
      FROM table_name GROUP BY id

    • Сделать возможным использование новой библиотеки GNU regexp вместо
      текущей (библиотека GNU должна быть намного быстрее, чем предыдущая).

    • Сделать так, чтобы ALTER TABLE не срывал работу INSERT DELAYED.

    • Сделать следующее исправление: если на столбцы есть ссылки в выражении
      UPDATE, они будут содержать значения, хранившиеся там до запуска
      процесса обновления.

    • Добавить эмуляцию pread()/pwrite() под Windows, чтобы сделать
      возможными одновременные вставки.

    • Разработать анализатор файла журнала для анализа и выдачи информации о
      том, какие таблицы используются наиболее часто, насколько часто
      выполняются мультитабличные связи и т.д. Это помогло бы пользователям
      выбирать такую конструкцию таблиц и областей, которая могла бы быть
      оптимизирована для выполнения наиболее эффективных запросов.

    • Добавить SUM(DISTINCT).

    • Добавить групповые функции ANY(), EVERY() и SOME(). В ANSI SQL эти
      функции работают только с булевыми столбцами, но мы можем расширить
      эти функции, чтобы они работали с любыми столбцами/выражениями,
      применив: value == 0 -> FALSE и value <> 0 -> TRUE.

    • Добиться, чтобы тип для MAX(column) был таким же как и тип столбцов:

      mysql> CREATE TABLE t1 (a DATE);
      mysql> INSERT INTO t1 VALUES (NOW());
      mysql> CREATE TABLE t2 SELECT MAX(a) FROM t1;
      mysql> SHOW COLUMNS FROM t2;
      
    • Придумать хороший синтаксис для оператора, который будет выполнять UPDATE
      над строкой при наличии таковой, и INSERT новой строки, если строка
      отсутствует (подобно тому, как REPLACE работает с INSERT / DELETE).

    1.10.5 То, что надо сделать когда-нибудь

    • Реализовать функцию: get_changed_tables(timeout,table1,table2,...).

    • Изменить чтение таблиц так, чтобы везде, где возможно. использовалась
      memmap. Сейчас memmap используется только для уплотненных таблиц.

    • Сделать лучше автоматический код временных меток (timestamp).
      Добавлять временные метки в журнал обновлений при помощи SET
      TIMESTAMP=#;
      .

    • Использовать в некоторых местах семафор чтения/записи для увеличения
      скорости.

    • Обеспечить полную поддержку внешних ключей в MyISAM-таблицах
      (возможно, после реализации хранимых процедур с триггерами).

    • Подготовить простые обзоры (сначала по одной таблице, позднее по
      любому выражению).

    • Реализовать автоматическое закрытие некоторых таблиц, если таблица,
      временная таблица или временные файлы получат ошибку 23 (недостаточно
      открытых файлов).

    • Если обнаружится поле=#, заменить все местонахождения поля на #.
      Сейчас такое делается только для некоторых простых случаев.

    • Заменить все константные выражения вычисляемыми, если возможно.

    • Реализовать оптимизацию ключ=выражение. К данному моменту делается
      оптимизация только для ключ=поле или ключ=константа.

    • Связывать некоторые функции копирования для улучшения кода.

    • Заменить sql_yacc.yy внутритекстовым синтаксическим анализатором,
      чтобы уменьшить ее размер и получать лучшие сообщения об ошибке (5
      дней).

    • Изменить собственный синтаксический анализатор так, чтобы он
      использовал только одно правило для различного количества аргументов в
      функции.

    • Использовать полные вычисляемые имена в части сортировки (для
      ACCESS97).

    • MINUS, INTERSECT и FULL OUTER JOIN (в настоящее время поддерживаются
      UNION [в 4.0] и LEFT OUTER JOIN).

    • SQL_OPTION MAX_SELECT_TIME=#, чтобы устанавливать ограничения по
      времени для запроса.

    • Сделать так, чтобы обновляемый журнал записывался в базу данных..

    • Сделать добавления в LIMIT, чтобы можно было делать восстановление
      данных с конца результирующего набора.

    • Сигналы предупреждений для функций соединения/чтения/записи клиента.

    • Необходимо обратить внимание на изменения на safe_mysqld; согласно
      FSSTND (которому пытается следовать Debian) PID-файлы должны
      помещаться в `/var/run/<progname>.pid’, a файлы журналов — в `/var/log’.
      Было бы хорошо, если бы было можно поместить «`DATADIR’» в первое
      объявление «`pidfile’» и «`log’», чтобы местоположение этих файлов можно было
      изменить одним оператором.

    • Разрешать клиенту запрашивать ведение журналов.

    • Добавить использование zlib() для gzip-файлов в LOAD DATA INFILE.

    • Исправить сортировку и группирование BLOB-столбцов (сейчас проблема
      частично решена).

    • Хранимые процедуры. Рассматриваются также триггеры.

    • Простой (атомарный) язык обновления, который может быть использован
      для написания циклов и т.п. в MySQL-сервере.

    • Произвести изменения для того, чтобы пользоваться семафорами при
      подсчете потоков. Но сначала нужно реализовать библиотеку семафоров
      для потоков MIT-pthreads.

    • Не устанавливать новое значение во время установки колонки в 0. вместо
      этого использовать NULL.

    • Добавить полную поддержку круглых скобок для JOIN.

    • В качестве альтернативы одному потоку/соединению управлять пулом
      потоков при обработке запросов.

    • Обеспечить возможность получать более чем одну блокировку при помощи
      GET_LOCK. Когда это будет реализовано, потребуется еще сделать
      обработку возможных тупиковых ситуаций, которые привнесет данное
      изменение.

    Время отводится согласно объемам работ, а не реальному времени.

    1.10.6 То, чего не планируется делать

    • Такого не существует; мы стремимся к полной совместимости с ANSI
      92/ANSI 99.

    1.11 Сравнение MySQL с другими СУБД

    Наши пользователи провели исследование скорости работы нескольких обычных
    серверов баз данных и серверов баз данных с открытым кодом. Нам известно о
    проводившихся сравнениях MySQL с сервером Oracle, сервером DB/2, Microsoft
    SQL Server и другими коммерческими программными продуктами. Однако по
    причинам юридического характера опубликовать результаты некоторых из этих
    сравнений в документации не представляется возможным.

    В данном разделе приведены результаты сравнения с mSQL (проводившегося по
    историческим причинам) и с PostgreSQL (так как эта СУБД также
    распространяется как ПО с открытым кодом). Если у вас имеются результаты
    подобного тестирования, которые мы могли бы опубликовать, просьба
    связаться с нами по адресу benchmarks@mysql.com.

    Сравнения всех имеющихся функций и типов а также данные о пределах
    возможностей различных СУБД вы найдете на веб-странице crash-me,
    расположенной по адресу http://www.mysql.com/information/crash-me.php.

    1.11.1 Сравнение MySQL и mSQL

    Производительность
    Точные результаты сравнения скорости работы можно найти в постоянно
    пополняющейся библиотеке проведенных тестов по MySQL (see section 5.1.4 Набор тестов MySQL (The MySQL Benchmark Suite)).

    СУБД mSQL, благодаря отсутствию затрат дополнительных
    ресурсов на создание потоков, а также за счет компактности синтаксического
    анализатора, небольшого количества функций и упрощенной системы
    безопасности, должна выигрывать в скорости выполнения:

    • тестов на выполнение циклов соединение-отсоединение, при каждом
      соединении выполняющих какой-нибудь простой запрос

    • операций INSERT над простыми таблицами, содержащими небольшое
      количество столбцов и ключей

    • CREATE TABLE и DROP TABLE

    • операций SELECT чего-нибудь, кроме индексов (очень просто выполняется
      просмотр таблицы)

    Поскольку такие операции очень просты, при больших затратах ресурсов на
    начальном этапе выиграть в скорости их выполнения достаточно сложно.
    Поэтому лучшие результаты MySQL может показать лишь после установки
    соединения. С другой стороны, MySQL значительно превосходит mSQL (и
    большинство других реализаций SQL) при:

    • выполнении сложных операций SELECT.

    • загрузке объемных результатов (протокол, применяющийся в MySQL,
      превосходит другие по качеству, скорости и безопасности).

    • работе с таблицами, имеющими строки переменной длины, так как
      обработка данных в MySQL реализована более эффективно и в нем
      допускается создание индексов для столбцов с типом VARCHAR.

    • обработке таблиц, содержащих большое количество столбцов.

    • обработке таблиц с длинными записями.

    • выполнении операций SELECT с несколькими выражениями.

    • выполнении операций SELECT над объемными таблицами.

    • одновременной работе с несколькими соединениями. Архитектура MySQL
      Server является полностью многопоточной. Для каждого соединения
      создается отдельный поток и, таким образом, ни одному из них не
      приходится ожидать завершения другого (если, конечно, один из потоков
      не занимается изменением таблицы, доступ к которой требуется другому
      потоку). В mSQL же после установки одного соединения остальным
      приходится ожидать его завершения, вне зависимости от сложности и
      времени выполнения примененного в этом соединении запроса. По
      завершении первого соединения начинает обслуживаться второе, а все
      остальные снова ждут своей очереди.

    • связывании таблиц. При изменении порядка таблиц в вызове SELECT,
      скорость работы mSQL может упасть ниже всяких допустимых пределов. При
      выполнении комплекта тестов производительности выполнение такой
      операции заняло в 15000 раз больше времени, чем у MySQL.
      Причиной столь плачевно низкой производительности является отсутствие
      в mSQL оптимизатора связей, который обеспечивал бы оптимальность
      используемого порядка соединения таблиц. Однако если в mSQL2
      расположить таблицы в правильном порядке, не перегружать оператор
      WHERE и использовать индексные столбцы, связывание будет выполнено
      относительно быстро! (see section 5.1.4 Набор тестов MySQL (The MySQL Benchmark Suite)).

    • ORDER BY и GROUP BY.

    • DISTINCT.

    • работе со столбцами с типами TEXT или BLOB.

    Возможности SQL
    • GROUP BY и HAVING.

      В mSQL функция GROUP BY отсутствует вовсе. В MySQL
      Server же GROUP BY имеется и работает как с HAVING, так и со следующими
      функциями: COUNT(), AVG(), MIN(), MAX(), SUM()и STD().
      Работа оператора
      COUNT(*) оптимизирована в расчете на быстрый возврат результатов, если
      оператор SELECT берет данные из одной таблицы, не используя никаких других
      столбцов и выражения WHERE. Функции MIN() и MAX() могут принимать
      строковые аргументы.

    • INSERT и UPDATE с вычислениями. MySQL может выполнять
      вычисления непосредственно в теле вызова INSERT или UPDATE. Вот
      пример:

      mysql> UPDATE SET x=x*10+y WHERE x<20;
      
    • Псевдонимы. В MySQL имеется возможность определения псевдонимов
      столбцов.

    • Квалификация имен столбцов. В MySQL, при условии уникальности
      имени столбца среди таблиц, использующихся при выполнении запроса, нет
      необходимости приводить его полный квалификатор.

    • SELECT с функциями. Функций в MySQL много (даже слишком много,
      чтобы их можно было перечислить здесь; see section 6.3 Функции, используемые в операторах SELECT и WHERE).

    Эффективность использования дискового пространства
    Можно ли уменьшить таблицы, и если да, то насколько? В MySQL
    имеются очень точные типы данных, а с их помощью можно создавать таблицы,
    занимающие минимум пространства. Примером чрезвычайно полезного типа
    данных MySQL может служить MEDIUMINT, длина значений которого составляет 3
    байта. При наличии 100 миллионов записей значение экономии даже одного
    байта на каждой из них трудно переоценить. Выбор типов столбцов в mSQL2
    значительно беднее, и поэтому снизить размеры таблиц заметно трудней.
    Стабильность
    Объективно оценить этот параметр трудно. Подробно вопрос стабильности
    MySQL рассмотрен в разделе section 1.4.3 Насколько стабильным является MySQL?. Данных и опыта,
    позволяющих судить о стабильности mSQL, у нас не имеется.
    Стоимость
    Цена лицензии является немаловажным фактором. По гибкости лицензии MySQL
    Server превосходит mSQL, да и стоит меньше. Вне зависимости от того, какой
    из продуктов вы выберете, не забудьте принять во внимание стоимость
    лицензии или технической поддержки по электронной почте.
    Perl-интерфейсы
    Perl-интерфейсы MySQL практически идентичны своим аналогам из mSQL,
    хотя и обладают некоторыми дополнительными возможностями.
    JDBC (Java)
    В настоящее время для MySQL разработано много разнообразных
    JDBC-драйверов:

    • MySQL Connector/J — родной драйвер для Java.
      Версия 3.x выпускается под двойным лицензированием (GPL и коммерческая лицензия).

    • Драйвер Resin: коммерческий JDBC-драйвер, распространяющийся как ПО с
      открытым кодом. http://www.caucho.com/projects/jdbc-mysql/index.xtp

    • Драйвер gwe: Java-интерфейс, разработанный компанией GWE technologies
      (более не поддерживается).

    • Драйвер jms: улучшенная версия драйвера gwe, разработанная Кельвином
      Заоку Жу (Xiaokun Kelvin ZHU, X.Zhu@brad.ac.uk) (более не
      поддерживается).

    • Драйвер twz: JDBC-драйвер типа 4, разработанный Терренсом В. Зеллерсом
      (Terrence W. Zellers zellert@voicenet.com). Это коммерческий продукт,
      но в частном порядке и для образовательных целей им можно пользоваться
      бесплатно (более не поддерживается).

    Рекомендуется использование драйвера mm. Драйвер Resin тоже, возможно,
    неплох (по крайней мере, результаты тестов выглядят хорошо), но
    достаточного количества информации по нему у нас пока нет. Мы знаем, что в
    mSQL имеется JDBC-драйвер, но для сравнения у нас не хватает опыта работы
    с ним.

    Скорость разработки
    Основная команда разработчиков MySQL немногочисленна, но мы
    привыкли писать код на C и C++ очень быстро. Так как потоки, функции,
    оператор GROUP BY и т.п. в mSQL все еще не реализованы, этой системе еще
    долго придется догонять нас. Более точное представление о положении вещей
    вы сможете получить, прочитав файл `HISTORY’ от mSQL за последний год, и
    сравнив его с разделом новостей (News) MySQL Reference Manual (see section C История изменений и обновлений MySQL).
    После этого сомнений относительно того, какая
    система развивалась быстрее, остаться не должно.
    Инструментальные программы
    Как для mSQL, так и для MySQL сторонними разработчиками было
    создано множество интересных инструментальных средств. Поскольку перенос
    программ из mSQL в MySQL сложностей не представляет, почти все
    интересные приложения, разработанные первоначально для mSQL,имеются и в
    вариантах для MySQL. В комплект поставки MySQL входит
    простая программа msql2mysql, исправляющая различия в написании наиболее
    популярных функций C API между mSQL и MySQL. Вызовы функции
    msqlConnect(), например, она заменяет на mysql_connect(). Обычно для
    перевода клиентской программы из mSQL в MySQL оказывается
    достаточно минимальных усилий.

    1.11.1.1 Как конвертировать инструментальные средства mSQL в MySQL

    Согласно нашему опыту, переделать такие инструментальные программы, как
    msql-tcl и msqljava, созданные на основе C API mSQL для работы с C API
    MySQL, несложно.

    Сделать это можно так:

    1. Пропустите исходный файл через сценарий оболочки msql2mysql. Для этого
      необходима программа replace, распространяющаяся вместе с MySQL
      Server.

    2. Откомпилируйте.

    3. Исправьте все найденные компилятором ошибки.

    Различия между реализациями C API в mSQL и MySQL заключаются в следующем:

    • В качестве типа в MySQL используется структура MYSQL (в mSQL в этом
      качестве применяется int).

    • Оператор mysql_connect() принимает в качестве параметра указатель на
      структуру MYSQL. Такую структуру можно легко объявить как глобальную
      или создать ее с помощью malloc(). Кроме того, mysql_connect()
      принимает еще два параметра, в которых указываются имя пользователя и
      его пароль. Для использования данной структуры по умолчанию этим
      параметрам нужно присвоить значения NULL, NULL.

    • mysql_error() принимает в качестве параметра структуру MYSQL. При
      переносе старого кода достаточно добавить параметр в вызов
      msql_error().

    • Для всех ошибок MySQL возвращает номер ошибки и текстовое
      сообщение. mSQL же возвращает только текстовое сообщение об ошибке.

    • Существует некоторая несовместимость, обусловленная тем, что в MySQL
      Server можно создавать несколько соединений с сервером из одного и того же
      процесса.

    1.11.1.2 Различия в клиент-серверных коммуникационных протоколах mSQL и MySQL

    Различий здесь имеется более чем достаточно для того, чтобы поддержку
    обоих протоколов одновременно обеспечить было невозможно (или, по меньшей
    мере, очень сложно).

    Ниже приведены наиболее заметные различия между коммуникационными
    протоколами MySQL и mSQL:

    • В буфере сообщения может находиться несколько столбцов результатов.

    • Буферы сообщений динамически увеличиваются, если запрос или результат
      не умещаются в их текущем объеме, ограничение на объем буфера
      устанавливается сервером и клиентом.

    • Все пакеты нумеруются, что позволяет обнаружить среди них
      повторяющиеся или пропуски.

    • Все значения столбцов отправляются в виде ASCII. Длины строк и
      столбцов посылаются в упакованном виде в двоичном виде (1, 2 или 3
      байта).

    • MySQL может считывать результаты без буферизации (без необходимости
      сохранения всех данных в клиенте).

    • Если одна операция считывания/записи занимает более 30 секунд, сервер
      закрывает соединение.

    • Если соединение бездействует в течение 8 часов, сервер его закрывает.

    1.11.1.3 Различия в синтаксисе SQL между mSQL 2.0 и MySQL

    Типы столбцов

    MySQL
    Имеются следующие дополнительные типы (не считая остальных; see section 6.5.3 Синтаксис оператора CREATE TABLE):

    • ENUM — тип для одного набора строк.

    • SET — тип для нескольких наборов строк.

    • BIGINT — тип для 64-битовых целых чисел.

    Кроме того, MySQL поддерживает следующие атрибуты дополнительных
    типов:

    • UNSIGNED — опция для целочисленных столбцов и столбцов чисел с
      плавающей запятой.

    • ZEROFILL — опция для целочисленных столбцов.

    • AUTO_INCREMENT — опция для целочисленных столбцов, являющихся
      первичными ключами. See section 8.4.3.31 mysql_insert_id().

    • DEFAULT — значение для всех столбцов.

    mSQL2
    Типы столбцов в mSQL соответствуют приведенным в таблице типам MySQL:

    Тип в mSQL Соответствующий тип в MySQL
    CHAR(len) CHAR(len)
    TEXT(len) TEXT(len). len — максимальная длина. Работает LIKE.
    INT INT. Со множеством опций!
    REAL REAL. Или FLOAT. Имеются как 4-битовые, так и 8-битовые варианты.
    UINT INT UNSIGNED
    DATE DATE. Использует формат ANSI SQL, а не собственный формат mSQL.
    TIME TIME
    MONEY DECIMAL(12,2). Значение с фиксированной точкой и двумя знаками после нее.

    Создание индексов

    MySQL
    Индексы могут указываться во время создания таблицы при помощи оператора
    CREATE TABLE.
    mSQL
    Индексы создаются после создания таблицы с помощью операторов CREATE
    INDEX
    .

    Вставка уникального идентификатора в таблицу

    MySQL
    Для указания типа столбца достаточно использовать AUTO_INCREMENT. See section 8.4.3.31 mysql_insert_id().
    mSQL
    Необходимо создать в таблице SEQUENCE и выбрать столбец _seq.

    Получение уникального идентификатора для строки

    MySQL
    Следует добавить к таблице первичный или уникальный ключ и использовать
    его. Новое в версии 3.23.11: если ключ PRIMARY или UNIQUE состоит только
    из одного целочисленного столбца, к нему можно обращаться и как к _rowid.
    mSQL
    Следует использовать столбец _rowid. Нельзя забывать о том, что _rowid
    может, в зависимости от множества факторов, со временем измениться.

    Получение времени последнего изменения столбца

    MySQL
    Нужно вставить в таблицу столбец TIMESTAMP. Этому столбцу автоматически
    присваиваются текущая дата и время при вызове операторов INSERT или
    UPDATE, если ему не присвоить определенного значения или присвоить
    значение NULL.
    mSQL
    Следует использовать столбец _timestamp.

    Сравнение значений NULL

    MySQL
    MySQL соответствует стандарту ANSI SQL, поэтому сравнение с NULL
    всегда возвращает результат NULL.
    mSQL
    В mSQL выражение NULL = NULL имеет значение TRUE. Поэтому при переводе
    старого кода из mSQL в MySQL =NULL необходимо заменить на IS NULL,
    а <>NULL — на IS NOT NULL.

    Сравнение строк

    MySQL
    Обычно сравнение строк проводится без учета регистра символов, с порядком
    сортировки, который определяется текущим набором символов (ISO-8859-1
    Latin1 по умолчанию). Если вам это не подходит, необходимо установить при
    объявлении столбцов атрибут BINARY, тогда сравнение будет проводиться в
    соответствии с ASCII-порядком, установленным на сервере MySQL.
    mSQL
    Все сравнения строк проводятся с учетом регистра символов в ASCII-порядке
    сортировки.

    Поиск без учета регистра символов

    MySQL
    LIKE может быть как чувствительным, так и нечувствительным к регистру
    оператором, в зависимости от столбцов, к которым он применяется. По
    возможности MySQL использует индексы, если аргумент LIKE не начинается с
    шаблонного символа.
    mSQL
    Следует использовать CLIKE.

    Обработка концевых пробелов

    MySQL
    Все пробелы в конце столбцов CHAR и VARCHAR удаляются. Если такое
    поведение нежелательно, используйте столбцы TEXT.
    mSQL
    Концевые пробелы сохраняются.

    Операторы WHERE

    MySQL
    MySQL правильно определяет приоритеты действий (AND имеет приоритет перед
    OR). Заставить MySQL вести себя так, как mSQL, можно при помощи
    скобок (как можно видеть в соответствующем примере).
    mSQL
    Все действия производятся слева направо. А это значит, что некоторые
    логические вычисления, в которых наличествует более трех аргументов, не
    могут быть выполнены вообще. Кроме того, это означает, что при переносе в
    MySQL некоторые запросы необходимо менять. Это довольно просто
    сделать при помощи скобок. Возьмем, к примеру, следующий запрос mSQL:

    mysql> SELECT * FROM table WHERE a=1 AND b=2 OR a=3 AND b=4;
    

    Чтобы MySQL вычислил результат этого запроса так же, как это сделал
    бы mSQL, нужно расставить скобки:

    mysql> SELECT * FROM table WHERE (a=1 AND (b=2 OR (a=3 AND (b=4))));
    

    Ограничения доступа

    MySQL
    Для хранения привилегий для каждого пользователя, удаленного компьютера и
    базы имеются соответствующие таблицы. See section 4.2.6 Как работает система привилегий.
    mSQL
    Имеется файл `mSQL.acl’, в котором можно определить привилегии
    чтения/записи для пользователей.

    1.11.2 Сравнение MySQL c PostgreSQL

    Читая этот раздел, помните о том, что оба программных продукта находятся в
    постоянном развитии. Мы (разработчики MySQL) и разработчики PostgreSQL
    постоянно заняты улучшением наших СУБД, поэтому обе системы являются
    серьезными альтернативами любым коммерческим СУБД.

    Приведенное ниже сравнение проводилось в MySQL AB. Мы старались быть как
    можно более точными и объективными, однако, зная MySQL наизусть, мы
    не можем похвастаться таким же знанием возможностей PostgreSQL, поэтому в
    чем-то могли и ошибиться. Однако мы будем тут же исправлять все замеченные
    неточности.

    Прежде всего хотелось бы отметить, что PostgreSQL и MySQL являются широко
    используемыми программными продуктами, которые разрабатывались с разными
    целями (хотя создатели обоих и стремятся довести их до полной
    совместимости со стандартом ANSI SQL). Это значит, что для решения одних
    задач больше подходит MySQL, для других же — PostgreSQL. Выбирая
    СУБД, проверьте, соответствуют ли ее возможности требованиям,
    предъявляемым решаемой задачей. Если требуется максимальная скорость
    работы, лучше всего, вероятно, будет остановить свой выбор на MySQL
    Server. Если же вам необходимы дополнительные возможности, имеющиеся
    только у PostgreSQL, этой СУБД и стоит пользоваться.

    1.11.2.1 Стратегии развития MySQL и PostgreSQL

    Расширяя возможности MySQL мы всегда стараемся принять оптимальное
    решение. Код должен быть настолько хорош, чтобы в обозримом будущем в нем
    не понадобилось ничего менять. Кроме того, мы не считаем целесообразным
    ради расширения возможностей приносить в жертву скорость, а вместо этого
    стараемся в каждом случае обеспечить максимально возможную
    производительность. В итоге несколько увеличивается время разработки, но
    результаты того стоят. Разработка в таком ключе возможна потому, что все
    новые фрагменты серверного кода перед включением в состав MySQL
    проверяются несколькими разработчиками.

    Мы считаем, что лучшим средством предоставления нашим пользователям новых
    возможностей является частый выпуск новых версий. Поэтому примерно раз в
    три недели у нас выходит несколько обновленная версия системы, а раз в
    год — полностью новая. Все версии проходят полное тестирование с помощью
    наших инструментальных средств на множестве платформ.

    Система PostgreSQL основана на ядре, созданном множеством разработчиков. В
    подобных случаях разумно сосредоточиться на оснащении системы новыми
    возможностями, но не заниматься оптимальным их воплощением, так как в
    случае возникновения необходимости всегда можно будет вернуться к
    оптимизации соответствующих участков кода.

    Еще одно значительное отличие MySQL от PostgreSQL заключается в
    том, что практически весь содержащийся в MySQL код создан разработчиками,
    работающими в MySQL AB и постоянно занятыми совершенствованием кода
    сервера. Исключением из этого правила являются системы транзакций и
    библиотека регулярных выражений regexp.

    Большая же часть кода PostgreSQL написана множеством разработчиков, никак
    друг с другом не связанных. Не так давно разработчики PostgreSQL объявили
    о том, что у их команды наконец-то хватило времени на просмотр всего кода,
    вошедшего в состав очередной версии PostgreSQL.

    У обоих вышеупомянутых методов разработки есть достоинства и недостатки.
    Мы, сотрудники MySQL AB, разумеется, считаем, что наша модель лучше, так
    как обеспечивает большую логичность кода, оптимальность и возможность его
    повторного использования, а также — меньшее количество ошибок. Будучи
    авторами кода сервера MySQL, мы с большим успехом можем координировать
    включение в систему новых возможностей и выход ее новых версий.

    1.11.2.2 Сравнение возможностей MySQL и PostgreSQL

    На странице crash-me (http://www.mysql.com/information/crash-me.php)
    приведен список ограничений и особенностей СУБД, которые могут быть
    обнаружены автоматически с помощью специальных программ. Однако не стоит
    забывать о том, что многие ограничения могут быть изменены настройкой
    соответствующих баз данных. Впрочем, эта web-страница оказывается очень
    кстати, если необходимо, чтобы создаваемое приложение нормально работало с
    несколькими СУБД или для перевода приложения с одной СУБД в другую.

    MySQL обладает следующими преимуществами перед PostgreSQL:

    • MySQL обычно намного превосходит PostgreSQL по скорости работы. Кроме
      того, в MySQL 4.0 реализован кэш запросов. Он позволяет во много раз
      увеличить скорость обработки запросов для сайтов, на которых
      преобладают неоднократно повторяющиеся запросы на чтение.

    • По количеству пользователей MySQL также намного превосходит
      PostgreSQL. Поэтому код тестируется значительно более придирчиво и
      опытным путем доказана большая его надежность, нежели у PostgreSQL.
      MySQL чаще, чем PostgreSQL, используется на производстве, в
      основном потому, что компания MySQL AB (ранее — TCX DataKonsult AB)
      предоставляет высококачественную коммерческую техническую поддержку
      MySQL с момента появления этой системы на рынке, а у PostgreSQL
      до самого последнего времени никакой поддержки не было.

    • MySQL работает в среде Windows лучше, чем PostgreSQL. MySQL
      Server запускается как настоящее (родное) Windows-приложение (в
      NT/2000/XP — сервис), в то время как PostgreSQL запускается в среде
      эмуляции, Cygwin. Нам доводилось слышать о недостаточной стабильности
      работы PostgreSQL в среде Windows, но самостоятельно эти сведения до
      сих пор мы проверить не могли.

    • MySQL оснащен большим количеством API для других языков и
      поддерживается большим количеством существующих программ, нежели
      PostgreSQL.

    • MySQL работает на высоконадежных промышленных системах 24/7
      (включенных 24 часа в сутки 7 дней в неделю). В большинстве случаев
      никаких «чисток» в MySQL производить не требуется. PostgreSQL же
      пока что не может работать в таких системах, так как иногда приходится
      запускать VACUUM для освобождения занятого последствиями работы команд
      UPDATE и DELETE пространства и проводить статистический анализ,
      необходимый для достижения максимальной производительности PostgreSQL.
      Запускать VACUUM необходимо и после каждого добавления к таблице
      нескольких столбцов. На напряженно работающих системах VACUUM нужно
      запускать более часто, в худших случаях — по несколько раз в день. А
      ведь во время работы VACUUM (а ее работа может продолжаться часы, если
      база данных достаточно велика) база практически «мертва». Впрочем, в
      PostgreSQL версии 7.2 выполнение основных функций этой программы
      больше не приводит к блокировке базы, и пользователи могут продолжать
      нормально работать с ней. Новая команда VACUUM FULL берется за дело
      более серьезно: она, как и в старых версиях, блокирует таблицу и
      сжимает копию таблицы на диске.

    • Репликация MySQL отлично протестирована и используется в таких сайтах,
      как:

      • Yahoo Finance (http://finance.yahoo.com/)

      • Mobile.de (http://www.mobile.de/)

      • Slashdot (http://www.slashdot.org/)

    • В комплект поставки MySQL входят два тестовых пакета, mysql-test-run и
      crash-me (http://www.mysql.com/information/crash-me.php), а также
      пакет для замеров производительности. Тестовая система постоянно
      обновляется, в нее добавляется код для тестирования всех новых
      возможностей и почти всех воспроизводимых ошибок, которые попали в
      поле нашего зрения. Перед выпуском каждой новой версии мы используем
      эти пакеты для тестирования MySQL на нескольких платформах.
      Наши тесты значительно превосходят по своим возможностям все
      существующие в PostgreSQL аналоги, и обеспечивают высокое качество
      кода MySQL.

    • Книг о MySQL вышло значительно больше, нежели о PostgreSQL.
      Книги о MySQL выпустили издательства O’Reilly, SAMS, Que и New Riders.
      Все возможности MySQL детально описаны в документации, так как это
      является обязательным условием включения новых возможностей в код.

    • MySQL поддерживает больше стандартных функций ODBC, чем
      PostgreSQL.

    • MySQL обладает значительно более мощной реализацией ALTER
      TABLE
      .

    • В MySQL предусмотрена возможность создания таблиц без
      транзакций, что необходимо приложениям, требующим максимально
      возможной скорости работы. Эти таблицы могут храниться в памяти,
      относиться к типу HEAP-таблиц или дисковых MyISAM. See section 7 Типы таблиц MySQL.

    • MySQL может работать с двумя поддерживающими транзакции
      обработчиками таблиц, а именно — InnoDB и BerkeleyDB. Так как все
      системы поддержки транзакций в разных условиях работают по-разному,
      это дает разработчику возможность найти наилучшее решение для условий,
      в которых будет работать его система. See section 7 Типы таблиц MySQL.

    • Команда слияния таблиц MERGE предоставляет в ваше распоряжение
      уникальную возможность создать представление нескольких идентичных
      таблиц и работать с ними как с одной. Это особенно удобно для работы с
      журналами, разбитыми, например, по месяцам. See section 7.2 Таблицы MERGE.

    • Возможность сжатия доступных только для чтения таблиц, не отменяющая
      прямого доступа к их записям, повышает производительность системы,
      снижая количество операций считывания с диска. Это особенно полезно
      при архивировании. See section 4.7.4 myisampack, MySQL-генератор сжатых таблиц (только для чтения).

    • В MySQL реализован полнотекстовый поиск. See section 6.8 Полнотекстовый поиск в MySQL.

    • Имеется возможность работы с несколькими базами через одно соединение
      (разумеется, в зависимости от привилегий пользователя).

    • Система MySQL с самого начала разрабатывалась в расчете на
      многопоточность, а PostgreSQL использует процессы. Переключение
      контекстов и доступ к общим данным несколькими потоками осуществляется
      значительно быстрее, нежели отдельными процессами. Таким образом MySQL
      Server в многопользовательских приложениях получает неплохое
      преимущество в производительности, а кроме того, таким образом MySQL
      Server удается значительно эффективней пользоваться преимуществами,
      предоставляемыми симметричными мультипроцессорными системами (SMP).

    • В MySQL реализована значительно более мощная система
      привилегий, нежели в PostgreSQL. В то время как PostgreSQL
      обеспечивает лишь привилегии INSERT, SELECT и UPDATE/DELETE над базой
      или таблицей, MySQL предоставляет возможность определения
      полного набора разнообразных привилегий на уровне базы, таблицы и
      столбца. Кроме того, MySQL позволяет задавать привилегии для
      комбинаций хост/пользователь. See section 4.3.1 Синтаксис команд GRANT и REVOKE.

    • В MySQL используется протокол связи между клиентом и сервером
      со сжатием данных, что увеличивает производительность системы в
      условиях низкоскоростных каналов связи.

    • Насколько нам известно, только в реляционной системе баз данных MySQL
      Server используется концепция «обработчика таблиц». Благодаря этому
      создается возможность работы с различными низкоуровневыми типами
      таблиц из ядра MySQL, причем каждая таблица может быть оптимизирована
      для различных характеристик производительности.

    • Все типы таблиц в MySQL (кроме InnoDB) реализованы в виде файлов (по
      одной таблице в файле), что значительно облегчает создание резервных
      копий, перенос, удаление и даже создание символьных ссылок между
      базами данных и таблицами, даже если сервер находится в нерабочем
      состоянии.

    • Наличие утилиты для восстановления и оптимизации таблиц MyISAM
      (наиболее распространенного типа таблиц в MySQL). Ее использование
      требуется только в случае физического повреждения файла данных
      (например, в результате аппаратного сбоя). Позволяет восстановить
      большую часть данных.

    • Обновление (апгрейд) MySQL проходит совершенно «безболезненно». При
      модернизации MySQL нет нужды в сохранении/восстановлении
      данных, что приходится делать при установке большинства обновлений
      PostgreSQL.

    Недостатки MySQL по сравнению с PostgreSQL:

    • Поддержка транзакций в MySQL пока что не настолько хорошо
      проверена, как в системе PostgreSQL.

    • Так как MySQL основан на использовании потоков (threads), пока
      что еще не безошибочно работающих в некоторых ОС, для обеспечения
      стабильной работы приходится либо использовать один из
      откомпилированных пакетов, доступных по адресу
      http://www.mysql.com/downloads/, либо точно выполнять содержащиеся в
      section 2.3 Установка исходного дистрибутива MySQL инструкции.

    • Блокировка таблиц, применяющаяся в нетранзакционных таблицах MyISAM,
      во многих случаях работает быстрее, нежели блокировки на уровне
      страниц, строк или контроль версий. Недостаток этого подхода в том,
      что если не учитывать механизм работы блокирования таблиц, один
      длительный запрос может надолго заблокировать таблицу. Обычно этого
      эффекта можно избежать, приняв соответствующие меры при разработке
      приложения. Если это не удастся, всегда можно изменить тип таблицы и
      сделать ее транзакционной. See section 5.3.2 Вопросы блокирования таблиц.

    • При помощи UDF (user-defined functions, определяемые пользователем
      функции) возможности MySQL можно расширить и дополнить обычными
      SQL-функциями или их объединениями. Но это сделать не так просто, да и
      система не настолько гибка в этом отношении, как PostgreSQL. See section 9.2 Добавление новых функций в MySQL.

    • В MySQL сложнее организовывались обновления, затрагивающие
      несколько таблиц сразу. Впрочем, это было исправлено в MySQL 4.0.2
      реализацией многотабличного UPDATE и в MySQL 4.1 — с помощью
      подзапросов. В MySQL 4.0 можно одновременно удалять данные из
      нескольких таблиц. See section 6.4.6 Синтаксис оператора DELETE.

    Ниже перечислены преимущества PostgreSQL по сравнению с MySQL на
    сегодняшний день.

    Так как нам известен план разработки новых версий MySQL, мы включили в
    приведенную ниже таблицу версии MySQL, в которых будет реализована
    поддержка соответствующих возможностей. К сожалению, в сопоставлении мы
    сделать этого не могли, так как план разработки PostgreSQL нам неизвестен.

    Возможность Версия MySQL
    Подзапросы 4.1
    Внешние ключи 5.0 (3.23 с InnoDB)
    Представления 5.0
    Хранимые процедуры 5.0
    Триггеры 5.0
    Объединения 4.0
    Полные связи 4.1
    Ограничения 4.1 или 5.0
    Курсоры 4.1 или 5.0
    R-деревья 4.1 (для таблиц MyISAM)
    Наследование таблиц Не планируется
    Расширяемая система типов Не планируется

    Другие причины, по которым можно предпочесть PostgreSQL:

    • В некоторых случаях PostgreSQL оказывается ближе к ANSI SQL.

    • Работу PostgreSQL можно ускорить, выполняя код в виде хранимых
      процедур.

    • При хранении географических данных R-деревья дают PostgreSQL
      преимущество перед MySQL (примечание: в MySQL версии 4.1 для
      таблиц MyISAM реализована поддержка R-деревьев).

    • Оптимизатор PostgreSQL в некоторых случаях способен дать лучший в
      сравнении с существующим на сегодняшний день оптимизатором MySQL
      результат. Особенно это заметно при слиянии таблиц без соответствующих
      ключей или при слиянии с использованием разных ключей в сочетании с
      логическим оператором OR. Набор результатов тестов скорости MySQL,
      расположенный по адресу
      http://www.mysql.com/information/benchmarks.html покажет, каких
      конструкций следует избегать при работе с различными базами данных.

    • Команда разработчиков PostgreSQL, пишущих код для сервера, больше.

    Недостатки PostgreSQL по сравнению с MySQL:

    • VACUUM затрудняет использование PostgreSQL в постоянно работающих
      системах.

    • Наличие только транзакционных таблиц.

    • Значительно более медленная работа команд INSERT, DELETE и UPDATE.

    Полный список недостатков приведен в первой таблице настоящего раздела.

    1.11.2.3 Тестирование скорости работы MySQL и PostgreSQL

    Единственная тестовая система с открытым кодом, способная тестировать
    скорость работы как MySQL Server, так и PostgreSQL (а также других СУБД),
    о существовании которой нам известно, — наша собственная разработка. Ее
    можно найти по адресу http://www.mysql.com/information/benchmarks.html.

    Мы много раз просили разработчиков PostgreSQL и некоторых пользователей
    PostgreSQL помочь нам расширить эту систему и превратить ее в совершенный
    инструмент тестирования скорости работы СУБД, но, к сожалению,
    безрезультатно.

    По этой причине разработчики MySQL потратили много времени, пытаясь выжать
    (для тестирования) из PostgreSQL все возможное, но из-за недостаточно
    совершенного знания PostgreSQL мы наверняка что-то упустили. На страничке
    тестов производительности проведенное нами исследование полностью
    задокументировано, так что любой может легко повторить его и проверить
    результаты.

    Обычно тесты запускаются без ключа --fast. При запуске с этим ключом
    используются все трюки, позволяющие заставить сервер работать как можно
    быстрее. Идея состоит в том, чтобы в обычном режиме тест показал, как
    сервер будет работать при настройках по умолчанию, в режиме --fast, — как
    производительность изменится, если разработчик приложения воспользуется
    расширениями сервера для ускорения его работы.

    При тестировании PostgreSQL в режиме --fast мы запускаем VACUUM после
    каждой операции UPDATE и DROP TABLE, чтобы обеспечить отличное состояние
    базы для последующих операторов SELECT. Время, уходящее на работу VACUUM,
    измеряется отдельно.

    Однако при тестировании PostgreSQL 7.1.1 мы не смогли запустить программу
    в режиме --fast, так как во время теста INSERT, postmaster (демон
    PostgreSQL) дал сбой и база данных была повреждена настолько, что
    перезапустить демон не удалось. Когда это случилось дважды, мы решили
    отложить тестирование в режиме --fast до выхода следующей версии
    PostgreSQL. Подробную информацию о компьютере, на котором выполнялись
    тесты, вы найдете на странице тестов.

    Перед тем как переходить к описанию других известных нам тестов, мы дадим
    побольше информации о таких тестах вообще.

    Нет ничего проще, чем написать тест, доказывающий, что любая СУБД является
    лучшей в мире, для этого достаточно ограничиться измерением результатов
    операций, с которыми эта СУБД справляется хорошо, и деликатно забыть об
    остальных. А если еще после этого выдать обобщенный график, то все
    становится еще проще.

    Это то же самое, если бы мы решили сравнить скорость работы MySQL Server
    и PostgreSQL просто сравнив общие результаты тестов MySQL, приведенных на
    нашей странице. По таким результатам MySQL Server оказался бы более чем в
    40 раз быстрее PostgreSQL, а это, конечно, неверно. Можно было бы подлить
    масла в огонь, избрав для тестирования PostgreSQL тесты, на которые эта
    система тратит больше всего времени, — и утверждать что MySQL Server
    более чем в 2000 раз быстрее PostgreSQL.

    Дело тут в том, что MySQL оптимизирует многое из того, чего не
    оптимизирует PostgreSQL. И наоборот. SQL-оптимизатор вообще очень сложная
    штука, и можно потратить годы исключительно на его улучшение.

    Исследуя результаты тестов, нужно искать задачи, которые должно выполнять
    ваше приложение, и результаты их выполнения использовать для принятия
    решения о выборе СУБД. Результаты тестов позволяют определить и задачи, с
    решением которых выбранная СУБД справляется не слишком хорошо, — таким
    образом можно определить, чего следует избегать и обходить в своих
    программах.

    Нам известно о двух тестах, утверждающих, что PostgreSQL по
    производительности превосходит MySQL Server. Оба они —
    многопользовательские, а у сотрудников MySQL AB пока что не нашлось
    времени написать такой тест. Основная причина — довольно сложно сделать
    это так, чтобы не поставить ни одну из СУБД в заведомо проигрышное
    положение.

    Один из этих тестов был заказан компанией Great Bridge, которая в течение
    16 месяцев пыталась построить бизнес на основе PostgreSQL, но в конце
    концов прекратила свою деятельность. Вероятно, это худший из когда-либо
    проводившихся кем-либо тестов. Он не только принимает во внимание лишь те
    области, в которых PostgreSQL оказывается «на коне», но и ставит
    абсолютно все остальные СУБД в заведомо проигрышное положение.

    Примечание: Нам стало известно о том, что даже некоторым ключевым
    разработчикам PostgreSQL отнюдь не понравилось то, как фирма Great Bridge
    проводила свое тестирование, так что команду разработчиков PostgreSQL мы в
    связи с этим тестом ни в чем не обвиняем.

    Упомянутый тест осуждался во многих сообщениях и телеконференциях, так что
    мы просто коротко перечислим его недостатки.

    • Тесты выполнялись дорогой коммерческой программой, что сделало задачу
      проверки их результатов невыполнимой для Open Source-компании,
      каковой мы являемся; мы даже не можем выяснить, как же на самом деле
      проводились эти тесты. Причем сама программа, даже не является
      специализированной утилитой для тестирования производительности — она
      представляет из себя универсальный инструмент для настройки и
      тестирования приложений. Называть ее «стандартным» тестом — это уж
      слишком большая натяжка.

    • Компания Great Bridge признала, что база данных PostgreSQL была
      оптимизирована (а перед проведением теста была запущена программа
      VACUUM) и специально настроена для проведения тестов, чего не делалось
      ни для одной другой СУБД, участвовавшей в тестировании. Они заявили
      буквально следующее: «Этот процесс оптимизирует индексы и
      высвобождает немного дискового пространства. Оптимизированные индексы
      в некоторой степени повышают производительность.» Проведенные нами
      тесты недвусмысленно свидетельствуют о том, что разница в скорости
      выполнения большого количества выборок (SELECT’ов) из базы данных,
      прошедшей чистку VACUUM, и не прошедшей ее, может быть десятикратной.

    • Результаты тестов тоже вызывают сомнения. В тестовой документации
      AS3AP упоминается о том, что при тестировании выполняются «выборки,
      простые связи, проекции, аггрегация, обновления с одним набором
      значений для атрибута и массовые обновления».

      PostgreSQL отлично выполняет операции SELECT и JOIN (особенно после
      VACUUM), но отнюдь не здорово справляется с INSERT или UPDATE. А
      результаты показывают, что выполнялись только операции SELECT (или
      операций обновления выполнялось очень мало). Это правдоподобно объясняет
      отличные результаты, показанные PostgreSQL в данном тесте. А причина, по
      которой MySQL показал неважные результаты, станет понятной несколько ниже.

    • Так называемый тест запускался с компьютера, работающего под
      управлением Windows и связывавшегося с Linux через ODBC, а такая
      конфигурация никакому нормальному пользователю СУБД, особенно при
      работе с многопользовательским приложением, даже в голову не придет. В
      этом случае тестируется не СУБД, а ODBC-драйвер и Windows-протокол,
      связывающие клиентов.

    • При связи базы с Oracle и MS-SQL (Great Bridge косвенно указала на
      примененные в тесте СУБД) использовался не соответствующий им
      протокол, а ODBC. Любой, кто когда-либо имел дело с Oracle, знает, что
      во всех настоящих приложениях используется не ODBC, а «родной»
      интерфейс. Проводить тесты через ODBC и утверждать, что они имеют
      какое-либо отношение к использованию СУБД в реальной работе, попросту
      нечестно. Нужно было провести два теста, с ODBC и без него, чтобы
      получить истинные факты (причем все СУБД перед проведением тестов
      должны были бы настраиваться специалистами).

    • Представители компании постоянно говорят о TPC-C тестах, но нигде не
      упоминают о том, что проведенный ими тест вовсе не относился к TPC-C,
      а проводить TPC-C они просто не имели права. TPC-C может проводиться
      только по правилам, утвержденным комитетом TPC Council
      (http://www.tpc.org/). Great Bridge в комитет не обращалась. Не
      сделав этого, она не только вступила в конфликт с торговой маркой TPC,
      но и дискредитировала свои тесты. Установленные комитетом TPC Council
      правила очень строги, чтобы никто не мог выдавать ложных результатов
      или делать недоказуемые заявления. Очевидно, у Great Bridge не было
      желания следовать этим правилам.

    • После проведения первого теста мы связались с Great Bridge и сообщили
      ее представителям о наиболее очевидных из допущенных при тестировании
      MySQL Server ошибок:

      • Использование отладочной версии нашего ODBC-драйвера

      • Запуск сервера под управлением ОС Linux, не оптимизированной для
        работы с потоками.

      • Использование старой версии MySQL, хотя уже существовала более
        современная

      • Отказ от запуска MySQL Server с соответствующими настройками для
        работы с большим количеством пользователей (по умолчанию после
        установки MySQL Server настраивается для минимального использования
        ресурсов)

      Great Bridge провела новый тест, с нашим оптимизированным драйвером ODBC
      и лучшей настройкой MySQL Server, но отказалась применить обновленную
      библиотеку glibc или нашу стандартную бинарную поставку (ее используют 80%
      наших пользователей), которая была статически слинкована с конкретной
      glibc. Насколько нам известно, компания Great Bridge не сделала абсолютно
      ничего для правильной настройки других СУБД при тестировании. Впрочем, мы
      уверены, что в Oracle или Microsoft они за советом не обращались. ;)

    • Тестирование было оплачено Great Bridge, и эта компания решила
      опубликовать не все результаты, а только выборочно — те, что были ей
      выгодны.

    Тим Пердью (Tim Perdue), преданный обожатель PostgreSQL и не слишком
    большой любитель MySQL, опубликовал свое сравнение на сайте PHPbuilder
    (http://www.phpbuilder.com/columns/tim20001112.php3).

    Узнав об этом, мы связались с Тимом по телефону с тем, чтобы обсудить
    некоторые странности в полученных им результатах. Он, например, утверждал,
    что MySQL Server в его тестах с трудом справлялся с обслуживанием пяти
    пользователей, хотя нам были известны пользователи с примерно такими же,
    как у Тима, компьютерами, работающие с MySQL Server при 2000 активных
    соединениях, выдающих до 400 запросов в секунду (причем в данном случае
    производительность ограничивалась каналом связи с web, а не базой данных).

    Похоже было, что Тим работал с ядром Linux, не умеющим нормально
    обрабатывать большое количество потоков, как, например, ядра до версии
    2.4, у которых возникали серьезные проблемы с обработкой большого
    количества потоков на мультипроцессорных компьютерах. В настоящем
    руководстве мы описали решение этой проблемы, и Тим должен был об этом
    знать.

    Существует еще одна возможная причина возникновения проблем: Тим мог
    воспользоваться старой библиотекой glibc или не загрузить
    откомпилированный исполняемый файл MySQL (слинкованный с исправленной
    библиотекой glibc) с нашего сайта, но скомпилировал его сам. В любом из
    этих случаев симптомы были бы именно такими, как описал Тим.

    Мы спросили Тима, может ли он предоставить нам его данные с тем, чтобы мы
    могли повторить тест, а также попросили его проверить свою версию MySQL и
    сообщить нам. Пока что он этого не сделал.

    Из за этого его тесту мы тоже не можем доверять. :(

    Все течет, все изменяется, и старые тесты теряют актуальность. Теперь
    MySQL обзавелся парой новых обработчиков таблиц, которые
    обеспечивают совершенно разные характеристики в разрезе
    скорости/параллелизма. See section 7 Типы таблиц MySQL. Было бы интересно
    узнать, какие результаты показали бы вышеупомянутые тесты с разными типами
    транзакционных таблиц в MySQL. Конечно, в PostgreSQL с тех пор тоже
    появились новые возможности. Так как эти тесты недоступны общественности,
    выяснить, как СУБД показала бы себя в них сегодня, мы не можем.

    Заключение:

    Единственные существующие на сегодня тесты, позволяющие сравнить MySQL
    Server и PostgreSQL, которые любой может загрузить и запустить, — это
    тесты из комплекта MySQL. Мы, сотрудники MySQL AB, считаем, что Open
    Source-СУБД должны тестироваться при помощи Open Source-инструментов!
    Только так можно получить гарантии того, что никто не сможет провести
    невоспроизводимые тесты и на основе их результатов утверждать, что одна
    СУБД лучше другой. Не зная всех фактов, подтвердить или опровергнуть
    подобные утверждения просто невозможно.

    Нам кажется странным, что невоспроизводимые тесты с участием PostgreSQL
    показывают преимущество этой системы, в то время как наши тесты, которые
    может повторить любой, четко доказывают обратное. Этим мы вовсе не хотим
    сказать, что PostgreSQL не годится для решения многих задач (еще как
    годится!) или что при определенных условиях эта система не обгонит MySQL
    Server. Но нам просто хотелось бы для разнообразия увидеть честный тест, в
    котором PostgreSQL покажет отличные результаты, — исключительно с целью
    поддержания товарищеской конкуренции!

    Дополнительную информацию о нашей тестовой системе вы можете получить из
    раздела section 5.1.4 Набор тестов MySQL (The MySQL Benchmark Suite).

    Мы занимаемся разработкой еще лучшего комплекса тестов, в состав которого
    будут включены многопользовательские тесты, а также более подробное
    описание того, что собственно делает каждый отдельный тест и как расширить
    комплекс.


    Go to the first, previous, next, last section, table of contents.


    Обратная связь
    Информация для авторов
    Rambler's Top100 TopList liveinternet.ru: показано число просмотров за 24 часа, посетителей за 24 часа и за сегодня This Web server launched on February 24, 1997
    Copyright © 1997-2000 CIT, © 2001-2019 CIT Forum

    Внимание! Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав. Подробнее…

    Содержание

    • 1 Многопоточность в Java
      • 1.1 Программирование многопоточного сокета в Java
      • 1.2 Многопоточная программа Socket сервера в Java
      • 1.3 Клиентская программа сервера
      • 1.4 Клиентская программа
      • 1.5 Как запустить эту программу?

    Все разработчики знакомы с написанием последовательных программ, каждая последовательная программа имеет начало, последовательность выполнения и конец. Поток — это один последовательный поток управления внутри программы. Это независимый путь выполнения программного кода. Большинство программ, написанных сегодня, выполняются как один поток, что вызывает проблемы при одновременном возникновении нескольких событий или действий. Когда выполняется несколько потоков, путь одного потока через один и тот же код обычно отличается от других. Каждый поток в Java создается и контролируется классом Java.lang.Thread.

    Существует два способа создания потока в Java;

    1. Внедрить интерфейс Runnable (Java.lang.Runnable)
    2. Расширением класса Thread (Java.lang.Thread)

    Многопоточность в Java

    Многопоточность в Java — это процесс одновременного выполнения нескольких потоков. Многопоточная программа содержит два или более процесса, которые могут выполняться одновременно, и каждый процесс может обрабатывать другую задачу, одновременно обеспечивая оптимальное использование доступных ресурсов, особенно когда ваш компьютер имеет несколько процессоров. Процесс выполнения нескольких потоков одновременно известен как многопоточность.

    Программирование многопоточного сокета в Java

    В предыдущем примере мы уже видели, как Single Socket Socket Program . В этом случае только один клиент может связываться с сервером. Он не позволит одновременных подключений клиентов. Попробуйте запустить другого клиента. Вы увидите, что второй клиент не может быть подключен, пока первый клиент не закроет его соединение. Чтобы разрешить одновременные соединения, мы должны знать многопоточное программирование. Здесь, в следующем многопоточном программировании сокета, вы можете подключить более одного клиента к серверу и обмениваться данными.

    Для каждого клиентского соединения сервер запускает дочерний поток для обработки запроса независимо от любых других входящих запросов.

     
    Socket serverClient=server.accept();
    ServerClientThread sct = new ServerClientThread(serverClient,counter);
    

    ServerClientThread — это новый класс, который расширяет класс Thread. Здесь вы можете видеть, а не обрабатывать входящие запросы в том же потоке, который принимает клиентское соединение, соединение передается клиентскому потоку, который обрабатывает запрос. Таким образом, поток, прослушивающий следующие входящие запросы, тратит как можно больше времени на вызов serverSocket.accept(). Таким образом, риск сводится к минимуму, поскольку клиентам отказывают в доступе к серверу, потому что поток прослушивания не находится внутри вызова accept(). Здесь клиентский поток фактически выполняет запрос. Тем временем сервер может принимать несколько клиентских запросов и запускать обработку. Поэтому отдельные потоки будут запущены, и они будут работать параллельно. В этом примере клиент отправляет номер на сервер и в ответ на каждый клиент сервер отправляет квадрат полученного номера.

    Многопоточная программа Socket сервера в Java

     
    import Java.net.*;
    import Java.io.*;
    public class MultithreadedSocketServer {
      public static void main(String[] args) throws Exception {
        try{
          ServerSocket server=new ServerSocket(8888);
          int counter=0;
          System.out.println("Server Started ....");
          while(true){
            counter++;
            Socket serverClient=server.accept();  // сервер принимает запрос на подключение клиента
            System.out.println(" >> " + "Client No:" + counter + " started!");
            ServerClientThread sct = new ServerClientThread(serverClient,counter); // отправляем запрос в отдельный поток
            sct.start();
          }
        }catch(Exception e){
          System.out.println(e);
        }
      }
    }
    

    Клиентская программа сервера

    Этот класс потока клиентов сервера обрабатывал запрос независимо от любых других входящих запросов. Следующая программа Java является частью программы Multithreaded Server Socket.

     
    class ServerClientThread extends Thread {
      Socket serverClient;
      int clientNo;
      int squre;
      ServerClientThread(Socket inSocket,int counter){
        serverClient = inSocket;
        clientNo=counter;
      }
      public void run(){
        try{
          DataInputStream inStream = new DataInputStream(serverClient.getInputStream());
          DataOutputStream outStream = new DataOutputStream(serverClient.getOutputStream());
          String clientMessage="", serverMessage="";
          while(!clientMessage.equals("bye")){
            clientMessage=inStream.readUTF();
            System.out.println("From Client-" +clientNo+ ": Number is :"+clientMessage);
            squre = Integer.parseInt(clientMessage) * Integer.parseInt(clientMessage);
            serverMessage="From Server to Client-" + clientNo + " Square of " + clientMessage + " is " +squre;
            outStream.writeUTF(serverMessage);
            outStream.flush();
          }
          inStream.close();
          outStream.close();
          serverClient.close();
        }catch(Exception ex){
          System.out.println(ex);
        }finally{
          System.out.println("Client -" + clientNo + " exit!! ");
        }
      }
    }
    

    Клиентская программа

    Это настоящая клиентская программа, запрашивающая подключение к серверу. Для каждого клиента вам нужно открыть отдельное окно консоли для запуска клиентской программы.

     
    import Java.net.*;
    import Java.io.*;
    public class TCPClient {
      public static void main(String[] args) throws Exception {
      try{
        Socket socket=new Socket("127.0.0.1",8888);
        DataInputStream inStream=new DataInputStream(socket.getInputStream());
        DataOutputStream outStream=new DataOutputStream(socket.getOutputStream());
        BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
        String clientMessage="",serverMessage="";
        while(!clientMessage.equals("bye")){
          System.out.println("Enter number :");
          clientMessage=br.readLine();
          outStream.writeUTF(clientMessage);
          outStream.flush();
          serverMessage=inStream.readUTF();
          System.out.println(serverMessage);
        }
        outStream.close();
        outStream.close();
        socket.close();
      }catch(Exception e){
        System.out.println(e);
      }
      }
    }
    

    Как запустить эту программу?

    Следующим шагом является запуск программы Java TCPClient Socket Program на том же компьютере или других компьютерах в той же сети. Когда вы запускаете клиентскую программу, она установит соединение с сервером и ожидает ввода с клиентской стороны. Клиентская программа неоднократно запрашивает у пользователя ввод целого числа, отправляет на сервер и получает квадрат целого с сервера. Если вы хотите протестировать несколько клиентов, для каждого клиента вам нужно открыть отдельное окно консоли для запуска клиентской программы. Когда клиент отправляет «bye» со стороны клиента, сервер закрывает соединение с клиентом. На следующем изображении вы можете увидеть, как сервер и несколько клиентов обмениваются данными с сервером.

    Если ваша программа «Сервер и клиент» работает на одном компьютере, дайте «127.0.0.1».

     
    Socket socket=new Socket("127.0.0.1",8888);
    

    В противном случае укажите IP-адрес устройства, на котором запущен MultithreadedSocketServer.

    Источник: http://net-informations.com/Java/net/multithreaded.htm

    Пишу многопоточный сервер и почему то не могу получить ответ от сервера в консоль . В качестве клиента использую curl -X POST -d «temperature=18» http://localhost:9000/queue/weather
    Вроде бы данные попадают в out но не получаю ответа от сервера . Все переменные правильные смотрел в дебаге. В простом сервере без пула все отрабатывает верно

    import java.io.*;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.nio.charset.StandardCharsets;
    import java.util.Arrays;
    import java.util.HashMap;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    
        public class Server {
        
            private final HashMap<String, Service> modes = new HashMap<>();
        
            public void start() {
                modes.put("queue", new QueueService());
                modes.put("topic", new TopicService());
                ExecutorService pool = Executors.newFixedThreadPool(
                        Runtime.getRuntime().availableProcessors()
                );
                try (ServerSocket server = new ServerSocket(9000)) {
                    while (!server.isClosed()) {
                        Socket socket = server.accept();
                        pool.execute(() -> {
                            try (OutputStream out = socket.getOutputStream();
                                 InputStream input = socket.getInputStream()) {
                                byte[] buff = new byte[1_000_000];
                                var total = input.read(buff);
                                var text = new String(Arrays.copyOfRange(buff, 0, total), StandardCharsets.UTF_8);
                                var req = Req.of(text);
                                var resp = modes.get(req.method()).process(req);
                                var temp = ("HTTP/1.1 " + resp.status() + " OKrn");
                                out.write(temp.getBytes());
                                var temp2 = resp.text();
                                out.write(temp2.getBytes());
                                out.flush();
                                System.out.println("hi");
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        });
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        
            public static void main(String[] args) {
                new Server().start();
            }
        }
    
    public class Req {
    
        private final String method;
        private final String mode;
        private final String text;
    
        private Req(String method, String mode, String text) {
            this.method = method;
            this.mode = mode;
            this.text = text;
        }
    
        public static Req of(String content) {
            /* TODO parse a content */
            String[] array = content.split("rn");
            String[] temp = array[0].split("/");
            String tempMethod = temp[1];
            String[] temp2 = temp[2].split(" ");
            String tempMode = temp2[0];
            return new Req(tempMethod, tempMode, array[7]);
        }
    
        public String method() {
            return method;
        }
    
        public String mode() {
            return mode;
        }
    
        public String text() {
            return text;
        }
    }
    
    
    public class Resp {
    
        private final String text;
        private final int status;
    
        public Resp(String text, int status) {
            this.text = text;
            this.status = status;
        }
    
        public String text() {
            return text;
        }
    
        public int status() {
            return status;
        }
    }
    
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.concurrent.ConcurrentLinkedQueue;
    import java.util.concurrent.atomic.AtomicInteger;
    
    public class QueueService implements Service {
    
        private  ConcurrentHashMap<String, ConcurrentLinkedQueue<String>> queue = new ConcurrentHashMap<>();
        private AtomicInteger id = new AtomicInteger();
    
        @Override
        public Resp process(Req req) {
            queue.putIfAbsent(req.mode(), new ConcurrentLinkedQueue<>());
            queue.get(req.mode()).add(req.text());
            var result = queue.get(req.mode()).poll();
            return new Resp(result, id.incrementAndGet());
        }
    }
    

    задан 15 июн 2021 в 6:24

    Terasan's user avatar

    TerasanTerasan

    1591 золотой знак1 серебряный знак10 бронзовых знаков

    4

    У вас неправильно реализован протокол HTTP. Из ответа HTTP должен быть способ узнать его длину.

    Нужно либо вернуть заголовок Content-Length — в этом случае клиент закончит обработку запроса по получению указаного количества байт.

    Либо нужно вернуть заголовок Connection: close, тогда клиент определяет конец сообщения по закрытому соединению.

    Кроме того, между заголовками и телом должны идти переводы строки.

    Вот так вот приблизительно:

    Resp resp = modes.get(req.method()).process(req);
    String temp = ("HTTP/1.1 " + resp.status() + " OKn");
    out.write(temp.getBytes());
    String header = "Connection: close";
    out.write(header.getBytes(StandardCharsets.UTF_8));
    out.write("rnrn".getBytes());
    String temp2 = resp.text();
    out.write(temp2.getBytes());
    out.flush();
    
    

    ответ дан 15 июн 2021 в 8:08

    Roman-Stop RU aggression in UA's user avatar

    3

    Возможно, вам также будет интересно:

  • Сервер мдлп не отвечает ошибка получения данных от сервера мдлп
  • Сервер выдает ошибку 502
  • Сервер выдает ошибку 403
  • Сервер возвратил непредвиденную ошибку 110 при попытке установки пакета 1с
  • Сервер вернул ошибку при отправке транзакции electrum

  • Понравилась статья? Поделить с друзьями:
    0 0 голоса
    Рейтинг статьи
    Подписаться
    Уведомить о
    guest

    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии