Comment Amplifer utilise Logux - un outil de communication entre le client et le serveur

Logux - un outil de communication entre client et serveur


Je m'appelle Vitaliy Rizo, je suis développeur front-end senior chez Amplifer. Je vais partager la façon dont nous utilisons Logux dans une application Web: nous organisons l'échange de données en temps réel, les notifications d'erreur sans recharger la page, la communication entre les onglets du navigateur et l'intégration avec Redux.


Amplifer est un service de publication sur les réseaux sociaux. Il était nécessaire d'avertir rapidement et de manière fiable les utilisateurs des erreurs sans recharger la page: si soudainement il n'était pas possible de traiter l'image, l'API VKontakte est tombée, ou Facebook a de nouveau décidé de ne pas publier le message. Pour l'avenir, je dirai que nous avons également prévu d'utiliser Logux pour discuter des publications et réécrire le popup de publication croisée de RSS. Mais d'abord, pourquoi nous n'étions pas satisfaits des solutions disponibles.


Solutions possibles: WebSocket, Firebase et Swarm.js


En général, WebSocket est utilisé pour implémenter les informations de mise à jour automatique. Avec lui, vous n'avez pas besoin d'envoyer des demandes d'informations toutes les secondes, comme vous le feriez avec des demandes HTTP traditionnelles, et la taille de l'en-tête sera petite. Cependant, WebSocket présente des inconvénients:


  • . , , , . , : , ;
  • , . ;
  • , , ;
  • , (CRDT), .

Firebase, , — . Firebase CRDT, Redux . CRDT Swarm.js, Redux , .


Logux


, — Logux. , Redux, CRDT . - , API-: , .


, , « ». :


imports-api.js


update (project, id, data) {
  return put(project, `settings/imports/${ id }`, convert(data))
}

imports.js


onUpdate (projectId, importId, changed) {
  return dispatch({
    projectId,
    importId,
    import: changed,
    type: 'amplifr/imports/update'
  })
}

:


imports.js


let dispatch = useDispatch()
let onUpdate = (projectId, importId, changed) => dispatch.sync({
  projectId,
  importId,
  import: changed,
  type: 'amplifr/imports/update'
}, { channels: ['imports/' + projectId] })

sync dispatch, , , . , Logux .


Logux


Nouvelle section d'erreur



— . , . , .


, Logux-. .


Logux Redux. Redux createStore. Logux, «» createLoguxCreator:


import { createLoguxCreator } from '@logux/redux'

let createStore = createLoguxCreator({
  credentials: loguxToken,
  subprotocol: '0.6.5',
  userId,
  server: 'wss://logux.amplifr.com'
})

let store = createStore(reducers)

store.client.start()

createLoguxCreator :


  1. . , . gon;
  2. Logux-;
  3. . , , .

Logux- , . , - posts/1, . , WebSocket.


? — , , posts/1. subscribe — :


import useSubscription from '@logux/redux/use-subscription'

let Notices = props => {
  let isSubscribing = useSubscription([`projects/${ props.id }`])

  if (isSubscribing) {
    return <Loader />
  } else {
    // Render Notices
  }
}

— , HTTP- Redux- Logux, Logux- :


def schedule_logux
  LoguxNotificationWorker.perform_async(
    { type: 'amplifr/notices/add', notice: Logux::NoticePresenter.new(notice).to_json },
    channels: ["projects/#{project.id}"]
  )
end

, . - , , . , :


import { useDispatch } from 'react-redux'

let dispatch = useDispatch()
let onNoticeHide = noticeId => dispatch.sync({
  type: 'amplifr/notices/hide',
  noticeId
}, {
  channels: [`projects/${ projectId }`]
})

, , Logux:


  • . ;
  • , ;
  • , (). -;
  • , , . , .


Amplificateur dû à une erreur dans la section d'erreur


-


Logux , . , - , . , . , , , .


Logux . «» , , localStorage. , Firefox Safari localStorage! Logux , , . , .


