Cómo Amplifer usa Logux, una herramienta para la comunicación entre el cliente y el servidor

Logux: una herramienta para la comunicación entre el cliente y el servidor


Mi nombre es Vitaliy Rizo, soy desarrollador senior de front-end en Amplifer. Permítanme compartir cómo usamos Logux en una aplicación web: organizamos el intercambio de datos en tiempo real, las notificaciones de errores sin volver a cargar la página, la comunicación entre las pestañas del navegador y la integración con Redux.


Amplifer es un servicio para publicar en redes sociales. Era necesario notificar a los usuarios de manera rápida y confiable de los errores sin volver a cargar la página: si de repente no era posible procesar la imagen, la API de VKontakte se cayó o Facebook decidió nuevamente no publicar la publicación. Mirando hacia el futuro, diré que también planeamos usar Logux para discutir publicaciones y reescribir la ventana emergente de publicación cruzada de RSS. Pero primero, sobre por qué no estábamos satisfechos con las soluciones disponibles.


Posibles soluciones: WebSocket, Firebase y Swarm.js


Por lo general, WebSocket se usa para implementar información de actualización automática. Con él, no necesita enviar solicitudes de información cada segundo, como tendría que hacer con las solicitudes HTTP tradicionales, y el tamaño del encabezado será pequeño. Sin embargo, WebSocket tiene desventajas:


  • . , , , . , : , ;
  • , . ;
  • , , ;
  • , (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


Nueva sección de error



— . , . , .


, 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:


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


Amplificador debido a un error en la sección de error


-


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


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


, , Logux , , . , , .


, Logux, .


Logux


Discusión de publicaciones en 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, .



Descarga del editor eterno


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


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


RSS Logux


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


AJAX Logux RSS. RSS- . RSS-, .


Configuración de alojamiento cruzado



. :


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 — . , , . - , .


Ejemplo de interfaz optimista


«» .


«», , 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 , . , - , :


Algoritmo de búsqueda de problemas de 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 tiene sus inconvenientes: el sistema no es simple, todavía hay errores y las soluciones no siempre se encuentran rápidamente;
  5. En Amplifer, las ventajas de Logux superaron las desventajas. Planeamos continuar usándolo cuando implementemos proyectos adecuados.


⌘⌘⌘


Espero que Logux encuentre uso en su proyecto. Si tiene alguna pregunta, escríbame en Twitter o por correo .


Gracias a Alexander Marfitsin y Andrei Sitnik por su ayuda en la preparación de este artículo.


All Articles