REST-сервер + тяжелый JavaScript-клиент был принципом, которому я следовал в своей недавней работе.
REST-сервер был реализован в node.js + Express + MongoDB (очень хорошая производительность записи) + Mongoose ODM (отлично для моделирования данных, включая проверки) + CoffeeScript (я бы сейчас использовал ES2015 вместо этого), который хорошо сработал для меня. Node.js может быть относительно молодым по сравнению с другими возможными серверными технологиями, но это позволило мне написать надежный API с интегрированными платежами.
Я использовал Ember.js в качестве среды JavaScript, и большая часть логики приложения была выполнена в браузере. Я использовал SASS (в частности, SCSS) для предварительной обработки CSS.
Ember - зрелая структура, поддерживаемая сильным сообществом. Это очень мощный фреймворк, в котором в последнее время проводится много работы, ориентированной на производительность, например, новый движок рендеринга Glimmer (по мотивам React).
Ember Core Team находится в процессе разработки FastBoot , который позволит вам выполнить вашу логику Ember JavaScript на стороне сервера (в частности, node.js) и отправлять предварительно отрендеренный HTML-код вашего приложения (что будет обычно запускается в браузере) пользователю. Это здорово для SEO и пользовательского опыта, так как он не ждет так долго, пока страница не появится.
Ember CLI - это отличный инструмент, который помогает вам организовать ваш код, и он хорошо масштабировался с ростом кодовой базы. Ember также имеет свою собственную аддонную экосистему, и вы можете выбирать из множества Ember Addons . Вы можете легко взять Bootstrap (в моем случае) или Foundation и добавить его в свое приложение.
Чтобы не обслуживать все через Express, я решил использовать nginx для обслуживания изображений и клиент с большим количеством JavaScript. Использование прокси-сервера nginx было полезно в моем случае:
upstream app_appName.com {
# replace 0.0.0.0 with your IP address and 1000 with your port of node HTTP server
server 0.0.0.0:1000;
keepalive 8;
}
server {
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;
client_max_body_size 32M;
access_log /var/log/nginx/appName.access.log;
error_log /var/log/nginx/appName.error.log;
server_name appName.com appName;
location / {
# frontend assets path
root /var/www/html;
index index.html;
# to handle Ember routing
try_files $uri $uri/ /index.html?/$request_uri;
}
location /i/ {
alias /var/i/img/;
}
location /api/v1/ {
proxy_pass http://app_appName.com;
proxy_next_upstream error timeout invalid_header http_500 http_502
http_503 http_504;
proxy_redirect off;
proxy_buffering off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
Pro: Мне нравится разделение API & amp; клиент. Умные люди говорят, что это путь. Великолепно в теории. Кажется передовым и захватывающим.
Я могу сказать, что это также здорово на практике. Еще одним преимуществом разделения REST API является то, что вы можете использовать его позже для других приложений. В идеальном мире вы должны иметь возможность использовать один и тот же API REST не только для веб-страниц, но и для мобильных приложений, если решите написать его.
Против: не так много прецедентов. Не много примеров этого сделано хорошо. Публичные примеры (twitter.com) чувствуют себя вялыми даже отказываются от этого подхода.
Теперь все выглядит иначе. Есть много примеров использования REST API + много клиентов, использующих его.
Я считаю, что ваша основная проблема в этом коде будет заключаться в проверке границ ваших массивов.
Если вы переключитесь на небезопасный код в C # и используете математику указателя, вы сможете для достижения того же (или потенциально более быстрого) кода.
Эта же проблема ранее подробно обсуждалась в этом вопросе .
Я думаю, вы видите результаты проверки границ массивов. Вы можете избежать проверки границ, используя небезопасный код.
Я считаю, что JITer может распознавать шаблоны, такие как циклы for, которые идут вверх до array.Length, и избегать проверки границ, но не похоже, что ваш код может это использовать.
Как уже говорили другие, одним из аспектов является проверка границ. В вашем коде также есть некоторая избыточность с точки зрения доступа к массиву. Мне удалось несколько улучшить производительность, изменив внутренний блок на:
int tmp1 = array1[i];
int tmp2 = array2[k];
if (tmp1 == tmp2)
{
calc = calc - array2[i] + array1[k];
}
else
{
calc = calc + tmp1 - tmp2;
}
Это изменение снизило общее время с ~ 8,8 до ~ 5 с.
Ради удовольствия, я попытался создать это на C # в Visual Studio 2010 и взглянул на разборку JITed:
else
calc = calc + array1[i] - array2[k];
000000cf mov eax,dword ptr [ebp-10h]
000000d2 add eax,dword ptr [ebp-14h]
000000d5 sub eax,edx
000000d7 mov dword ptr [ebp-10h],eax
Они внесли ряд улучшений в джиттер в 4.0 версии CLR.
Если критический путь производительности вашего приложения полностью состоит из обработки неконтролируемых массивов, я бы посоветовал вам не переписывать его на C #.
Но тогда, если ваше приложение уже нормально работает на языке X , Я бы посоветовал вам не переписывать его на языке Y.
Чего вы хотите добиться от перезаписи? По крайней мере, серьезно подумайте о смешанном языковом решении, используя уже отлаженный код C для высокопроизводительных разделов и используя C # для получения приятного пользовательского интерфейса или удобной интеграции с новейшими богатыми библиотеками .NET.
C # выполняет проверку границ
при выполнении расчетной части в небезопасном коде C #. Работает ли она так же хорошо, как собственная реализация?
Я уверен, что оптимизация для C отличается от C #. Также вы должны ожидать, по крайней мере, небольшого снижения производительности. .NET добавляет еще один уровень к приложению с помощью фреймворка.
Компромисс - более быстрая разработка, огромные библиотеки и функции при (что должно быть) небольшой скорости.