Amplifer如何使用Logux-客户端和服务器之间进行通信的工具

Logux-客户端与服务器之间进行通信的工具


我叫Vitaliy Rizo,我是Amplifer的高级前端开发人员。我将分享我们如何在Web应用程序中使用Logux:我们组织实时数据交换,不重新加载页面的错误通知,浏览器标签之间的通信以及与Redux的集成。


Amplifer是用于在社交网络上发布的服务。有必要在不重新加载页面的情况下快速可靠地通知用户错误:如果突然无法处理图像,则VKontakte API掉了,或者Facebook决定不再发布。展望未来,我会说我们还计划使用Logux讨论出版物,并重写RSS中的交叉发布弹出窗口。但是首先,为什么我们对可用的解决方案不满意。


可能的解决方案:WebSocket,Firebase和Swarm.js


通常,WebSocket用于实现自动更新信息。使用它,您不需要像传统的HTTP请求那样每秒发送一次信息请求,并且报头大小很小。但是,WebSocket有以下缺点:


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


新错误部分



— . , . , .


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


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


由于错误部分中的错误导致的放大器


-


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


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


, , Logux , , . , , .


, Logux, .


Logux


讨论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, .



永恒编辑器下载


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


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


RSS Logux


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


AJAX Logux RSS. RSS- . RSS-, .


跨主机设置



. :


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


乐观接口示例


«» .


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


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的缺点是:系统不简单,仍然存在错误,并且解决方案并非总是能很快找到;
  5. 在Amplifer中,Logux的优势超过了缺点。我们计划在实施合适的项目时继续使用它。


⌘⌘⌘


希望Logux在您的项目中有所用。如有任何疑问,请在Twitter上通过邮件给我写信


感谢Alexander MarfitsinAndrei Sitnik在撰写本文时提供的帮助。


All Articles