Aneane pau nā huahana lako polokalamu hou i kekahi mau lawelawe. ʻO ka manawa pinepine, lilo nā manawa pane lōʻihi o nā kaila interservice i kumu o nā pilikia hana. ʻO ka hopena maʻamau i kēia ʻano pilikia, ʻo ia ka hoʻopili ʻana i nā noi interservice he nui i hoʻokahi pūʻolo, i kapa ʻia ʻo batching.
Inā ʻoe e hoʻohana i ka hoʻoili pūʻulu, ʻaʻole paha ʻoe e hauʻoli i nā hopena ma ke ʻano o ka hana a i ʻole ka wehewehe code. ʻAʻole maʻalahi kēia ʻano i ka mea kelepona e like me kou manaʻo. No nā kumu like ʻole a ma nā kūlana like ʻole, hiki ke ʻokoʻa nā hopena. Ke hoʻohana nei i nā hiʻohiʻona kikoʻī, e hōʻike wau i nā pono a me nā pōʻino o kekahi mau ala.
Papahana hoikeike
No ka akaka, e nānā kākou i kekahi laʻana o kekahi o nā lawelawe ma ka noi aʻu e hana nei.
ʻO ka wehewehe ʻana i ke koho ʻana i ke kahua no nā laʻanaHe mea maʻamau ka pilikia o ka hana maikaʻi ʻole a ʻaʻole pili i nā ʻōlelo kikoʻī a i ʻole platform. E hoʻohana kēia ʻatikala i nā hiʻohiʻona code Spring + Kotlin e hōʻike i nā pilikia a me nā hoʻonā. Hoʻomaopopo like ʻia ʻo Kotlin (a ʻaʻole hiki ke hoʻomaopopo ʻia) i nā mea hoʻomohala Java a me C #, ʻoi aku, ʻoi aku ka maʻalahi o ke code ma mua o Java. I mea e maʻalahi ai ka hoʻomaopopo ʻana i nā mea hoʻomohala Java maʻemaʻe, e pale wau i ka ʻeleʻele ʻeleʻele o Kotlin a hoʻohana wale i ka magic keʻokeʻo (ma ka ʻuhane o Lombok). E loaʻa kekahi mau ala hoʻonui, akā kamaʻāina maoli lākou i nā polokalamu Java āpau ma ke ʻano he ʻano static, no laila he kō liʻiliʻi kēia e hōʻino ʻole ai i ka ʻono o ke kīʻaha.
Aia kahi lawelawe ʻae palapala. Hana kekahi i palapala a waiho ʻia no ke kūkākūkā ʻana, i ka manawa e hana ʻia ai nā hoʻoponopono, a ma hope ua ʻae ʻia ka palapala. ʻAʻole ʻike ka lawelawe ʻae ʻia i kekahi mea e pili ana i nā palapala: he kamaʻilio wale nō ia o nā mea ʻae me nā hana liʻiliʻi liʻiliʻi ʻaʻole mākou e noʻonoʻo ma aneʻi.
No laila, aia nā lumi kamaʻilio (e pili ana i nā palapala) me kahi hoʻonohonoho i koho mua ʻia o nā mea komo i kēlā me kēia. E like me nā kamaʻilio maʻamau, loaʻa nā memo i nā kikokikona a me nā faila a hiki ke pane a i mua paha:
data class ChatMessage(
// nullable так как появляется только после persist
val id: Long? = null,
/** Ссылка на автора */
val author: UserReference,
/** Сообщение */
val message: String,
/** Ссылки на аттачи */
// из-за особенностей связки JPA+СУБД проще поддерживать и null, и пустые списки
val files: List<FileReference>? = null,
/** Если является ответом, то здесь будет оригинал */
val replyTo: ChatMessage? = null,
/** Если является пересылкой, то здесь будет оригинал */
val forwardFrom: ChatMessage? = null
)
He loulou nā faila a me nā loulou mea hoʻohana i nā kāʻei kapu ʻē aʻe. Eia mākou e noho nei penei:
typealias FileReference = Long
typealias UserReference = Long
Mālama ʻia ka ʻikepili mea hoʻohana ma Keycloak a kiʻi ʻia ma o REST. Pēlā nō nā faila: nā faila a me ka metainformation e pili ana iā lākou e noho ana i kahi lawelawe mālama waihona.
ʻO nā kelepona a pau i kēia mau lawelawe noi kaumaha. ʻO ia ke ʻano o ka lawe ʻana i kēia mau noi i ʻoi aku ka nui ma mua o ka manawa e hana ai lākou e ka lawelawe ʻaoʻao ʻekolu. Ma kā mākou mau papa hoʻāʻo, ʻo ka manawa kelepona maʻamau no ia mau lawelawe he 100 ms, no laila e hoʻohana mākou i kēia mau helu i ka wā e hiki mai ana.
Pono mākou e hana i kahi mea hoʻoponopono REST maʻalahi no ka loaʻa ʻana o nā memo N hope loa me nā ʻike pono āpau. ʻO ia hoʻi, ke manaʻoʻiʻo nei mākou ua aneane like ke ʻano o ka memo ma ka frontend a pono e hoʻouna ʻia nā ʻikepili āpau. ʻO ka ʻokoʻa ma waena o ke kumu hoʻohālike mua, pono e hōʻike ʻia ka faila a me ka mea hoʻohana ma kahi ʻano decrypted liʻiliʻi i mea e hoʻopili ai iā lākou:
/** В таком виде отдаются ссылки на сущности для фронта */
data class ReferenceUI(
/** Идентификатор для url */
val ref: String,
/** Видимое пользователю название ссылки */
val name: String
)
data class ChatMessageUI(
val id: Long,
/** Ссылка на автора */
val author: ReferenceUI,
/** Сообщение */
val message: String,
/** Ссылки на аттачи */
val files: List<ReferenceUI>,
/** Если являтся ответом, то здесь будет оригинал */
val replyTo: ChatMessageUI? = null,
/** Если являтся пересылкой, то здесь будет оригинал */
val forwardFrom: ChatMessageUI? = null
)
Pono mākou e hoʻokō i kēia mau mea:
interface ChatRestApi {
fun getLast(n: Int): List<ChatMessageUI>
}
ʻO ka UI postfix ke ʻano o nā hiʻohiʻona DTO no ke alo, ʻo ia hoʻi, ka mea e lawelawe ai mākou ma o REST.
ʻO ka mea kupanaha paha ma aneʻi ʻaʻole mākou e hele nei i kahi kamaʻilio kamaʻilio a ʻaʻole hoʻi i ke kumu ChatMessage/ChatMessageUI. Ua hana au i kēia me ka manaʻo i ʻole e hoʻopili i ke code o nā hiʻohiʻona (ua kaʻawale nā kamaʻilio, no laila hiki iā mākou ke manaʻo he hoʻokahi wale nō).
Hoʻokaʻawale ʻana i ka manaʻoHoʻohana ka papa ChatMessageUI a me ke ʻano ChatRestApi.getLast i ka ʻano ʻikepili List inā he Set i kauoha ʻia. He ʻino kēia i ka JDK, no laila ʻaʻole e hana ka haʻi ʻana i ke ʻano o nā mea i ka pae interface (mālama i ke kauoha i ka wā e hoʻohui ai a wehe ʻia). No laila ua lilo i mea maʻamau ka hoʻohana ʻana i kahi List i nā hihia kahi e pono ai kahi Set i kauoha ʻia (aia pū kekahi LinkedHashSet, akā ʻaʻole kēia he interface).
Ka palena nui: E manaʻo mākou ʻaʻohe kaulahao lōʻihi o nā pane a i ʻole nā hoʻoili. ʻO ia, aia lākou, akā ʻaʻole i ʻoi aku ko lākou lōʻihi ma mua o ʻekolu mau leka. Pono e hoʻouna ʻia ke kaulahao holoʻokoʻa o nā memo i mua.
No ka loaʻa ʻana o ka ʻikepili mai nā lawelawe o waho, aia nā API penei:
interface ChatMessageRepository {
fun findLast(n: Int): List<ChatMessage>
}
data class FileHeadRemote(
val id: FileReference,
val name: String
)
interface FileRemoteApi {
fun getHeadById(id: FileReference): FileHeadRemote
fun getHeadsByIds(id: Set<FileReference>): Set<FileHeadRemote>
fun getHeadsByIds(id: List<FileReference>): List<FileHeadRemote>
fun getHeadsByChat(): List<FileHeadRemote>
}
data class UserRemote(
val id: UserReference,
val name: String
)
interface UserRemoteApi {
fun getUserById(id: UserReference): UserRemote
fun getUsersByIds(id: Set<UserReference>): Set<UserRemote>
fun getUsersByIds(id: List<UserReference>): List<UserRemote>
}
Hiki ke ʻike ʻia ka hoʻolako mua ʻana o nā lawelawe o waho no ka hoʻoili ʻana i ka hui, a ma nā ʻano ʻelua: ma o Set (me ka mālama ʻole ʻana i ke ʻano o nā mea, me nā kī kū hoʻokahi) a ma o List (hiki ke loaʻa nā kope - mālama ʻia ke kauoha).
Nā hoʻokō maʻalahi
Hoʻokō naive
ʻO ka hoʻokō naive mua o kā mākou REST controller e like me kēia i ka nui o nā hihia:
class ChatRestController(
private val messageRepository: ChatMessageRepository,
private val userRepository: UserRemoteApi,
private val fileRepository: FileRemoteApi
) : ChatRestApi {
override fun getLast(n: Int) =
messageRepository.findLast(n)
.map { it.toFrontModel() }
private fun ChatMessage.toFrontModel(): ChatMessageUI =
ChatMessageUI(
id = id ?: throw IllegalStateException("$this must be persisted"),
author = userRepository.getUserById(author).toFrontReference(),
message = message,
files = files?.let { files ->
fileRepository.getHeadsByIds(files)
.map { it.toFrontReference() }
} ?: listOf(),
forwardFrom = forwardFrom?.toFrontModel(),
replyTo = replyTo?.toFrontModel()
)
}
Ua maopopo loa nā mea a pau, a he mea nui kēia.
Hoʻohana mākou i ka hana pūʻulu a loaʻa ka ʻikepili mai kahi lawelawe waho i nā pūʻulu. Akā he aha ka hopena i kā mākou huahana?
No kēlā me kēia memo, hoʻokahi kelepona iā UserRemoteApi e kiʻi i ka ʻikepili ma ke kahua mea kākau a hoʻokahi kelepona iā FileRemoteApi e kiʻi i nā faila i hoʻopili ʻia. Me he mea la. E ʻōlelo kākou ua loaʻa nā kahua forwardFrom a me replyTo no ChatMessage ma ke ʻano e koi ʻole ai kēia i nā kelepona pono ʻole. Akā ʻo ka hoʻohuli ʻana iā lākou i ChatMessageUI e alakaʻi i ka recursion, ʻo ia hoʻi, hiki ke piʻi nui nā helu kelepona. E like me kā mākou i ʻike ai ma mua, e manaʻo mākou ʻaʻole nui kā mākou nesting a ua kaupalena ʻia ke kaulahao i ʻekolu mau leka.
ʻO ka hopena, e loaʻa iā mākou mai ʻelua a ʻeono mau kelepona i nā lawelawe o waho no kēlā me kēia memo a hoʻokahi kelepona JPA no ka pūʻolo o nā memo. E ʻokoʻa ka huina o nā kelepona mai 2*N+1 a i 6*N+1. ʻEhia ka nui o kēia ma nā ʻāpana maoli? E ʻōlelo kākou he 20 mau memo e hāʻawi i kahi ʻaoʻao. No ka loaʻa ʻana iā lākou, mai 4 s a 10 s. Weliweli! Makemake au e mālama i loko o 500 ms. A no ka mea, ua moe lākou i ka hana ʻana i ka ʻōwili maʻemaʻe ma ka ʻaoʻao mua, hiki ke pālua ʻia nā koi hana no kēia hopena.
Kākoʻo:
- ʻO ke code ka pōkole a me ka kākau ʻana iā ia iho (kahi moeʻuhane o kahi hui kākoʻo).
- He maʻalahi ke code, no laila ʻaʻohe manawa kūpono e pana iā ʻoe iho i ka wāwae.
- ʻAʻole like ka hoʻoili ʻana o ka hui i kahi mea ʻē a hoʻohui pū ʻia i loko o ka loiloi.
- E hoʻololi maʻalahi ʻia nā hoʻololi loiloi a ma ka ʻāina.
Minu:
ʻO ka hana weliweli ma muli o nā ʻeke liʻiliʻi loa.
Hiki ke ʻike pinepine ʻia kēia ala i nā lawelawe maʻalahi a i ʻole nā prototypes. Inā he mea nui ka wikiwiki o ka hoʻololi ʻana, ʻaʻole pono e hoʻopili i ka ʻōnaehana. I ka manawa like, no kā mākou lawelawe maʻalahi loa ka hana, no laila he haiki loa ka laulā o ka hoʻohana ʻana o kēia ala.
Ka hana like ʻole
Hiki iā ʻoe ke hoʻomaka i ka hoʻoponopono ʻana i nā memo āpau i ka like - e ʻae kēia iā ʻoe e kāpae i ka hoʻonui laina i ka manawa ma muli o ka nui o nā memo. ʻAʻole kēia he ala maikaʻi loa no ka mea e hopena i kahi haʻahaʻa kiʻekiʻe nui ma ka lawelawe waho.
He maʻalahi loa ka hoʻokō ʻana i ka hana parallel:
override fun getLast(n: Int) =
messageRepository.findLast(n).parallelStream()
.map { it.toFrontModel() }
.collect(toList())
Me ka hoʻohana ʻana i ka hoʻoili ʻana i nā leka like, loaʻa iā mākou ka 300-700 ms kūpono, ʻoi aku ka maikaʻi ma mua o ka hoʻokō naive, akā ʻaʻole naʻe i wikiwiki.
Me kēia ala, e hoʻokō ʻia nā noi i ka userRepository a me fileRepository i ka manawa like, ʻaʻole maikaʻi loa. No ka hoʻoponopono ʻana i kēia, pono ʻoe e hoʻololi nui i ka loiloi kelepona. No ka laʻana, ma o CompletionStage (aka CompletableFuture):
private fun ChatMessage.toFrontModel(): ChatMessageUI =
CompletableFuture.supplyAsync {
userRepository.getUserById(author).toFrontReference()
}.thenCombine(
files?.let {
CompletableFuture.supplyAsync {
fileRepository.getHeadsByIds(files).map { it.toFrontReference() }
}
} ?: CompletableFuture.completedFuture(listOf())
) { author, files ->
ChatMessageUI(
id = id ?: throw IllegalStateException("$this must be persisted"),
author = author,
message = message,
files = files,
forwardFrom = forwardFrom?.toFrontModel(),
replyTo = replyTo?.toFrontModel()
)
}.get()!!
Hiki ke ʻike ʻia ua liʻiliʻi ka hoʻomaopopo ʻana i ke code palapala maʻalahi. ʻO kēia no ka mea pono mākou e hoʻokaʻawale i nā kelepona i nā lawelawe waho mai kahi i hoʻohana ʻia ai nā hopena. ʻAʻole hewa kēia. Akā ʻaʻole nani ke ʻano o ka hoʻohui ʻana i nā kelepona a ua like me ka "noodle" maʻamau.
Inā hoʻohana ʻoe i nā coroutines, e ʻoi aku ka maikaʻi o nā mea a pau:
private fun ChatMessage.toFrontModel(): ChatMessageUI =
join(
{ userRepository.getUserById(author).toFrontReference() },
{ files?.let { fileRepository.getHeadsByIds(files)
.map { it.toFrontReference() } } ?: listOf() }
).let { (author, files) ->
ChatMessageUI(
id = id ?: throw IllegalStateException("$this must be persisted"),
author = author,
message = message,
files = files,
forwardFrom = forwardFrom?.toFrontModel(),
replyTo = replyTo?.toFrontModel()
)
}
ʻAuhea:
fun <A, B> join(a: () -> A, b: () -> B) =
runBlocking(IO) {
awaitAll(async { a() }, async { b() })
}.let {
it[0] as A to it[1] as B
}
ʻO ka manaʻo, me ka hoʻohana ʻana i kēlā ʻano hana like, e loaʻa iā mākou ka 200-400 ms, kahi kokoke i kā mākou mau manaʻo.
ʻO ka mea pōʻino, ʻaʻole i loaʻa kēlā ʻano hoʻohālikelike maikaʻi, a ʻo ke kumukūʻai e uku ʻia he ʻino loa: me nā mea hoʻohana liʻiliʻi wale nō e hana ana i ka manawa like, e hāʻule ka nui o nā noi i nā lawelawe, ʻaʻole e hana like ʻia, no laila mākou e hoʻi i kā mākou kaumaha 4 s.
ʻO kaʻu hopena i ka wā e hoʻohana ai i ia lawelawe he 1300–1700 ms no ka hoʻoponopono ʻana i nā memo 20. ʻOi aku ka wikiwiki o kēia ma mua o ka hoʻokō mua, akā ʻaʻole naʻe e hoʻonā i ka pilikia.
Nā hoʻohana ʻokoʻa o nā nīnau likeHe aha inā ʻaʻole hāʻawi nā lawelawe ʻaoʻao ʻekolu i ka hoʻoili ʻana? No ka laʻana, hiki iā ʻoe ke hūnā i ka nele o ka hoʻokō ʻana i ka hoʻokō batch i loko o nā ala interface:
interface UserRemoteApi {
fun getUserById(id: UserReference): UserRemote
fun getUsersByIds(id: Set<UserReference>): Set<UserRemote> =
id.parallelStream()
.map { getUserById(it) }.collect(toSet())
fun getUsersByIds(id: List<UserReference>): List<UserRemote> =
id.parallelStream()
.map { getUserById(it) }.collect(toList())
}
Maikaʻi kēia inā manaʻo ʻoe e ʻike i ka hoʻoili ʻana o ka hui i nā mana e hiki mai ana.
Kākoʻo:
- E hoʻokō maʻalahi i ka hoʻoili like ʻana me ka memo.
- scalability maikaʻi.
ʻAla:
- Pono e hoʻokaʻawale i ka loaʻa ʻana o ka ʻikepili mai kāna hana ʻana i ka wā e hoʻoili ai i nā noi i nā lawelawe like ʻole i ka like.
- Hoʻonui i ka ukana ma nā lawelawe ʻaoʻao ʻekolu.
Hiki ke ʻike ʻia he ʻano like ka laulā o ka hoʻohana ʻana me ke ʻano naive. Maikaʻi ka hoʻohana ʻana i ke ʻano noi like inā makemake ʻoe e hoʻonui i ka hana o kāu lawelawe i nā manawa he nui ma muli o ka hoʻohana ʻole ʻana o nā poʻe ʻē aʻe. I kā mākou hiʻohiʻona, hoʻonui ʻia ka hana e 2,5 mau manawa, akā ʻaʻole lawa kēia.
kāʻei kapu
Hiki iā ʻoe ke hana i ka caching ma ka ʻuhane o JPA no nā lawelawe o waho, ʻo ia hoʻi, e mālama i nā mea i loaʻa i loko o kahi kau i ʻole e loaʻa hou iā lākou (me ka wā o ka hoʻoili ʻana ʻana). Hiki iā ʻoe ke hana iā ʻoe iho, hiki iā ʻoe ke hoʻohana i ka Spring me kāna @Cacheable, a hiki iā ʻoe ke hoʻohana mau i kahi cache i mākaukau e like me EhCache me ka lima.
ʻO kahi pilikia maʻamau ʻo ia ka pono o nā cache inā loaʻa iā lākou nā hits. I kā mākou hihia, hiki ke hoʻopaʻa ʻia ma ke kahua o ka mea kākau (e ʻōlelo mākou, 50%), akā ʻaʻole loa e loaʻa nā hits ma nā faila. Hāʻawi kēia ala i kekahi mau hoʻomaikaʻi, akā ʻaʻole ia e hoʻololi i ka hana (a pono mākou i kahi holomua).
Pono nā huna huna (lōʻihi) i ka manaʻo paʻakikī paʻakikī. Ma keʻano laulā, ʻoi aku ʻoe i lalo i ka hoʻoponopono ʻana i nā pilikia hana me ka hoʻohana ʻana i nā cache intersession, ʻoi aku ka maikaʻi.
Kākoʻo:
- E hoʻokō i ka cache me ka hoʻololi ʻole i ke code.
- Hoʻonui i ka huahana i nā manawa he nui (i kekahi mau hihia).
ʻAla:
- Hiki ke hōʻemi ʻia ka hana inā hoʻohana hewa ʻia.
- Hoʻomanaʻo nui ma luna o ke poʻo, ʻoi aku me nā huna huna lōʻihi.
- Paʻakikī invalidation, nā hewa e alakaʻi i nā pilikia paʻakikī i ka wā holo.
ʻO ka pinepine, hoʻohana ʻia nā cache no ka hoʻoponopono wikiwiki ʻana i nā pilikia hoʻolālā. ʻAʻole kēia manaʻo ʻaʻole pono lākou e hoʻohana. Eia naʻe, pono ʻoe e mālama iā lākou me ka akahele a loiloi mua i ka loaʻa ʻana o ka hana, a laila hoʻoholo wale.
Ma kā mākou hiʻohiʻona, e hāʻawi nā caches i ka hoʻonui hana ma kahi o 25%. I ka manawa like, he nui nā hemahema o nā cache, no laila ʻaʻole wau e hoʻohana iā lākou ma aneʻi.
Nā hopena
No laila, ua nānā mākou i kahi hoʻokō naive o kahi lawelawe e hoʻohana ana i ka hoʻoili ʻana i ka batch, a me kekahi mau ala maʻalahi e wikiwiki ai.
ʻO ka pōmaikaʻi nui o kēia mau ʻano a pau he maʻalahi, kahi i loaʻa ai nā hopena leʻaleʻa.
ʻO kahi pilikia maʻamau me kēia mau ʻano hana maikaʻi ʻole, ma muli o ka nui o nā ʻeke. No laila, inā ʻaʻole kūpono kēia mau hoʻonā iā ʻoe, a laila pono e noʻonoʻo i nā ʻano radical hou aʻe.
ʻElua mau kuhikuhi nui e hiki ai iā ʻoe ke ʻimi i nā hoʻonā:
- hana asynchronous me ka ʻikepili (koi i ka hoʻololi paradigm, no laila ʻaʻole i kūkākūkā ʻia ma kēia ʻatikala);
- ka hoʻonui ʻana i nā pūʻulu me ka mālama ʻana i ka hana synchronous.
ʻO ka hoʻonui ʻana i nā pūʻulu e hōʻemi nui i ka helu o nā kelepona waho a ma ka manawa like e mālama i ke code synchronous. ʻO ka ʻaoʻao aʻe o ka ʻatikala e pili ana i kēia kumuhana.
Source: www.habr.com