在前面几篇讨论里我们介绍了在集群环境里的一些编程模式、分布式数据结构及具体实现方式。到目前为止,我们已经实现了把程序任务分配给处于很多服务器上的actor,能够最大程度的利用整体系统的硬件资源。这是因为通过akka-cluster能够把很多服务器组合成一个虚拟的整体系统,编程人员不需要知道负责运算的actor具体在那台服务器上运行。当然,我所指的整体系统是一种分布式的系统,实质底层还是各集群节点作为完整个体独立运行的,所以核心理念还是需要将程序分割成能独立运算的任务,然后分派给可能分布在很多服务器上的actor去运算。在上一篇的cluster-load-balance里我们采用了一种fire-and-forget模式把多项独立任务分配给集群节点上的actor,然后任由它们各自完成运算,中途不做任何交互、控制。这也是一种典型的无内部状态的运算模式。对外界来讲就是开始、完成,中间没有关于运算进展或当前状态的交流需要。但在现实里,很多任务是无法完全进行独立细分的,或者再细分会影响系统效率。比如网上购物网站每个客户的购物车:它记录了客户在网上的所有商品拣选过程,每一个拣选动作都代表更新的购物车状态,直到完成结算。那么在一个可能有几十万用户同时在线购物的网站,保留在内存的购物车状态应该是任何机器都无法容纳的,只有回到传统的数据库模式了,还是要面对无法解决的多并发系统效率问题。这么分析,集群分片技术可能是最好的解决方法了。

简单讲:集群分片技术就是把一堆带唯一标识identifier的actor,即entity分布到集群节点上去。控制程序可以通过唯一ID与entityr进行交互,控制整个运算过程。这样,我们可以把程序分成相对合理的包含多个过程状态的细分任务。这些细分任务是由分布在集群节点上的entity来运算的,产生的状态当然也使用的是各集群节点上的资源,如此解决上面所提到的内存容量问题。akka-cluster提供的actor位置透明化机制能在系统崩溃、增减集群节点时自动重新部署所有的actor以达到负责均衡。而用户通过固定的ID就能联络目标entity,无论它被转移到任何集群节点上。

集群分片由分片管理ShardRegion和分片定位ShardCoordinator共同协作实现,目标是把消息正确传递给指定ID的entity。分片定位负责确定分片所在集群节点,分片管理则对每个集群节点上分片内的entity进行定位。ShardCoordinator是个cluster-singleton,而ShardRegion则必须部署在每个集群节点上。每个分片内的entity必须是一个类型的actor。发给entity的消息内部必须包含分片编号和entity ID。通过从消息中解析位置信息后由ShardCoordinator确定负责传递消息的ShardRegion,相关的ShardRegion按ID把消息发送至目标entity。

每个节点上的ShardRegion是通过下面这个start函数构建的:

  /**
* Scala API: Register a named entity type by defining the [[akka.actor.Props]] of the entity actor
* and functions to extract entity and shard identifier from messages. The [[ShardRegion]] actor
* for this type can later be retrieved with the [[#shardRegion]] method.
*
* Some settings can be configured as described in the `akka.cluster.sharding` section
* of the `reference.conf`.
*
* @param typeName the name of the entity type
* @param entityProps the `Props` of the entity actors that will be created by the `ShardRegion`
* @param settings configuration settings, see [[ClusterShardingSettings]]
* @param extractEntityId partial function to extract the entity id and the message to send to the
* entity from the incoming message, if the partial function does not match the message will
* be `unhandled`, i.e. posted as `Unhandled` messages on the event stream
* @param extractShardId function to determine the shard id for an incoming message, only messages
* that passed the `extractEntityId` will be used
* @param allocationStrategy possibility to use a custom shard allocation and
* rebalancing logic
* @param handOffStopMessage the message that will be sent to entities when they are to be stopped
* for a rebalance or graceful shutdown of a `ShardRegion`, e.g. `PoisonPill`.
* @return the actor ref of the [[ShardRegion]] that is to be responsible for the shard
*/
def start(
typeName: String,
entityProps: Props,
settings: ClusterShardingSettings,
extractEntityId: ShardRegion.ExtractEntityId,
extractShardId: ShardRegion.ExtractShardId,
allocationStrategy: ShardAllocationStrategy,
handOffStopMessage: Any): ActorRef = {...}

