存活1億年!南太平洋海底發現恐龍時代的微生物

摘錄自2020年7月29日自由時報報導

日本海洋研究開發機構和高知大學所組成的團隊,在南太平洋海底下約有1億年至430萬年的地層從發現微生物,這些微生物並未變成化石,而是在提供營養後,竟然可以從長期休眠中復甦。

據日本《共同社》報導,日本研究團隊在2010年於紐西蘭以東的海域進行挖掘研究,他們從3700至5700公尺深的海底中挖掘7處地層,發現了像是被封閉在細微粒子組成的粘土之中的微生物,為了確認微生物是否仍然存活還是變成化石,團隊開始提供氧氣和糖等餌食進行了觀察。

實驗啟動3週後,微生物竟然復甦開始進食,約2個月後最大增至1萬倍以上。細胞分裂平均從喂餌5天後開始,微生物平均復甦率為77%,而年代最久遠的1億年地層,存活率更是高達99.1%。

海洋
國際新聞
南太平洋

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

akka-typed(6) – cluster:group router, cluster-load-balancing

先談談akka-typed的router actor。route 分pool router, group router兩類。我們先看看pool-router的使用示範:

      val pool = Routers.pool(poolSize = 4)( // make sure the workers are restarted if they fail
 Behaviors.supervise(WorkerRoutee()).onFailure[Exception](SupervisorStrategy.restart)) val router = ctx.spawn(pool, "worker-pool") (0 to 10).foreach { n => router ! WorkerRoutee.DoLog(s"msg $n")
      }

上面例子里的pool是個pool-router,意思是一個有4個routees的routee池。每個routee都是通過WorkerRoutee()構建的,意味着routee池中只有一個種類的actor。pool-router是通過工廠方法直接在本地(JVM)構建(spawn)所有的routee。也就是說所有routee都是router的子actor。

再看看group-router的使用例子:

val serviceKey = ServiceKey[Worker.Command]("log-worker") // this would likely happen elsewhere - if we create it locally we // can just as well use a pool
      val workerRoutee = ctx.spawn(WorkerRoutee(), "worker-route") ctx.system.receptionist ! Receptionist.Register(serviceKey, workerRoutee) val group = Routers.group(serviceKey) val router = ctx.spawn(group, "worker-group") // the group router will stash messages until it sees the first listing of registered // services from the receptionist, so it is safe to send messages right away
      (0 to 10).foreach { n => router ! WorkerRoutee.DoLog(s"msg $n") }

group-router與pool-router有較多分別:

1、routee是在router之外構建的,router是用一個key通過Receptionist獲取同key的actor清單作為routee group的

2、Receptionist是集群全局的。任何節點上的actor都可以發送註冊消息在Receptionist上登記

3、沒有size限制,任何actor一旦在Receptionist上登記即變成routee,接受router管理

應該說如果想把運算任務分配在集群里的各節點上并行運算實現load-balance效果,group-router是最合適的選擇。不過對不同的運算任務需要多少routee則需要用戶自行決定,不像以前akka-classic里通過cluster-metrics根據節點負載情況自動增減routee實例那麼方便。

Receptionist: 既然說到,那麼就再深入一點介紹Receptionist的應用:上面提到,Receptionist是集群全局的。就是說任何節點上的actor都可以在Receptonist上註冊形成一個生存在集群中不同節點的actor清單。如果Receptionist把這個清單提供給一個用戶,那麼這個用戶就可以把運算任務配置到各節點上,實現某種意義上的分佈式運算模式。Receptionist的使用方式是:通過向本節點的Receptionist發送消息去登記ActorRef,然後通過Receptionist發布的登記變化消息即可獲取最新的ActorRef清單:

  val WorkerServiceKey = ServiceKey[Worker.TransformText]("Worker") ctx.system.receptionist ! Receptionist.Register(WorkerServiceKey, ctx.self) ctx.system.receptionist ! Receptionist.Subscribe(Worker.WorkerServiceKey, subscriptionAdapter)

Receptionist的登記和清單獲取是以ServiceKey作為關聯的。那麼獲取的清單內應該全部是一種類型的actor,只不過它們的地址可能是跨節點的,但它們只能進行同一種運算。從另一個角度說,一項任務是分佈在不同節點的actor并行進行運算的。

在上篇討論里提過:如果發布-訂閱機制是在兩個actor之間進行的,那麼這兩個actor也需要在規定的信息交流協議框架下作業:我們必須注意消息類型,提供必要的消息類型轉換機制。下面是一個Receptionist登記示範:

object Worker { val WorkerServiceKey = ServiceKey[Worker.TransformText]("Worker") sealed trait Command final case class TransformText(text: String, replyTo: ActorRef[TextTransformed]) extends Command with CborSerializable final case class TextTransformed(text: String) extends CborSerializable def apply(): Behavior[Command] = Behaviors.setup { ctx =>
      // each worker registers themselves with the receptionist
      ctx.log.info("Registering myself with receptionist") ctx.system.receptionist ! Receptionist.Register(WorkerServiceKey, ctx.self) Behaviors.receiveMessage { case TransformText(text, replyTo) => replyTo ! TextTransformed(text.toUpperCase) Behaviors.same } } }

Receptionist登記比較直接:登記者不需要Receptionist返回消息,所以隨便用ctx.self作為消息的sender。注意TransformText的replyTo: ActorRef[TextTransformed],代表sender是個可以處理TextTransformed消息類型的actor。實際上,在sender方是通過ctx.ask提供了TextTransformed的類型轉換。

