Найти тему

Как я писал биржевого робота для Quik. Часть 2 (Java->Python)

Материал также доступен в виде видео:

Пока не забыл, хотелось бы обсудить такую актуальную для программистов во все времена тему, как сериализацию/десериализацию объектов.

Для тех кто в танке, программы выполняются на разных машинах и обмениваются данными по сети.

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

Между тем, перед тем как передать объект, хранящийся в памяти на одном узле, его надо как-то закодировать, так как на том конце может быть другая программа, другой язык, да и вообще другой тип устройства, например, телефон а не компьютер.

И вот для промежуточного хранения файлов используют разные форматы.

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

Но в какой-то момент решили, что уж больно много в нем лишних символов передается, и надо что-то полегче.

Придумали Json, и этот формат с фигурными скобочками вместо тегов де-факто доминирует в передаче пакетов; многие таже используют его в качестве формата хранения данных. Например, noSql - БД mongoDB, и мало этого, она его использует еще и в качестве языка запросов, что как по мне, какая-то дичь, даже если забыть про остальные проблемы, которых я с ней нахлебался.

Но вернемся к нашим баранам.

Используя фреймворк Akka, который построен на очередях сообщений между Акторами (Actors) и поддерживает распределенную установку частей программы, я постоянно наблюдал сообщения о том, что я балбес, поскольку использую дефолтную Java сериализацию, что не рекомендуется.

Я, в свою очередь, считал, что балбесы те, кто выводит мне такие сообщения, поскольку между домашним сервером и клиентом у меня гигабитная сетка, и на объем мне наплевать; а что может быть быстрее, чем нативная сериализациия разработчиков Java - уж они-то знают, как лучше сохранять и загружать объекты.

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

Тем не менее, в какой-то момент мне понадобилось прочитать те же данные из Питона (Python), который ясное дело, Java объекты не жрет.

Для сериализации я решил использовать библиотеку Spray, которая делала все практически из коробки, нужно было только указать DefaultJsonProtocol и задать несколько имплисит JsonFormat для вложенных кейс классов (классов для данных - в Scala).

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

Интересный факт - стандартная сериализация валится на объектах типа Map (что при гуглении приводит нас к ошибке на распараллеливании вычислений Hadoop Spark, которая тоже не решена).

А вот Json валится, только если у вас число в качестве ключа Map.

И чтобы данные сериализовались Java writeObject, я переделал все на Sequence (для джавистов - List).

Каково же было мое удивление, когда размер файлов упал вдвое (!) с 66 до 30Мб, в время чтения сократилось в 40 (!) раз - с 66,3с до 1,5с.

Вот и думайте теперь, переходить ли на новые технологии, или сидеть на том, чему вас учили 20 лет назад.

Счет, как говориться на табло.

В связи с вышеизложенным, возможно, Spark можно существенно оптимизировать, отказавшись от этого вот тормозного вызова readObject.

val d = if (file.exists()) {

import java.io.FileInputStream

import java.io.ObjectInputStream

val log_start = System.currentTimeMillis()

// val is = new FileInputStream(file)

// val ois = new ObjectInputStream(is)

// val ret = ois.readObject.asInstanceOf[IndexedSeq[Seq[(Seq[MarketState], BigDecimal, BigDecimal,Long,Long)]]]

// ois.close()

// is.close()

val ret = Source.fromFile(file).mkString.parseJson.convertTo[IndexedSeq[Seq[(Seq[MarketState], BigDecimal, BigDecimal,Long,Long)]]]

logger.debug(s"---loaded ${(System.currentTimeMillis()-log_start)} ")

ret

} else {

val ret =

Await.result(Future.sequence(com.robot.data.DataProvider.GetData(tool,

sdf.parse(t1Str).getTime,

sdf.parse(t2Str).getTime,interval = 1000//60*60000

,smoothingInterval = 1000

,justChartPoints = false

,returnFuture = true)),Duration(100000,"s"))

// import java.io.FileOutputStream

// val fos = new FileOutputStream(file)

// val oo = new ObjectOutputStream(fos)

// oo.writeObject(ret)

// oo.close

// fos.close()

val writer = new PrintWriter(file);

writer.write(ret.toJson.compactPrint);

writer.close();

ret

}