这个函数登记了名称为typeName类型entity的分片。函数返回ActorRef,说明ShardRegion是在本节点上的一个actor。下面是调用示范:

     ClusterSharding(system).start(
typeName = Counter.shardName,
entityProps = Counter.props(),
settings = ClusterShardingSettings(system),
extractEntityId = Counter.idExtractor,
extractShardId = Counter.shardResolver)
... object Counter { trait Command
case object Increment extends Command
case object Decrement extends Command
case object Get extends Command
case object Stop extends Command trait Event
case class CounterChanged(delta: Int) extends Event // Sharding Name
val shardName: String = "Counter" // outside world if he want to send message to sharding should use this message
case class CounterMessage(id: Long, cmd: Command) // id extrator
val idExtractor: ShardRegion.ExtractEntityId = {
case CounterMessage(id, msg) => (id.toString, msg)
} // shard resolver
val shardResolver: ShardRegion.ExtractShardId = {
case CounterMessage(id, msg) => (id % ).toString
} def props() = Props[Counter] }

entityProps是ShardRegion用来重构entity的。typeName是用来查找ShardRegion的,如下:

val counterRegion: ActorRef = ClusterSharding(system).shardRegion("Counter")
counterRegion ! Get()

用"Counter"获得ShardRegion的ActorRef后所有本节点的消息都是通过这个ShardRegion actor来定位,转达。所以每个ShardRegion都必须具备消息目的地entity的分片编号及entityID的解析方法:extractShardId和extractEntityId。在有些情况下由于节点角色的关系在某个节点不部署任何entity,但本节点需要向其它节点的entity发送消息,这时需要构建一个中介ProxyOnlyShardRegion:

  /**
* Java/Scala API: Register a named entity type `ShardRegion` on this node that will run in proxy only mode,
* i.e. it will delegate messages to other `ShardRegion` actors on other nodes, but not host any
* entity actors itself. The [[ShardRegion]] actor for this type can later be retrieved with the
* [[#shardRegion]] method.
*
* Some settings can be configured as described in the `akka.cluster.sharding` section
* of the `reference.conf`.
*
* @param typeName the name of the entity type
* @param role specifies that this entity type is located on cluster nodes with a specific role.
* If the role is not specified all nodes in the cluster are used.
* @param messageExtractor functions to extract the entity id, shard id, and the message to send to the
* entity from the incoming message
* @return the actor ref of the [[ShardRegion]] that is to be responsible for the shard
*/
def startProxy(
typeName: String,
role: Optional[String],
messageExtractor: ShardRegion.MessageExtractor): ActorRef = {...}

还有一个重要问题是如何弃用passivate entity,以释放占用资源。akka-cluster提供的方法是通过定义一个空转时间值idle-timeout,如果空转超出此时间段则可以进行passivate。下面是一段应用示范:两分钟空转就passivate entity

class ABC extends Actor {
...
// passivate the entity when no activity
context.setReceiveTimeout(.minutes) ... override def receive ..... override def receiveCommand: Receive = {
case Increment ⇒ persist(CounterChanged(+))(updateState)
case Decrement ⇒ persist(CounterChanged(-))(updateState)
case Get(_) ⇒ sender() ! count
case ReceiveTimeout ⇒ context.parent ! Passivate(stopMessage = Stop)
case Stop ⇒ context.stop(self)
}
/* 或者
override def unhandled(msg: Any): Unit = msg match {
case ReceiveTimeout => context.parent ! Passivate(stopMessage = PoisonPill)
case _ => super.unhandled(msg)
}
*/
}

又或者通过设定配置来实现自动的passivation:

在配置文件中设定:akka.cluster.sharding.passivate-idle-entity-after = 120 s   // off to disable

下面是官网提供的一个说明passivation-stop-message的示范代码:

trait CounterCommand
case object Increment extends CounterCommand
final case class GetValue(replyTo: ActorRef[Int]) extends CounterCommand case object Idle extends CounterCommand
case object GoodByeCounter extends CounterCommand def counter2(shard: ActorRef[ClusterSharding.ShardCommand], entityId: String): Behavior[CounterCommand] = {
Behaviors.setup { ctx ⇒ def become(value: Int): Behavior[CounterCommand] =
Behaviors.receiveMessage[CounterCommand] {
case Increment ⇒
become(value + )
case GetValue(replyTo) ⇒
replyTo ! value
Behaviors.same
case Idle ⇒
// after receive timeout
shard ! ClusterSharding.Passivate(ctx.self)
Behaviors.same
case GoodByeCounter ⇒
// the stopMessage, used for rebalance and passivate
Behaviors.stopped
} ctx.setReceiveTimeout(.seconds, Idle)
become()
}
} sharding.init(Entity(
typeKey = TypeKey,
createBehavior = ctx ⇒ counter2(ctx.shard, ctx.entityId))
.withStopMessage(GoodByeCounter))

实际上是向主管ShardRegion发送Passivation消息,并指定停止方式。

还有必须注意的是如果使用BackoffSupervisor监控entity:必须使用Backoff.OnStop,因为persist异常会直接停掉entity。Backoff.OnStop策略会重构entity(BackoffSupervisedEntity),再启动。那么如果实施passivation时真的需要停止entity呢?我们可以如下操作:

    case "stop" =>
context.stop(self)
context.parent ! PoisonPill

context.parent是BackoffSupervisor,需要同时停掉。

下面我们就设计一个例子来示范集群分片应用。为了更贴近现实,在例子使用了event-sourcing,persistentActor等尚未完整介绍的技术和工具。我会在接着的讨论里介绍它们的原理和使用方式。这个例子模仿一个水果店收银业务:有三台pos机,顾客到任何pos机前录入商品、数量,然后结账。这个示范的主要目的是任何时间如果后端服务器出现故障,正在录入过程中的销售单状态都能得到完整恢复。

我们先看看这个pos前端的源代码:

import akka.actor._
import akka.cluster._
import akka.persistence._
import akka.pattern._
import scala.concurrent.duration._ object POSTerminal {
case class Fruit(code: String, name: String, price: Double)
case class Item(fruit: Fruit, qty: Int) sealed trait Command {
}
case class Checkout(fruit: Fruit, qty: Int) extends Command
case object ShowTotol extends Command
case class PayCash(amount: Double) extends Command
case object Shutdown extends Command sealed trait Event {}
case class ItemScanned(fruit: Fruit, qty: Int) extends Event
case object Paid extends Event case class Items(items: List[Item] = Nil) {
def itemAdded(evt: Event): Items = evt match {
case ItemScanned(fruit,qty) =>
copy( Item(fruit,qty) :: items ) //append item case _ => this //nothing happens
}
def billPaid = copy(Nil) //clear all items
override def toString = items.reverse.toString()
} def termProps = Props(new POSTerminal()) //backoff suppervisor must use onStop mode
def POSProps: Props = {
val options = Backoff.onStop(
childProps = termProps,
childName = "posterm",
minBackoff = second,
maxBackoff = seconds,
randomFactor = 0.20
)
BackoffSupervisor.props(options)
} } class POSTerminal extends PersistentActor with ActorLogging {
import POSTerminal._
val cluster = Cluster(context.system) // self.path.parent.name is the type name (utf-8 URL-encoded)
// self.path.name is the entry identifier (utf-8 URL-encoded) but entity has a supervisor
override def persistenceId: String = self.path.parent.parent.name + "-" + self.path.parent.name var currentItems = Items() override def receiveRecover: Receive = {
case evt: Event => currentItems = currentItems.itemAdded(evt)
log.info(s"***** ${persistenceId} recovering events ... ********")
case SnapshotOffer(_,loggedItems: Items) =>
log.info(s"***** ${persistenceId} recovering snapshot ... ********")
currentItems = loggedItems
} override def receiveCommand: Receive = {
case Checkout(fruit,qty) =>
log.info(s"*********${persistenceId} is scanning item: $fruit, qty: $qty *********")
persist(ItemScanned(fruit,qty))(evt => currentItems = currentItems.itemAdded(evt)) case ShowTotol =>
log.info(s"*********${persistenceId} on ${cluster.selfAddress} has current scanned items: *********")
if (currentItems.items == Nil)
log.info(s"**********${persistenceId} None transaction found! *********")
else
currentItems.items.reverse.foreach (item =>
log.info(s"*********${persistenceId}: ${item.fruit.name} ${item.fruit.price} X ${item.qty} = ${item.fruit.price * item.qty} *********")) case PayCash(amt) =>
log.info(s"**********${persistenceId} paying $amt to settle ***********")
persist(Paid) { _ =>
currentItems = currentItems.billPaid
saveSnapshot(currentItems) //no recovery
} //shutdown this node to validate entity relocation and proper state recovery
case Shutdown =>
log.info(s"******** node ${cluster.selfAddress} is leaving cluster ... *******")
cluster.leave(cluster.selfAddress)
}
}

