Сериал "Тайна "Взгляни""

Java задняя часть Архитектура распределенный
Сериал "Тайна "Взгляни""

⚠️Эта статья является первой подписанной статьей сообщества Nuggets, перепечатка без разрешения запрещена.


причина события

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

image-20210907025636984.png


Кодирование, тестирование и проверка кода выполняются за один раз, а затем приходит уведомление об отказе.Архитектор сказал, что при реализации интерфейса сериализации будьте осторожны, чтобы не забыть конфигурациюserialversionUID, а также очень интимно рассказал мне, что у IDEA есть плагин, который может автоматически генерировать UID, и его рекомендуется скачать и использовать (Адрес плагина IDEA serialversionUID), после настройки в соответствии с требованиями, тестирования, компиляции и публикации за один раз, введите сегодняшний режим сна (😎)


испуг во сне

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

Проблемы онлайн? При чем тут я (🤪) Это точно не моя проблема, но на всякий случай вспомним, что мы сегодня делали.

**Что вы наделали? **Система средней платформы онлайн. **Что изменилось? **Добавлен интерфейс сериализации для некоторых классов и добавленserialversionUID... Что будет причиной этого?Ошибка вызова интерфейса... COE...

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


Нани? Мы не используем сериализацию Java?

Оглядываясь назад на то, что я знаю о сериализации, я открывал различные статьи о сериализации, и все они указывают мне на ответ:Мое изменение определенно повлияет на сериализацию, программа сообщит об ошибке, как показано ниже.

Exception in thread "main" java.io.InvalidClassException: ser.demo.StuDemo; local class incompatible: stream classdesc serialVersionUID = 6395135316924936201, local class serialVersionUID = 1
	at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:616)
	at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1843)
	at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1713)
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2000)
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1535)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:422)
	at ser.demo.App.main(App.java:27)

В настоящее время в Интернете нет сообщений об ошибках, есть только одна возможность, а именно: нашRPC框架Собственный метод сериализации не используется. Архитектор был в нерешительности.После консультации, как я догадался, он также узнал от архитектора еще несколько методов сериализации, таких как: MessagePack, Hessian и т.д.


Общие методы сериализации

Java-сериализация

Java-сериализацияЭто преобразование объекта в массив байтов двоичного представления и достижение цели сохранения путем сохранения или передачи этих двоичных массивов.

Чтобы добиться сериализации, необходимо реализовать интерфейс java.io.Serializable. Десериализация — это процесс, противоположный сериализации, то есть процесс преобразования бинарных массивов в объекты.При десериализации необходимо иметь шаблон исходного класса для преобразования Основной метод восстановления объекта заключается в следующих двух методах: Роль интерфейса Serializable заключается в том, чтобы определить, реализована ли сериализация и согласованы ли объекты до и после.

Сериализация: java.io.ObjectOutputStream#writeObject0

Десериализация: java.io.ObjectInputStream#readObject0

Взяв в качестве примера тестовый класс (StuDemo), сериализованный результат выглядит следующим образом:

// 序列化
FileOutputStream fos = new FileOutputStream("C:\\Users\\Kerwin\\Desktop\\log\\object.out");
ObjectOutputStream oos = new ObjectOutputStream(fos);
StuDemo demo = new StuDemo("Kerwin");
oos.writeObject(demo);
oos.flush();
oos.close();
   
// 结果如下
//  sr ser.demo.StuDemoX??莅	 L namet Ljava/lang/String;xpt Kerwin

Куча перевёрнутых символов, но всё равно видно, что содержимое файла примерно указывает на некий класс, какие там поля, соответствующие значения и прочая информация.


Сериализация MessagePack

MessagePack(сокращенно Msgpack) — это эффективный формат двоичной сериализации, который позволяет обмениваться данными между языками, такими как JSON, но он быстрее и меньше, чем JSON.

Быстрее и меньше означает более высокую производительность, как это достигается?

Когда Msgpack сериализуется, поля не будут помечены ключом и будут храниться только в порядке полей.Подобно массиву, его метод кодированиятип + длина + содержание,Следующее:

image-20210907041011662.png

Этот эффективный метод кодирования имеет некоторые ограничения, такие как:

  • Сервер не может добавлять поля в любом месте по своему желанию, потому что, если клиент не обновится, десериализация завершится ошибкой.
  • Инструментарий класса коллекции, предоставляемый сторонним пакетом, нельзя использовать в качестве возвращаемого значения.

Он используется следующим образом:

// 其中 StuDemo 类需要增加 @Message 注解标识需要被MessagePack序列化
// MessagePack 序列化方式不需要依赖 Serializable
public static void main(String[] args) throws IOException {
    StuDemo demo = new StuDemo("Kerwin");
    MessagePack pack = new MessagePack();

    // 序列化
    byte[] bytes = pack.write(demo);

    // 反序列化
    StuDemo res = pack.read(bytes, StuDemo.class);
    System.out.println(res.getName());
}

PS: RPC-инфраструктура нашей компании в настоящее время использует метод сериализации MessagePack, и поэтому при настройке serialVersionUID выше не возникает проблем. Точно так же, с учетом ограничений базовой сериализации, в нашем новом документе также четко упоминаются вышеуказанные ограничения, такие как поля, которые должны быть добавлены в конце и т. д.


сериализация гессиана2

Hessian — это динамическая типизированная, двоичная, компактная и переносимая на разные языки структура сериализации.На основе Hessian производительность и степень сжатия Hessian2 значительно улучшены.

