T_RPC_Framework
Это фреймворк для удалённого вызова процедур (RPC).
См. проект: T_RPC_Framework_Demo
Шаги (на основе версии 3.8):
Запустите Nacos.
Импортируйте зависимости:
<dependency>
<groupId>fun.ticsmyc.rpc</groupId>
<artifactId>t-rpc-all</artifactId>
<version>3.8</version>
</dependency>
Поместите файл конфигурации trpc.properties в каталог resources (необязательно):
port=8999 # номер порта для сервера (по умолчанию — 8888)
loadbalancer=round # механизм балансировки нагрузки для клиента, можно выбрать random (случайный) или round (циклический), по умолчанию — случайный
serializer=json # сериализатор для запросов клиента, можно выбрать kryo (по умолчанию), json, protobuf или hessian
networkIO=netty #можно использовать netty и socket. Обнаружено, что netty работает быстрее
nameServiceAddress=127.0.0.1:8848 #адрес реестра
В классе конфигурации объявите:
@EnableTRPC
Предоставьте услуги:
В интерфейсе сервиса объявите:
@TRPCInterface //этот шаг предназначен для решения проблемы, когда один класс реализации реализует несколько интерфейсов.
В классе реализации сервиса объявите:
@TRPCService(group="t") // атрибут group используется для обработки ситуации, когда один интерфейс хочет зарегистрировать несколько классов реализации.
Используйте услуги:
Через аннотацию @RpcClient можно внедрить службу RPC.
@RpcClient(group = "t")
private HelloService helloService;
└─src
└─main
├─java
│ └─fun
│ └─ticsmyc
│ └─rpc
│ ├─client :клиент
│ │ ├─annotation:аннотации для использования клиентом
│ │ ├─proxy :динамический прокси
│ │ └─transport :сетевая передача
│ │ ├─bio :основан на socket
│ │ └─netty :основан на netty
│ │ ├─codec :кодирование и декодирование
│ │ └─handler :пользовательский обработчик
│ │ └─util:инструменты для клиентов
│ ├─common :общий
│ │ ├─entity :объекты для сетевого взаимодействия
│ │ ├─enumeration :перечисления
│ │ ├─exception :исключения
│ │ ├─factory :фабрики
│ │ └─serializer :сериализация
│ │ └─impl
│ ├─nacos
│ │ ├─loadbalance 负载均衡器
│ │ │ └─impl
│ │ └─registry 注册中心
│ │ └─impl
│ ├─server :сервер
│ │ ├─annotation:аннотации для клиентов
│ │ ├─handler :бизнес-логика: вызов соответствующего сервиса на основе полученной информации
│ │ ├─provider:локальные сервисы сервера
│ │ │ └─impl
│ │ └─transport :сетевая передача
│ │ ├─bio
│ │ └─netty
│ │ ├─codec
│ │ └─handler
│ └─test
└─resources
Другие версии см. в каталоге [Historical version record.md](./wiki/Historical version record.md)
Конкретная реализация: Существует три ситуации, которые можно определить с помощью методов класса AopUtils.
Этот метод не всегда надёжен. Обычно при написании динамического прокси внутренний класс сохраняется, но Spring динамический прокси сохраняет тип TargetSource. Если это SingletonTargetSource, то будет сохранён один экземпляр проксируемого класса, но если это другой тип, структура проксируемого класса может измениться, и этот метод поиска проксируемого класса перестанет работать.
Почему нельзя использовать IOC-контейнер для внедрения зависимостей так же, как MyBatis, и нужно создавать собственные аннотации и внедрять их вручную?
MyBatis использует ImportBeanDefinitionRegistrar (обрабатывается BeanFactoryPostProcessor), чтобы получить метаданные конфигурации (путь к базовому пакету). Интеграция Spring с Mybatis работает следующим образом:
Нетрудно заметить, что каждый интерфейс Mapper имеет только одну определённую реализацию, поэтому для каждого интерфейса Mapper можно создать FactoryBean для создания Bean.
В этом RPC-фреймворке, поскольку рассматривается случай, когда одному интерфейсу соответствует несколько реализаций, каждому интерфейсу может потребоваться несколько FactoryBeans, ситуация становится сложной. Поэтому проще создать собственные аннотации, а затем сканировать аннотации в BeanPostProcessor и генерировать прокси-классы, используя рефлексию для ручного внедрения.
Механизм повторного подключения при сбое клиента
Способ отправки запроса клиентом: сначала определяется метод запроса и группа, затем из nacos получается IP и порт поставщика услуг. Затем используется netty для инициирования сетевого запроса.
Первоначально механизм повторной попытки подключения был реализован после получения IP и порта, и если соединение не удалось, оно будет повторяться. Кажется, нет никаких проблем.
Во время тестирования было обнаружено, что из-за задержки nacos информация о поставщике услуг часто обновляется, что приводит к тому, что IP и порты, полученные клиентом, становятся устаревшими, и многократные попытки подключения всё равно не будут успешными.
Наконец, было решено повторно получать IP поставщика услуг от nacos каждый раз при повторном подключении.
Проблемы с десериализацией при сериализации JSON
JSON — это текстовый сериализатор, и при десериализации, если исходный тип неизвестен, это может привести к сбою десериализации.
Если использовать объектный тип для приёма десериализованного объекта, невозможно определить исходный тип, и он станет String или другим странным типом. Например, объект Date будет десериализован в Long, вложенный объект RpcRequest будет десериализован в LinkedHashMap и т.д.
Поэтому можно только добавить параметр Class в тело запроса, а после десериализации определить, была ли десериализация успешной. Если десериализация не удалась, она будет сериализована в двоичный формат, а исходный тип будет повторно десериализован.
В этой ситуации использование двоичного сериализатора лучше, ниже приводится сравнение нескольких двоичных сериализаторов.
Ошибка синхронизации соединения клиента, приводящая к нескольким пакетам сердцебиения на стороне сервера
Эта ошибка проявляется следующим образом: только с одним сервером установлено соединение, но каждый раз отправляется несколько пакетов сердцебиения.
bootstrap.connect(xxxx).addListener( ()->{
//Код 1
this.channel = sync.channel();
}).sync();
//Код 2
После установления соединения код 1 и код 2 выполняются синхронно в двух потоках, и невозможно гарантировать порядок выполнения кода 1 и кода 2.
Когда код 1 присваивает значение каналу, операция может произойти позже, чем код 2, что приведёт к ошибке синхронизации. Следует дождаться завершения кода 1, прежде чем выполнять код 2.
Если код 2 выполняется раньше кода 1, канал ещё не назначен, проверка вернёт null, что вызовет операцию повторного подключения. В конечном итоге система будет поддерживать несколько соединений с этим сервером, что приведёт к отправке нескольких пакетов сердцебиения каждый раз.
BeanPostProcessor вызывает сбой @Value
Сцена: хотите преобразовать конфигурацию серверной части из static в @Component. Используйте файл свойств для написания конфигурации и используйте @Value для внедрения.
Используйте InitializingBean для присвоения значений. Обнаружено, что атрибут всё ещё находится в стадии ссылки на файл конфигурации («${}» и т. д.), и значение не было заменено содержимым файла конфигурации.
Причина: экземпляры BeanPostProcesser создаются в порядке приоритета, и экземпляры с высоким приоритетом создаются перед экземплярами с низким приоритетом. Во время создания экземпляра внутренние зависимые Bean также будут созданы. Эти Bean, созданные слишком рано, не могут воспользоваться обработкой BeanPostProcesser того же уровня приоритета или более низкого уровня, поэтому @Value не будет заменён.
@Value обрабатывается AutowiredAnnotationBeanPostProcessor, и этот BeanPostProcessor также имеет уровень приоритета PriorityOrdered.
Подробности можно найти в комментарии к интерфейсу PriorityOrdered:
* <p>Note: {@code PriorityOrdered} post-processor beans are initialized in
* a special phase, ahead of other post-processor beans. This subtly
* affects their autowiring behavior: they will only be autowired against
* beans which do not require eager initialization for type matching.
Если вам нужно зарегистрировать службу через BeanPostProcessor во время запуска Bean, вы должны убедиться, что содержимое файла конфигурации было прочитано до инициализации Bean-контейнера, поэтому статическое использование по-прежнему подходит, но использование статического метода также легко вызвать nullptr.
Проблема автоматического вызова toString в idea при отладке
Idea автоматически вызывает toString для классов в процессе отладки и отображает результаты в интерфейсе. Если не сделать специальную обработку, то при вызове метода toString для класса-прокси, созданного на основе интерфейса rpc сервиса клиента, также будет вызвана логика rpc, что приведёт к отправке запроса на вызов java.lang.Object_t. Это естественно вызовет ошибку запроса.
Решение: добавить логику короткого замыкания в метод invoke динамического прокси. Если вызывается метод класса Object или специфический метод класса-прокси, выполнять локальный вызов, не задействуя логику rpc. (Решение в MyBatis для MapperProxy.)
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Комментарии ( 0 )