我用下面几项来总结一下:

1、POSTerminal是具体的业务运算前端,包裹在BackoffSupervisor里。能保证这个entity在因异常如持久化失败造成停顿时能进行重试。所以,使用了Backoff.onStop方式。

2、persistenceId=self.path.parent.parent.name+"-"+self.path.parent.name 代表: 店号-机号 如: 1-1021。actor.path.name的产生是由ShardRegion具体操作的,其实就是ExtactShardId-ExtractEntityId。

3、注意这个状态类型Item,它的方法itemAdded(evt): Item 即返回新状态。所以必须谨记用currentItems=itemAdded(evt)这样的语法。

下面是构建和启动ClusterSharding的源代码:

object POSShard {
import POSTerminal._ val shardName = "POSManager"
case class POSCommand(id: Long, cmd: Command) {
def shopId = id.toString.head.toString
def posId = id.toString
} val getPOSId: ShardRegion.ExtractEntityId = {
case posCommand: POSCommand => (posCommand.posId,posCommand.cmd)
}
val getShopId: ShardRegion.ExtractShardId = {
case posCommand: POSCommand => posCommand.shopId
} def create(port: Int) = {
val config = ConfigFactory.parseString(s"akka.remote.netty.tcp.port=$port")
.withFallback(ConfigFactory.load())
val system = ActorSystem("posSystem",config) ClusterSharding(system).start(
typeName = shardName,
entityProps = POSProps,
settings = ClusterShardingSettings(system),
extractEntityId = getPOSId,
extractShardId = getShopId
)
} }

用下面的代码来测试:

object POSDemo extends App {
POSShard.create()
Thread.sleep()
POSShard.create()
POSShard.create()
val posref = POSShard.create()
scala.io.StdIn.readLine() val apple = Fruit("","high grade apple",10.5)
val orange = Fruit("","sunkist orage",12.0)
val grape = Fruit("","xinjiang red grape",15.8) posref ! POSCommand(, Checkout(apple,))
posref ! POSCommand(,Checkout(grape,)) posref ! POSCommand(,ShowTotol)
scala.io.StdIn.readLine() posref ! POSCommand(,Shutdown)
scala.io.StdIn.readLine() posref ! POSCommand(,Checkout(orange,)) posref ! POSCommand(,ShowTotol)
scala.io.StdIn.readLine() posref ! POSCommand(,Checkout(orange,)) posref ! POSCommand(,ShowTotol)
scala.io.StdIn.readLine() }

运算结果如下:

