Высокопроизводительные сервисы на Crystal, ведение от новичка

в 15:40, , рубрики: crystal, ruby, высокая производительность, Программирование

Введение

DISCLAIMER

мнение автора может не совпадать с вашим мнением, добро пожаловать в комментарии.

В доисторические времена высокопроизводительные Web-приложения можно было писать, в основном, на C или C++. Поддерживать такие приложения было не просто дорого, а очень дорого.

Потребность в среде программирования, в которой удобно писать, читать и изменять высокопроизводительные приложения, появилась давно.

В первую очередь термины "производительность" и "надежность" относятся к Erlang. В своей нише он великолепен, но синтаксис заставляет желать лучшего. Собственно, именно поэтому появился Elixir, но речь сейчас не об этой экосистеме.

Если же немного снизить планку надежности, то здесь перед нами широкий выбор, включая Node, Go, Nim и Crystal. Можно взглянуть на типичные сравнительные данные по производительности, включая более обширные.

Все эти среды программирования предлагают сборщик мусора, что уменьшает сложность поддержки кода.

При это Node предлагает всем понятный язык программирования (и вариации на тему), но динамическая типизация снижает производительность в несколько раз относительно других претендентов.

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

Так почему же Crystal?

LLVM

Crystal предоставляет фронтэнд для инфраструктуры LLVM, позволяя компилировать код под все поддерживаемые платформы (x64, ARM, Web Assembly etc.).

Статическая типизация без указания типов

Crystal способен в большинстве случаев сам вывести типы используемых данных.

Например, мы где-то отправляем в метод speak в одном случае строку, в другом случае целое число.

В таком случае Crystal на этапе компиляции знает, что метод speak принимает параметр выведенного типа String | Int32.

Мы можем написать такой код:

speak(15)
speak("Whatever")

def speak( thing )

  thing.say_every # Ошибка компиляции, у переменной thing тип (Int32 или String), а у него нет метода say_every

  # Здесь можно пользоваться методами класса Object, а также всех классов, общих предков String и Int32
  if thing.is_a?(String)
    # здесь Crystal гарантирует нам, что у переменной thing тип String, и можно пользоваться его методами.
    return thing.sub(/Athes+/, "")
  elsif thing.responds_to?(:to_s)
    # Здесь Crystal гарантирует нам, что у thing есть метод to_s, и его можно вызвать 
    return thing.to_s
  end
end

Важно отметить, что все значения являются объектами, включая числа и строки. Цена всеобъектности — нулевая, ибо операции над объектами определены на этапе статического анализа в компиляторе.

Проверка на пустое значение

В общем случае специальная проверка в коде на пустое значение не требуется. Например, в этом примере попытка передачи пустого значения приведет к ошибке компиляции, так как my_string имеет тип (String или Nil), а у типа Nil нет метода  upcase:

if rand(2) > 0
  my_string = "hello world"
end

puts my_string.upcase

Модель параллелизма

Crystal поддерживает зеленые потоки, каналы, и опцию SO_REUSEPORT — аналогично Go. В настоящее время идёт работа над качественной поддержкой многопоточности на уровне операционной системы, что приведет к эффективным приложениям на нескольких процессорных ядрах.

Библиотеки

В Crystal фактически встроен свой менеджер зависимостей — Shards.

Функциональность аналогична ruby bundler, perl carton etc. При этом зависимости описываются в shards.yml проекта.

BDD, общий интерфейс по работе с РСУБД и прочие приятности включены в стандартную библиотеку.

Я не стану упоминать различные библиотеки для анализа данных и прочая, для этого есть список Awesome Crystal.

Из того, что не входит в стандартную поставку, но полезно для web-приложений:

Kemal

require "kemal"

# Matches GET "http://host:port/"
get "/" do
  "Hello World!"
end

# Creates a WebSocket handler.
# Matches "ws://host:port/socket"
ws "/socket" do |socket|
  socket.send "Hello from Kemal!"
end

Kemal.run

crystal-pg

PG_DB.query_one("select ARRAY[1, null, 3]", &.read(Array(Int32?))
# => [1, nil, 3]

PG_DB.query_one("select '{hello, world}'::text[]", &.read(Array(String))
# => ["hello", "world"]

Remarkdown

require "remarkdown"

Remarkdown.to_html("Hello **world**")

Замечания

  • Надо сказать, что изучил Crystal я на этих выходных, успев выпустить один проект (благодаря сходству синтаксиса с Ruby). И эта статья является точкой зрения новичка в мире Crystal.

  • Язык пока в процессе реализации, так что большие проекты пока на нём делать не стоит. Версия 1.0 планируется в конце года, но это неточно.

Автор: akzhan

Источник

Поделиться

* - обязательные к заполнению поля