Задача: обеспечить работу системы на Linux без потери в производительности с минимальными телодвижениями.
Напрашиваются 3 варианта решения задачи:
- Использовать Mono
- Использовать .NET Core
- Переписать все на Java\Go\Python
3-й вариант отбрасывается сразу же, на переписывание уйдет несколько человеко-лет разработки. Остаются Mono и .NET Core.
Что не так с Mono
Хоть и технология появилась давно и успешно используется (например, в Xamarin) есть несколько сомнений насчет дальнейшего развития Mono:
- Приобретение Xamarin Microsoft'ом. Microsoft публично делает ставку на .NET Core и вкладывает в него деньги. Процесс разработки .NET Core открыт, доступны даты релизов, roadmap и т.д. Что будет с Mono вне Xamarin вопрос. Будет ли Microsoft одновременно развивать 2 конкурирующие реализации .NET?
- Отсутствие фидбека. С какой производительностью работает Mono? Можно ли применять в enterprise? Про Mono почти не говорят на конференциях, такое чувство, что технологию никто не использует
В итоге выбор пал на .NET Core. Задача усложняется, что нужно одновременно поддерживать и .NET 4.6.1 реализацию, и .NET Core в одной кодовой базе, так как не всех клиентов можно обновить и нужна обратная совместимость.
Миграция проектных файлов
Для начала нужно установить Update 3 для Visual Studio 2015 и свежий .NET Core SDK (свежие билды публикуются на странице проекта). Затем для каждого csproj-файла нужно создать его аналог — project.json. Заходим в папку с каждый проектом, в командной строке вводим
dotnet new
.Появится project.json, из него нужно удалить строку про emitEntryPoint, если сборка не содержит метода Main. Далее, создаем пустой sln-файл, добавляем в него project.json файлы, как проекты. В каждый project.json следует прописать зависимости на проекты (раздел dependencies), пробуем скомпилировать. Если ошибок компиляции нет — поздравляю. Единственная проблема, что старый солюшен с csproj-проектами перестанет работать, чтобы оба проекта работали side by side есть решение. Выглядит довольно странно, но работает.
Что не будет работать
В нашем приложении столкнулись со следующими проблемами:
- app.config — Разработчики corefx отказались от устаревшего API работы с конфигурационными файлами. Теперь конфигурации рекомендуется хранить в json файле.
- PerformanceCounter — Windows специфика, для использования не на Windows платформе следует искать какую-то альтернативу.
- DataSet, DataTable — считается устаревшими API.
- LCID у CultureInfo — у культуры теперь нет свойства LCID, CultureInfo теперь можно создать только по имени.
- Свойство Assembly у экземпляра Type — чтобы получить описание сборки у типа необходимо использовать метод-расширение GetTypeInfo(). Ломает совместимость с текущим кодом, особенно в ресурсных файлах.
- MachineKey — Windows специфика, нет возможности получить уникальный идентификатор текущей машины.
- Thread.SetData, Thread.GetData — добавят во второй версии стандарта.
Это все, что касается API стандартной библиотеки. Очевидно, следует перерабатывать код использующий нативные вызовы (в нашем случае код проверки прав доступа и работа с дескрипторами безопасности). Файлы с таким legacy-кодом исключали из компиляции (раздел exclude в project.json) или отключали директивой условной компиляции (NETCOREAPP1_0).
SOAP
В текущей реализации .NET Core нет способа из коробки создать Web-сервис работающий по SOAP. Есть пример с MSDN, где показывают как можно при особом желании поддержать SOAP протокол. Мы для себя решили отказаться от SOAP и перевести веб-сервис на REST. Никаких проблем нет, ASP.NET Core не отличается от ASP.NET Web API. Есть DI с которым можно жить. Контроллеры, роуты, даже swagger работает — все на месте.
Остались еще большие проблемы: аутентификация пользователей (раньше это делал IIS) и какую библиотеку использовать для управления правами доступа, интеграции с LDAP.
Первую проблему можно решить поставив перед нашим приложением Apache с необходимым расширением и включив reverse proxy. Библиотеку для управления ACL еще не нашли.
Выводы
Visual Studio 2015 Update 3 и Resharper на проектах .NET Core работают идеально, с отладкой и скоростью компиляции проблем нет. Наконец-то для доставки приложения можно использовать Docker. Unit-тесты работают.
До полной уверенности нужно проводить нагрузочные тесты. Но в целом технология имеет право на жизнь, хоть и есть проблемы с тулингом: например, не поддерживается msbuild. Рекомендую .NET разработчикам присмотреться и начать использовать .NET Core в своих проектах. Для нас пока опыт только положительный.
Комментарии (32)
Nagg
11.10.2016 13:36+5Про Mono почти не говорят на конференциях, такое чувство, что технологию никто не использует
Кроме Unity и Xamarin приложений ;-)
HolyCode
12.10.2016 11:40На прошедшем в конце мая DevCon'16 пара евангелистов и премьер филды сказали, цитирую: в головном говорят, что пока Моно активно не PR'им ибо он не Cloud-Ready.
Hydro
11.10.2016 14:10А проект действительно крупный?
Меня недавно от такого шага остановило то, что в .NET Core по бороде пошел Reflection API, который я активно использовалkotov_a
11.10.2016 14:51Сейчас снял метрики (в количестве строк кода):
- 17.5к строк логика обработки данных
- 31.5к строк вызова хранимых процедур (по большей части сгенерированный код)
- 17к строк описание моделей, интерфейсов
Всего сборок 14.hameleon86
11.10.2016 22:09+2Ну это совсем не крупный проект
kotov_a
11.10.2016 22:16Однозначно не самый большой проект на C# в индустрии, но кодовой базы вполне достаточно, чтобы сделать выводы о пригодности технологии и собрать критичные грабли.
Taritsyn
11.10.2016 14:26+1Единственная проблема, что старый солюшен с csproj-проектами перестанет работать, чтобы оба проекта работали side by side есть решение. Выглядит довольно странно, но работает.
Чтобы решить эту проблему я разделил проект на 2 директории:ИМЯ_ПРОЕКТА
иИМЯ_ПРОЕКТА.Net4
. В первой директории оставил:xproj
-файл,project.json
, весь общий код, а также код, необходимый для .NET Core-версии. Во второй:csproj
-файл,packages.config
и код, необходимый для .NET Framework 4.0-версии (файлы с общим кодом добавляются в csproj-файл в виде ссылок).
force
11.10.2016 14:39С этим .NET Core иногда нужно столько переписывать, что пункт 3 из вашего списка не кажется таким уж проблемным. То, что я увидел в .NET Core:
1. Полностью переделана конфигурация. Т.е. сегодня модно в JSON, завтра в protobuf будет. Придётся переписывать весь конфиг для проектов.
2. Очень сильно перекочеврыжен рефлекшен. В большинстве случаев решается через GetTypeInfo (обезьянкой ходишь по коду и вставляешь эту строчку). Опять же, переписывать кучу всего.
3. Потеря кучи библиотек, или сидеть ждать, когда авторы переделают, или пытаться самостоятельно, если исходники есть. Банальный log4net не работает
4. Постоянная смена API. Например, Kestrel с каждой версией полностью меняет API. При этом «каждая» версия это (1.0.0-beta, 1.0.0-rc, 1.0.0). До кучи бардак в репозиториях — тот же кестрел лежит под тремя именами.
5. Периодически мелкое изменение API стандартных библиотек в самых неожиданных местах. Например у RSA нельзя получить XML с параметрами. Т.е. весь код надо обкладывать ифами и проводить тестирование
При этом по факту, под mono работает то, что и вроде бы и не должно работать, например HttpListener. Скорость — не ясно, но числодробилки на .NET реализовывать странно, а в бизнес.приложениях всё равно всё упирается в базу данных, или как у вас в SOAP, который архитектурно никогда быстрым не был.kotov_a
11.10.2016 15:19По пунткам:
- Верно, но на фоне объема нашего приложение сложность чтения настроек слишком мала, можно пренебречь.
- Согласен полностью, мартышкин труд.
- Мы используем: Newtonsoft.Json, StackExchange.Redis.StrongName, Npgsql. Такая проблема действительно есть, сам жду когда спортируют мой любимый MiniProfiler. NLog не используете?
- Именно поэтому ждал RTM, чтобы не вникать в эти мелкие изменения. Сейчас такой проблемы не заметил, на версию 1.0.1 обновились без сложностей.
- Все верно, так и поступили.
Это и пугает, фидбека о технологии очень мало, неизвестно, что ожидать. Проблемы с базой действительно возникают (поэтому используем Redis), но хотелось бы, чтобы рантайм языка программирования (как и стандартная библиотеки) не подводили и работали быстро.force
11.10.2016 15:333. Исторически сложился log4net — особого смысла менять, пока не видим.
Пока, имхо, стоит ждать .NET Core 2.0, смотреть что у них получится, заодно фидбека и best practices накопится. С Mono, если удастся подобрать идентичное железо попробую натравить свою числодробилку, оптимизированную под .NET, на Mono и .NET Core, посмотрю на результаты. Если не забуду, отпишусь :)
Taritsyn
11.10.2016 14:53+3Свойство Assembly у экземпляра Type — чтобы получить описание сборки у типа необходимо использовать метод-расширение GetTypeInfo(). Ломает совместимость с текущим кодом, особенно в ресурсных файлах.
Это является проблемой только тогда, когда нужно поддерживать 2 версии проекта (для .NET Core и .NET Framework 4.0).
Чтобы сгладить различия между разными реализациями Reflection, я использую полифиллы (например, TypeInfo.cs и TypeExtensions.cs).
С генерацией ресурсных файлов сложнее. Пришлось отказаться от стандартных генераторов в пользу собственного, написанного под Sake и KoreBuild.ForNeVeR
11.10.2016 15:18Воу, вот за генератор ресурсов спасибо.
Эх, а он у вас тоже только строковые ресурсы поддерживает, да? Никаких там
ImageList
в него не запаковать? :(Taritsyn
11.10.2016 16:33Да, только строковые ресурсы. За основу брал код стандартного генератора от Microsoft.
mayorovp
11.10.2016 14:54По поводу SOAP немного не понял. Что именно не работает в .NET Core: HttpBinding вообще или всего лишь шаринг порта + активация?
kotov_a
11.10.2016 15:27Нет веб-сервисов как таковых (WCF, aspx, svc и так далее). Соответственно, байндингов тоже нет. Есть ASP.NET Core, который из коробки поддерживает только REST.
mayorovp
11.10.2016 15:29Там в примере с msdn есть строчка:
var encoder = binding.CreateBindingElements().Find<MessageEncodingBindingElement>()?.CreateMessageEncoderFactory().Encoder;
Значит, как минимум библиотеки WCF доступны.
kotov_a
11.10.2016 15:36Полагаю, строчка относится к .NET 4.x. В .NET Core есть поддержка клиентского WCF и есть всеобщий молебен с просьбами поддержать серверный. Там же есть несколько ссылок на проекты, которые в какой-то мере могут заменить WCF.
mayorovp
11.10.2016 15:38-1Эта строчка относится к .NET Core, потому что является частью примера по запуску WCF-службы на ASP.NET Core.
dotnetdonik
12.10.2016 11:34Если запустить ASP.NET Core под 4.6, тогда и service model будет доступна. Собрать проект, который референсит service model как netcoreapp на текущий момент нельзя, насколько помниться.
mayorovp
11.10.2016 15:41Немного пояснений по svc-файлам. svc-файл — это не веб-сервис как таковой, а механизм его активации. Когда запрос попадает на svc-файл, обработчик svc-файлов конструирует и настраивает ServiceHost, после чего соответствующий URL перестает обслуживаться IIS и начинает обслуживаться ServiceHost.
После этого те запросы, которые пришли на svc-файл в IIS, тихонько "подкладываются" во входную очередь http-транспорта.
k0st1x
12.10.2016 11:22> Что будет с Mono вне Xamarin вопрос. Будет ли Microsoft одновременно развивать 2 конкурирующие реализации .NET?
у Mono Project есть публичные доски в трелло для слежки за процессом разработки, оно же и является roadmap'ом
https://trello.com/monoproject1
HolyCode
12.10.2016 11:23Хороший вектор на переход к Core. По поводу аутентификации была такая же проблема, находил статейку где учётки и права можно с IIS портировать в базейку, а её уже юзать под Core Identity. Я конечно не вижу всей Вашей картины, но покапать можно туда.
xeon
12.10.2016 15:25+1Удивляет, что не упомянуты проблемы сторонних библиотек. У нас в солюшене 100+ проектов и десятки nuget пакетов и сторонних библиотек, далеко не все из которых имеют поддержку .net core и в принципе могут работать на Linux, даже под mono. Не очень ясно, когда можно будет взлетать со всем этим хозяйством.
caballero
12.10.2016 16:06-3переход на core вряд ли сейчас оправдан — майкрософт может еше не один раз перевернуть там все с ног на голову как недавно.
Лично я не вижу пока особого смысла запускать .NET проекты под линуксом. Обычно если разраб выбирает .NET то он выбирает и остальной стек майкрософта начиная с самой платформы и заканчивая mssql например. Опять же множество библиотек и наработок которые не будут выполнятся под линуксом пока не будут портированы.
ИМХО пока основная фишка core — это опенсорс. Но с таким же успехом можно писать приложения на яве — тоже как правило опенсорс и кросплатформеность, Не говоря про количество библиотек несравнимое со всем дотнетом вместе взятым.
.
.
323066
18.10.2016 10:39Где-то с полгода назад мы пробовали перевести крупный .NET проект с 12-летней историей на Mono. В связи с плавным переходом на микросервисную архитекруту, просто завораживала возможность ранать и деплоить все единообразно(docker, kubernetes, все дела).
За несколько человеко-недель удалось собрать проект под моно и поправить невероятно странные ошибки, которые возникали из-за отличий работы неоторых библиотек на .NET и Mono(в частности ReLinq просто собирал разные expression trees под обеими платформами). Потом запустили перформанс тесты, которые у нас есть в неплохом количестве. Результаты оказались удручающими. Некоторые сервисы просели в разы. У нас в проекте много бизнес-логики, но также очень много работы с деревьями выражений, свой QueryProvider. Пытались разобраться какие места конкретно тормозят, но оказалось не так-то просто. Профайлер под моно грустный(как и monodevelop). Пытались вырезать из конкретного сервиса логику, до тех пор пока не доберемся до места где происходит различие, но не особо получилось. Кажется, что рефлексия, работа со строками, сборка мусора под mono проседает, но 100% до конца не разобрались. В общем провал.
Ждем нативного докера под windows.
Sterk
Что в конечном итоге с производительностью?
kotov_a
Полноценное нагрузочное тестирование еще не проводили. Методы в духе: получить запрос по REST, сходить в базу (MS SQL, PosgreSQL), сохранить результат в Redis, отдать ответ клиенту — работают гарантированно не медленней, аналогичной реализации в .NET 4.6.1.