Nano Hash - криптовалюты, майнинг, программирование

Как изящно закрыть конечную точку при возникновении исключения?

Я написал небольшой веб-сервис JAX-WS, который я запускаю вне контейнера с Endpoint.publish():

Endpoint endpoint = Endpoint.create(new MyServiceImpl());
endpoint.publish("http://localhost:4425/myService");

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

com.sun.xml.internal.ws.server.ServerRtException: Ошибка выполнения сервера: java.net.BindException: Адрес уже используется: bind

Я мог бы обернуть все методы моего веб-сервиса с помощью try/catch, но это кажется немного повторяющимся. Я также попытался установить класс очистки через Thread.setDefaultUncaughtExceptionHandler(), но это не сработало, когда мой метод веб-службы выдал исключение.

Есть ли более элегантный способ решить эту проблему, чем прибегать к бесчисленным блокам try/catch?


Основываясь на ответе Вальдхайнца, я попытался использовать классы Jetty в пользу значений по умолчанию JDK. Код компилируется, но при выполнении завершается сразу после publish. При использовании классов JDK основной поток оставался в живых до тех пор, пока я не завершу процесс вручную. Любые идеи, что происходит не так? Интересно, происходит ли где-то исключение, но его проглатывают, поэтому я его не вижу.

Endpoint endpoint = Endpoint.create(new MyServiceImpl());

Server s = new Server(new InetSocketAddress(HOST, PORT));
ServerConnector connector = new ServerConnector(s);
connector.setReuseAddress(true);
s.setConnectors(new Connector[] { connector });
s.setHandler(new ContextHandlerCollection());

JettyHttpServer server = new JettyHttpServer(s, false);
JettyHttpContext context = (JettyHttpContext) server.createContext(PATH);
endpoint.publish(context);

  • Вместо того, чтобы прибегать к «бесчисленным» блокам try/catch, вы можете попробовать использовать аспект для перехвата исключений. 15.05.2014
  • Вы пробовали метод stop? docs.oracle. com/javase/7/docs/api/javax/xml/ws/ 15.05.2014
  • @SajanChandran Как это помогает мне перехватывать исключения? 15.05.2014
  • @EngineerDollery Хорошая идея, буду иметь в виду, если не будет других вариантов. 15.05.2014
  • Если ваш сокет находится в состоянии TIME_WAIT, не имеет значения, как был закрыт Endpoint. Важно то, что следующий процесс, пытающийся выполнить привязку к порту, устанавливает флаг SO_REUSEADDR перед привязкой к сокету. Вы можете проверить, является ли TIME_WAIT вашей проблемой, используя netstat. 15.05.2014

Ответы:


1

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

15.05.2014
  • Я не смог найти способ сделать это, используя JDK HttpServer по умолчанию, но это возможно с другими реализациями JAX-WS SPI, такими как Jetty jetty-jaxws2-spi или подобными. 15.05.2014
  • Я думаю, что вы на что-то здесь. Я попытался продвинуть это решение немного дальше, см. Мои правки к вопросу выше. Какие-нибудь мысли? 15.05.2014
  • Я думаю, вам не хватает только s.join();, что позволило бы основному потоку ждать, пока сервер не выключится (когда и почему он выключится, это другая тема). 15.05.2014
  • Спасибо за совет. Мой код был немного более сломан, чем просто отсутствующий join(), см. мой ответ для полного дампа. 19.05.2014

  • 2

    Пользовательский пул потоков для конечной точки может помочь:

    ThreadFactory factory = new ThreadFactory() {
    
      @Override
      public Thread newThread(Runnable target) {
        final Thread thread = new Thread(target);
        thread.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
    
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                // put error handling code here
            }
    
        });
        return thread;
      }
    
    
    };
    
    ExecutorService executor = Executors.newCachedThreadPool(factory);
    Endpoint endpoint = Endpoint.create(new MyServiceImpl());
    endpoint.setExecutor(executor);
    endpoint.publish("http://localhost:4425/myService");
    
    15.05.2014

    3

    Используя подсказку от Waldheinz, я настроил свой веб-сервис для использования Jetty следующим образом:

    Server s = new Server(PORT);
    ServerConnector connector = new ServerConnector(s);
    connector.setReuseAddress(true); // avoid bind errors
    s.setHandler(new ContextHandlerCollection());
    s.setConnectors(new Connector[] { connector });
    
    System.setProperty("com.sun.net.httpserver.HttpServerProvider",
            "org.eclipse.jetty.http.spi.JettyHttpServerProvider");
    
    Endpoint.publish(HOST + ":" + PORT + PATH, new MyServiceImpl());
    

    Кажется, это хорошо справляется с проблемами. Очки вознаграждения достаются Вальдхайнцу за то, что он направил меня по правильному пути. Спасибо Jk1 за альтернативное предложение.

    19.05.2014

    4

    Я понимаю, что ответ уже принят, но, к сожалению, он основан на решении, специфичном для Jetty. Я не пробовал ничего из того, что собираюсь предложить, но первое, что приходит на ум, это использовать Endpoint.setExecutor(Executor).

    Одна из возможностей — создать ThreadFactory. который явно устанавливает обработчик необработанных исключений для каждого создаваемого им потока. код гуавы ThreadFactoryBuilder может помочь:

    public class MyHandler implements Thread.UncaughtExceptionHandler {
    
        private final Endpoint endpoint;
    
        public MyHandler(Endpoint endpoint) {
            this.endpoint = endpoint;
        }
    
        @Override
        public void uncaughtException(Thread t, Throwable e) {
            // ...
        }
    }
    
    Endpoint endpoint = Endpoint.create(new MyServiceImpl());
    Thread.UncaughtExceptionHandler handler = new MyHandler(endpoint);
    ThreadFactory factory = new ThreadFactoryBuilder().setUncaughtExceptionHandler(handler).build();
    Executor executor = Executors.newSingleThreadExecutor(factory);
    endpoint.setExecutor(executor);
    endpoint.publish("http://localhost:4425/myService");
    

    Другой возможностью является расширение ThreadPoolExecutor и переопределить afterExecute(Runnable, Throwable).

    public class ServiceExecutor extends ThreadPoolExecutor {
    
        private final Endpoint endpoint;
    
        // ThreadPoolExecutor has four ctors
        public ServiceExecutor(Endpoint endpoint, ...) {
            this.endpoint = endpoint;
        }
    
        @Override
        protected void afterExecute(Runnable r, Throwable t) {
            super.afterExecute(r, t);
            if (t != null) {
                // ...
            }
        }
    }
    
    Endpoint endpoint = Endpoint.create(new MyServiceImpl());
    Executor executor = new ServiceExecutor(endpoint, ...);
    endpoint.setExecutor(executor);
    endpoint.publish("http://localhost:4425/myService");
    
    20.05.2014
    Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

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

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

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

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..