#
使用自定义CONNECTION FACTORY,这样会覆盖SPRING 的AUTO CONFIGURATION。
ActiveMQConnectionFactoryFactory.java
import java.lang.reflect.InvocationTargetException;
import java.util.Collections;
import java.util.List;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.springframework.boot.autoconfigure.jms.activemq.ActiveMQConnectionFactoryCustomizer;
import org.springframework.boot.autoconfigure.jms.activemq.ActiveMQProperties;
import org.springframework.boot.autoconfigure.jms.activemq.ActiveMQProperties.Packages;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Factory to create a {@link ActiveMQConnectionFactory} instance from properties defined
* in {@link SecondaryActiveMQProperties}.
*
* @author Phillip Webb
* @author Venil Noronha
*/
class ActiveMQConnectionFactoryFactory {
private static final String DEFAULT_EMBEDDED_BROKER_URL = "vm://localhost?broker.persistent=false";
private static final String DEFAULT_NETWORK_BROKER_URL = "tcp://localhost:61616";
private final ActiveMQProperties properties;
private final List<ActiveMQConnectionFactoryCustomizer> factoryCustomizers;
ActiveMQConnectionFactoryFactory(ActiveMQProperties properties,
List<ActiveMQConnectionFactoryCustomizer> factoryCustomizers) {
Assert.notNull(properties, "Properties must not be null");
this.properties = properties;
this.factoryCustomizers = (factoryCustomizers != null) ? factoryCustomizers : Collections.emptyList();
}
public <T extends ActiveMQConnectionFactory> T createConnectionFactory(Class<T> factoryClass) {
try {
return doCreateConnectionFactory(factoryClass);
}
catch (Exception ex) {
throw new IllegalStateException("Unable to create " + "ActiveMQConnectionFactory", ex);
}
}
private <T extends ActiveMQConnectionFactory> T doCreateConnectionFactory(Class<T> factoryClass) throws Exception {
T factory = createConnectionFactoryInstance(factoryClass);
if (this.properties.getCloseTimeout() != null) {
factory.setCloseTimeout((int) this.properties.getCloseTimeout().toMillis());
}
factory.setNonBlockingRedelivery(this.properties.isNonBlockingRedelivery());
if (this.properties.getSendTimeout() != null) {
factory.setSendTimeout((int) this.properties.getSendTimeout().toMillis());
}
Packages packages = this.properties.getPackages();
if (packages.getTrustAll() != null) {
factory.setTrustAllPackages(packages.getTrustAll());
}
if (!packages.getTrusted().isEmpty()) {
factory.setTrustedPackages(packages.getTrusted());
}
customize(factory);
return factory;
}
private <T extends ActiveMQConnectionFactory> T createConnectionFactoryInstance(Class<T> factoryClass)
throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
String brokerUrl = determineBrokerUrl();
String user = this.properties.getUser();
String password = this.properties.getPassword();
if (StringUtils.hasLength(user) && StringUtils.hasLength(password)) {
return factoryClass.getConstructor(String.class, String.class, String.class).newInstance(user, password,
brokerUrl);
}
return factoryClass.getConstructor(String.class).newInstance(brokerUrl);
}
private void customize(ActiveMQConnectionFactory connectionFactory) {
for (ActiveMQConnectionFactoryCustomizer factoryCustomizer : this.factoryCustomizers) {
factoryCustomizer.customize(connectionFactory);
}
}
String determineBrokerUrl() {
if (this.properties.getBrokerUrl() != null) {
return this.properties.getBrokerUrl();
}
if (this.properties.isInMemory()) {
return DEFAULT_EMBEDDED_BROKER_URL;
}
return DEFAULT_NETWORK_BROKER_URL;
}
}
TwinJmsConnectionFactoryConfiguration.java
import java.util.stream.Collectors;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.messaginghub.pooled.jms.JmsPoolConnectionFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.jms.JmsPoolConnectionFactoryFactory;
import org.springframework.boot.autoconfigure.jms.activemq.ActiveMQConnectionFactoryCustomizer;
import org.springframework.boot.autoconfigure.jms.activemq.ActiveMQProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Profile;
@Configuration
@Profile({"local"})
public class TwinJmsConnectionFactoryConfiguration {
@Bean
@ConfigurationProperties(prefix = "spring.activemq.primary")
public ActiveMQProperties primaryActiveMQProperties() {
return new ActiveMQProperties();
}
@Bean(destroyMethod = "stop")
@Primary
@ConditionalOnProperty(prefix = "spring.activemq.pool", name = "enabled", havingValue = "true")
public JmsPoolConnectionFactory connectionFactory(ActiveMQProperties primaryActiveMQProperties,
ObjectProvider<ActiveMQConnectionFactoryCustomizer> factoryCustomizers) {
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactoryFactory(primaryActiveMQProperties,
factoryCustomizers.orderedStream().collect(Collectors.toList()))
.createConnectionFactory(ActiveMQConnectionFactory.class);
return new JmsPoolConnectionFactoryFactory(primaryActiveMQProperties.getPool())
.createPooledConnectionFactory(connectionFactory);
}
////////////////////////////////////////////////////////////////////////////////
@Bean
@ConfigurationProperties(prefix = "spring.activemq.sescond")
public ActiveMQProperties sescondActiveMQProperties() {
return new ActiveMQProperties();
}
@Bean(destroyMethod = "stop")
@ConditionalOnProperty(prefix = "spring.activemq.pool", name = "enabled", havingValue = "true")
public JmsPoolConnectionFactory sescondPooledJmsConnectionFactory(ActiveMQProperties sescondActiveMQProperties,
ObjectProvider<ActiveMQConnectionFactoryCustomizer> factoryCustomizers) {
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactoryFactory(sescondActiveMQProperties,
factoryCustomizers.orderedStream().collect(Collectors.toList()))
.createConnectionFactory(ActiveMQConnectionFactory.class);
return new JmsPoolConnectionFactoryFactory(sescondActiveMQProperties.getPool())
.createPooledConnectionFactory(connectionFactory);
}
}
From here:
The difference between the PooledConnectionFactory and the CachingConnectionFactory is a difference in implementation. Below are some of the characteristics that differ between them:
Although both the PooledConnectionFactory and the CachingConnectionFactory state that they each pool connections, sessions and producers, the PooledConnectionFactory does not actually create a cache of multiple producers. It simply uses a singleton pattern to hand out a single cached producer when one is requested. Whereas the CachingConnectionFactory actually creates a cache containing multiple producers and hands out one producer from the cache when one is requested.
The PooledConnectionFactory is built on top of the Apache Commons Pool project for pooling JMS sessions. This allows some additional control over the pool because there are features in Commons Pool that are not being used by the PooledConnectionFactory. These additional features include growing the pool size instead of blocking, throwing an exception when the pool is exhausted, etc. You can utilize these features by creating your own Commons Pool GenericObjectPool using your own customized settings and then handing that object to the PooledConnectionFactory via the setPoolFactory method. See the following for additional info: http://commons.apache.org/pool/api-1.4/org/apache/commons/pool/impl/GenericObjectPoolFactory.html
The CachingConnectionFactory has the ability to also cache consumers. Just need to take care when using this feature so that you know the consumers are cached according to the rules noted in the blog post.
But most importantly, the CachingConnectionFactory will work with any JMS compliant MOM. It only requires a JMS connection factory. This is important if you are using more than one MOM vendor which is very common in enterprise organizations (this is mainly due to legacy and existing projects). The important point is that the CachingConnectionFactory works very well with many different MOM implementations, not only ActiveMQ.
From here:
If you have clustered ActiveMQs, and use failover transport it has been reported that CachingConnectionFactory is not a right choice.
The problem I’m having is that if one box goes down, we should start sending messages on the other, but it seems to still be using the old connection (every send times out). If I restart the program, it’ll connect again and everything works. Source: Autoreconnect problem with ActiveMQ and CachingConnectionFactory
The problem is that cached connections to the failed ActiveMQ was still in use and that created the problem for the user. Now, the choice for this scenario is PooledConnectionFactory.
If you’re using ActiveMQ today, and chances are that you may switch to some other broker (JBoss MQ, WebSphere MQ) in future, do not use PooledConnectionFactory, as it tightly couples your code to ActiveMQ.
正好做Mongodb主从复制尝试使用Spring Boot Data Mongodb Starter插件链接访问Mongodb数据库集群。
遇到的坑:
- spring.data.mongodb.host和spring.data.mongodb.port形式不适合集群配置,会报host无法识别异常
- spring.data.mongodb.uri中经常抛出authentication failed异常
解决办法:
- 对于第一个坑,请使用spring.data.mongodb.uri。如果使用了uri,则其余的host/username/password/db/auth-db这些全部无效。
- 对于第二个坑,请在spring.data.mongodb.uri中指定replicaSet和authsource,另外记得把所有集群节点服务器地址都列全。
如果auth-db和db是同一个,则无需加authsource,如果不同,则加authsource=admin
我没有把authsource指定,所以一直报authentication failed异常。然后只好一点点去发掘问题点,最后查到在com.mongodb.ConnectionString类中的createCredentials中
private MongoCredential createCredentials(final Map<String, List<String>> optionsMap, final String userName,
final char[] password) {
AuthenticationMechanism mechanism = null;
String authSource = (database == null) ? "admin" : database;
String gssapiServiceName = null;
String authMechanismProperties = null;
for (final String key : AUTH_KEYS) {
String value = getLastValue(optionsMap, key);
if (value == null) {
continue;
}
if (key.equals("authmechanism")) {
mechanism = AuthenticationMechanism.fromMechanismName(value);
} else if (key.equals("authsource")) {
authSource = value;
} else if (key.equals("gssapiservicename")) {
gssapiServiceName = value;
} else if (key.equals("authmechanismproperties")) {
authMechanismProperties = value;
}
}
MongoCredential credential = null;
if (mechanism != null) {
switch (mechanism) {
case GSSAPI:
credential = MongoCredential.createGSSAPICredential(userName);
if (gssapiServiceName != null) {
credential = credential.withMechanismProperty("SERVICE_NAME", gssapiServiceName);
}
break;
case PLAIN:
credential = MongoCredential.createPlainCredential(userName, authSource, password);
break;
case MONGODB_CR:
credential = MongoCredential.createMongoCRCredential(userName, authSource, password);
break;
case MONGODB_X509:
credential = MongoCredential.createMongoX509Credential(userName);
break;
case SCRAM_SHA_1:
credential = MongoCredential.createScramSha1Credential(userName, authSource, password);
break;
default:
throw new UnsupportedOperationException(format("The connection string contains an invalid authentication mechanism'. "
+ "'%s' is not a supported authentication mechanism",
mechanism));
}
} else if (userName != null) {
credential = MongoCredential.createCredential(userName, authSource, password);
}
if (credential != null && authMechanismProperties != null) {
for (String part : authMechanismProperties.split(",")) {
String[] mechanismPropertyKeyValue = part.split(":");
if (mechanismPropertyKeyValue.length != 2) {
throw new IllegalArgumentException(format("The connection string contains invalid authentication properties. "
+ "'%s' is not a key value pair", part));
}
String key = mechanismPropertyKeyValue[0].trim().toLowerCase();
String value = mechanismPropertyKeyValue[1].trim();
if (key.equals("canonicalize_host_name")) {
credential = credential.withMechanismProperty(key, Boolean.valueOf(value));
} else {
credential = credential.withMechanismProperty(key, value);
}
}
}
return credential;
}
authSource默认会指向我们目标数据的数据库。然而在身份验证机制中我们通常需要指向admin。(非常想报粗口,代码作者在这里脑袋被men挤了么)。所以需要强制指定authSource中指定。具体指定方式如下:
mongodb://{用户名}:{密码}@{host1}:27017,{host2}:27017,{host3}:27017/{目标数据库}?replicaSet={复制集名称}&write=1&readPreference=primary&authsource={授权数据库}
这时发现如果处理总数足够大时,被处理的ITEMS总数会少于应该处理的总数。
+------------+--------------+-------------+-----------------+------------------+--------------------+----------------+-----------+-------------------------
| READ_COUNT | FILTER_COUNT | WRITE_COUNT | READ_SKIP_COUNT | WRITE_SKIP_COUNT | PROCESS_SKIP_COUNT | ROLLBACK_COUNT | EXIT_CODE | EXIT_MESSAGE
-+------------+--------------+-------------+-----------------+------------------+--------------------+----------------+-----------+-------------------------
| 1 | 0 | 1 | 0 | 0 | 0 | 0 | COMPLETED |
| 30006 | 0 | 29947 | 0 | 59 | 0 | 61 | COMPLETED | Waited for 101 results.
To see how many connections are configured for your DB to use:
select @@max_connections;
To change it:
set global max_connections = 200;
To see how many are connected at the current time:
show processlist;
vi /etc/my.cnf
[mysqld]
max_connections = 500
MongDB Client请求查询数据,需要包括五个阶段:
MongoDB Client需要找到可用的MongoDB Server
MongoDB Client需要和MongoDB Server建立(new)Connection
应用程序处理线程从Connection Pool中获取Connection
数据传输(获取连接后,进行Socket通信,获取数据)
断开Collection
那么,MongoDB Client驱动设置中网络相关等待超时参数serverSelectionTimeout、connectTimeout、maxWaitTime和socketTimeout分别对应上面哪个环节呢?
参数serverSelectionTimeout:对应第1个环节,即MongoDB Client需要找到可用的MongoDB Server所需要的等待时间, MongDB部署的生产一般由多个服务器组成,要么作为一个复制集或者作为一个分片集群,参数 serverSelectionTimeout的值即为多长时间内找不到合适服务器时候就决定放弃的时间间隔;
参数connectTimeout:对应第2个环节,每次创建Connection,对应的网络等待。单位毫秒数, 0表示没有限制;
参数maxWaitTime:对应第3个环节,应用程序处理线程从连接池中获取Collection,对应的网络等待时间。单位毫秒数,0表示 不等待,负数表示等待时间不确定;
参数socketTimeout:对应第4个环节,获取Connection后,就有了Socket通信,获取数据,即在MonogoDB Client 和MonogoDB Server的Socket通信过程中的网络等待时间。单位毫秒数,默认配置为0,也就是没有限制, 没有超 时限制,系统出了问题也不容易发现,应该根据实际情况,给出合理的超时时间。
其他相关参数如下:
connectionsPerHost:对mongo实例来说,每个host允许链接的最大链接数,这些链接空闲时会放入池中,如果链接被耗尽,任何请求链接的操作会被阻塞等待链接可用,推荐配置10
minPoolsSize:当Connection空闲时,Connection Pool中最少Connection保有量;
threadsAllowedToBlockForConnectionMultiplier:每个Connection的可以阻塞等待的线程队列数,它以上面connectionsPerHost值相乘的结果就是阻塞等待的线程队列最大值。如果连接线程排满了队列就会抛出“Out of semaphores to get db”错误。
socketKeepAlive:该标志用于控制socket保持活动的功能,通过防火墙保持连接活着
socketKeepAlive=false
autoConnectRetry:这个控制是否在一个Connection时,系统会自动重试
#true:假如Connection不能建立时,驱动将重试相同的server,有最大的重试次数,默认为15次,这样可以避免一些server因为一些阻塞操作零时down而驱动抛出异常,这个对平滑过度到一个新的master,也是很有用的,注意:当集群为复制集时,驱动将在这段时间里,尝试链接到旧的master上,而不会马上链接到新master上
#false 当在进行socket读写时,不会阻止异常抛出,驱动已经有自动重建破坏链接和重试读操作. 推荐配置false
autoConnectRetry=false
#重新打开链接到相同server的最大毫秒数,推荐配置为0,如果 autoConnectRetry=true,表示时间为15s
#com.jd.mongodbclient2.mongo.JDClientMongo.maxAutoConnectRetryTime=false
————————————————
版权声明:本文为CSDN博主「pursuer211」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/pursuer211/article/details/82994027
摘要: 前言在本系列文章的第 1 部分,我们搭建了一个用户缴费通知的批处理任务。尽管这个简单的应用展现了 Spring Batch 的基本功能,但是它与真实的应用相去甚远。在实际应用中,我们的 Job 可能必须要包含多个 Step,为了提高性能,我们可能需要考虑 Job 的并发问题。Spring Batch 在这些方面又提供了哪些好的特性呢?让我们继续。Step Flow通过前文我们已经知道,Step 是...
阅读全文
摘要: 引言总述本系列文章旨在通过示例搭建以及特性介绍,详细讲述如何利用 Spring Batch 开发企业批处理应用。本系列文章共分为三部分,第一部分初步介绍了批处理以及 Spring Batch 的相关概念,同时搭建了一个简单的基于 Spring Batch 的批处理应用。第二部分介绍了 Step Flow 以及并发支持。第三部分则主要介绍了 Spring Batch 对任务监控的支持。下面让我们进入...
阅读全文