Receptionist.Subscribe需要Receptionist返回一個actor清單,所以是個request/response模式。那麼發送給Receptionist消息中的replyTo必須是發送者能處理的類型,如下:

  def apply(): Behavior[Event] = Behaviors.setup { ctx => Behaviors.withTimers { timers =>
      // subscribe to available workers
      val subscriptionAdapter = ctx.messageAdapter[Receptionist.Listing] { case Worker.WorkerServiceKey.Listing(workers) => WorkersUpdated(workers) } ctx.system.receptionist ! Receptionist.Subscribe(Worker.WorkerServiceKey, subscriptionAdapter) ... } 

ctx.messageAdapter進行了一個從Receptionist.Listing返回類型到WorkersUpdated類型的轉換機制登記:從Receptionist回復的List類型會被轉換成WorkersUpdated類型,如下:

... Behaviors.receiveMessage { case WorkersUpdated(newWorkers) => ctx.log.info("List of services registered with the receptionist changed: {}", newWorkers) ...

另外,上面提過的TextTransformed轉換如下:

 ctx.ask[Worker.TransformText,Worker.TextTransformed](selectedWorker, Worker.TransformText(text, _)) { case Success(transformedText) => TransformCompleted(transformedText.text, text) case Failure(ex) => JobFailed("Processing timed out", text) }

ctx.ask將TextTransformed轉換成TransformCompleted。完整的Behavior定義如下:

object Frontend { sealed trait Event private case object Tick extends Event private final case class WorkersUpdated(newWorkers: Set[ActorRef[Worker.TransformText]]) extends Event private final case class TransformCompleted(originalText: String, transformedText: String) extends Event private final case class JobFailed(why: String, text: String) extends Event def apply(): Behavior[Event] = Behaviors.setup { ctx => Behaviors.withTimers { timers =>
      // subscribe to available workers
      val subscriptionAdapter = ctx.messageAdapter[Receptionist.Listing] { case Worker.WorkerServiceKey.Listing(workers) => WorkersUpdated(workers) } ctx.system.receptionist ! Receptionist.Subscribe(Worker.WorkerServiceKey, subscriptionAdapter) timers.startTimerWithFixedDelay(Tick, Tick, 2.seconds) running(ctx, IndexedSeq.empty, jobCounter = 0) } } private def running(ctx: ActorContext[Event], workers: IndexedSeq[ActorRef[Worker.TransformText]], jobCounter: Int): Behavior[Event] = Behaviors.receiveMessage { case WorkersUpdated(newWorkers) => ctx.log.info("List of services registered with the receptionist changed: {}", newWorkers) running(ctx, newWorkers.toIndexedSeq, jobCounter) case Tick =>
        if (workers.isEmpty) { ctx.log.warn("Got tick request but no workers available, not sending any work") Behaviors.same } else { // how much time can pass before we consider a request failed
          implicit val timeout: Timeout = 5.seconds val selectedWorker = workers(jobCounter % workers.size) ctx.log.info("Sending work for processing to {}", selectedWorker) val text = s"hello-$jobCounter" ctx.ask[Worker.TransformText,Worker.TextTransformed](selectedWorker, Worker.TransformText(text, _)) { case Success(transformedText) => TransformCompleted(transformedText.text, text) case Failure(ex) => JobFailed("Processing timed out", text) } running(ctx, workers, jobCounter + 1) } case TransformCompleted(originalText, transformedText) => ctx.log.info("Got completed transform of {}: {}", originalText, transformedText) Behaviors.same case JobFailed(why, text) => ctx.log.warn("Transformation of text {} failed. Because: {}", text, why) Behaviors.same }

現在我們可以示範用group-router來實現某種跨節點的分佈式運算。因為group-router是通過Receptionist來實現對routees管理的,而Receptionist是集群全局的,意味着如果我們在各節點上構建routee,然後向Receptionist登記,就會形成一個跨節點的routee ActorRef清單。如果把任務分配到這個清單上的routee上去運算,應該能實現集群節點負載均衡的效果。下面我們就示範這個loadbalancer。流程很簡單:在一個接入點 (serviceActor)中構建workersRouter,然後3個workerRoutee並向Receptionist登記,把接到的任務分解成子任務逐個發送給workersRouter。每個workerRoutee完成任務后將結果發送給一個聚合器Aggregator,Aggregator在核對完成接收所有workerRoutee返回的結果后再把匯總結果返回serverActor。先看看這個serverActor:

object Service { val routerServiceKey = ServiceKey[WorkerRoutee.Command]("workers-router") sealed trait Command extends CborSerializable case class ProcessText(text: String) extends Command { require(text.nonEmpty) } case class WrappedResult(res: Aggregator.Response) extends Command def serviceBehavior(workersRouter: ActorRef[WorkerRoutee.Command]): Behavior[Command] = Behaviors.setup[Command] { ctx => val aggregator = ctx.spawn(Aggregator(), "aggregator") val aggregatorRef: ActorRef[Aggregator.Response] = ctx.messageAdapter(WrappedResult) Behaviors.receiveMessage { case ProcessText(text) => ctx.log.info("******************** received ProcessText command: {} ****************",text) val words = text.split(' ').toIndexedSeq aggregator ! Aggregator.CountText(words.size, aggregatorRef) words.foreach { word => workersRouter ! WorkerRoutee.Count(word, aggregator) } Behaviors.same case WrappedResult(msg) => msg match { case Aggregator.Result(res) => ctx.log.info("************** mean length of words = {} **********", res) } Behaviors.same } } def singletonService(ctx: ActorContext[Command], workersRouter: ActorRef[WorkerRoutee.Command]) = { val singletonSettings = ClusterSingletonSettings(ctx.system) .withRole("front") SingletonActor( Behaviors.supervise( serviceBehavior(workersRouter) ).onFailure( SupervisorStrategy .restartWithBackoff(minBackoff = 10.seconds, maxBackoff = 60.seconds, randomFactor = 0.1) .withMaxRestarts(3) .withResetBackoffAfter(10.seconds) ) , "singletonActor" ).withSettings(singletonSettings) } def apply(): Behavior[Command] = Behaviors.setup[Command] { ctx => val cluster = Cluster(ctx.system) val workersRouter = ctx.spawn( Routers.group(routerServiceKey) .withRoundRobinRouting(), "workersRouter" ) (0 until 3).foreach { n => val routee = ctx.spawn(WorkerRoutee(cluster.selfMember.address.toString), s"work-routee-$n") ctx.system.receptionist ! Receptionist.register(routerServiceKey, routee) } val singletonActor = ClusterSingleton(ctx.system).init(singletonService(ctx, workersRouter)) Behaviors.receiveMessage { case job@ProcessText(text) => singletonActor ! job Behaviors.same } } }

整體goup-router和routee的構建是在apply()里,並把接到的任務轉發給singletonActor。singletonActor是以serviceBehavior為核心的一個actor。在servceBehavior里把收到的任務分解並分別發送給workersRouter。值得注意的是:serviceBehavior期望接收從Aggregator的回應,它們之間存在request/response模式信息交流,所以需要Aggregator.Response到WrappedResult的類型轉換機制。還有:子任務是通過workersRoute發送給個workerRoutee的,我們需要各workerRoutee把運算結果返給給Aggregator,所以發送給workersRouter的消息包含了Aggregator的ActorRef,如:workersRouter ! WorkerRoutee.Count(cnt,aggregatorRef)。

Aggregator是個persistentActor, 如下:

 

object Aggregator { sealed trait Command sealed trait Event extends CborSerializable sealed trait Response case class CountText(cnt: Int, replyTo: ActorRef[Response]) extends Command case class MarkLength(word: String, len: Int) extends Command case class TextCounted(cnt: Int) extends Event case class LengthMarked(word: String, len: Int) extends Event case class Result(meanWordLength: Double) extends Response case class State(expectedNum: Int = 0, lens: List[Int] = Nil) var replyTo: ActorRef[Response] = _ def commandHandler: (State,Command) => Effect[Event,State] = (st,cmd) => { cmd match { case CountText(cnt,ref) => replyTo = ref Effect.persist(TextCounted(cnt)) case MarkLength(word,len) => Effect.persist(LengthMarked(word,len)) } } def eventHandler: (State, Event) => State = (st,ev) => { ev match { case TextCounted(cnt) => st.copy(expectedNum = cnt, lens = Nil) case LengthMarked(word,len) => val state = st.copy(lens = len :: st.lens) if (state.lens.size >= state.expectedNum) { val meanWordLength = state.lens.sum.toDouble / state.lens.size replyTo ! Result(meanWordLength) State() } else state } } val takeSnapShot: (State,Event,Long) => Boolean = (st,ev,seq) => { if (st.lens.isEmpty) { if (ev.isInstanceOf[LengthMarked]) true
          else
            false } else
         false } def apply(): Behavior[Command] = Behaviors.supervise( Behaviors.setup[Command] { ctx => EventSourcedBehavior( persistenceId = PersistenceId("33","2333"), emptyState = State(), commandHandler = commandHandler, eventHandler = eventHandler ).onPersistFailure( SupervisorStrategy .restartWithBackoff(minBackoff = 10.seconds, maxBackoff = 60.seconds, randomFactor = 0.1) .withMaxRestarts(3) .withResetBackoffAfter(10.seconds) ).receiveSignal { case (state, RecoveryCompleted) => ctx.log.info("**************Recovery Completed with state: {}***************",state) case (state, SnapshotCompleted(meta))  => ctx.log.info("**************Snapshot Completed with state: {},id({},{})***************",state,meta.persistenceId, meta.sequenceNr) case (state,RecoveryFailed(err)) => ctx.log.error("*************recovery failed with: {}***************",err.getMessage) case (state,SnapshotFailed(meta,err)) => ctx.log.error("***************snapshoting failed with: {}*************",err.getMessage) }.snapshotWhen(takeSnapShot) } ).onFailure( SupervisorStrategy .restartWithBackoff(minBackoff = 10.seconds, maxBackoff = 60.seconds, randomFactor = 0.1) .withMaxRestarts(3) .withResetBackoffAfter(10.seconds) ) }

注意這個takeSnapShot函數:這個函數是在EventSourcedBehavior.snapshotWhen(takeSnapShot)調用的。傳入參數是(State,Event,seqenceNr),我們需要對State,Event的當前值進行分析后返回true代表做一次snapshot。

看看一部分显示就知道任務已經分配到幾個節點上的routee:

20:06:59.072 [ClusterSystem-akka.actor.default-dispatcher-15] INFO com.learn.akka.WorkerRoutee$ - ************** processing [this] on akka://ClusterSystem@127.0.0.1:51182 ***********
20:06:59.072 [ClusterSystem-akka.actor.default-dispatcher-3] INFO com.learn.akka.WorkerRoutee$ - ************** processing [text] on akka://ClusterSystem@127.0.0.1:51182 ***********
20:06:59.072 [ClusterSystem-akka.actor.default-dispatcher-36] INFO com.learn.akka.WorkerRoutee$ - ************** processing [be] on akka://ClusterSystem@127.0.0.1:51182 ***********
20:06:59.236 [ClusterSystem-akka.actor.default-dispatcher-16] INFO com.learn.akka.WorkerRoutee$ - ************** processing [will] on akka://ClusterSystem@127.0.0.1:51173 ***********
20:06:59.236 [ClusterSystem-akka.actor.default-dispatcher-26] INFO com.learn.akka.WorkerRoutee$ - ************** processing [is] on akka://ClusterSystem@127.0.0.1:25251 ***********
20:06:59.236 [ClusterSystem-akka.actor.default-dispatcher-13] INFO com.learn.akka.WorkerRoutee$ - ************** processing [the] on akka://ClusterSystem@127.0.0.1:51173 ***********
20:06:59.236 [ClusterSystem-akka.actor.default-dispatcher-3] INFO com.learn.akka.WorkerRoutee$ - ************** processing [that] on akka://ClusterSystem@127.0.0.1:25251 ***********
20:06:59.236 [ClusterSystem-akka.actor.default-dispatcher-3] INFO com.learn.akka.WorkerRoutee$ - ************** processing [analyzed] on akka://ClusterSystem@127.0.0.1:25251 ***********

這個例子的源代碼如下:

package com.learn.akka

import akka.actor.typed._
import akka.persistence.typed._
import akka.persistence.typed.scaladsl._
import scala.concurrent.duration._
import akka.actor.typed.receptionist._
import akka.actor.typed.scaladsl.Behaviors
import akka.actor.typed.scaladsl._
import akka.cluster.typed.Cluster
import akka.cluster.typed.ClusterSingleton
import akka.cluster.typed.ClusterSingletonSettings
import akka.cluster.typed.SingletonActor
import com.typesafe.config.ConfigFactory

object WorkerRoutee {
  sealed trait Command extends CborSerializable
  case class Count(word: String, replyTo: ActorRef[Aggregator.Command]) extends Command

  def apply(nodeAddress: String): Behavior[Command] = Behaviors.setup {ctx =>
    Behaviors.receiveMessage[Command] {
      case Count(word,replyTo) =>
        ctx.log.info("************** processing [{}] on {} ***********",word,nodeAddress)
        replyTo ! Aggregator.MarkLength(word,word.length)
        Behaviors.same
    }
  }
}
object Aggregator {
  sealed trait Command
  sealed trait Event extends  CborSerializable
  sealed trait Response

  case class CountText(cnt: Int, replyTo: ActorRef[Response]) extends Command
  case class MarkLength(word: String, len: Int) extends Command
  case class TextCounted(cnt: Int) extends Event
  case class LengthMarked(word: String, len: Int) extends Event
  case class Result(meanWordLength: Double) extends Response

  case class State(expectedNum: Int = 0, lens: List[Int] = Nil)

  var replyTo: ActorRef[Response] = _

  def commandHandler: (State,Command) => Effect[Event,State] = (st,cmd) => {
    cmd match {
      case CountText(cnt,ref) =>
        replyTo = ref
        Effect.persist(TextCounted(cnt))
      case MarkLength(word,len) =>
        Effect.persist(LengthMarked(word,len))
    }
  }
  def eventHandler: (State, Event) => State = (st,ev) => {
    ev match {
      case TextCounted(cnt) =>
        st.copy(expectedNum = cnt, lens = Nil)
      case LengthMarked(word,len) =>
        val state = st.copy(lens = len :: st.lens)
        if (state.lens.size >= state.expectedNum) {
          val meanWordLength = state.lens.sum.toDouble / state.lens.size
          replyTo ! Result(meanWordLength)
          State()
        } else state
    }
  }
  val takeSnapShot: (State,Event,Long) => Boolean = (st,ev,seq) => {
      if (st.lens.isEmpty) {
          if (ev.isInstanceOf[LengthMarked])
            true
          else
            false
      } else
         false
  }
  def apply(): Behavior[Command] = Behaviors.supervise(
    Behaviors.setup[Command] { ctx =>
      EventSourcedBehavior(
        persistenceId = PersistenceId("33","2333"),
        emptyState = State(),
        commandHandler = commandHandler,
        eventHandler = eventHandler
      ).onPersistFailure(
        SupervisorStrategy
          .restartWithBackoff(minBackoff = 10.seconds, maxBackoff = 60.seconds, randomFactor = 0.1)
          .withMaxRestarts(3)
          .withResetBackoffAfter(10.seconds)
      ).receiveSignal {
        case (state, RecoveryCompleted) =>
          ctx.log.info("**************Recovery Completed with state: {}***************",state)
        case (state, SnapshotCompleted(meta))  =>
          ctx.log.info("**************Snapshot Completed with state: {},id({},{})***************",state,meta.persistenceId, meta.sequenceNr)
        case (state,RecoveryFailed(err)) =>
          ctx.log.error("*************recovery failed with: {}***************",err.getMessage)
        case (state,SnapshotFailed(meta,err)) =>
          ctx.log.error("***************snapshoting failed with: {}*************",err.getMessage)
      }.snapshotWhen(takeSnapShot)
    }
  ).onFailure(
    SupervisorStrategy
      .restartWithBackoff(minBackoff = 10.seconds, maxBackoff = 60.seconds, randomFactor = 0.1)
      .withMaxRestarts(3)
      .withResetBackoffAfter(10.seconds)
  )
}
object Service {
  val routerServiceKey = ServiceKey[WorkerRoutee.Command]("workers-router")

  sealed trait Command extends CborSerializable

  case class ProcessText(text: String) extends Command {
    require(text.nonEmpty)
  }

  case class WrappedResult(res: Aggregator.Response) extends Command

  def serviceBehavior(workersRouter: ActorRef[WorkerRoutee.Command]): Behavior[Command] = Behaviors.setup[Command] { ctx =>
    val aggregator = ctx.spawn(Aggregator(), "aggregator")
    val aggregatorRef: ActorRef[Aggregator.Response] = ctx.messageAdapter(WrappedResult)
    Behaviors.receiveMessage {
      case ProcessText(text) =>
        ctx.log.info("******************** received ProcessText command: {} ****************",text)
        val words = text.split(' ').toIndexedSeq
        aggregator ! Aggregator.CountText(words.size, aggregatorRef)
        words.foreach { word =>
          workersRouter ! WorkerRoutee.Count(word, aggregator)
        }
        Behaviors.same
      case WrappedResult(msg) =>
        msg match {
          case Aggregator.Result(res) =>
            ctx.log.info("************** mean length of words = {} **********", res)
        }
        Behaviors.same
    }
  }

  def singletonService(ctx: ActorContext[Command], workersRouter: ActorRef[WorkerRoutee.Command]) = {
    val singletonSettings = ClusterSingletonSettings(ctx.system)
      .withRole("front")
    SingletonActor(
      Behaviors.supervise(
        serviceBehavior(workersRouter)
      ).onFailure(
        SupervisorStrategy
          .restartWithBackoff(minBackoff = 10.seconds, maxBackoff = 60.seconds, randomFactor = 0.1)
          .withMaxRestarts(3)
          .withResetBackoffAfter(10.seconds)
      )
      , "singletonActor"
    ).withSettings(singletonSettings)
  }

  def apply(): Behavior[Command] = Behaviors.setup[Command] { ctx =>
    val cluster = Cluster(ctx.system)
    val workersRouter = ctx.spawn(
      Routers.group(routerServiceKey)
        .withRoundRobinRouting(),
      "workersRouter"
    )
    (0 until 3).foreach { n =>
      val routee = ctx.spawn(WorkerRoutee(cluster.selfMember.address.toString), s"work-routee-$n")
      ctx.system.receptionist ! Receptionist.register(routerServiceKey, routee)
    }
    val singletonActor = ClusterSingleton(ctx.system).init(singletonService(ctx, workersRouter))
    Behaviors.receiveMessage {
      case job@ProcessText(text) =>
        singletonActor ! job
        Behaviors.same
    }
  }

}

object LoadBalance {
  def main(args: Array[String]): Unit = {
    if (args.isEmpty) {
      startup("compute", 25251)
      startup("compute", 25252)
      startup("compute", 25253)
      startup("front", 25254)
    } else {
      require(args.size == 2, "Usage: role port")
      startup(args(0), args(1).toInt)
    }
  }

  def startup(role: String, port: Int): Unit = {
    // Override the configuration of the port when specified as program argument
    val config = ConfigFactory
      .parseString(s"""
      akka.remote.artery.canonical.port=$port
      akka.cluster.roles = [$role]
      """)
      .withFallback(ConfigFactory.load("cluster-persistence"))

    val frontEnd = ActorSystem[Service.Command](Service(), "ClusterSystem", config)
    if (role == "front") {
      println("*************** sending ProcessText command  ************")
      frontEnd ! Service.ProcessText("this is the text that will be analyzed")
    }

  }

}

cluster-persistence.conf

akka.actor.allow-java-serialization = on
akka {
  loglevel = INFO
  actor {
    provider = cluster
    serialization-bindings {
      "com.learn.akka.CborSerializable" = jackson-cbor
    }
  }
 remote {
    artery {
      canonical.hostname = "127.0.0.1"
      canonical.port = 0
    }
  }
  cluster {
    seed-nodes = [
      "akka://ClusterSystem@127.0.0.1:25251",
      "akka://ClusterSystem@127.0.0.1:25252"]
  }
  # use Cassandra to store both snapshots and the events of the persistent actors
  persistence {
    journal.plugin = "akka.persistence.cassandra.journal"
    snapshot-store.plugin = "akka.persistence.cassandra.snapshot"
  }
}
akka.persistence.cassandra {
  # don't use autocreate in production
  journal.keyspace = "poc"
  journal.keyspace-autocreate = on
  journal.tables-autocreate = on
  snapshot.keyspace = "poc_snapshot"
  snapshot.keyspace-autocreate = on
  snapshot.tables-autocreate = on
}

datastax-java-driver {
  basic.contact-points = ["192.168.11.189:9042"]
  basic.load-balancing-policy.local-datacenter = "datacenter1"
}

build.sbt

name := "learn-akka-typed"

version := "0.1"

scalaVersion := "2.13.1"
scalacOptions in Compile ++= Seq("-deprecation", "-feature", "-unchecked", "-Xlog-reflective-calls", "-Xlint")
javacOptions in Compile ++= Seq("-Xlint:unchecked", "-Xlint:deprecation")

val AkkaVersion = "2.6.5"
val AkkaPersistenceCassandraVersion = "1.0.0"


libraryDependencies ++= Seq(
  "com.typesafe.akka" %% "akka-cluster-sharding-typed" % AkkaVersion,
  "com.typesafe.akka" %% "akka-persistence-typed" % AkkaVersion,
  "com.typesafe.akka" %% "akka-persistence-query" % AkkaVersion,
  "com.typesafe.akka" %% "akka-serialization-jackson" % AkkaVersion,
  "com.typesafe.akka" %% "akka-persistence-cassandra" % AkkaPersistenceCassandraVersion,
  "com.typesafe.akka" %% "akka-slf4j" % AkkaVersion,
  "ch.qos.logback"     % "logback-classic"             % "1.2.3"
)

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

熱浪和雷雨襲擊法國 高溫乾旱助長森林大火

摘錄自2020年08月01日中央通訊社法國報導

法國今天(1日)同時發布熱浪及大雷雨橘色警戒,巴黎、里昂等地氣溫飆至40度。極端氣候導致法國缺水危機日益嚴重,多個省分今天下令限水。

氣象局表示,今天是「強烈熱度的高峰」,而熱浪又遇上週末及8月暑假出遊車潮,出現熱度加乘效應。這波熱浪於今日達到高峰,並會持續整個週末。面對熱浪與大雷雨,今天全法國96個省中有32個已經發出橘色警戒,其中13個熱浪警報、19個大雷雨警報。

法國近日接連發生森林大火。27日波爾多北部吉倫特省(Gironde)和巴黎南方盧瓦雷省(Loiret)兩處的惡火燒掉超過500公頃的森林。30日晚間,西南部鄰近大西洋的安格雷鎮(Anglet)的森林也出現熊熊大火,燒毀165公頃森林。所幸幾起事故都沒有傷及人員性命。雖然3起森林大火起火原因仍在調查中,但據信連日高溫及季節乾旱,皆助長火勢。

尤其是聖讓德呂(Saint-Jean-de-Luz)30日下午出現超高溫41.9度,創下當地有史以來最高溫紀錄。

生物多樣性
土地利用
國際新聞
法國
森林大火
乾旱
棲地保育
森林
災害

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

Spring系列.依賴注入配置

依賴注入的配置

Spring的依賴注入分為基於構造函數的依賴注入基於setter方法的依賴注入

基於構造函數的依賴注入

        <!-- 通過構造器參數索引方式依賴注入 -->
	<bean id="byIndex" class="cn.javass.spring.chapter3.HelloImpl3">
		<constructor-arg index="0" value="Hello World!"/>
		<constructor-arg index="1" value="1"/>
	</bean>
	<!-- 通過構造器參數類型方式依賴注入 -->
	<bean id="byType" class="cn.javass.spring.chapter3.HelloImpl3">
		<constructor-arg type="java.lang.String" value="Hello World!"/>
		<constructor-arg type="int" value="2"/>
	</bean>
	<!-- 通過構造器參數名稱方式依賴注入 -->
	<bean id="byName" class="cn.javass.spring.chapter3.HelloImpl3">
		<constructor-arg name="message" value="Hello World!"/>
		<constructor-arg name="index" value="3"/>
	</bean>
	<!-- 通過靜態的工廠方法注入 -->
	<bean id="byName" class="cn.javass.spring.chapter3.HelloImpl3" factory-method="getBean">
		<constructor-arg name="message" value="Hello World!"/>
		<constructor-arg name="index" value="3"/>
	</bean>

基於setter方法的依賴注入

    <bean class="...HelloImpl4">
	    <property name="message" value="Hello"/>
	    <property name="index" value="1"/>  //value中的值全部是字符串形式,如果轉換出錯會報異常
	</bean>
	<bean id="Hello2" class="com.csx.personal.web.services.HelloImpl2">
		<property name="msg" ref="message"/>  //msg屬性是一個類對象
	</bean>

循環依賴:創建Bean A需要Bean B,創建Bean B需要Bean C,創建Bean C需要Bean A 這樣就形成了循環依賴。 Spring的解決方案:Spring創建Bean的時候會維護一個池,在創建A的時候會去池中查找A是否在池子中,假如發現就拋出循環依賴異常。

避免依賴注入時的循環依賴:可以使用setter方式注入,不要使用構造器形式的注入。

依賴配置常見列子

常量值注入配置

    <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <!-- results in a setDriverClassName(String) call -->
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
        <property name="username" value="root"/>
        <property name="password" value="masterkaoli"/>
    </bean>

注入其他Bean

這邊分別給出了一個引用當前容器和引用父容器中Bean的列子。

   <bean id="Hello2" class="com.csx.personal.web.services.HelloImpl2">
	<property name="msg">        //msg屬性是一個類對象
            <ref  bean="message"/>   //引用同一個容器中id="message"的Bean
       </property>
   </bean>
    
    <!-- 引用父容器中的Bean -->
    <!-- in the parent context -->
    <bean id="accountService" class="com.something.SimpleAccountService">
    <!-- insert dependencies as required as here -->
    </bean>
 
    <!-- in the child (descendant) context -->
    <bean id="accountService" <!-- bean name is the same as the parent bean -->
    class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="target">
        <ref parent="accountService"/> <!-- notice how we refer to the parent bean -->
    </property>
    <!-- insert other configuration and dependencies as required here -->
    </bean>

注入內部Bean

內部bean:這種bean一般只讓某個外部bean使用(和內部類相似),不讓容器中的其他Bean使用。

    <bean id="outer" class="...">
        <property name="target">
             <!-- this is the inner bean -->
            <bean class="com.example.Person"> 
                <property name="name" value="Fiona Apple"/>
                <property name="age" value="25"/>
            </bean>
        </property>
   </bean>

集合的注入

集合類的注入建議使用util命名空間

    <util:map id="myMap" key-type="java.lang.String" value-type="java.lang.String">
        <entry key="key1" value="chen"/>
        <entry key="key2" value="zhao"/>
    </util:map>

    <util:list id="myList" value-type="java.lang.String">
        <value>chen</value>
        <value>zhao</value>
    </util:list>
    
    <util:set id="mySet" value-type="java.lang.String" scope="singleton">
        <value>chen</value>
        <value>zhao</value>
    </util:set>
    
    <util:properties id="myProp" location="classpath:xx.properties"/>

null值和空字符串的注入

   <bean class="...HelloImpl4">
        <property name="message"><null/></property> //null值
        <property name="index" value=""/>  //空字符串
  </bean>

使用depends-on屬性

depends-on屬性用來指定bean的初始化順序。這個屬性只對scope是單列的bean生效

    <!--在實例化beanOne之前先實例化manager和accountDao這兩個bean-->
    <bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao">
	<property name="manager" ref="manager" />
    </bean>
    <bean id="manager" class="ManagerBean" />
    <bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />

懶加載

bean的定義中有一個lazy-init這個屬性,用來設置單列bean在容器初始化后是否實例化這個bean。默認情況下容器是會實例化所有單例bean的,我們也建議這麼做,因為這樣能在容器初始化階段就發現bean配置是否正確。如果一個Bean按照下面的設置,lazy-init被設置為true那麼它不會被容器預初始化,只有在被使用的時候才被初始化。但是如果有一個單列類依賴了這個Bean,那麼這個被設置成懶加載的Bean還是會被預初始化。

   <bean id="lazy" class="com.something.ExpensiveToCreateBean" lazy-init="true"/>

如果想設置全局的單例Bean都不要預先初始化,那麼可以在xml中做如下設置:

    <beans default-lazy-init="true">
        <!-- no beans will be pre-instantiated... -->
    </beans>

Autowiring相關

當我們要往一個bean的某個屬性里注入另外一個bean,我們會使用property +ref標籤的形式。但是對於大型項目,假設有一個bean A被多個bean引用注入,如果A的id因為某種原因修改了,那麼所有引用了A的bean的ref標籤內容都得修改,這時候如果使用autowire=”byType”,那麼引用了A的bean就完全不用修改了。

 <!--autowire的用法如下,對某個Bean配置autowire模式 -->
 <!--和給Bean的屬性添加@AutoWired註解的效果一致-->
 <bean id="auto" class="example.autoBean" autowire="byType"/>

autowire的幾種模式:

  • no模式:也是默認模式,這種模式下不會進行自動屬性注入,需要我們自己通過value或者ref屬性進行配置;
  • byName模式:通過屬性的名稱自動裝配,Spring會在容器中查找名稱與bean屬性名稱一致的bean,並自動注入到bean屬性中。當然bean的屬性需要有setter方法。例如:bean A有個屬性master,master的setter方法就是setMaster,A設置了autowire=”byName”,那麼Spring就會在容器中查找名為master的bean通過setMaster方法注入到A中;
  • byType:通過類型自動裝配(注入)。Spring會在容器中查找類(Class)與bean屬性類一致的bean,並自動注入到bean屬性中,如果容器中包含多個這個類型的bean,Spring將拋出異常。如果沒有找到這個類型的bean,那麼注入動作將不會執行;
  • constructor:類似於byType,但是是通過構造函數的參數類型來匹配。假設bean A有構造函數A(B b, C c),那麼Spring會在容器中查找類型為B和C的bean通過構造函數A(B b, C c)注入到A中。與byType一樣,如果存在多個bean類型為B或者C,則會拋出異常。但時與byType不同的是,如果在容器中找不到匹配的類的bean,將拋出異常,因為Spring無法調用構造函數實例化這個bean;
  • default : 採用父級標籤(即beans標籤的default-autowire屬性)的配置。

需要注意的是上面這5中方式注入都需要我們提供相應的setter方法,通過@Autowired的方式不需要提供相應的setter方法。

自動裝配的缺點

  • 屬性和構造函數參數設置中的顯式依賴項會覆蓋自動裝配;

將Bean排除自動裝配

如果按照下面的方式配置了Bean,那麼這個Bean將不會作為自動裝配的候選Bean。但是autowire-candidate自會對byType形式的自動注入生效,如果我們是通過byName的形式進行自動注入,那麼還是能注入這個Bean。

    <bean id="auto" class="example.autoBean" autowire="byType" autowire-candidate="false"/>

如果我們只想讓某些Bean作為自動裝配的候選Bean,那麼可以進行全局設置。如果做了下面的配置,那麼只有id為bean1和bean2的Bean才會成為自動裝配的候選Bean。同時default-autowire-candidates的值支持正則表達式形式。但是強烈建議不要配置這個選項的值,使用默認的配置就行。

    <beans default-autowire-candidates="bean1,bean2">
    </beans>

方法注入(單例依賴原型Bean)

我們在配置bean的時候首先要考慮這個bean是要配置成單例模式還是其他模式。 在我們的應用中,絕大多數類都是單例類。當單例類引用單例類,或者原型類引用原型類、單例類時,我們只要像普通的配置方式就行了。但是當一個單例類引用原型類時,就會出現問題。這種情況可以使用下面的方式進行注入。

   @Service
   @Scope("prototype")
   public class MyService1 {
public void service() {
   System.out.println(this.toString() + ":id");
}
	}
	
	@Service
public abstract class MyService {

    private MyService1 service1;
    public void useService(){
     service1 = createService1();
     service1.service();
    }

    @Lookup("myService1")
    public abstract MyService1 createService1();

}
	
//也可以這樣配置bean
<bean id="serviceC" class="com.csx.demo.springdemo.service.ServiceC">
    <lookup-method bean="serviceD" name="createService"/>
</bean>
<bean id="serviceD" class="com.csx.demo.springdemo.service.ServiceD"     scope="prototype"/>

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

印度最大鱷魚動物園受疫情衝擊 陷資金短缺危機

摘錄自2020年8月11日中央社報導

印度馬德拉斯鱷魚動物園主管表示,實施防疫封鎖令後,遊客大量減少,造成門票收入劇減,動物園恐在4個月內耗盡資金,無法餵養動物、支付薪資以及進行研究。

位於印度南部城市清奈(Chennai)南方約40公里的馬德拉斯鱷魚庫(Madras Crocodile Bank),年銷售約500萬張門票,通常約占動物園營收半數。但馬德拉斯鱷魚庫園長傑蘇達桑(Allwin Jesudasan)表示,暑假季節實施封鎖期間,遊客減少將近250萬人,估計損失18萬7000美元(約新台幣550萬元)。

傑蘇達桑告訴路透社,「我們目前的資金情況僅能再維持運作3或4個月。」馬德拉斯鱷魚庫在網站發布募捐聲明表示:「我們的資深員工已主動減薪10%至50%,並大幅刪減園內活動,僅剩不可或缺活動。」資金耗盡後員工及動物的未來命運,無法立即知悉

生物多樣性
國際新聞
印度
動物園
武漢肺炎
動物與大環境變遷

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

疫情成保育契機 威尼斯潟湖重生

摘錄自2020年08月16日台灣醒報報導

疫情使威尼斯觀光停擺,在地人趁機復甦生態!義大利威尼斯潟湖曾因為人口眾多及排水污染,湖中鹽分提高,導致大量蘆葦消失,生態環境被破壞。然而,因為疫情期間人口稀少,科學家及當地農夫藉此發起潟湖復甦計劃,以運河系統引進淡水、移植海草、種植蘆葦,以恢復原有生態。

鹽沼是沿海潮間帶和陸地間的一種生態系統,海水或鹹水會規律地湧入流出該地區。據《今日泰倫加納》報導,人工的運河系統將思樂河的淡水導入潟湖。能培養豐富生態的鹽沼如今只剩下34公頃。

威尼斯福斯卡里宮大學研究員亞瑞安娜指出,潟湖的一半以上曾經是蘆葦床和鹽沼,約1萬7000公頃並擁有豐富生態,「健康的潟湖鹽分應介於0到15鹽度,但現在威尼斯的鹽度是30,已與海水相去不遠。」計劃發起人布魯薩則表示:「我們的重整計劃將從其他地方導入乾淨的淡水,來沖淡鹽分,還它原始的樣貌。」

污染治理
國際新聞
義大利
威尼斯
潟湖
武漢肺炎
動物與大環境變遷

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

Cypress系列(18)- 可操作類型的命令 之 點擊命令

如果想從頭學起Cypress,可以看下面的系列文章哦

https://www.cnblogs.com/poloyy/category/1768839.html

 

前言

  • 啥是可操作類型?就是可以和 DOM 元素交互的命令,比如:點擊,雙擊…..等等等
  • 這些命令模擬用戶和應用程序交互,Cypress 會觸發瀏覽器事件,進而觸發應用程序綁定的時間

這一篇着重講點擊操作,一共有三個命令

  1. click:單擊
  2. dbclick:雙擊
  3. rightclick:右鍵

 

.click() 的語法和用法

單擊某個元素

 

六種基礎語法格式

// 單擊某個元素
.click()

// 帶參數的單擊
.click(options)

// 在某個位置點擊
.click(position)

// 在某個位置點擊,且帶參數
.click(position, options)

// 根據頁面坐標點擊
.click(x, y)

// 根據頁面坐標點擊,且帶參數
.click(x, y, options)

 

正確用法

宗旨:先獲取 DOM 元素,再對 DOM 元素操作

 

錯誤用法

 

position 位置參數

每個元素都有九個 position,具體可看下圖

 

坐標 x, y

距離 DOM 元素左上角的坐標,x 是橫軸,y 是豎軸

 

options 可選參數

共有四個

 

 

如何傳 options ?

  •  .click({ multiple: true }) 
  •  .click({ multiple: true , force: true}) 

 

force: true 的作用

背景

  • Cypress 可以通過 Test Runner 的快照找到阻止 DOM 元素交互的情況,但某些情況下可能會阻礙測試的進行
  • 比如:有一個嵌套的導航結構,用戶必須將鼠標 hover 在一個非常特定的模式中,才能拿到所需的鏈接
  • 當測試時,其實我們只是想獲取鏈接而已,前面過多的繁瑣操作可能會導致測試失敗

 

作用

  • 當設置了 force: true 時,Cypress 會強制操作命令的發生,避開前面的所有檢查
  • 你可以傳遞 { force: true } 給大多數操作命令

 

栗子

// 強制點擊,和所有後續事件
// 即使該元素 “不可操作”,也會觸發點擊操作
cy.get('button').click({ force: true })

 

當使用 force 時,將執行這些操作

  • 繼續執行所有默認操作
  • 強制在元素上觸發事件

 

當使用 force 時,將不會執行這些操作

  • 滾動到視圖中
  • 確保可見
  • 確保未禁用
  • 確保沒有分離
  • 確保它不是只讀的
  • 確保它沒有動畫
  • 確保未覆蓋
  • 向後代觸發事件

 

總結

總而言之, { force: true } 跳過檢查,它將始終在所需元素處觸發事件

 

.click() 具體的栗子

.click() 的栗子

測試文件代碼

 

 

測試結果

 

.click(position)

測試文件代碼

 

測試結果

 

.click(x, y)

測試文件代碼

 

測試結果

 

{force: true} 的栗子

.click(options)

 

.click(position, options)

 

.click(x, y, options)

 

{multiple : true } 的栗子

測試文件代碼

 

測試結果

 cy.get(‘ ul > li ‘) 共匹配四個 DOM 元素,他們均觸發單擊操作

 

單擊組合鍵

 .click() 命令還可以與 .type() 命令結合使用修飾符來觸發組合鍵操作,以便在單擊時結合鍵盤操作,例如ALT + click

 

以下修飾符可以和 .click() 結合使用

修飾符 作用 別名
{alt}
等價於 alt 鍵 {option}
{ctrl} 等價於 ctrl 鍵 {control}
{shift} 等價於 shift 鍵  

 

栗子

 

.dblclick()

雙擊,跟 click() 的語法 & 用法一致,只是變成了雙擊

cy.get("#main1").dblclick()
cy.get("#main1").dblclick("top")
cy.get("#main1").dblclick(15, 15)

 

.rightclick()

右鍵,跟 click() 的語法 & 用法一致,只是變成了右鍵點擊

cy.get("#li1").rightclick()
cy.get("#li1").rightclick("top")
cy.get("#li1").rightclick(15, 15)

 

.click() 注意事項

可操作性

執行 .click()  必須是 DOM 元素達到了可操作狀態

 

關於斷言

 .click() 將自動等待元素達到可操作狀態。

 .click() 將自動等待後面鏈接的斷言通過

 

超時時間

 .click() 如果 DOM 元素一直達不到可操作狀態,可能會超時

 .click() 如果後面鏈接的斷言一直不通過,可能會超時

 

.click() 會觸發的鼠標事件

在命令日誌中單擊 click 時,控制台console 將輸出以下鼠標事件

 

結尾

本文是博主基於對蔡超老師的《Cypress 從入門到精通》閱讀理解完后輸出的博文,並附上了自己的理解

對書籍感興趣的,大家可以參考本篇博客:https://www.cnblogs.com/poloyy/p/13052972.html,考慮自身需求進行購買

 

我的博客即將同步至騰訊雲+社區,邀請大家一同入駐:https://cloud.tencent.com/developer/support-plan?invite_code=12vd92hxgwgj1

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

英雞肉加工廠爆疫情 7人陽性、5人隔離

摘錄自2020年8月25日自由時報報導

歐洲部分國家先前接連出現肉品加工廠員工確診武漢肺炎(COVID-19)。英國家禽生產公司「班翰姆家禽」(Banham Poultry)的1處雞肉加工廠傳出7名工作人員檢測陽性,另有5人隔離等待結果出爐。

綜合外媒報導,公衛主任史密斯(Louise Smith)透露,位於英格蘭諾福克郡阿特爾伯勒鎮、有數百人的雞肉加工廠出現疫情,將針對更多員工進行檢測,同時採取行動阻止病毒傳播。加工廠高層倫斯伯格(Blaine van Rensburg)回應,公司仍在營運中。

國際新聞
英國
疫情下的社會衝突

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

汽車的“私人定製”模式你了解多少?

但是我們從原來的滾動發展,到現在看清楚市場創新會帶來變化,另一方面,也看到創新給我們帶來的問題,其中最大的問題是經銷商網絡相對弱,得益於上汽大通C2B定製化業務模式,使我們可以不斷地做強線上大數據平台,盡可能多得與用戶作在線交流、在線用戶的需求分析、在線訂單的匹配、在線定價的討論、在線產品的改進等。

自主品牌汽車產業如何更好適應全球的汽車產業新一輪革命的來臨,把汽車大國更好的推向汽車強國的道路上邁進,這是現在我們自主汽車產業發展最為重要的一個本質問題。

創新也是每一家企業的重要命題,而不是每個企業都能突破重圍,走向創新,那如何站在消費者角度來思考,是創新最重要的靈感,那又如何來相互結合呢?當前互聯網大數據信息時代的新技術、新思維,正在注入汽車市場,而我國汽車產業正按着“中國製造2025”,“互聯網+”,“雙創”和“工業4.0”展望和規劃在行動中,歸納起來按汽車產業發展規律看就是一種“大規模定製的商業模式”的年代的來臨,通俗的講汽車產業要實行的是“量體裁衣”辦法,成為新世紀汽車產業競爭與合作的新態勢的一種結晶體,而21世紀的競爭不再是產品與服務了,而是商業模型的競爭。

上汽大通正是在慢慢的構建屬於大通的創新商業模式,用C2B智能化大規模定製的創新理念探索前行,可能你會覺得大規模定製在傳統汽車工業年代是行不通的,而在後信息化的今天是可以打破的,互聯網已經緊密的把信息與製造業融合在一起,成為新一輪汽車產業革命的商業模式了。

看的出來在上汽大通的整個產品矩陣中,實施部署了“新能源+互聯網+X”的技術路線,從一家傳統意義上的商用車公司,轉型成為一家数字化C2B跨界車企,將C2B智能化大規模定製當作戰略方向。

在今年,2017年上汽大通汽車有限公司營銷商務年會上,上汽集團副總裁藍青松也表示了,上汽大通原來是機遇式發展,有收購機會就把它拿下用做商用車公司,然後,我們進入市場,覺得MpV市場有個機會,之後我們又看到皮卡、房車的市場機會。

但是我們從原來的滾動發展,到現在看清楚市場創新會帶來變化,另一方面,也看到創新給我們帶來的問題,其中最大的問題是經銷商網絡相對弱,得益於上汽大通C2B定製化業務模式,使我們可以不斷地做強線上大數據平台,盡可能多得與用戶作在線交流、在線用戶的需求分析、在線訂單的匹配、在線定價的討論、在線產品的改進等。這樣,我們能夠在線上更有效的集合,同時也能夠給現有的經銷商網絡帶來更多的潛客。雖然目前上汽大通的經銷商數量不多,但隨着C2B項目的推進,這方面矛盾將變成優勢。

至此,上汽大通C2B戰略形成了SUV D90、T60皮卡、RV80房車為主的產品矩陣,為用戶提供可“定製化”的多元化產品,享受C2B個性化定製汽車帶來的樂趣和價值。上汽大通正改變“以車企為中心、用戶被動接受”的傳統B2C模式,變成以用戶為中心的C2B模式,以自身的一小步,帶動汽車行業的一大步。

在今年大力推行C2B項目的同時,上汽大通在傳統市場的增長依然有力,在海內外市場皆取得了不俗的成績,再加上今年,上汽大通持續擴大“國賓車”的影響力,成為杭州G20峰會、ApEC領導人峰會兩大頂級會議用車,形成“國內G20,海外ApEC”的雙峰會格局。

從“大規模定製”來看,用戶可能既是需求者也形同生產者,用戶可以在網絡上提出自己定製汽車的種種要求,只有當用戶認可后,廠商再去生產,其實汽車產業商業模式也是在不斷創新,每一種創新型商業模式的形成,都是汽車廠商實踐的範式,當被認同后,就逐步進化為一種正式的商業模式,而每一種商業模式的出現都是量級化的變革,我是很期待上汽大通能否用顛覆性的效應來引領中國汽車工業的創新發展。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

四套“組合拳”助大眾2016年在華銷量破紀錄!

奧迪Q3車型交付量達89,800輛,同比增長27。6%。斯柯達斯柯達汽車延續2015年銷量兩位數增長的態勢,2016年全年汽車交付量達317,100輛(2015年同期交付281,700 輛),同比增長12。6%。保時捷在豪華跑車細分市場,保時捷2016年共交付汽車65,200輛(2015年同期交付58,000輛),同比增長12。

近日,大眾汽車集團(中國)公布2016年全年銷量結果:2016年,大眾汽車集團(中國)及兩家合資企業——上汽大眾和一汽-大眾在中國大陸及香港地區的汽車交付量達到398萬輛(2015年同期交付355萬輛,同比增長12.2%),其中進口車型交付量約178,700輛。此數據一舉刷新了大眾汽車集團在華的年銷售記錄。

大眾汽車品牌中國CEO馮思翰博士(Dr. Stephan Wöllenstein)表示:“憑藉極具吸引力的產品序列,大眾汽車品牌在中國刷新了銷量紀錄。我們對2017年進一步增長充滿期待,將推出包括衍生車型在內的11款車型,為消費者提供更多選擇。我們希望大眾汽車能夠成為中國大陸地區首個年銷量突破300萬輛的單一品牌。”

大眾

大眾汽車品牌繼續以強勁的市場表現保持在中國乘用車市場的領軍地位,2016年共交付汽車300萬輛(2015年同期交付263萬輛,同比增長14%)。旨在滿足中國市場需求的全新車型以及品牌旗下持續熱銷的主要車型鞏固了品牌的銷量增長。在中國市場銷量排名前十的車型中,朗逸、捷達、速騰和桑塔納等四款車型均來自大眾汽車品牌。

奧迪

奧迪品牌仍然是中國豪華車細分市場中最受歡迎的品牌,2016年全年共交付汽車591,600輛(2015年同期交付570,900輛,同比增長3.6%,含香港地區)。2016年,奧迪在高檔緊湊車型細分市場的需求量大增,其中,奧迪A3車型交付量達89,200輛,同比增長29.7%;奧迪Q3車型交付量達89,800輛,同比增長27.6%。

斯柯達

斯柯達汽車延續2015年銷量兩位數增長的態勢,2016年全年汽車交付量達317,100輛(2015年同期交付281,700 輛),同比增長12.6%。

保時捷

在豪華跑車細分市場,保時捷2016年共交付汽車65,200輛(2015年同期交付58,000輛),同比增長12.5%。

期待未來銷量進一步增長

SUV和MpV繼續成為推動市場增長的主要力量。大眾汽車品牌致力於豐富在華的SUV序列,到2020年將擴充至10款SUV車型。備受期待的Teramont及全新途觀L將於2017年上市。“上汽大眾Teramont、全新途觀L將重磅登場,一汽-大眾引領生活方式的跨界旅行車C-TREK蔚領的全年交付量也同樣值得期待。”馮思翰博士表示,“2016年,高爾夫·嘉旅和途安L市場表現突出,我們在MpV市場也已構建起強有力的產品陣容。”

2017年,代表性的轎車車型將延續增長勢頭,其中包括全新一代邁騰,以及於2016年10月上市且備受好評的全新豪華轎車pHIDEON輝昂。作為大眾汽車品牌新能源汽車規劃的一部分,pHIDEON輝昂 GTE將於今年晚些時候亮相。

可見在2017年,大眾汽車集團將進一步拓展自身的產品範圍及技術技術深度,特別是在新能源汽車方面即將大展拳腳的種種跡象都表明,大眾汽車集團在中國汽車市場的領先地位將會越來越難以撼動。

本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化