Pull to refresh

Comments 42

Сюда б еще сравнения легковесности с другими.
Будет интересно, как в конечном итоге 5-й спринг заинтегрируется с 9-й джавой в разрезе java.util.concurrent.Flow. Много статей пивотал пишет про это, но пока не было релизов — не ясно.

Spring использует Project Reactor для реактивности, а Реактор реализует стандартные API Reactive Streams из Java 9 (именно java.util.concurrent.Flow). Т.е. Java 9 предоставляет API, Реактор реализацию, Спринг использует Реактор и таким образом интегрируется с Java 9.

NodeJS какой-то, не представляю, во что это может превратиться в большом проекте.

Я вот например начал знакомство со спринг и закончил почти сразу же. Какие-то конфигураторы для конфигураторов и ко всему этому фреймворк для запуска. Жуть какая-то, spring превратился в огромный комбайн и переплюнул java ee.
Сейчас например остановился на JSF. Для сайтов уровня hello world — самое то. Что-то крупнее hello world еще не пробовал писать. Может в этом случае я выиграл бы от такого комбайна как spring?
На самом деле, это именно то, что нужно.
Бекенд в чистом видел – это API сервер, который ничего не должен знать про фронтенд. Сейчас все больше и больше мобильных клиентов, которым нужно отдавать только данные. Веб-сайты постепенно отходят на задний план.

Да серверный ренден еще остается, но для этого будут нужны «тяжеловесные» фреймворки. Для остальних же, нужны крайне легковесные, которым хватало бы каких-нибудь дешевых t1.micro.

Я сам не сильно люблю Spring, но Spring WebFlux – это совсем другое дело, если все так, как выглядит, то это замечательный инструмент (пока они не превратят его в Spring WebFlux Boot).

Думаю, не превратят. Он и планируется как легковесная микрофреймворковая штука для желающих. Хотя точно так же он может работать и в рамках обычного Spring приложения, просто @Controller может вернуть не ответ, а Flux / Mono в качестве ответа — и все, реактивный контроллер готов. И даже можно объявить бин с типом RouterFunction и привязать роутер к существующему приложению, а @Controller аннотации не использовать вообще. Более того, чтобы оценить масштаб — по-умолчанию, Spring WebFlux даже не тащит зависимость на Spring MVC, они прям вообще разные.

Думаю, не превратят.
Это же Spring, не стоит их недооценивать :-)

Кстати, попробовал потыкать webflux из примера на ApacheBenchmark – он вообще не отвечает на запросы. Ну а по консоли видно, что это сейчас на стадии альфы или в лучшем случае беты.
Webflux вполне вменяемо функционирует. На запросы отвечает, фильтры работают, конвертеры работают. Можно линк на пример?

Интересно, ab действительно не работает, а wrk вполне себе.


wrk -t 10 -c 1000 -d 10s http://localhost:8080/

Команда Spring это одна из самых прагматичных и профессиональных, которые я встречал, так что не стоит их недооценивать ;)


Текущий статус Release Candidate, релиз будет до конца этого года.

та же фигня. Ни стрингом, ни ЖСОНом

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


Комбайн как Spring рулит когда в приложении много разных аспектов, типа интеграции, облачных штук, безопасности. В общем случае (не в крайних) их проще и быстрее сделать на Спринге, чем писать самому. Если приложение просто отдает html / json по данным из базы — его теперь можно сделать очень просто (хотя и раньше было просто, если уж откровенно).

UFO just landed and posted this here

Например, здесь https://docs.spring.io/spring/docs/5.0.0.RC3/spring-framework-reference/web.html#web-reactive


Если же нужно просто взять Spring (Boot) как есть с его DI и транзакциями, и прикрутить роутер, все делается просто регистрацией бина в конфигурации:


@Bean
public RounterFunction routerFunction() {
  return route(...)
}
UFO just landed and posted this here