[akka.tcp://posSystem@127.0.0.1:2551*********1-1021 is scanning item: Fruit(0001,high grade apple,10.5), qty: 2 *********
[akka.tcp://posSystem@127.0.0.1:2551*********1-1021 is scanning item: Fruit(0003,xinjiang red grape,15.8), qty: 1 *********
[akka.tcp://posSystem@127.0.0.1:2551*********1-1021 on akka.tcp://posSystem@127.0.0.1:2551 has current scanned items: *********
[akka.tcp://posSystem@127.0.0.1:2551*********1-1021: high grade apple 10.5 X 2 = 21.0 *********
[akka.tcp://posSystem@127.0.0.1:2551*********1-1021: xinjiang red grape 15.8 X 1 = 15.8 ********* [akka.tcp://posSystem@127.0.0.1:2551******** node akka.tcp://posSystem@127.0.0.1:2551 is leaving cluster ... *******
[akka.tcp://posSystem@127.0.0.1:2551/system/remoting-terminator] Remoting shut down. [akka.tcp://posSystem@127.0.0.1:2552***** 1-1021 recovering events ... ********
[akka.tcp://posSystem@127.0.0.1:2552***** 1-1021 recovering events ... ********
[akka.tcp://posSystem@127.0.0.1:2552********1-1021 is scanning item: Fruit(0002,sunkist orage,12.0), qty: 10 *********
[akka.tcp://posSystem@127.0.0.1:2552*********1-1021 on akka.tcp://posSystem@127.0.0.1:2552 has current scanned items: *********
[akka.tcp://posSystem@127.0.0.1:2552*********1-1021: high grade apple 10.5 X 2 = 21.0 *********
[akka.tcp://posSystem@127.0.0.1:2552*********1-1021: xinjiang red grape 15.8 X 1 = 15.8 *********
[akka.tcp://posSystem@127.0.0.1:2552*********1-1021: sunkist orage 12.0 X 10 = 120.0 *********

从结果显示看到:一开始1-1021是在2551节点上运行的。我们用Shutdown关停2551后ClusterSharding立即在2552上重构了1-1021并且恢复了之前的状态。能够在系统出现故障无法使用的情况下自动对运行中的actor进行迁移、状态恢复,正是我们这次讨论的核心内容。

下面是本次示范的源代码:

build.sbt

name := "akka-cluster-sharding"

version := "0.2"

scalaVersion := "2.12.8"

libraryDependencies := Seq(
"com.typesafe.akka" %% "akka-cluster-sharding" % "2.5.19",
"com.typesafe.akka" %% "akka-persistence" % "2.5.19",
"com.typesafe.akka" %% "akka-persistence-cassandra" % "0.92",
"com.typesafe.akka" %% "akka-persistence-cassandra-launcher" % "0.92" % Test
)

resources/application.conf

akka.actor.warn-about-java-serializer-usage = off
akka.log-dead-letters-during-shutdown = off
akka.log-dead-letters = off akka {
loglevel = INFO
actor {
provider = "cluster"
} remote {
log-remote-lifecycle-events = off
netty.tcp {
hostname = "127.0.0.1"
port =
}
} cluster {
seed-nodes = [
"akka.tcp://posSystem@127.0.0.1:2551"]
log-info = off
} persistence {
journal.plugin = "cassandra-journal"
snapshot-store.plugin = "cassandra-snapshot-store"
} }

Entities.scala

import akka.actor._
import akka.cluster._
import akka.persistence._
import akka.pattern._
import scala.concurrent.duration._ object POSTerminal {
case class Fruit(code: String, name: String, price: Double)
case class Item(fruit: Fruit, qty: Int) sealed trait Command {
}
case class Checkout(fruit: Fruit, qty: Int) extends Command
case object ShowTotol extends Command
case class PayCash(amount: Double) extends Command
case object Shutdown extends Command sealed trait Event {}
case class ItemScanned(fruit: Fruit, qty: Int) extends Event
case object Paid extends Event case class Items(items: List[Item] = Nil) {
def itemAdded(evt: Event): Items = evt match {
case ItemScanned(fruit,qty) =>
copy( Item(fruit,qty) :: items ) //append item case _ => this //nothing happens
}
def billPaid = copy(Nil) //clear all items
override def toString = items.reverse.toString()
} def termProps = Props(new POSTerminal()) //backoff suppervisor must use onStop mode
def POSProps: Props = {
val options = Backoff.onStop(
childProps = termProps,
childName = "posterm",
minBackoff = second,
maxBackoff = seconds,
randomFactor = 0.20
)
BackoffSupervisor.props(options)
} } class POSTerminal extends PersistentActor with ActorLogging {
import POSTerminal._
val cluster = Cluster(context.system) // self.path.parent.name is the type name (utf-8 URL-encoded)
// self.path.name is the entry identifier (utf-8 URL-encoded) but entity has a supervisor
override def persistenceId: String = self.path.parent.parent.name + "-" + self.path.parent.name var currentItems = Items() override def receiveRecover: Receive = {
case evt: Event => currentItems = currentItems.itemAdded(evt)
log.info(s"***** ${persistenceId} recovering events ... ********")
case SnapshotOffer(_,loggedItems: Items) =>
log.info(s"***** ${persistenceId} recovering snapshot ... ********")
currentItems = loggedItems
} override def receiveCommand: Receive = {
case Checkout(fruit,qty) =>
log.info(s"*********${persistenceId} is scanning item: $fruit, qty: $qty *********")
persist(ItemScanned(fruit,qty))(evt => currentItems = currentItems.itemAdded(evt)) case ShowTotol =>
log.info(s"*********${persistenceId} on ${cluster.selfAddress} has current scanned items: *********")
if (currentItems.items == Nil)
log.info(s"**********${persistenceId} None transaction found! *********")
else
currentItems.items.reverse.foreach (item =>
log.info(s"*********${persistenceId}: ${item.fruit.name} ${item.fruit.price} X ${item.qty} = ${item.fruit.price * item.qty} *********")) case PayCash(amt) =>
log.info(s"**********${persistenceId} paying $amt to settle ***********")
persist(Paid) { _ =>
currentItems = currentItems.billPaid
saveSnapshot(currentItems) //no recovery
} //shutdown this node to validate entity relocation and proper state recovery
case Shutdown =>
log.info(s"******** node ${cluster.selfAddress} is leaving cluster ... *******")
cluster.leave(cluster.selfAddress)
}
}

Shards.scala

import akka.actor._
import akka.cluster.sharding._
import com.typesafe.config.ConfigFactory object POSShard {
import POSTerminal._ val shardName = "POSManager"
case class POSCommand(id: Long, cmd: Command) {
def shopId = id.toString.head.toString
def posId = id.toString
} val getPOSId: ShardRegion.ExtractEntityId = {
case posCommand: POSCommand => (posCommand.posId,posCommand.cmd)
}
val getShopId: ShardRegion.ExtractShardId = {
case posCommand: POSCommand => posCommand.shopId
} def create(port: Int) = {
val config = ConfigFactory.parseString(s"akka.remote.netty.tcp.port=$port")
.withFallback(ConfigFactory.load())
val system = ActorSystem("posSystem",config) ClusterSharding(system).start(
typeName = shardName,
entityProps = POSProps,
settings = ClusterShardingSettings(system),
extractEntityId = getPOSId,
extractShardId = getShopId
)
} }

POSDemo.scala

import POSTerminal._
import POSShard._ object POSDemo extends App {
POSShard.create()
Thread.sleep()
POSShard.create()
POSShard.create()
val posref = POSShard.create()
scala.io.StdIn.readLine() val apple = Fruit("","high grade apple",10.5)
val orange = Fruit("","sunkist orage",12.0)
val grape = Fruit("","xinjiang red grape",15.8) posref ! POSCommand(, Checkout(apple,))
posref ! POSCommand(,Checkout(grape,)) posref ! POSCommand(,ShowTotol)
scala.io.StdIn.readLine() posref ! POSCommand(,Shutdown)
scala.io.StdIn.readLine() posref ! POSCommand(,Checkout(orange,)) posref ! POSCommand(,ShowTotol)
scala.io.StdIn.readLine() posref ! POSCommand(,Checkout(orange,)) posref ! POSCommand(,ShowTotol)
scala.io.StdIn.readLine() }

Akka-Cluster(6)- Cluster-Sharding:集群分片,分布式交互程序核心方式的更多相关文章

  1. akka-typed(7) - cluster:sharding, 集群分片

    在使用akka-typed的过程中发现有很多地方都简化了不少,变得更方便了,包括:Supervision,只要用Behaviors.supervise()把Behavior包住,很容易就可以实现这个a ...

  2. Akka Cluster之集群分片

    一.介绍  当您需要在集群中的多个节点之间分配Actor,并希望能够使用其逻辑标识符与它们进行交互时,集群分片是非常有用的.你无需关心Actor在集群中的物理位置,因为这可能也会随着时间的推移而发生变 ...

  3. Akka(13): 分布式运算:Cluster-Sharding-运算的集群分片

    通过上篇关于Cluster-Singleton的介绍,我们了解了Akka为分布式程序提供的编程支持:基于消息驱动的运算模式特别适合分布式程序编程,我们不需要特别的努力,只需要按照普通的Actor编程方 ...

  4. akka 集群分片

    akka 集群 Sharding分片 分片上下级结构 集群(多台节点机) —> 每台节点机(1个片区) —> 每个片区(多个分片) —> 每个分片(多个实体) 实体: 分片管理的 A ...

  5. MySQL Cluster 7.3.5 集群配置实例(入门篇)

    一.环境说明: CentOS6.3(32位) + MySQL Cluster 7.3.5,规划5台机器,资料如下: 节点分布情况: MGM:192.168.137. NDBD1:192.168.137 ...

  6. MySQL Cluster 7.3.5 集群配置参数优化(优化篇)

    按照前面的教程:MySQL Cluster 7.3.5 集群配置实例(入门篇),可快速搭建起基础版的MySQL Cluster集群,但是在生成环境中,还是有很多问题的,即配置参数需要优化下, 当前生产 ...

  7. Redis Cluster 4.0.9 集群安装搭建

    Redis Cluster 4.0.9集群搭建步骤:yum install -y gcc g++ gcc-c++ make openssl cd redis-4.0.9 make mkdir -p / ...

  8. Mongodb中Sharding集群

    随着mongodb数据量的增多,可能会达到单个节点的存储能力限制,以及application较大的访问量也会导致单个节点无法承担,所以此时需要构建集群环境,并通过sharding方案将整个数据集拆分成 ...

  9. Mongodb Sharding 集群配置

    mongodb的sharding集群由以下3个服务组成: Shards  Server: 每个shard由一个或多个mongod进程组成,用于存储数据 Config  Server: 用于存储集群的M ...

随机推荐

  1. OSPFv3综合实验(GNS3)

    一.实验目的 1.  掌握 OSPFv3(v2) 的配置方法 2.  掌握在帧中继环境下 OSPFv3 (v2)的配置方法 3.  掌握 OSPFv3(v2) NSSA 的配置方法 4.  掌握外部路 ...

  2. git tag 查看标签列表、切换标签

    1.查看标签列表 git tag 2.切换标签(需要指定分支 test 为分支.v0.17.7 为标签版本) git checkout -b test v0.17.7

  3. IIS发布MVC ASP.NET网站

    发布网站后,发现无法访问,最后在配置文件上添加一段: <system.codedom> <compilers> <compiler language="c#;c ...

  4. SSH 免密码登陆到多台机器

    场景: 需要从主机1.100免密码多了到1.115及1.116 实现: 登陆1.100 $ cd ~/.ssh/ $ ssh-keygen -t rsa 然后三个回车 会有两个文件产生,id_rsa ...

  5. Gym - 101848B Almost AP 暴力

    题目链接:http://codeforces.com/gym/101848/problem/B 给出一串数字要你最多改动三个数字使这一串数字成为等差数列.因为最多改动三个数字所以可以先求出相邻两项的差 ...

  6. ArrayList增加扩容问题 源码分析

    public class ArrayList<E>{ private static final int DEFAULT_CAPACITY = 10;//默认的容量是10 private s ...

  7. 【微信小程序】模仿58同城页面制作以及动态数据加载

    完成动态数据的加载,如下 使用上班的空余时间慢慢的学习,相信总有一天我会很熟悉的掌握这门技术. 本次学习小总结: 微信小程序使用的代码基本与HTML.CSS.JS等前段有关知识一样. 微信小程序js使 ...

  8. php中的问题整理

    1.什么是 CSRF 攻击 ?XSS 攻击?如何防范? CSRF,跨站请求伪造,攻击方伪装用户身份发送请求从而窃取信息或者破坏系统.讲述基本原理:用户访问A网站登陆并生成了cookie,再访问B网站, ...

  9. 利用IO和File类实现拷贝文件目录问题

    /* 复制文件夹 参数 File src,File dest */ public static void copy(File src,File dest){ if (src.isDirectory() ...

  10. 学习Acegi应用到实际项目中(11)- 切换用户

    在某些应用场合中,可能需要用到切换用户的功能,从而以另一用户的身份进行相关操作.这一点类似于在Linux系统中,用su命令切换到另一用户进行相关操作. 既然实际应用中有这种场合,那么我们就有必要对其进 ...