By Qiqing, from Xianyu Technology
The Xianyu IM framework was built in 2016-2017. During this period, many iterations and upgrades led to the accumulation of historical burdens. After the IM interface became Flutter, the architecture became more complex. The development level summarized that the current Xianyu architecture mainly has the following problems:
There is a summary below of the main problems of Xianyu IM's current architecture from the public's perspective:
Xianyu started a special IM architecture upgrade project this year to solve the current IM pain points, focusing on solving the double-end consistency pain points in the client. The preliminary plan is to realize a unified Android/iOS logical architecture across ends. In the current industry, cross-end schemes can be preliminarily classified into the following structure. Cross-end schemes at the GUI level include Weex, ReactNative, H5, and Uni-App. Most of its memory models need to be bridged to Native mode storage. At the logical level, cross-end schemes generally include C/C++ and other virtual machine-independent languages to implement cross-end, but assembly language is also feasible. In addition, there are two architectures independent of the system above, namely Flutter and KMM (Google implements a similar Flutter architecture based on Kotlin), in which Flutter runs a specific DartVM and mounts memory data into its isolate.
Considering that Xianyu is the cutting-edge explorer of Flutter, Flutter is preferred in the scheme. However, Flutter's isolate is more like the concept of a process (the underlying implementation does not use process mode.) Compared with Android, multiple threads of Android's Dalvik virtual machine run to share a memory Heap in the same process scenario, while DartVM's Isolate runs to isolate their respective Heap. Therefore, the communication mode between isolates is relatively complicated. It needs to go through the serialization and deserialization process. The whole model is shown in the following figure:
If the Flutter application is implemented according to the official hybrid architecture and multiple FlutterAcitivty/FlutterController are opened, multiple Engines will be generated at the bottom layer, and there will be multiple isolates corresponding to them. Isolate communication is similar to process communication (similar to socket or AIDL.) Here, drawing on the design concept of FlutterBoost, the FlutterIM architecture shares the Engine of multiple pages, and the memory model will naturally support shared reading. The schematic diagram is listed below:
The following figure shows an old architecture solution. Its core problems mainly focus on the poor abstraction of Native logic. At the logic level, multi-thread concurrency is also designed to multiply the problems. Android, iOS, and Flutter have complicated interaction, high development and maintenance costs, serious core layer coupling, and no plug-in concept.
Considering the historical architecture, the new architecture design has evolved:
From top to bottom, the architecture consists of the business layer, distribution layer, logic layer, and data source layer. The data source layer originates from push or network requests and is encapsulated in the Native layer. The Flutter plug-in throws the message protocol data to the core logic layer on the Flutter side. After processing, the data source layer becomes the Entity of Flutter DB, and some message protocol entities are attached to the entity. The core logic layer flattens and packages the complicated data, mounts the session memory model data or the message memory model data in the distribution layer, and distributes it to the business logic through the subscription of observer mode. Flutter IM focuses on transforming the logic layer and the distribution layer, encapsulating and isolating the IM core logic and the business-level data model. After the core logic layer interacts with the database, the data is encapsulated into
moduleData in the distribution layer. It is distributed to the business layer data model by subscription. DB is also heavily dependent in the IM model. Individuals carry out comprehensive encapsulation and solution of DB database management to realize a lightweight and high-performance Flutter DB management framework.
The DB storage of Flutter IM architecture depends on the database plug-in. The mainstream plug-in is Sqflite. Its storage model is listed below:
According to the DB storage model of the Sqflite plug-in in the figure above, there will be two waiting queues. One is the synchronous execution queue of the Flutter layer, and the other is the thread execution queue of the Native layer. Its Android implementation mechanism is HandlerThread. Therefore Query/Save reads and writes in the same thread queue, resulting in slow response speed. It is easy to cause DB SQL accumulation. In addition, the cache model is missing. Therefore, the following improvement solutions are customized for personal customization:
Flutter will preferentially obtain the query from the Entity Cache layer when designing queries through the primary key of the table. If the cache does not exist, the Sqflite plug-in is used to query and transform the Sqflite plug-in to support sync/Async synchronous and asynchronous operations. There will also be synchronous thread queues and asynchronous thread queues corresponding to the Native side to ensure the data throughput rate.
The IM architecture relies heavily on DB databases, and the industry does not have a complete database ORM management solution currently. Referring to Android's OrmLite/GreenDao, I designed a Flutter ORM database management solution. Its core idea is listed below:
Flutter does not support reflection, so it cannot operate like Android's open-source database mode, but it can use APT mode to bind Entity and Orm Entity together. Operation OrmEntity is Operation Entity, and the entire code style design is very similar to OrmLite. The reference code is listed below:
FlutterIM architecture is mainly divided into two granularities: session and message in the memory data model. The session memory data model is entrusted to the
SessionModuleData, and the message memory data model is entrusted to the
MessageModuleData. Session memory data has a root node RootNotice. Then, its mount
PSessionMessageNotice child node set (here
PSessionMessageNotice is the ORM mapped session DB table model.) Message memory data will be managed by a MessageConatiner container. The PMMessage (PMMessage is the message DB table model mapped by ORM) message collection in this session is mounted internally.
According to the previous chapter,
PSessionMessageNotice designed an OrmEntity Cache. Considering that the number of sessions in IM is limited, PSessionMessageNotice are all directly cached in the Cache. The advantage of this approach is that the session data elements are the same object in the cache, which is easy to ensure the data consistency of repeated reading and writing. However, considering the particularity that its number can be unlimited,
PSessionMessageNotice is mounted here in the memory management of the MessageContainer. The number of PMessages in the container is verified at the time of exiting the session. Proper scale-out can reduce the memory overhead. The following figure shows the model:
Flutter IM state management scheme is relatively simple. The session/message dimension of the data source is implemented by the subscription distribution method of observer mode. The architecture is similar to EventBus mode. No matter you use
provider, they have little impact on the page-level state management. It is more important to keep a plug-in abstraction at the core. The architecture is listed below:
The following is the current message synchronization model. There are multi-thread concurrency scenarios in the model, such as ACCS Thread, Main Thread, Region Thread, which leads to the problem of high concurrency of multi-threads. The isolation scheme of native push and network request synchronization is handled through the Lock mechanism and queue frequency reduction, which is complicated and error-prone. The overall Region Version Gap determines whether there is a domain hole and then performs domain synchronization to supplement data. The improved synchronization model is listed below:
There is no multi-thread scenario on the Flutter side. The Handler MSMQ is implemented synchronously and asynchronously through a kind of tag bit conversion. The architecture is much clearer and simpler, avoiding the overhead and synchronization problems caused by locks.
JS cross-end is not safe, and C++ cross-end cost is a bit high, so Flutter will be a better choice. At that time, the fundamental purpose of upgrading FlutterIM architecture was never because of Flutter. It was due to the heavy historical burden, high maintenance cost at the code level, poor extensibility of new services, uncoordinated workforce ratio, and continuous feedback of public opinion on difficult and miscellaneous diseases that caused us to explore new solutions. After verifying the logical cross-end feasibility of the Flutter mode in the ultra-complex business scenario of Xianyu IM, Xianyu will always keep the cutting-edge exploration on the Flutter road and finally be able to feedback to the ecosystem. To sum up, the exploration process lies in the courage to take the first step. Then, you will be surprised and discovered.
XianYu Tech - July 31, 2019
XianYu Tech - September 11, 2020
XianYu Tech - November 11, 2021
卓凌 - September 8, 2020
XianYu Tech - September 11, 2020
Alibaba Clouder - September 21, 2020
Accelerate software development and delivery by integrating DevOps with the cloudLearn More
Web App Service allows you to deploy, scale, adjust, and monitor applications in an easy, efficient, secure, and flexible manner.Learn More
A one-stop, cloud-native platform that allows financial enterprises to develop and maintain highly available applications that use a distributed architecture.Learn More
Explore Web Hosting solutions that can power your personal website or empower your online business.Learn More
More Posts by XianYu Tech