SSLPreProcessor под большой нагрузкой

Один из наших старых проектов использует образец SSLPreProcessor от Grizzly 1.9 для связи SSL. К сожалению это SSLPreProcessor Класс кажется ненадежным при высокой нагрузке, в то время как наше приложение записывает в поток SSL из потока (не Grizzly) таймера. (Стресс-тесты не указывают на какие-либо проблемы со связью без SSL.)

Некоторое исследование ниже, TL;DR находится в конце вопроса.

Мы нашли возможную ошибку вокруг fromSelectionKey() метод, который используется таким образом:

    if (CustomProtocolServer.this.protocol == Controller.Protocol.TLS) {
        AsyncQueueDataProcessor preProcessor =
                SSLPreProcessor.fromSelectionKey(ctx.getSelectionKey());
        ctx.getAsyncQueueWritable().writeToAsyncQueue(b, callback, preProcessor);
    } else {
        ctx.getAsyncQueueWritable().writeToAsyncQueue(b, callback);
    }

Подобные структуры существуют и в официальных примерах, например, в классе CustomProtocolServer.

когда fromSelectionKey() возвращается null writeToAsyncQueue() называется с nullpreProcessor параметр, который на самом деле совпадает с else блок выше. Если я прав, это портит поток SSL и вызывает нестабильность.

fromSelectionKey()является следующим:

public static SSLPreProcessor fromSelectionKey(SelectionKey key) {
    Object attachmentObj = key.attachment();
    if(!(attachmentObj instanceof ThreadAttachment)) {
        CustomProtocolHelper.log("SelectionKey : "+key+ " without SSLEngine. Maybe key is not ready yet.");
        return null;
    }
    ThreadAttachment attachment = (ThreadAttachment) attachmentObj;
    SSLEngine sslEngine = attachment.getSSLEngine();
    if(sslEngine==null) {
       CustomProtocolHelper.log("SelectionKey : "+key+ " without SSLEngine. Maybe key is not ready yet.");
       return null;

    }
    ...
}

Интересно то, что он используетsslEngine полеThreadAttachment, Это поле является простой ссылкой без какой-либо синхронизации (а также его установщик и получатель):

public class ThreadAttachment extends SelectionKeyActionAttachment
        implements AttributeHolder {

    ...

    private SSLEngine sslEngine; 

    ...

    public SSLEngine getSSLEngine() {
        return sslEngine;
    }

    public void setSSLEngine(SSLEngine sslEngine) {
        this.sslEngine = sslEngine;
    }

    ...
}

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

Это поле изменено ГризлиWorkerThreadImpl.updateAttachment()дважды. updateAttachment()получает блокировку с помощью метода getAttachment(), который вызывает ThreadAttachment.associate() где associate()приобретает блокировку

Если наш поток таймера вызываетfromSelectionKey()этот замок не получен образцомSSLPreProcessorпоэтому иногда он видит nullsslEngine и, наконец, повреждает поток SSL, записывая напрямую в него (с нулевым препроцессором).

Я пытался изменитьfromSelectionKey()метод для вызоваThreadAttachment.associate()а также deassociate() также, но это вызвало странное поведение приложения (при минимальной загрузке).

Еще одна идея, которую мы попробовали, это добавитьvolatileмодификатор кThreadAttachment.sslEngineполе и опросить его в цикле на некоторое время:

-       public static SSLPreProcessor fromSelectionKey(SelectionKey key) {
+       public static SSLPreProcessor fromSelectionKey(final SelectionKey key) {
+               final Stopwatch stopwatch = Stopwatch.createStarted();
+               while (true) {
+                       final SSLPreProcessor preProcessor = fromSelectionKey2(key);
+                       if (preProcessor != null) {
+                               return preProcessor;
+                       }
+                       final boolean timeout = stopwatch.elapsed(TimeUnit.SECONDS) > 10;
+                       if (timeout) {
+                               final Object attachmentObj = key.attachment();
+                               return null;
+                       }
+               }
+       }
+
+       public static SSLPreProcessor fromSelectionKey2(SelectionKey key) {

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

TL; DR: образецSSLPreProcessor от Grizzly 1.9 предназначен для обеспечения безопасности потоков? Если да, то как его использовать из других тем?

0 ответов

Другие вопросы по тегам