OrbitDB:无服务器,分布式,点对点数据库

OrbitDB入门

本指南将帮助您熟悉在JavaScript应用程序中使用OrbitDB。OrbitDB和IPFS都可以在Node.js应用程序和浏览器应用程序中运行。(尽管不支持Windows)。

背景

OrbitDB是一个点对点数据库,意味着每个对等体都有自己的特定数据库实例。在对等体之间自动复制数据库,从而在来自任何对等体的更新时生成数据库的最新视图。也就是说,数据库被拉到客户端。

这意味着每个应用程序都包含他们正在使用的完整数据库。与客户端 – 服务器模型相比,这反过来改变了数据建模,其中通常有一个用于所有条目的大数据库:在OrbitDB中,应该基于该数据的访问权限来存储,“分区”或“分片”数据。例如,在类似Twitter的应用程序中,推文不会保存在数百万用户同时写入的全局“推文”数据库中,而是每个用户都有自己的推文数据库。为了跟踪用户,对等方将订阅用户的订阅源,即。复制他们的Feed数据库。

OrbitDB支持多种数据模型(详见下文),因此开发人员可以通过多种方式构建数据。结合点对点范例,数据建模是构建可扩展去中心化应用程序的重要因素。

这可能不直观,或者您可能不确定最佳方法是什么,我们很乐意帮助您确定数据建模和应用程序需求,随时联系我们

安装

从npm 安装orbit-dbipfs

npm install orbit-db ipfs

建立

在程序中需要OrbitDB和IPFS并创建实例:

const IPFS = require('ipfs')
const OrbitDB = require('orbit-db')

// OrbitDB uses Pubsub which is an experimental feature
// and need to be turned on manually.
// Note that these options need to be passed to IPFS in
// all examples in this document even if not specified so.
const ipfsOptions = {
  EXPERIMENTAL: {
    pubsub: true
  }
}

// Create IPFS instance
const ipfs = new IPFS(ipfsOptions)

ipfs.on('ready', () => {
  // Create OrbitDB instance
  const orbitdb = await OrbitDB.createInstance(ipfs)
})

orbitdb 现在是我们可以用来与数据库交互的OrbitDB实例。

创建一个数据库

首先,选择要使用的数据模型。可用的数据模型是:

然后,创建一个数据库实例(在本例中我们将使用Key-Value数据库):

const ipfs = new IPFS()
ipfs.on('ready', async () => {
  const orbitdb = await OrbitDB.createInstance(ipfs)
  const db = await orbitdb.keyvalue('first-database')
})

地址

创建数据库时,将为OrbitDB分配一个地址。地址由三部分组成:

/orbitdb/Qmd8TmZrWASypEp4Er9tgWP4kCNQnW4ncSnvjvyHQ3EVSU/first-database

第一部分,/orbitdb指定正在使用的协议。第二部分是IPFS multihash Qmd8TmZrWASypEp4Er9tgWP4kCNQnW4ncSnvjvyHQ3EVSU,它是数据库清单,其中包含数据库信息,例如名称和类型,以及指向访问控制器的指针。最后一部分first-database是数据库的名称。

为了使用对等方复制数据库,需要将地址提供给其他对等方,以便它们开始复制数据库。

可以db.address从数据库实例访问数据库地址:

const address = db.address
// address == '/orbitdb/Qmdgwt7w4uBsw8LXduzCd18zfGXeTmBsiR8edQ1hSfzcJC/first-database'

例如:

const ipfs = new IPFS()
ipfs.on('ready', async () => {
  const orbitdb = await OrbitDB.createInstance(ipfs)
  const db = await orbitdb.keyvalue('first-database')
  console.log(db.address.toString())
  // /orbitdb/Qmd8TmZrWASypEp4Er9tgWP4kCNQnW4ncSnvjvyHQ3EVSU/first-database
})

表现

地址的第二部分IPFS multihash Qmdgwt7w4uBsw8LXduzCd18zfGXeTmBsiR8edQ1hSfzcJC是数据库的清单。它是一个IPFS对象,包含有关数据库的信息。

