- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我目前正在尝试使用自动发现服务来实现集群 Play + akka 实现。然而,我似乎遇到了游戏中包含的 Guice DI 加载器的问题。他们的文档摘录如下:
https://www.playframework.com/documentation/2.5.x/ScalaAkka#Integrating-with-Akka
While we recommend you use the built in actor system, as it sets up everything such as the correct classloader, lifecycle hooks, etc, there is nothing stopping you from using your own actor system. It is important however to ensure you do the following:
Register a stop hook to shut the actor system down when Play shuts down Pass in the correct classloader from the Play Environment otherwise Akka won’t be able to find your applications classes
Ensure that either you change the location that Play reads it’s akka configuration from using play.akka.config, or that you don’t read your akka configuration from the default akka config, as this will cause problems such as when the systems try to bind to the same remote ports
我已经完成了他们推荐的上述配置,但是我似乎无法绕过游戏仍然绑定(bind)它的内置ActorSystemProvider从BuiltInModule:
class BuiltinModule extends Module {
def bindings(env: Environment, configuration: Configuration): Seq[Binding[_]] =
{
def dynamicBindings(factories: ((Environment, Configuration) => Seq[Binding[_]])*) = {
factories.flatMap(_(env, configuration))
}
Seq(
bind[Environment] to env,
bind[ConfigurationProvider].to(new ConfigurationProvider(configuration)),
bind[Configuration].toProvider[ConfigurationProvider],
bind[HttpConfiguration].toProvider[HttpConfiguration.HttpConfigurationProvider],
// Application lifecycle, bound both to the interface, and its implementation, so that Application can access it
// to shut it down.
bind[DefaultApplicationLifecycle].toSelf,
bind[ApplicationLifecycle].to(bind[DefaultApplicationLifecycle]),
bind[Application].to[DefaultApplication],
bind[play.Application].to[play.DefaultApplication],
bind[Router].toProvider[RoutesProvider],
bind[play.routing.Router].to[JavaRouterAdapter],
bind[ActorSystem].toProvider[ActorSystemProvider],
bind[Materializer].toProvider[MaterializerProvider],
bind[ExecutionContextExecutor].toProvider[ExecutionContextProvider],
bind[ExecutionContext].to[ExecutionContextExecutor],
bind[Executor].to[ExecutionContextExecutor],
bind[HttpExecutionContext].toSelf,
bind[CryptoConfig].toProvider[CryptoConfigParser],
bind[CookieSigner].toProvider[CookieSignerProvider],
bind[CSRFTokenSigner].toProvider[CSRFTokenSignerProvider],
bind[AESCrypter].toProvider[AESCrypterProvider],
bind[play.api.libs.Crypto].toSelf,
bind[TemporaryFileCreator].to[DefaultTemporaryFileCreator]
) ++ dynamicBindings(
HttpErrorHandler.bindingsFromConfiguration,
HttpFilters.bindingsFromConfiguration,
HttpRequestHandler.bindingsFromConfiguration,
ActionCreator.bindingsFromConfiguration
)
}
}
我尝试创建自己的 GuiceApplicationBuilder 来绕过这个问题,但是现在它只是将重复的绑定(bind)异常移至来自BuiltInModule。
这就是我正在尝试的:
AkkaConfigModule:
package module.akka
import com.google.inject.{AbstractModule, Inject, Provider, Singleton}
import com.typesafe.config.Config
import module.akka.AkkaConfigModule.AkkaConfigProvider
import net.codingwell.scalaguice.ScalaModule
import play.api.Application
/**
* Created by dmcquill on 8/15/16.
*/
object AkkaConfigModule {
@Singleton
class AkkaConfigProvider @Inject() (application: Application) extends Provider[Config] {
override def get() = {
val classLoader = application.classloader
NodeConfigurator.loadConfig(classLoader)
}
}
}
/**
* Binds the application configuration to the [[Config]] interface.
*
* The config is bound as an eager singleton so that errors in the config are detected
* as early as possible.
*/
class AkkaConfigModule extends AbstractModule with ScalaModule {
override def configure() {
bind[Config].toProvider[AkkaConfigProvider].asEagerSingleton()
}
}
Actor 系统模块:
package module.akka
import actor.cluster.ClusterMonitor
import akka.actor.ActorSystem
import com.google.inject._
import com.typesafe.config.Config
import net.codingwell.scalaguice.ScalaModule
import play.api.inject.ApplicationLifecycle
import scala.collection.JavaConversions._
/**
* Created by dmcquill on 7/27/16.
*/
object ActorSystemModule {
@Singleton
class ActorSystemProvider @Inject() (val lifecycle: ApplicationLifecycle, val config: Config, val injector: Injector) extends Provider[ActorSystem] {
override def get() = {
val system = ActorSystem(config.getString(NodeConfigurator.CLUSTER_NAME_PROP), config.getConfig("fitnessApp"))
// add the GuiceAkkaExtension to the system, and initialize it with the Guice injector
GuiceAkkaExtension(system).initialize(injector)
system.log.info("Configured seed nodes: " + config.getStringList("fitnessApp.akka.cluster.seed-nodes").mkString(", "))
system.actorOf(GuiceAkkaExtension(system).props(ClusterMonitor.name))
lifecycle.addStopHook { () =>
system.terminate()
}
system
}
}
}
/**
* A module providing an Akka ActorSystem.
*/
class ActorSystemModule extends AbstractModule with ScalaModule {
import module.akka.ActorSystemModule.ActorSystemProvider
override def configure() {
bind[ActorSystem].toProvider[ActorSystemProvider].asEagerSingleton()
}
}
应用程序加载器:
class CustomApplicationLoader extends GuiceApplicationLoader {
override def builder(context: ApplicationLoader.Context): GuiceApplicationBuilder = {
initialBuilder
.overrides(overrides(context): _*)
.bindings(new AkkaConfigModule, new ActorSystemModule)
}
}
我需要完成的主要事情是配置 ActorSystem,以便我可以通过编程方式加载 Akka 集群的种子节点。
上述方法是正确的方法还是有更好的方法来实现此目的?如果这是正确的方法,那么对于游戏/指导的 DI 设置,我是否从根本上不理解什么?
更新
对于该架构,play+akka 位于同一节点。
最佳答案
最后我尝试做一些比必要的更复杂的事情。我没有执行上述流程,而是简单地以编程方式扩展了初始配置,以便可以以编程方式检索必要的网络信息。
最终结果基本上由几个类组成:
NodeConfigurator:此类包含相关实用程序方法,用于从 application.conf 检索属性,然后以编程方式创建配置以与 kubernetes 发现服务结合使用。
object NodeConfigurator {
/**
* This method given a class loader will return the configuration object for an ActorSystem
* in a clustered environment
*
* @param classLoader the configured classloader of the application
* @return Config
*/
def loadConfig(classLoader: ClassLoader) = {
val config = ConfigFactory.load(classLoader)
val clusterName = config.getString(CLUSTER_NAME_PROP)
val seedPort = config.getString(SEED_PORT_CONF_PROP)
val host = if (config.getString(HOST_CONF_PROP) equals "eth0-address-or-localhost") {
getLocalHostAddress.getOrElse(DEFAULT_HOST_ADDRESS)
} else {
config.getString(HOST_CONF_PROP)
}
ConfigFactory.parseString(formatSeedNodesConfig(clusterName, getSeedNodes(config), seedPort, host))
.withValue(HOST_CONF_PROP, ConfigValueFactory.fromAnyRef(host))
.withValue("fitnessApp.akka.remote.netty.tcp.hostname", ConfigValueFactory.fromAnyRef(host))
.withFallback(config)
.resolve()
}
/**
* Get the local ip address which defaults to localhost if not
* found on the eth0 adapter
*
* @return Option[String]
*/
def getLocalHostAddress: Option[String] = {
import java.net.NetworkInterface
import scala.collection.JavaConversions._
NetworkInterface.getNetworkInterfaces
.find(_.getName equals "eth0")
.flatMap { interface =>
interface.getInetAddresses.find(_.isSiteLocalAddress).map(_.getHostAddress)
}
}
/**
* Retrieves a set of seed nodes that are currently running in our cluster
*
* @param config akka configuration object
* @return Array[String]
*/
def getSeedNodes(config: Config) = {
if(config.hasPath(SEED_NODES_CONF_PROP)) {
config.getString(SEED_NODES_CONF_PROP).split(",").map(_.trim)
} else {
Array.empty[String]
}
}
/**
* formats the seed node addresses in the proper format
*
* @param clusterName name of akka cluster
* @param seedNodeAddresses listing of current seed nodes
* @param seedNodePort configured seed node port
* @param defaultSeedNodeAddress default seed node address
* @return
*/
def formatSeedNodesConfig(clusterName: String, seedNodeAddresses: Array[String], seedNodePort: String, defaultSeedNodeAddress: String) = {
if(seedNodeAddresses.isEmpty) {
s"""fitnessApp.akka.cluster.seed-nodes = [ "akka.tcp://$clusterName@$defaultSeedNodeAddress:$seedNodePort" ]"""
} else {
seedNodeAddresses.map { address =>
s"""fitnessApp.akka.cluster.seed-nodes += "akka.tcp://$clusterName@$address:$seedNodePort""""
}.mkString("\n")
}
}
val CLUSTER_NAME_PROP = "fitnessAkka.cluster-name"
val HOST_CONF_PROP = "fitnessAkka.host"
val PORT_CONF_PROP = "fitnessAkka.port"
val SEED_NODES_CONF_PROP = "fitnessAkka.seed-nodes"
val SEED_PORT_CONF_PROP = "fitnessAkka.seed-port"
private val DEFAULT_HOST_ADDRESS = "127.0.0.1"
}
CustomApplicationLoader:只需使用 play 的可重写应用程序加载器即可从 NodeConfigurator 获取生成的配置,然后用它扩展初始配置。
class CustomApplicationLoader extends GuiceApplicationLoader {
override def builder(context: ApplicationLoader.Context): GuiceApplicationBuilder = {
val classLoader = context.environment.classLoader
val configuration = Configuration(NodeConfigurator.loadConfig(classLoader))
initialBuilder
.in(context.environment)
.loadConfig(context.initialConfiguration ++ configuration)
.overrides(overrides(context): _*)
}
}
AkkaActorModule:提供依赖项可注入(inject) actor 引用,与 API 一起使用来显示集群成员。
class AkkaActorModule extends AbstractModule with AkkaGuiceSupport {
def configure = {
bindActor[ClusterMonitor]("cluster-monitor")
}
}
ClusterMonitor:这是一个参与者,它只是监听集群事件并另外接收消息以生成当前集群状态。
class ClusterMonitor @Inject() extends Actor with ActorLogging {
import actor.cluster.ClusterMonitor.GetClusterState
val cluster = Cluster(context.system)
private var nodes = Set.empty[Address]
override def preStart(): Unit = {
cluster.subscribe(self, initialStateMode = InitialStateAsEvents, classOf[MemberEvent], classOf[UnreachableMember])
}
override def postStop(): Unit = cluster.unsubscribe(self)
override def receive = {
case MemberUp(member) => {
nodes += member.address
log.info(s"Cluster member up: ${member.address}")
}
case UnreachableMember(member) => log.warning(s"Cluster member unreachable: ${member.address}")
case MemberRemoved(member, previousStatus) => {
nodes -= member.address
log.info(s"Cluster member removed: ${member.address}")
}
case MemberExited(member) => log.info(s"Cluster member exited: ${member.address}")
case GetClusterState => sender() ! nodes
case _: MemberEvent =>
}
}
object ClusterMonitor {
case class GetClusterState()
}
应用程序:只是一个测试 Controller ,用于输出已加入集群的节点列表
class Application @Inject() (@Named("cluster-monitor") clusterMonitorRef: ActorRef) extends Controller {
implicit val addressWrites = new Writes[Address] {
def writes(address: Address) = Json.obj(
"host" -> address.host,
"port" -> address.port,
"protocol" -> address.protocol,
"system" -> address.system
)
}
implicit val timeout = Timeout(5, TimeUnit.SECONDS)
def listClusterNodes = Action.async {
(clusterMonitorRef ? GetClusterState).mapTo[Set[Address]].map { addresses =>
Ok(Json.toJson(addresses))
}
}
}
上述 Controller 的结果产生类似于以下的输出:
$ http GET 192.168.99.100:30760/cluster/nodes
HTTP/1.1 200 OK
Content-Length: 235
Content-Type: application/json
Date: Thu, 18 Aug 2016 02:50:30 GMT
[
{
"host": "172.17.0.3",
"port": 2551,
"protocol": "akka.tcp",
"system": "fitnessApp"
},
{
"host": "172.17.0.4",
"port": 2551,
"protocol": "akka.tcp",
"system": "fitnessApp"
},
{
"host": "172.17.0.5",
"port": 2551,
"protocol": "akka.tcp",
"system": "fitnessApp"
}
]
关于scala - 使用 Play 框架设置 Akka 集群,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38995437/
这个问题已经有答案了: Is there any way to accept only numeric values in a JTextField? (20 个回答) It's possible i
我使用戴尔 XPS M1710。笔记本电脑的盖子、侧面扬声器和前置扬声器都有灯(3 组灯可以单独调节)和鼠标垫下方的灯。在 BIOS 中,我可以更改这些灯的颜色,至少是每个组。另外,我可以在鼠标垫下打
我知道我可以使用 在 iOS 5 中打开设置应用 [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"prefs://"
我有一个 Django 应用程序,我正在尝试为其设置文档。目录结构如下: - doc - project | - manage.py 我已经设置了路径以便 Sphinx 可以看到东西,但是当我尝试使用
我正在使用 768mb ram 运行 centos 5.5。我一直在日志中获取 server reached MaxClients setting, consider raising the MaxC
我在具有以下配置的服务器内运行了 Drupal 安装: StartServers 5 MinSpareServers 5 MaxSpareServers 15 MaxClien
是否可以使用 Microsoft.Web.Administration 包为给定的 location 配置 asp 设置? 我想以编程方式将以下部分添加到本地 IIS applicationHost.
我一直在阅读为 kube-proxy 提供参数的文档,但没有解释应该如何使用这些参数。我使用 az aks create 创建我的集群使用 azure-cli 程序,然后我获得凭据并使用 kubect
我想知道与在 PHP 中使用 setcookie() 函数相比,在客户端通过 JavaScript 设置一些 cookie 是否有任何明显的优势?我能想到的唯一原因是减少一些网络流量(第一次)。但不是
我有一个按钮可以将 body class 设置为 .blackout 我正在使用 js-cookie设置cookie,下面的代码与我的按钮相关联。 $('#boToggle').on('click'
我有一堆自定义的 HTML div。我将其中的 3 存储在具有 slide 类的 div 中。然后,我使用该幻灯片类调用 slick 函数并应用如下设置: $('.slide').slick({
我正在创建一个应该在 Windows 8(桌面)上运行的应用 我需要: 允许用户使用我的应用启动“文件历史记录”。我需要找到打开“文件历史记录”的命令行。 我需要能够显示“文件历史记录”的当前设置。
我刚买了一台新的 MacBook Pro,并尝试在系统中设置 RVM。我安装了 RVM 并将默认设置为 ➜ rvm list default Default Ruby (for new shells)
由于有关 Firestore 中时间戳行为即将发生变化的警告,我正在尝试更改我的应用的初始化代码。 The behavior for Date objects stored in Firestore
在 ICS 中,网络 -> 数据使用设置屏幕中现在有“限制后台数据”设置。 有没有办法以编程方式为我的应用程序设置“限制后台数据”? 或 有没有办法为我的应用程序调出具有选项的“数据使用”设置? 最佳
我正在尝试使用 NextJS 应用程序设置 Jest,目前在 jest.config.js : module.exports = { testPathIgnorePatterns: ["/.n
我最近升级到 FlashDevelop 4,这当然已经将我之前的所有设置恢复到原来的状态。 我遇到的问题是我无法在新设置窗口的哪个位置找到关闭它在方括号、大括号等之前插入的自动空格的选项。 即它会自动
有没有办法以编程方式访问 iPhone/iPod touch 设置? 谢谢。比兰奇 最佳答案 大多数用户设置可以通过读取存储在 /User/Library/Preferences/ 中的属性列表来访问
删除某些值时,我需要选择哪些设置来维护有序队列。我创建了带有自动增量和主键的 id 的表。当我第一次插入值时,没问题。就像 1,2,3,4,5... 当删除某些值时,顺序会发生变化,例如 1,5,3.
我正在尝试设置示例 Symfony2 项目,如此处所示 http://symfony.com/doc/current/quick_tour/the_big_picture.html 在访问 confi
我是一名优秀的程序员,十分优秀!