jboss-eap-6 HA 单例在独立配置中部署多个 Web 存档
jboss-eap-6 HA singleton deploying multiple web archives in standalone configuration
我可以在我的独立集群中部署我的耳朵和 wars。我的 war 中的 2 个用于 HA 单例。在开始第一个独立 jboss-eap-6 后不久,我开始了第二个。当我所有的应用程序都成功部署后,我打开 J-Console,我注意到我的一个单例 war 在第一个 jboss-eap-6 和第二个单例 [=] 上 运行ning 22=] 是 运行ning 在第二个 jboss-eap-6 上。同样在 Jconsole 中,只有 1 个 jboss-eap-6 报告为主。
我的问题是:在 jboss-eap-6 standalone.xml 中有什么方法我只能强制 1 jboss-eap-6 到 运行 单例哈 wars。或者我是否必须将 war 打包到耳朵中?
我认为 standalone.xml
中没有任何内容会改变 war 的行为。在任何情况下,您都应该将 standalone-ha.xml
用于部署了 HA 单例的集群。
JBoss 高可用性单例架构在 JBoss EAP 5 和 6 之间发生了显着变化。
在 JBoss EAP 5 下,您刚刚将可部署对象放在 deploy-hasingleton
特殊部署文件夹中。在 JBoss EAP 6 下,您的 classes 需要实现 JBoss 服务层,特别是 org.jboss.msc.service.Service
以及 org.jboss.msc.service.ServiceActivator
。正是这些服务 classes 的实现控制了 HA Singleton 的实例化和管理。我没有尝试将 hasingleton 部署为 war,我有一些疑问,因为我怀疑依赖服务 classes 可能在 Web 容器中不可用。
ServiceActivator
负责管理 Service
的生命周期。 ServiceActivator
实现 class 需要在文件 META-INF/service/org.jboss.msc.service.ServiceActivator
中列出,以便 JBoss 在其启动/部署期间激活它。
示例:
创建服务激活器
public abstract class SingletonActivator<T extends Serializable> implements ServiceActivator {
@Override
public SingletonService<String> instantiateSingleton() {
return new SingletonService<String>();
}
public ServiceName getServiceName() {
return ServiceName.JBOSS.append("my", "ha", "singleton");
}
/**
* Activated by the Service Activator
*
* @param service
* @param serviceName
* - the Singleton Service Name that is registered in the JBOSS cluster
*/
@Override
public final void activate(ServiceActivatorContext context) {
SingletonService<T> service = instantiateSingleton();
SingletonService<T> singleton = new SingletonService<T>(service, getServiceName());
/*
* The NamePreference is a combination of the node name (-Djboss.node.name) and the name of
* the configured cache "singleton". If there is more than 1 node, it is possible to add more than
* one name and the election will use the first available node in that list.
*/
// e.g. singleton.setElectionPolicy(new PreferredSingletonElectionPolicy(new SimpleSingletonElectionPolicy(), new NamePreference("node1/singleton")));
// or singleton.setElectionPolicy(new PreferredSingletonElectionPolicy(new SimpleSingletonElectionPolicy(), new NamePreference("node1/singleton"), new
// NamePreference("node2/singleton")));
singleton.build(new DelegatingServiceContainer(context.getServiceTarget(), context.getServiceRegistry())).setInitialMode(ServiceController.Mode.ACTIVE).install();
}
}
创建一个 HA 单例服务Class,它完全负责查找和调用包含您的业务逻辑的 EJB
public class SingletonService<T> implements Service<T> {
protected ScheduledExecutorService deployDelayThread = null;
/**
* The node we are running on
*/
protected String nodeName;
/**
* A flag whether the service is started (or scheduled to be started)
*/
protected final AtomicBoolean started = new AtomicBoolean(false);
/**
* Container life cycle call upon activation. This will construct the singleton instance in this JVM and start the Timer.
*/
@Override
public final void start(StartContext context) throws StartException {
this.nodeName = System.getProperty("jboss.node.name");
logger.info("Starting service '" + this.getClass().getName() + "' on node " + nodeName);
if (!started.compareAndSet(false, true)) {
throw new StartException("The service " + this.getClass().getName() + " is still started!");
}
// MSC does not allow this thread to be blocked so we let the service know that the start is asynchronous and the result will be advised later.
// We delay the actual deployment of the Singleton for a few seconds to allow time for a HASingleton Election to be held and won by one of the instances.
// If the winner is not this instance (prior to deployemnt) then stop(Context) is invoked which sets started to false and the deployment does not occur.
// context.asynchronous();
deployDelayThread.schedule(new StartSingletonAsync(context), 10, TimeUnit.SECONDS);
context.complete();
}
/** Introduces a 5s delay in starting the Singleton bean giving time for the the ha singleton election to be held and won */
private class StartSingletonAsync implements Runnable {
private StartSingletonAsync(StartContext context) {
}
@Override
public void run() {
try {
startSingletonBean();
} catch (StartException e) {
logger.info("Start Exception", e);
}
// be nice to the garbage collector, we don't need this any more
deployDelayThread.shutdown();
deployDelayThread = null;
}
}
private void startSingletonBean() throws StartException {
try {
if (!started.get()) {
throw new StartException("Aborted due to service stopping");
}
// Start your EJB
InitialContext ic = new InitialContext();
bean = ic.lookup(getJndiName());
bean.startHaSingleton();
logger.info("*** Master Only: HASingleton service " + getJndiName() + " started on master:" + nodeName);
if (!bean.isRunning()) {
logger.error("ERROR Bean should be running");
}
} catch (NamingException e) {
throwStartException(e);
}
}
private void throwStartException(Exception e) throws StartException {
String message = "Could not initialize HASingleton" + getJndiName() + " on " + nodeName;
logger.error(message, e);
throw new StartException(message, e);
}
/**
* Container life cycle call when activated
*/
@Override
public final void stop(StopContext context) {
if (deployDelayThread != null) {
deployDelayThread.shutdownNow();
}
if (!started.compareAndSet(true, false) || bean == null) {
logger.warn("The service '" + this.getClass().getName() + "' is not active!");
} else {
try {
InitialContext ic = new InitialContext();
bean = (JmxMBean) ic.lookup(getJndiName());
bean.stopHaSingleton();
logger.info("*** Master Only: HASingleton service " + getJndiName() + " stopped on master:" + nodeName);
} catch (EJBException e) {
// Note: all these exceptions are already logged by JBoss
} catch (NamingException e) {
logger.error("Could not stop HASingleton service " + getJndiName() + " on " + nodeName, e);
}
logger.info("MASTER ONLY HASingleton service '" + this.getClass().getName() + "' Stopped on node " + nodeName);
}
}
private String getJndiName() {
return "java.global/path/to/your/singleton/ejb";
}
}
最后在 META-INF/servcie/org.jboss.msc.service.ServiceActivator
中列出您的激活器 class
com.mycompany.singletons.SingletonActivator
您可能还需要将依赖项添加到 jar 中的清单 META-INF/MANIFEST.MF
文件,如下所示:Dependencies: org.jboss.msc, org.jboss.as.clustering.singleton, org.jboss.as.server
Redhat 在 https://access.redhat.com/documentation/en-US/JBoss_Enterprise_Application_Platform/6.4/html/Development_Guide/Implement_an_HA_Singleton.html 提供了更广泛的实施指南。您可能需要创建一个 Redhat 帐户才能访问它。 JBoss 发行版中还有一个快速入门示例。
所以经过进一步评估,2 个单例最终会在几分钟后合并在一起,从而创建 1 个预期的单例。
我可以在我的独立集群中部署我的耳朵和 wars。我的 war 中的 2 个用于 HA 单例。在开始第一个独立 jboss-eap-6 后不久,我开始了第二个。当我所有的应用程序都成功部署后,我打开 J-Console,我注意到我的一个单例 war 在第一个 jboss-eap-6 和第二个单例 [=] 上 运行ning 22=] 是 运行ning 在第二个 jboss-eap-6 上。同样在 Jconsole 中,只有 1 个 jboss-eap-6 报告为主。
我的问题是:在 jboss-eap-6 standalone.xml 中有什么方法我只能强制 1 jboss-eap-6 到 运行 单例哈 wars。或者我是否必须将 war 打包到耳朵中?
我认为 standalone.xml
中没有任何内容会改变 war 的行为。在任何情况下,您都应该将 standalone-ha.xml
用于部署了 HA 单例的集群。
JBoss 高可用性单例架构在 JBoss EAP 5 和 6 之间发生了显着变化。
在 JBoss EAP 5 下,您刚刚将可部署对象放在 deploy-hasingleton
特殊部署文件夹中。在 JBoss EAP 6 下,您的 classes 需要实现 JBoss 服务层,特别是 org.jboss.msc.service.Service
以及 org.jboss.msc.service.ServiceActivator
。正是这些服务 classes 的实现控制了 HA Singleton 的实例化和管理。我没有尝试将 hasingleton 部署为 war,我有一些疑问,因为我怀疑依赖服务 classes 可能在 Web 容器中不可用。
ServiceActivator
负责管理 Service
的生命周期。 ServiceActivator
实现 class 需要在文件 META-INF/service/org.jboss.msc.service.ServiceActivator
中列出,以便 JBoss 在其启动/部署期间激活它。
示例: 创建服务激活器
public abstract class SingletonActivator<T extends Serializable> implements ServiceActivator {
@Override
public SingletonService<String> instantiateSingleton() {
return new SingletonService<String>();
}
public ServiceName getServiceName() {
return ServiceName.JBOSS.append("my", "ha", "singleton");
}
/**
* Activated by the Service Activator
*
* @param service
* @param serviceName
* - the Singleton Service Name that is registered in the JBOSS cluster
*/
@Override
public final void activate(ServiceActivatorContext context) {
SingletonService<T> service = instantiateSingleton();
SingletonService<T> singleton = new SingletonService<T>(service, getServiceName());
/*
* The NamePreference is a combination of the node name (-Djboss.node.name) and the name of
* the configured cache "singleton". If there is more than 1 node, it is possible to add more than
* one name and the election will use the first available node in that list.
*/
// e.g. singleton.setElectionPolicy(new PreferredSingletonElectionPolicy(new SimpleSingletonElectionPolicy(), new NamePreference("node1/singleton")));
// or singleton.setElectionPolicy(new PreferredSingletonElectionPolicy(new SimpleSingletonElectionPolicy(), new NamePreference("node1/singleton"), new
// NamePreference("node2/singleton")));
singleton.build(new DelegatingServiceContainer(context.getServiceTarget(), context.getServiceRegistry())).setInitialMode(ServiceController.Mode.ACTIVE).install();
}
}
创建一个 HA 单例服务Class,它完全负责查找和调用包含您的业务逻辑的 EJB
public class SingletonService<T> implements Service<T> {
protected ScheduledExecutorService deployDelayThread = null;
/**
* The node we are running on
*/
protected String nodeName;
/**
* A flag whether the service is started (or scheduled to be started)
*/
protected final AtomicBoolean started = new AtomicBoolean(false);
/**
* Container life cycle call upon activation. This will construct the singleton instance in this JVM and start the Timer.
*/
@Override
public final void start(StartContext context) throws StartException {
this.nodeName = System.getProperty("jboss.node.name");
logger.info("Starting service '" + this.getClass().getName() + "' on node " + nodeName);
if (!started.compareAndSet(false, true)) {
throw new StartException("The service " + this.getClass().getName() + " is still started!");
}
// MSC does not allow this thread to be blocked so we let the service know that the start is asynchronous and the result will be advised later.
// We delay the actual deployment of the Singleton for a few seconds to allow time for a HASingleton Election to be held and won by one of the instances.
// If the winner is not this instance (prior to deployemnt) then stop(Context) is invoked which sets started to false and the deployment does not occur.
// context.asynchronous();
deployDelayThread.schedule(new StartSingletonAsync(context), 10, TimeUnit.SECONDS);
context.complete();
}
/** Introduces a 5s delay in starting the Singleton bean giving time for the the ha singleton election to be held and won */
private class StartSingletonAsync implements Runnable {
private StartSingletonAsync(StartContext context) {
}
@Override
public void run() {
try {
startSingletonBean();
} catch (StartException e) {
logger.info("Start Exception", e);
}
// be nice to the garbage collector, we don't need this any more
deployDelayThread.shutdown();
deployDelayThread = null;
}
}
private void startSingletonBean() throws StartException {
try {
if (!started.get()) {
throw new StartException("Aborted due to service stopping");
}
// Start your EJB
InitialContext ic = new InitialContext();
bean = ic.lookup(getJndiName());
bean.startHaSingleton();
logger.info("*** Master Only: HASingleton service " + getJndiName() + " started on master:" + nodeName);
if (!bean.isRunning()) {
logger.error("ERROR Bean should be running");
}
} catch (NamingException e) {
throwStartException(e);
}
}
private void throwStartException(Exception e) throws StartException {
String message = "Could not initialize HASingleton" + getJndiName() + " on " + nodeName;
logger.error(message, e);
throw new StartException(message, e);
}
/**
* Container life cycle call when activated
*/
@Override
public final void stop(StopContext context) {
if (deployDelayThread != null) {
deployDelayThread.shutdownNow();
}
if (!started.compareAndSet(true, false) || bean == null) {
logger.warn("The service '" + this.getClass().getName() + "' is not active!");
} else {
try {
InitialContext ic = new InitialContext();
bean = (JmxMBean) ic.lookup(getJndiName());
bean.stopHaSingleton();
logger.info("*** Master Only: HASingleton service " + getJndiName() + " stopped on master:" + nodeName);
} catch (EJBException e) {
// Note: all these exceptions are already logged by JBoss
} catch (NamingException e) {
logger.error("Could not stop HASingleton service " + getJndiName() + " on " + nodeName, e);
}
logger.info("MASTER ONLY HASingleton service '" + this.getClass().getName() + "' Stopped on node " + nodeName);
}
}
private String getJndiName() {
return "java.global/path/to/your/singleton/ejb";
}
}
最后在 META-INF/servcie/org.jboss.msc.service.ServiceActivator
com.mycompany.singletons.SingletonActivator
您可能还需要将依赖项添加到 jar 中的清单 META-INF/MANIFEST.MF
文件,如下所示:Dependencies: org.jboss.msc, org.jboss.as.clustering.singleton, org.jboss.as.server
Redhat 在 https://access.redhat.com/documentation/en-US/JBoss_Enterprise_Application_Platform/6.4/html/Development_Guide/Implement_an_HA_Singleton.html 提供了更广泛的实施指南。您可能需要创建一个 Redhat 帐户才能访问它。 JBoss 发行版中还有一个快速入门示例。
所以经过进一步评估,2 个单例最终会在几分钟后合并在一起,从而创建 1 个预期的单例。