Асинхронные методы Hystrix в javanica не работают внутри Java-приложения Spring-Boot

Я использую spring-cloud-starter (т.е. весеннюю загрузку со всеми функциями микросервисов). Когда я создаю метод hystrix в компоненте, аннотированном с помощью javanica @HystrixCommand, следуйте инструкциям на сайте javanica github (https://github.com/Netflix/Hystrix/tree/master/hystrix-contrib/hystrix-javanica), чтобы этот метод работал асинхронно, независимо от того, использую ли я их «Future‹>» или Reactive Execution 'Observable‹>', ничего не запускается/не выполняется, и я получаю
java.lang.ClassCastException: springbootdemo.EricComponent$1 cannot be cast to springbootdemo.Eric всякий раз, когда пытаюсь получить результат (в случае Future‹>) или получить обратный вызов (в случае Reactive Execution .. и println's не запускайте, так что он действительно не работал).

public class Application { ...
}
@RestController
@RequestMapping(value = "/makebunchofcalls/{num}")
class EricController { ..

    @RequestMapping(method={RequestMethod.POST})
    ArrayList<Eric> doCalls(@PathVariable Integer num) throws IOException {
        ArrayList<Eric> ale = new ArrayList<Eric>(num);
        for (int i =0; i<num; i++) {
            rx.Observable<Eric> oe = this.ericComponent.doRestTemplateCallAsync(i);
            oe.subscribe(new Action1<Eric>() {
                @Override
                public void call(Eric e) {  // AT RUNTIME, ClassCastException
                    ale.add(e);
                }
            });
        }

        return ale;
    }

@Component
class EricComponent { ...

    // async version =========== using reactive execution via rx library from netflix ==============

    @HystrixCommand(fallbackMethod = "defaultRestTemplateCallAsync", commandKey = "dogeAsync")
    public rx.Observable<Eric> doRestTemplateCallAsync(int callNum) {
        return new ObservableResult<Eric>() {
            @Override
            public Eric invoke() {  // NEVER CALLED
                try {
                    ResponseEntity<String> result = restTemplate.getForEntity("http://doges/doges/24232/photos", String.class);  // actually make a call
                    System.out.println("*************** call successfull: " + new Integer(callNum).toString() + " *************");
                } catch (Exception ex) {
                    System.out.println("=============== call " + new Integer(callNum).toString() + " not successfull: " + ex.getMessage() + " =============");
                }
                return new Eric(new Integer(callNum).toString(), "ok");
            }
        };
    }

    public rx.Observable<Eric> defaultRestTemplateCallAsync(int callNum) {
        return new ObservableResult<Eric>() {
            @Override
            public Eric invoke() {
                System.out.println("!!!!!!!!!!!!! call bombed " + new Integer(callNum).toString() + "!!!!!!!!!!!!!");
                return new Eric(new Integer(callNum).toString(), "bomb");
            }
        };
    }
}

Почему я должен получить EricComponent$1 вместо Eric? Кстати, Eric - это просто простой класс с двумя строками... он опущен.

Я полагаю, что мне нужно выполнить явное выполнение, но это намекает на меня, потому что: 1) выполнение этого с помощью Future‹> метод queue() недоступен, как утверждает документация, и 2) выполнение этого с помощью Observable‹> действительно т способ выполнить это, что я получаю.


person RubesMN    schedule 14.10.2014    source источник


Ответы (1)


У вас есть аннотация @EnableHystrix в вашем классе приложений?

Метод subscribe является асинхронным, и вы пытаетесь заполнить список в методе синхронного контроллера, поэтому здесь может быть проблема. Можете ли вы изменить subscribe на toBlockingObservable().forEach() и посмотреть, поможет ли это?

Обновление №1, которое мне удалось продублировать. Ваш метод по умолчанию не должен возвращать Observable<Eric>, только Eric.

public Eric defaultRestTemplateCallAsync(final int callNum) {
    System.out.println("!!!!!!!!!!!!! call bombed " + new Integer(callNum) + "!!!!!!!!!!!!!");
    return new Eric(new Integer(callNum).toString(), "bomb");
}

Обновление №2 См. мой код здесь https://github.com/spencergibb/communityanswers/tree/so26372319

Обновление №3. Когда я закомментировал атрибут fallbackMethod, он пожаловался, что не может найти общедоступную версию EricComponent для АОП. Я сделал EricComponent public static и все заработало. Класс верхнего уровня в своем собственном файле будет работать. Мой код, указанный выше, работает (при условии, что вызов restTemplate работает) и возвращает n OK.

person spencergibb    schedule 15.10.2014
comment
Спасибо @spencergibb за время, которое вы потратили на это. У меня был @EnableHystrix.. и я смог избежать ClassCastException в соответствии с вашим предложением заменить Observable<Eric> на Eric по умолчанию. Кто-то должен обновить документацию Javanica, чтобы указать это. Однако, когда я запускаю это, я получаю все вызовы println's. Кроме того, метод invoke() для возвращаемого ObservableResult<Eric> по-прежнему никогда не выполняется. - person RubesMN; 15.10.2014
comment
Согласно вашему обновлению № 3, оказывается, что разделение EricComponent на отдельный файл сработало отлично. Теперь все работает. Кроме того, переключение на блокировку с помощью BlockingObservables и неблокировку с помощью обычных Observables также работает и дает мне то, что мне нужно для моего POC. Еще раз спасибо @spencergibb. - person RubesMN; 24.10.2014
comment
У меня те же проблемы с асинхронными методами (Future‹T›) в модульных тестах. Мои обычные стратегии (@EnableAspectJAutoProxy и т. д.), к сожалению, не работают :( - person jocull; 23.10.2019
comment
Наконец-то я понял проблему - я пытался вызвать аннотированный метод из самого класса (например, this.myHystrixMethod). Очевидно, это не очень хорошо сочетается с переписыванием класса, которое делает AspectJ. Как только я начал напрямую вызывать необработанный аннотированный метод, все было в порядке. - person jocull; 23.10.2019