ʻO nā pilikia o ka hoʻoili ʻana i ka hulina hui a me kā lākou mau hoʻonā (mahele 1)

ʻO nā pilikia o ka hoʻoili ʻana i ka hulina hui a me kā lākou mau hoʻonā (mahele 1)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(nInt): 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(nInt): List<ChatMessage>
}
data class FileHeadRemote(
  val id: FileReference,
  val name: String
)
interface FileRemoteApi {
  fun getHeadById(idFileReference): FileHeadRemote
  fun getHeadsByIds(idSet<FileReference>): Set<FileHeadRemote>
  fun getHeadsByIds(idList<FileReference>): List<FileHeadRemote>
  fun getHeadsByChat(): List<FileHeadRemote>
}
data class UserRemote(
  val id: UserReference,
  val name: String
)
interface UserRemoteApi {
  fun getUserById(idUserReference): UserRemote
  fun getUsersByIds(idSet<UserReference>): Set<UserRemote>
  fun getUsersByIds(idList<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(nInt) =
    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:

  1. ʻO ke code ka pōkole a me ka kākau ʻana iā ia iho (kahi moeʻuhane o kahi hui kākoʻo).
  2. He maʻalahi ke code, no laila ʻaʻohe manawa kūpono e pana iā ʻoe iho i ka wāwae.
  3. ʻAʻole like ka hoʻoili ʻana o ka hui i kahi mea ʻē a hoʻohui pū ʻia i loko o ka loiloi.
  4. 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(nInt) =
  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())
  ) authorfiles ->
    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 <ABjoin(a: () -> Ab: () -> B) =
  runBlocking(IO{
    awaitAll(async a() }async b() })
  }.let {
    it[0as to it[1as 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(idUserReference): UserRemote
  fun getUsersByIds(idSet<UserReference>): Set<UserRemote> =
    id.parallelStream()
      .map getUserById(it}.collect(toSet())
  fun getUsersByIds(idList<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:

  1. E hoʻokō maʻalahi i ka hoʻoili like ʻana me ka memo.
  2. scalability maikaʻi.

ʻAla:

  1. 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.
  2. 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:

  1. E hoʻokō i ka cache me ka hoʻololi ʻole i ke code.
  2. Hoʻonui i ka huahana i nā manawa he nui (i kekahi mau hihia).

ʻAla:

  1. Hiki ke hōʻemi ʻia ka hana inā hoʻohana hewa ʻia.
  2. Hoʻomanaʻo nui ma luna o ke poʻo, ʻoi aku me nā huna huna lōʻihi.
  3. 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

Pākuʻi i ka manaʻo hoʻopuka