可以从IPFS获取数据库清单,它看起来像这样:

{
  "Data": "{\"name\":\"a\",\"type\":\"feed\",\"accessController\":\"/ipfs/QmdjrCN7SqGxRapsm6LuoS4HrWmLeQHVM6f1Zk5A3UveqA\"}",
  "Hash": "Qmdgwt7w4uBsw8LXduzCd18zfGXeTmBsiR8edQ1hSfzcJC",
  "Size": 102,
  "Links": []
}

身分

数据库中的每个条目都由创建该条目的人签名。可以通过数据库实例的标识成员变量访问标识,其中包括用于对条目进行签名的公钥:

const identity = db.identity
console.log(identity.toJSON())
// prints
{
  id: '0443729cbd756ad8e598acdf1986c8d586214a1ca9fa8c7932af1d59f7334d41aa2ec2342ea402e4f3c0195308a4815bea326750de0a63470e711c534932b3131c',
  publicKey: '0446829cbd926ad8e858acdf1988b8d586214a1ca9fa8c7932af1d59f7334d41aa2ec2342ea402e4f3c0195308a4815bea326750de0a63470e711c534932b3131c',
  signatures: {
    id: '3045022058bbb2aa415623085124b32b254b8668d95370261ade8718765a8086644fc8ae022100c736b45c6b2ef60c921848027f51020a70ee50afa20bc9853877e994e6121c15',
    publicKey: '3046022100d138ccc0fbd48bd41e74e40ddf05c1fa6ff903a83b2577ef7d6387a33992ea4b022100ca39e8d8aef43ac0c6ec05c1b95b41fce07630b5dc61587a32d90dc8e4cf9766'
  },
  type: 'orbitdb'
}

创建身份

const Identities = require('orbit-db-identity-provider')
const options = { id: 'local-id' }
const identity = await Identities.createIdentity(options)

通过将其作为options对象中的参数传递,可以在OrbitDB中使用此标识:

const orbitdb = await OrbitDB.createInstance(ipfs, { identity: identity })

该标识还包含证明拥有id和OrbitDB公钥的签名。这包括在内以允许证明OrbitDB中的外部公钥的所有权。你可以在这里阅读更多

可以使用以下命令检索OrbitDB公钥:

console.log(db.identity.publicKey)
// 04d009bd530f2fa0cda29202e1b15e97247893cb1e88601968abfe787f7ea03828fdb7624a618fd67c4c437ad7f48e670cc5a6ea2340b896e42b0c8a3e4d54aebe

如果要授予其他对等方访问权限以写入数据库,则需要以十六进制形式获取其公钥,并在创建数据库时将其添加到访问控制器。如果您希望其他人为您提供写入权限,则需要向他们提供您的公钥(输出orbitdb.identity.publicKey)。有关更多信息,请参阅:访问控制

访问控制

您可以指定对数据库具有写访问权限的对等项。您可以定义一组可以写入数据库或允许任何人写入数据库的对等项。默认情况下,如果没有另外指定,则只给予数据库的创建者写访问权限

注意 OrbitDB目前仅支持动态添加写访问。也就是说,一旦添加了写访问权限就无法撤销。将来,OrbitDB将支持访问撤销和读取访问控制。目前,如果需要删除访问权限,则数据库的地址将发生变化。

通过accessController在创建时传递指定访问控制器类型和数据库访问权限的对象来设置访问权限。OrbitDB目前支持写访问。访问权限被指定为可以写入数据库的对等方的公钥数组。可以从每个对等体的identity.publicKey属性检索给予访问的公钥。

const ipfs = new IPFS()
ipfs.on('ready', async () => {
  const orbitdb = await OrbitDB.createInstance(ipfs)
  const options = {
    // Give write access to ourselves
    accessController: {
      write: [orbitdb.identity.publicKey]
    }
  }

  const db = await orbitdb.keyvalue('first-database', options)
  console.log(db.address.toString())
  // /orbitdb/Qmd8TmZrWASypEp4Er9tgWP4kCNQnW4ncSnvjvyHQ3EVSU/first-database
})