, , Logux , , . , , .


, Logux, .


Logux


Discussion des publications dans Amplifer


, Logux, . — , :


let PostEditor = { isApprovable, postId, … } => {
  let isSubscribing = useSubscription(isApprovable ? [`posts/${ postId }`] : [])

  if (isSubscribing) {
    return <Loader />
  } else {
    // Render PostEditor
  }
}

notes :


import { useDispatch, useSelector } from 'react-redux'

let dispatch = useDispatch()
let notes = useSelector(notes.filter(note => note.postId === postId))
let onNoteAdd = note => dispatch.sync({
  type: 'amplifr/notes/add',
  note
}, {
  channels: [`posts/${ postId }`]
})

. , :


export default function notes (state = [], action) {
  if (action.type === 'amplifr/notes/add') {
    return state.concat([action.note])
  } else if (action.type === 'amplifr/posts/close') {
    return state.filter(i => i.postId !== action.postId)
  } else {
    return state
  }
}

. Logux, .



Téléchargement de l'éditeur éternel


, @subscribe. , isSubscribing: true. , , . .


, . , squid 3, WebSocket ( Logux WebSocket). , , . — .


RSS Logux


, Logux AJAX-. , «» «» Firefox.


AJAX Logux RSS. RSS- . RSS-, .


Paramètres d'hébergement croisé



. :


import { useDispatch } from 'react-redux'

let dispatch = useDispatch()
let onCreate = (projectId, importId, import) => {
  return dispatch.sync({
    importId,
    import,
    type: 'amplifr/imports/add'
  }, { channels: ['imports/' + projectId] })
}

let onUpdate = (projectId, importId, changed) => {
  return dispatch.sync({
    importId,
    changed,
    type: 'amplifr/imports/update'
  }, { channels: ['imports/' + projectId] })
}

, , , Logux Optimitstic UI — . , , . - , .


Exemple d'interface optimiste


«» .


«», , RSS- . , . Logux (dispatch.sync(…).catch(…)), . -, , .


: catch() JSON, try { JSON.parse(…) } catch { … }. .


Logux ?


Logux WebSocket, , SPA . , . , , :


import status from '@logux/client/status'

let connected = false

status(store.client, state => {
  if (state === 'synchronized') connected = true
}

setTimeout(() => {
  if (!connected) {
    sentry.track(new Error('No Logux connection for 10 minutes'))
  }
}, 60 * 1000)

100 , . , - , :


Algorithme de recherche de problème Logux


, WebSocket: -, , , , , AdBlocker Kaspersky Protection. , , , Logux, .


Logux ,


Logux , . , , . RSS-, , . - , , , , .


, . — Logux. -. , , , , :


import log from '@logux/client/log'

let store = createStore(reducers)

log(store.client)

window.store = store

:


  1. RSS- ;
  2. , ;
  3. RSS-, ;
  4. ;
  5. !

:


window.store.client.log.store.created.filter(i => i[0].type === 'amplifr/popups/set')

, - : meta.tab undefined. , . , client.id client.tabId @logux/redux id tabId. , , Logux , , .


, - «» Logux, :


Logux : «? , , ?». , — , , Redux. « », , . Logux .


:


  1. Logux, Redux, CRDT ;
  2. C Logux , ;
  3. Logux , ;
  4. Logux a ses inconvénients: le système n'est pas simple, il y a encore des bugs et les solutions ne sont pas toujours trouvées rapidement;
  5. Dans Amplifer, les avantages de Logux l'emportaient sur les inconvénients. Nous prévoyons de continuer à l'utiliser lors de la mise en œuvre de projets adaptés.


⌘⌘⌘


J'espère que Logux trouvera son utilité dans votre projet. Si vous avez des questions, écrivez-moi sur Twitter ou par mail .


Merci à Alexander Marfitsin et Andrei Sitnik pour leur aide dans la préparation de cet article.


All Articles