Есть несколько вариантов использования реактивного веба в Spring:


  • Без Application context, DI, конфигураций и т.п. — как описано в статье
  • Используя @Controller аннотации и возвращая Flux<T> или Mono<T> из методов контроллера
  • Не используя контроллеры, определить бин типа RouterFunction с явно заданной таблицей машрутов и хендлерами.

Да, все верно, это старый-добрый Спринг, WebFlux это новая парадигма для веб-разработки, даже не замена MVC, а дополнение. А в качестве приятного бонуса, теперь на Spring можно писать microframework-style приложения à la Sparkframework, Ninja etc. без контекста приложений, DI и автоконфигураций.

Выглядит пока все очень классно, просто и удобочитаемо. Захотелось попробовать. Автору спасибо за статью)
Раньше порталы писались без всяких фреймворков.
Все говорят о каком то облегчении жизни, типа так быстрее.
Два месяца и 1 человек — готовый портал без фреймворка с нуля.
Неужели сейчас делается еще быстрее?
Может, и не быстрее, но тот самый 1 человек обойдется дешевле за счет более низкого порога вхождения, допустит меньше багов, а через два месяца другой такой же человек сможет быстрее войти в курс дела.

Хорошая попытка, Спринг, но нет, это плохая попытка. Получается, для того, чтобы вывести JSON вместо строки, нужно указать content-type:


.contentType(APPLICATION_JSON)

А дальше начинается счастье и проклятие спринга — спринговая магия. Какой сериализатор мы используем? Как его настроить? А если я хочу выдавать не json с content-type application/json? А если я хочу свой сериализатор вкрутить? И вообще зачем веб-серверу зависеть от спринга?


21 век на дворе, сейчас всем нужны:


  • http server, где спрингом бы и не пахло
  • сериализатор json, где спрингом бы и не пахло
  • возможность скомпоновать 1 и 2, вне зависмости от релиализации.
Думаю, тут магии пока нет. Следующая строка (как я предполагаю) возвращает сериализованный ответ:
.body(fromObject(new Hello("world")))

И там уже (как я очень надеюсь) можно использовать любой сериализатор.
Content-type указывать необязательно, достаточно передать какой-нибудь отличный от базовых типов объект, например ...body(fromObject(myDto)). По факту на выходе автоматом получится json. А вот берет ли на себя webflux корректную расстановку хидеров; это нужно проверить.
А если я хочу свой сериализатор вкрутить?

берите и вкручивайте и даже свой ContentType можете в виде строки передать — там никакой магии — сплошная документация

http server, где спрингом бы и не пахло

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


Иными словами — как вы в общем виде компоновать-то 1 и 2 собираетесь, если у вас нет контейнера, где лежит сериализатор, http сервер, и что-нибудь еще?

До DI с этим без особых усилий жили. Да, кода было больше, но код был явный.

Что мешает написать:
HttpServer
 .withCodecRegistry(
   new CodecRegistry()
     .bind(APPLICATION_JSON, new JsonCodec( ... ))
     .bind(APPLICATION_XML, new XmlCodec( ... ))
 )
 .withRouteRegistry(
   new RouteRegistry()
      .bind(GET("/"), (r) -> Response.ok());
 )
 ...
 .run(8080);
Или что-то подобное…
Более того, чтобы файл не был на тысячи строк, можно этот конфиг разбить на несколько: RouteConfig, ErrorHandlerConfig, CodecConfig, etc.

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

Лямбды и чистые функции позвляют значительно улучшать конфиги. В этом плане инновационные языки — Scala и Javascript. Вкратце, идея в том, что любой сетевой обработчик, фильтр и собственно сервер — это функция Request -> Response. Т.е. приложение можно собирать функциональной композицией индивидуальных обработчиков, фильтров и роутеров.

Ну да. И что вы хотели этим сказать, что вам DI не нужно?

DI нужен, не нужна конфигурация через данные. Конфигурация кодом — лучше.

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