Hessian будет хранить все атрибуты сложных объектов в структуре, аналогичной Map для сериализации, поэтому, когда в родительском классе и подклассе есть переменные-члены с тем же именем, он сначала сериализует подкласс, а затем сериализует родительский класс, поэтому он приведет к тому, что значение переменной-члена с тем же именем подкласса будет перезаписано родительским классом и т. д.

Он имеет восемь основных целей дизайна,Официальный сайт:

  • Типы сериализации должны быть самоописываемыми, т. е. не требуются определения внешней схемы или интерфейса.
  • Должен быть независимым от языка, включая поддержку языков сценариев
  • Должен быть доступен для чтения или записи за один проход
  • Должен быть максимально компактным (сжатым)
  • должно быть просто
  • должно быть как можно быстрее
  • Строки Unicode должны поддерживаться
  • Должен поддерживать 8-битные двоичные данные
  • Шифрование должно поддерживаться

Он используется следующим образом:

public class StuHessianDemo implements Serializable {

    private static final long serialVersionUID = -640696903073930546L;

    private String name;

    public StuHessianDemo(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
public static void main(String[] args) throws IOException {
    StuHessianDemo hessianDemo = new StuHessianDemo("Kerwin");

    ByteArrayOutputStream stream = new ByteArrayOutputStream();
    HessianOutput hessianOutput = new HessianOutput(stream);
    hessianOutput.writeObject(hessianDemo);

    ByteArrayInputStream inputStream = new ByteArrayInputStream(stream.toByteArray());

    // Hessian的反序列化读取对象
    HessianInput hessianInput = new HessianInput(inputStream);
    System.out.println(((StuHessianDemo) hessianInput.readObject()).getName());
}

// 结果:Kerwin

Основание для выбора

Из вышеизложенного мы узнали о нескольких часто используемых методах сериализации, их преимуществах и недостатках.Например, MessagePack — это максимальное сжатие и скорость, а Hessian2 полагается на интерфейс Serializable.На основе обеспечения безопасности и информативности, как как можно больше Стремление к использованию пространства, эффективности и т. д., а метод сериализации Java всегда подвергался критике, и сложно дождаться элегантного зала Поэтому, когда каркас RPC выбирает базовый метод сериализации, необходимо выбирать определенную последовательность в соответствии со своими потребностями.

Выбор основан на следующем, с приоритетом от высокого к низкому:

image-20210907051517893.png


небольшая мысль

Статус сериализации JSON

На самом деле сериализация JSON является наиболее знакомым методом сериализации. Для него не требуется реализовывать сам интерфейс Serializable. Почему большинство RPC-фреймворков не используют его в качестве метода сериализации по умолчанию?

После понимания вышеприведенного содержания мы знаем, что ключ по-прежнему заключается в производительности, эффективности и накладных расходах, поскольку JSON — это структура сериализации текстового типа, которая использует KEY-VALUE для хранения данных, что является дополнительным пространством для сериализации. больше, не говоря уже о необходимости полагаться на отражение при десериализации, поэтому производительность еще больше снижается.

Однако сам JSON чрезвычайно удобочитаем, поэтому он используется в качестве стандарта де-факто для протокола HTTP в Интернете.


Зачем настраивать serialVersionUID

В «Effect Java» упоминается предложение:

Независимо от выбранного вами метода сериализации, объявите явный UID сериализованной версии для каждого написанного вами сериализуемого класса.

Зачем архитектору напоминать мне о его реализации? Почему в книге так сказано?

Полное имя разложенного serialVersionUID: последовательный UID версии, последовательный UID версии, каждый сериализуемый класс имеет длинное поле для явного указания номера, если кодер не определяет его, система будет использовать структуру этого класса. Криптографическая хэш-функция. (SHA-1), чтобы идентификационный номер автоматически генерировался во время выполнения, на который влияют имя класса, имя интерфейса, общедоступные и защищенные переменные-члены, после внесения связанных изменений, таких как добавление важных общедоступных методов, которые повлияют на UID. , что приводит к возникновению исключений.

Так что это дело привычки и избежание потенциальных рисков.


Суммировать

К этому моменту мы узнали, что сериализация Java, которую мы изучили ранее, настолько непрактична (даже до такой степени, что на нее можно жаловаться), и мы также знаем основные секреты некоторых мер предосторожности при использовании фреймворка (таких как добавление полей в MsgPack). Ниже приведены некоторые небольшие предложения по сериализации:

  1. независимо от того, зависим он или нетSerializable, параметры интерфейса рекомендуются для реализации интерфейса сериализации.
  2. Если вы реализуете интерфейс сериализации, вы должны сами реализовать serialVersionUID.
  3. Объекты параметров интерфейса не должны использовать специальные типы данных (такие как сторонние коллекции MsgPack и т. д.), чрезмерно сложные структуры (наследование и т. д.), иначе возникнет множество необъяснимых проблем.
  4. Когда возникает несогласованность данных сервера/клиента, первое, что приходит на ум, — это проблема сериализации, и тщательно исследуются характеристики текущего метода сериализации.

Если вы считаете этот контент полезным:

  1. Конечно, ставьте лайки и поддерживайте~
  2. Кроме того, вы можете искать и следить за официальной учетной записью "это Кервин», давайте вместе пойдем по дороге технологий~ 😋

использованная литература

  1. Официальный сайт MsgPack
  2. "Эффект Ява"