Интеграция Spring Reactive с Spring MVC + MySQL

99
13 июня 2021, 04:20

Пытаюсь разобраться, могу ли я использовать Spring Reactive (Flux/Mono) вместе с Spring MVC ? Структура микросервисы с использованием Spring MVC + Feign Client, Eureka Server (Netflix OSS), Hystrix, база данных MySQL.

Мой первый микросервис addDistanceClient добавляет данные в базу данных. Вот пример контроллера:

@RequestMapping("/")
@RestController
public class RemoteMvcController {
    @Autowired
    EmployeeService service;
    @GetMapping(path = "/show")
    public List<EmployeeEntity> getAllEmployeesList() {
        return service.getAllEmployees();
    }
}

Здесь я могу использовать Mono/Flux, я думаю не будет никаких проблем.

Мой второй микросервис showDistanceClient - он не связан напрямую с базой данных. У него есть метод, который вызывает метод (который описан выше) у первого микросервиса для получения данных из базы данных. Здесь используется Feign Client.

Контроллер второго микросервиса:

@Controller
@RequestMapping("/")
public class EmployeeMvcController {
    private ServiceFeignClient serviceFeignClient;
    @RequestMapping(path = "/getAllDataFromAddService")
    public String getData2(Model model) {
        List<EmployeeEntity> list = ServiceFeignClient.FeignHolder.create().getAllEmployeesList();
        model.addAttribute("employees", list);
        return "resultlist-employees";
    }
}

и сам ServiceFeignClient, с помощью которого мы вызываем метод на первом микросервисе, выглядит следующий образом:

@FeignClient(name = "add-client", url = "http://localhost:8081/", fallback = Fallback.class)
public interface ServiceFeignClient {
    class FeignHolder {
         public static ServiceFeignClient create() {
            return HystrixFeign.builder().encoder(new GsonEncoder()).decoder(new GsonDecoder()).target(ServiceFeignClient.class, "http://localhost:8081/", new FallbackFactory<ServiceFeignClient>() {
                @Override
                public ServiceFeignClient create(Throwable throwable) {
                    return new ServiceFeignClient() {
                        @Override
                        public List<EmployeeEntity> getAllEmployeesList() {
                            System.out.println(throwable.getMessage());
                            return null;
                        }
                    };
                }
            });
        }
    }
    @RequestLine("GET /show")
    List<EmployeeEntity> getAllEmployeesList();
}

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

Моя цель: Сделать так, используя Spring Reactive (не уверен что мне это поможет, но думаю я мыслю в правильном направлении) сделать так, чтобы сообщение что сервис в даный момент недоступен и крутящийся спиннер на втором микросервисе автоматически пропали и отобразились данные из базы данных, как только первый микросервис снова оживет (без повторной отправки запроса, т.е. без перезагрузки страницы). Смогу ли я это сделать через Spring WebFlux ? Я знаю что через Spring WebFlux используется поток, который сам нас уведомит, если в нем появятся данные, нам не нужно здесь делать повторную отправку запроса.

Я начал думать над этим и не могу понять, как мне это сделать:

1) используя Spring reactive В таком случае мне надо во втором микросервисе sgowDistanceClient внедрить Flux/Mono в MVC модель, которая возвращает html. Я не понимаю как. Я знаю как это сделать с REST.

2) Если первый пункт неверный, может мне надо использовать WebSocket для этого ? Если это так, поделитесь пожалуйста ползными ссылками с примерами. Буду очень благодарен.

Действительно эта тема мне очень интересна и я хочу разобраться в ней. Я буду вам очень благодарен за вашу помощь. Спасибо всем!

ИЗМЕНЕНО:

Я переделал оба контроллера под REST + WebFlux. Все работает для меня.

Первый сервис addDistanceClient и его контроллер:

@RestController
@RequestMapping("/")
public class BucketController {
    @Autowired
    private BucketRepository bucketRepository;
    // Get all Bucket from the database (every 1 second you will receive 1 record from the DB)
    @GetMapping(value = "/stream/buckets/delay", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<Bucket> streamAllBucketsDelay() {
        return bucketRepository.findAll().delayElements(Duration.ofSeconds(5));
    }
}

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

Второй сервис showDistanceClient и его контроллер. Здесь я использовал WebClient вместо Feign Client.

@RestController
@RequestMapping("/")
public class UserController {
    @Autowired
    private WebClient webClient;
    @Autowired
    private WebClientService webClientService;
    // Using WebClient
    @GetMapping(value = "/getDataByWebClient",produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<Bucket> getDataByWebClient() {
        return webClientService.getDataByWebClient();
    }
}

и его Сервис слой (WebClientService):

@Service
public class WebClientService {
    private static final String API_MIME_TYPE = "application/json";
    private static final String API_BASE_URL = "http://localhost:8081";
    private static final String USER_AGENT = "User Service";
    private static final Logger logger = LoggerFactory.getLogger(WebClientService.class);
    private WebClient webClient;
    public WebClientService() {
        this.webClient = WebClient.builder()
                .baseUrl(API_BASE_URL)
                .defaultHeader(HttpHeaders.CONTENT_TYPE, API_MIME_TYPE)
                .defaultHeader(HttpHeaders.USER_AGENT, USER_AGENT)
                .build();
    }
    public Flux<Bucket> getDataByWebClient() {
        return webClient.get()
                .uri("/stream/buckets/delay")
                .exchange()
                .flatMapMany(clientResponse -> clientResponse.bodyToFlux(Bucket.class));
    }
}

Теперь все работает в реактивной среде. Отлично.

Но моя проблема осталась нерешенной.

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

Как мне добиться этого ?

Answer 1

Вопрос закрыт. Как оказалось MVC нельзя подружить с WebFlux (точнее можно, но мне нужно возвращать не модель, а реальный json). Также тот функционал нельзя осуществить: если первый сервис умирает то коннекшен теряется. Невозможно продолжать его держать. Тут скорее помогут паттерн Retry - но его сложно внедрить в Реактивную среду без свистопляски. Всем спасибо.

READ ALSO
ошибка cookie php [дубликат]

ошибка cookie php [дубликат]

Сейчас учу php,дошел до изучения куки,но функция setcookie не работает ни в какую и вызывает ошибкуВот собственно код(даже скопи-пастил код из офф

119
Запрос MySQL с 3 и более вариантами OR

Запрос MySQL с 3 и более вариантами OR

Помогите, делаю поиск по БД, но не могу понять как правильно сформировать запросЕсть таблица, в которой 10 столбцов, Я хочу что бы через форму...

87
Запаковать видео в бинарный файл

Запаковать видео в бинарный файл

Пытаюсь загрузить видео на Facebook через Graph-Api, там указано:

112