Это нормально, но я не могу сказать, что это однозначно лучше. Или хуже. Во-первых, кроме явного кода вы выбрали инициализацию статическую, где все определяется при компиляции. Где-то это хорошо, где-то не факт. Это не лучше и не хуже — это разные потребности.


Скажем прямо — у меня и так не видно, чтобы скажем http server где-то зависел от спринга. От вот этих самых сериализаторов и обработчиков — да, зависит. Они возможно даже инжектятся спрингом — только сервер про спринг (который DI) не знает.

Во-первых, кроме явного кода вы выбрали инициализацию статическую, где все определяется при компиляции.
Никто не мешает использовать конфиги – поменял конфиг и рестартовал и погнали.

Спринг крутой, он позволяет быстро написать то, что будет работать.
Но вот я, к примеру, всегда хотел выкинуть из спринга все ненужное мне в конкретном проекте. У меня API сервер, который умеет только JSON и ничего больше. Но спринг такой классный, что подсовывает мне дополнительно кучу кода, который нужен для поддержки xml, html, а еще загружает классы для AOP и кучу своих связок для других вещей, которые нафик не нужны. А ведь RAM то ограничен.

Но это уже offtop с моей стороны пошел.

Знаете, у меня в прошлом проекте были "аналитики", которые тоже говорили: "А чо, поменял конфиги, рестартовал и погнали". А то что на входе непрерывный поток данных (финансовых), который можно потерять пока рестартуем — это им в голову не приходило. Всякие проекты бывают, попросту говоря.


Что же до "загружает всякие классы для AOP" — ну так вообще говоря, это не является обязательным.


Единственное что меня лично в этом вопросе расстраивает — это тот факт, что не всегда очевидно, зачем конкретно тут вот появился AOP (или что-то еще). Хотя если подумать, обычно ответ недалеко — включил аннотацию @Cacheable для кеширования результатов метода — так оно что, святым духом что-ли будет вызовы метода перехватывать? Нет же, будет либо Proxy, либо AOP, со всеми вытекающими. Ну т.е., нужно иногда понимать, как оно там внутри, что за неонки.

Знаете, у меня в прошлом проекте были «аналитики», которые тоже говорили: «А чо, поменял конфиги, рестартовал и погнали». А то что на входе непрерывный поток данных (финансовых), который можно потерять пока рестартуем — это им в голову не приходило. Всякие проекты бывают, попросту говоря.
Инструмент выбирается под проект. Я не говорил, что Spring – это абсолютное зло, он хорош для много. Я лишь сказал, что для большинства задач, с которыми я сталкивался в своей карьере он излишне «жирный».

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

Только в битве за RAM не забывайте, что вообще весь Spring сам по себе, занимает не так уж и много памяти — до 32Мб. Есть интересное исследование на эту тему от Дейва Сайера.

У нас в проекте используется Spring для REST API. Интересно мнение профи — будут ли они переписывать REST на Spring WebFlux?

Если "они" это Spring, а не команда вашего проекта — то нет, не будут. Более того, REST и WebFlux они ортогональны, т.е. REST не надо "переписывать" на WebFlux, REST можно запускать поверх WebFlux, но не обязательно. Все старые парадигмы остаются.

Пока для микросервисов использую Dropwizard и что-то пока не вижу особых стимулов для переезда на модную реактивщину. Кто нибудь сравнивал производительность?

Реактивные подходы они скорее не для производительности (она теоретически может быть даже хуже для отдельных запросов), а для конкурентности — т.е. обработать больше клиентов одновременно.

Я так понимаю, что текущий интерфейс хэндлера не предполагает асинхронных задач. Тогда что неблокирующего в этом вебсервере?

Предполагает. Это не очень видно, но он возвращает не ответ типа T, а Mono<T> или Flux<T> — реактивный тип из Project Reactor.

Sign up to leave a comment.

Articles