要为另一个对等方提供写访问权限,您需要通过某种方式获取其公钥。他们需要为你提供OrbitDB实例密钥的输出:orbitdb.identity.publicKey

键看起来像这样:042c07044e7ea51a489c02854db5e09f0191690dc59db0afd95328c9db614a2976e088cab7c86d7e48183191258fc59dc699653508ce25bf0369d67f33d5d77839

授予另一个对等体访问权限以写入数据库:

const ipfs = new IPFS()
ipfs.on('ready', async () => {
  const orbitdb = await OrbitDB.createInstance(ipfs)

  const options = {
    // Setup write access
    accessController: {
      write: [
        // Give access to ourselves
        orbitdb.identity.publicKey,
        // Give access to the second peer
        '042c07044e7ea51a489c02854db5e09f0191690dc59db0afd95328c9db614a2976e088cab7c86d7e48183191258fc59dc699653508ce25bf0369d67f33d5d77839',
      ]
    }
  }

  const db1 = await orbitdb.keyvalue('first-database', options)
  console.log(db1.address.toString())
  // /orbitdb/Qmdgwt7w4uBsw8LXduzCd18zfGXeTmBsiR8edQ1hSfzcJC/first-database

  // Second peer opens the database from the address
  const db2 = await orbitdb.keyvalue(db1.address.toString())
})

公共数据库

访问控制机制还支持任何人都可以写入的“公共”数据库。

这可以通过向*写访问数组添加一个来完成:

const ipfs = new IPFS()
ipfs.on('ready', async () => {
  const orbitdb = await OrbitDB.createInstance(ipfs)

  const options = {
    // Give write access to everyone
    accessController: {
      write: ['*']
    }
  }

  const db = await orbitdb.keyvalue('first-database', options)
  console.log(db.address.toString())
  // /orbitdb/QmRrauSxaAvNjpZcm2Cq6y9DcrH8wQQWGjtokF4tgCUxGP/first-database
})

请注意,与前一个示例相比,访问控制器哈希是如何不同的

创建数据库后授予访问权限

为了给访问另一个对数据库的创建后,必须设置访问控制type到一个AccessController支持动态地添加写访问如OrbitDBAccessController

db = await orbitdb1.feed('AABB', {
  accessController: {
    type: 'orbitdb', //OrbitDBAccessController
    write: [identity1.publicKey]
  }
})

await db.access.grant('write', identity2.publicKey) // grant access to identity2

自定义访问控制器

您可以通过实现AccessController 接口并将其添加到AccessControllers对象,然后将其传递给OrbitDB 来创建自定义访问控制器。

class OtherAccessController extends AccessController {

    static get type () { return 'othertype' } // Return the type for this controller

    async canAppend(entry, identityProvider) {
      // logic to determine if entry can be added, for example:
      if (entry.payload === "hello world" && entry.identity.id === identity.id && identityProvider.verifyIdentity(entry.identity))
        return true

      return false
      }

    async grant (access, identity) {} // Logic for granting access to identity
}

let AccessControllers = require('orbit-db-access-controllers')
AccessControllers.addAccessController({ AccessController: OtherAccessController })

const orbitdb = await OrbitDB.createInstance(ipfs, {
  AccessControllers: AccessControllers
})

const db = await orbitdb.keyvalue('first-database', {
  accessController: {
    type: 'othertype',
    write: [id1.publicKey]
  }
})

添加条目

要向数据库添加条目,我们只需调用db.put(key, value)

const ipfs = new IPFS()
ipfs.on('ready', async () => {
  const orbitdb = await OrbitDB.createInstance(ipfs)
  const db = await orbitdb.keyvalue('first-database')
  await db.put('name', 'hello')
})

要将条目添加到其他数据库,请参阅:

排比

我们目前不支持并行更新。需要以顺序方式执行对数据库的更新。写吞吐量是每秒数百或数千次写入(取决于您的平台和硬件,YMMV),因此这不应该太慢地减慢您的应用程序。如果确实如此,请告诉我们

一个接一个地更新数据库:

await db.put('key1', 'hello1')
await db.put('key2', 'hello2')
await db.put('key3', 'hello3')

不:

// This is not supported atm!
Promise.all([
  db.put('key1', 'hello1'),
  db.put('key2', 'hello2'),
  db.put('key3', 'hello3')
])

获得一个条目

为了从数据库中获取值或条目,我们调用适当的查询函数,该函数对于每种数据库类型是不同的。

核心价值:

const ipfs = new IPFS()
ipfs.on('ready', async () => {
  const orbitdb = await OrbitDB.createInstance(ipfs)
  const db = await orbitdb.keyvalue('first-database')
  await db.put('name', 'hello')
  const value = db.get('name')
})

其他数据库,请参阅:

持久性

OrbitDB在磁盘上自动保存数据库的状态。这意味着在打开数据库时,开发人员可以选择在使用数据库之前在本地加载持久化数据库。强烈建议在使用之前在本地加载数据库

const ipfs = new IPFS()
ipfs.on('ready', async () => {
  const orbitdb = await OrbitDB.createInstance(ipfs)

  const db1 = await orbitdb.keyvalue('first-database')
  await db1.put('name', 'hello')
  await db1.close()

  const db2 = await orbitdb.keyvalue('first-database')
  await db2.load()
  const value = db2.get('name')
  // 'hello'
})

如果开发人员未调用load(),则数据库将可操作但不会立即提供持久数据。相反,当新的更新来自同行时,OrbitDB将在后台加载数据。

复制数据库

为了拥有相同的数据,即 查询为所有对等体返回相同的结果,必须在对等体之间复制OrbitDB数据库。这在OrbitDB中自动发生,对等方只需要从一个地址打开一个OrbitDB,它就会开始复制数据库。

要知道数据库何时更新,我们可以监听replicated数据库的事件:db2.events.on('replicated', () => ...)。当replicated事件被触发时,这意味着我们从对等体接收了数据库的更新。这是查询数据库以获得新结果的好时机。

在两个节点之间复制数据库:

// Create the first peer
const ipfs1 = new IPFS({ repo: './ipfs1' })
ipfs1.on('ready', async () => {
  // Create the database
  const orbitdb1 = await OrbitDB.createInstance(ipfs1, { directory: './orbitdb1' })
  const db1 = await orbitdb1.log('events')

  // Create the second peer
  const ipfs2 = new IPFS({ repo: './ipfs2' })
  ipfs2.on('ready', async () => {
    // Open the first database for the second peer,
    // ie. replicate the database
    const orbitdb2 = await OrbitDB.createInstance(ipfs2, { directory: './orbitdb2' })
    const db2 = await orbitdb2.log(db1.address.toString())

    // When the second database replicated new heads, query the database
    db2.events.on('replicated', () => {
      const result = db2.iterator({ limit: -1 }).collect().map(e => e.payload.value)
      console.log(result.join('\n'))
    })

    // Start adding entries to the first database
    setInterval(async () => {
      await db1.add({ time: new Date().getTime() })
    }, 1000)
  })
})

自定义商店

使用自定义存储来实现默认OrbitDB数据股票储不支持的特定于案例的功能。然后,您可以使用OrbitDB轻松添加和使用自定义商店:

// define custom store type
class CustomStore extends DocumentStore {
  constructor (ipfs, id, dbname, options) {
    super(ipfs, id, dbname, options)
    this._type = CustomStore.type
  }

  static get type () {
    return 'custom'
  }
}

// add custom type to orbitdb
OrbitDB.addDatabaseType(CustomStore.type, CustomStore)

// instantiate custom store
let orbitdb = await OrbitDB.createInstance(ipfs, { directory: dbPath })
let store = orbitdb.create(name, CustomStore.type)

原文:https://kauri.io/article/6ae5ffa612044a09be856ff390ce6990/v1/orbitdb:-serverless-distributed-peer-to-peer-database

提示:投资有风险,入市需谨慎,本资讯不作为投资理财建议。请理性投资,切实提高风险防范意识;如有发现的违法犯罪线索,可积极向有关部门举报反映。
你可能还喜欢