В go практически любая функция может вернуть ошибку
Начнем с того что ошибка в го ничем не отличается от значения. И их очень легко пропустить и пойти дальше с ошибочным значением. res, err := makeSomething() мы забили на ошибку и пошли дальше с результатом res — ни в джаве, ни в расте такого уродства вы не получите
Аналогичный код на java писать практически невозможно:
на джаве вы можете писать
используя checked exceptions
unchecked exceptions
взять vavr и гонять везде Either
сделать такую же фигню как и в Го и возвращать Pair
и не забываем что в Го вы так же можете получить Panic
Скорость написание кода для меня упирается в скорость мыслительного процесса
вот смотрите пример чистой логики на джава, которая читает сообщение из кафки и потом в нее же записывает.
@Incoming("fruit-in")
@Outgoing("fruit-out")
fun makeSomething(item: Fruit): Fruit {
...clean logic
}
можно теперь пример кода на Го, который затмит сие творение?
А у меня код на go выглядит чисто и просто так просто потому, что он на go.
опять же можно пример такого чистого кода?
вот пример на джаве когда есть список урлов и надо пройтись по всем им, получить данные с них, но не более 5 одновременных вызовов и потом все сгруппировать
а теперь попробуйте вы показать такое же на Го, а потом мы начнет усложнять задачу — прикрутить ретрай, разные варианты реагирования на разные ошибки, кеширование и тд.
скрывает сотни других однотипных конструкций в java
чесно, я вообще не понимаю какие конструкции вы имеете ввиду.
Делаем ендпоинт который может жсон принимать а может хмл, получаем обьект с него, кладем в постгрес его.
и второй ендпоинт который получает ид и читаем с Редиса а если нет то идем в постгрес, достаем и заодно кладем в редис.
Вы утверждаете что сделаете быстрее реализацию чем я на джаве, с юнит и интеграционными тестами, которая будет собираться и прогоняться на машинах разработчиков под вин, линукс, мак а так же в Дженкинсе.
Давайте поспорим?
нет, потому что хоть го и будет меньше памяти наверное есть по сравнению с монстрообразным спрингбутом, но скорость разработки отобьет все расходы на память.
Мне кажется что здесь и ошибка. Библиотеки не живут в вакууме, за редким исключением. И мы в данном случае говорим про обработку ошибок в ФП стиле, значит что моя библиотека будет отдавать ошибку в этой парадигме и делать свою реализацию для Try|Either вообще нет смысла.
Поэтому мне нравится Reactor(или RxJava) — что там есть Mono/Flux и через них удобно выразить и обработать и ошибки (Mono.error()) и пустые значения ('Mono.empty()') и асинхронность с коробки.
зачем вы возвращаете монаду Optional<Something> чтобы завернуть ее потом в Mono<Optional<Something>> — если у вас уже реактивный стек так почему не так
public Mono<Something> findByUniqueKey(...) {...}
Многоступенчатая вложенность кода из-за flatMap.
Опять же — бейте на слои, классы и функции чтобы все было плоским, как то так .flatMap(this::createRequest) а уже вложеная логика идет в createRequest и в результате получается понятно и плоско.
Неудобная обработка ошибок и их выброс.
Наоборот, оборачивание повсюду try...catch забирает кучу сил, а так есть разные методы как реагировать на ошибку и на каком уровне.
Сложная обработка поведения для Mono.empty().
Ничуть не сложнее чем Optional.empty + заставляет вас убирать null тем самым код становится менее бажный.
Сложности с логированием, если надо в лог добавить что-то глобальное, например traceId. (в >статье не описываю, но те же проблемы с другими ThreadLocal переменными, например >SpringSecurity)
Достаточно один раз написать свой паблишер, который добавить в reactor.core.publisher.Hooks#onEachOperator(Function) и он будет пробрасывать в контекст все что вам нужно и не захламлять бизнес код работой с MDC.
Неудобно дебажить.
Опять же, проблема в ваших простынях. Такой код и на Java Streams будет тяжело понимать и дебажить.
Неявное api для параллелизации.
Переводом на Реактор я наоборот значительно сократил код который работал со многими потоками. Все что надо есть из коробки, как для работы реактивных библиотек типа Lettuce так и для старого кода когда надо плодить потоки.
и да и нет.
у вас активных консюмеров топика не может быть больше чем кол-ва партишинов. но вы можете держать про запас у вас могут быть разные топики и следовательно кафка распределит консюмеров по ним.
Kafka Streams может умереть и по другой причине: не пойманные исключения в вашем коде.… Второй вариант кажется лучше — совсем не ловить исключение и дать Kafka Streams умереть...
Вот это меня удивляет больше всего — почему они не могут просто откатывать оффсет и репроцесить сообщение, если добавлять счетчик ретраем в метаданные то было бы вообще хорошо. И тогда пользовательский код решал что бы хватит уже ретраить или же продолжаем ретраить пока не подымется база.
Начнем с того что ошибка в го ничем не отличается от значения. И их очень легко пропустить и пойти дальше с ошибочным значением.
res, err := makeSomething()мы забили на ошибку и пошли дальше с результатомres— ни в джаве, ни в расте такого уродства вы не получитена джаве вы можете писать
EitherPairи не забываем что в Го вы так же можете получить Panic
вот смотрите пример чистой логики на джава, которая читает сообщение из кафки и потом в нее же записывает.
можно теперь пример кода на Го, который затмит сие творение?
опять же можно пример такого чистого кода?
вот пример на джаве когда есть список урлов и надо пройтись по всем им, получить данные с них, но не более 5 одновременных вызовов и потом все сгруппировать
а теперь попробуйте вы показать такое же на Го, а потом мы начнет усложнять задачу — прикрутить ретрай, разные варианты реагирования на разные ошибки, кеширование и тд.
чесно, я вообще не понимаю какие конструкции вы имеете ввиду.
что в вашем понимании относительно простая задача?
А зачем?
У меня прикручен ломбок и объекты выглядят чисто и просто.
Для меня Го не плохо, а шаг назад. Я должен писать тонны однотипного кода, вместо сосредоточения на бизнес логике.
Делаем ендпоинт который может жсон принимать а может хмл, получаем обьект с него, кладем в постгрес его.
и второй ендпоинт который получает ид и читаем с Редиса а если нет то идем в постгрес, достаем и заодно кладем в редис.
Вы утверждаете что сделаете быстрее реализацию чем я на джаве, с юнит и интеграционными тестами, которая будет собираться и прогоняться на машинах разработчиков под вин, линукс, мак а так же в Дженкинсе.
Давайте поспорим?
нет, потому что хоть го и будет меньше памяти наверное есть по сравнению с монстрообразным спрингбутом, но скорость разработки отобьет все расходы на память.
Пока вы будете писать
Проекты на спрингбуте или кваркусе уже будут в продакшене :)))
Мне кажется что здесь и ошибка. Библиотеки не живут в вакууме, за редким исключением. И мы в данном случае говорим про обработку ошибок в ФП стиле, значит что моя библиотека будет отдавать ошибку в этой парадигме и делать свою реализацию для
Try|Eitherвообще нет смысла.А почему бы и нет? Например в resilience4j оно используется и ничем не мешает.
Поэтому мне нравится Reactor(или RxJava) — что там есть
Mono/Fluxи через них удобно выразить и обработать и ошибки (Mono.error()) и пустые значения ('Mono.empty()') и асинхронность с коробки.Вместо такого кода
я бы сделал отдельный обьект для хранения информации:
и тогда код стал бы
я обычно ставлю в самом конце цепочти
doOnError(err -> log.error("Unexpected exception", err))Очень помагает
ReactorDebugAgent.init();Все становится красивым и понятным.Та портянка кода конечно сложно, но просто надо разбивать все на слои и небольшие функции.
Тогда у вас будет простой короткий список вызовов.
Вот такой подход он неверный, тк он не реактивный.
зачем вы возвращаете монаду
Optional<Something>чтобы завернуть ее потом вMono<Optional<Something>>— если у вас уже реактивный стек так почему не такОпять же — бейте на слои, классы и функции чтобы все было плоским, как то так
.flatMap(this::createRequest)а уже вложеная логика идет вcreateRequestи в результате получается понятно и плоско.Наоборот, оборачивание повсюду
try...catchзабирает кучу сил, а так есть разные методы как реагировать на ошибку и на каком уровне.Ничуть не сложнее чем
Optional.empty+ заставляет вас убиратьnullтем самым код становится менее бажный.Достаточно один раз написать свой паблишер, который добавить в
reactor.core.publisher.Hooks#onEachOperator(Function)и он будет пробрасывать в контекст все что вам нужно и не захламлять бизнес код работой сMDC.Опять же, проблема в ваших простынях. Такой код и на Java Streams будет тяжело понимать и дебажить.
Переводом на Реактор я наоборот значительно сократил код который работал со многими потоками. Все что надо есть из коробки, как для работы реактивных библиотек типа Lettuce так и для старого кода когда надо плодить потоки.
бейте на много партишинов ваш топик
и да и нет.
у вас активных консюмеров топика не может быть больше чем кол-ва партишинов. но вы можете держать про запас у вас могут быть разные топики и следовательно кафка распределит консюмеров по ним.
давайте либо много диска чтобы помещалось либо делаете несколько инстансов на которых бегают стримы и у каждого свой локальный сторадж
Вот это меня удивляет больше всего — почему они не могут просто откатывать оффсет и репроцесить сообщение, если добавлять счетчик ретраем в метаданные то было бы вообще хорошо. И тогда пользовательский код решал что бы хватит уже ретраить или же продолжаем ретраить пока не подымется база.
А так стримы хороши когда сами в себе :(
а там их несколько и надо смотреть на свободный.
это уже слишком сложно для меня-борчика, поэтому пойду лучше пожую что-то.
всех с новым годом.
а была бы супруга то таскала бы вас по музеям и стали бы отличать Моне от Мане :)))
когда человека все устраивает то он не пишет плаксивые статьи на техническом сайте