paulwong

2019年8月16日 #

如何优雅地停止SPRING BATCH中的REMOTE CHUNKING JOB

SPRING BATCH中的REMOTE CHUNKING JOB,由于是基于MASTER/SLAVE的架构,其中某个STEP是会在远程机器中执行,如果要停止这个JOB,需要考虑两个问题:
1、什么时候发出停止指令
2、如何等待远程STEP的完成

一般停止JOB,可用JobOperator.stop(long executionId)来停止,但这个无法确定什么时候发出停止指令,如果是在CHUNK的处理中途发出,则会出现回滚的现象。
BATCH_STEP_EXECUTION thead tr {background-color: ActiveCaption; color: CaptionText;} th, td {vertical-align: top; font-family: "Tahoma", Arial, Helvetica, sans-serif; font-size: 8pt; padding: 4px; } table, td {border: 1px solid silver;} table {border-collapse: collapse;} thead .col0 {width: 173px;} .col0 {text-align: right;} thead .col1 {width: 82px;} .col1 {text-align: right;} thead .col2 {width: 282px;} thead .col3 {width: 164px;} .col3 {text-align: right;} thead .col4 {width: 161px;} thead .col5 {width: 161px;} thead .col6 {width: 109px;} thead .col7 {width: 127px;} .col7 {text-align: right;} thead .col8 {width: 109px;} .col8 {text-align: right;} thead .col9 {width: 118px;} .col9 {text-align: right;} thead .col10 {width: 117px;} .col10 {text-align: right;} thead .col11 {width: 142px;} .col11 {text-align: right;} thead .col12 {width: 150px;} .col12 {text-align: right;} thead .col13 {width: 166px;} .col13 {text-align: right;} thead .col14 {width: 137px;} .col14 {text-align: right;} thead .col15 {width: 109px;} thead .col16 {width: 156px;} thead .col17 {width: 161px;}
STEP_EXECUTION_ID VERSION STEP_NAME JOB_EXECUTION_ID START_TIME END_TIME STATUS COMMIT_COUNT READ_COUNT FILTER_COUNT WRITE_COUNT READ_SKIP_COUNT WRITE_SKIP_COUNT PROCESS_SKIP_COUNT ROLLBACK_COUNT EXIT_CODE EXIT_MESSAGE LAST_UPDATED
2304 169 step2HandleXXX 434 2020-06-22 16:27:54 2020-06-22 16:32:46 STOPPED 167 5010 0 4831 0 155 0 161 STOPPED org.springframework.batch.core.JobInterruptedException 2020-06-22 16:32:46


另外SPRING BATCH也不会等远程STEP执行完成,就将JOB的状态设为Complete。

发出停止的指令应通过ChunkListener达成:

public class ItemMasterChunkListener extends ChunkListenerSupport{
    
    private static final Logger log = LoggerFactory.getLogger(ItemMasterChunkListener.class);
    
    
    @Override
    public void beforeChunk(ChunkContext context) {
        log.info("ItemMasterProcessor.beforeChunk");
    }


    @Override
    public void afterChunk(ChunkContext context) {
        log.info("ItemMasterProcessor.afterChunk");
        if(XXXX.isStoppingOrPausing()) {
            log.info("context.getStepContext().getStepExecution().setTerminateOnly()");
            context.getStepContext().getStepExecution().setTerminateOnly();
        }
    }


    @Override
    public void afterChunkError(ChunkContext context) {
        log.info("ItemMasterProcessor.afterChunkError");
    }


}


配置BEAN:

@Bean
@StepScope
public ItemMasterChunkListener novaXItemMasterChunkListener() {
     return new ItemMasterChunkListener();
}
    
this.masterStepBuilderFactory
                    .<X, X>get("step2Handle")
                    .listener(itemMasterChunkListener())
                    .build();


由于是在CHUNK完成的时候发出停止指令,就不会出现ROLLBACK的情况。

等待远程STEP完成,通过读取MQ上的MESSAGE是否被消费完成,PENDDING的MESSAGE为0的条件即可。

public class JobExecutionListenerSupport implements JobExecutionListener {

    /* (non-Javadoc)
     * @see org.springframework.batch.core.domain.JobListener#afterJob()
     
*/
    @Override
    public void afterJob(JobExecution jobExecution) {
        Integer totalPendingMessages = 0;
        String queueName = "";
        
        
        String messageSelector = "JOB_EXECUTION_ID=" + jobExecution.getJobInstance().getInstanceId();
        do{
            totalPendingMessages = 
                    this.jmsTemplate.browseSelected(queueName, messageSelector, 
                                (session, browser) -> 
                                    Collections.list(browser.getEnumeration()).size()
                            );
            
            String brokerURL = null;
            if(jmsTemplate.getConnectionFactory() instanceof JmsPoolConnectionFactory) {
                JmsPoolConnectionFactory connectionFactory =
                        (JmsPoolConnectionFactory)jmsTemplate.getConnectionFactory();
                ActiveMQConnectionFactory activeMQConnectionFactory =
                        (ActiveMQConnectionFactory)connectionFactory.getConnectionFactory();
                brokerURL = activeMQConnectionFactory.getBrokerURL();
            } else if(jmsTemplate.getConnectionFactory() instanceof CachingConnectionFactory) {
                CachingConnectionFactory connectionFactory =
                        (CachingConnectionFactory)jmsTemplate.getConnectionFactory();
                ActiveMQConnectionFactory activeMQConnectionFactory =
                        (ActiveMQConnectionFactory)connectionFactory.getTargetConnectionFactory();
                brokerURL = activeMQConnectionFactory.getBrokerURL();
            }
            
            LOGGER.info("queueName = {}, {}, totalPendingMessages = {}, url={}", 
                    queueName, messageSelector, totalPendingMessages, brokerURL);
            Assert.notNull(totalPendingMessages, "totalPendingMessages must not be null.");
            try {
                Thread.sleep(5_000);
            } catch (InterruptedException e) {
                LOGGER.error(e.getMessage(), e);
            }
        } while(totalPendingMessages.intValue() > 0);
        
    }

    /* (non-Javadoc)
     * @see org.springframework.batch.core.domain.JobListener#beforeJob(org.springframework.batch.core.domain.JobExecution)
     
*/
    @Override
    public void beforeJob(JobExecution jobExecution) {
    }

}


这样整个JOB就能无异常地停止,且会等待远程STEP完成。

Reference:
https://docs.spring.io/spring-batch/docs/4.1.3.RELEASE/reference/html/common-patterns.html#stoppingAJobManuallyForBusinessReasons

https://stackoverflow.com/questions/13603949/count-number-of-messages-in-a-jms-queue

https://stackoverflow.com/questions/55499965/spring-batch-stop-job-execution-from-external-class

https://stackoverflow.com/questions/34621885/spring-batch-pollable-channel-with-replies-contains-chunkresponses-even-if-job


posted @ 2020-06-23 11:00 paulwong 阅读(97) | 评论 (0)编辑 收藏

为啥文件的CHECKSUM中SHA512比MD5高级?

https://stackoverflow.com/questions/2117732/reasons-why-sha512-is-superior-to-md5

posted @ 2020-06-16 10:21 paulwong 阅读(21) | 评论 (0)编辑 收藏

GIT资源


http://jartto.wang/tags/git/

posted @ 2020-06-04 10:38 paulwong 阅读(23) | 评论 (0)编辑 收藏

彻底搞懂 Git-Rebase

根据分支1新建了功能分支1,并在此上开发一段时间,后来分支1被别人提交了代码,因此分支1比功能分支1要新,这时,可以将功能分支1与分支1进行合并,但会多出很多COMMIT,这时就出现了rebase,
GIT会将功能分支1上的所有COMMIT另存一个文件,回退到分支1原始状态,再更新至当前分支1的状态,再把另存文件的COMMIT执行一遍,就成了已经合并的新的功能分支1。

http://jartto.wang/2018/12/11/git-rebase/

posted @ 2020-06-04 10:37 paulwong 阅读(19) | 评论 (0)编辑 收藏

How To Run Java Jar Application with Systemd on Linux

https://computingforgeeks.com/how-to-run-java-jar-application-with-systemd-on-linux/

systemd自启动java程序
https://www.cnblogs.com/yoyotl/p/8178363.html
------------------------------------------------------------

[Unit]
Description=TestJava
After=network.target

[Service]
Type=forking
ExecStart=/home/test/startTest.sh
ExecStop=/home/test/stopTest.sh

[Install]
WantedBy=multi-user.target

-------------------------------------------------------------
How to Autorun application at the start up in Linux
https://developer.toradex.com/knowledge-base/how-to-autorun-application-at-the-start-up-in-linux

How to automatically run program on Linux startup
https://www.simplified.guide/linux/automatically-run-program-on-startup


Systemd 入门教程:实战篇
https://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-part-two.html

Systemd 入门教程:命令篇

http://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-commands.html

posted @ 2020-05-11 16:16 paulwong 阅读(52) | 评论 (0)编辑 收藏

MariaDB Galera Cluster

What is MariaDB Galera Cluster?
https://mariadb.com/kb/en/what-is-mariadb-galera-cluster/

Prepare yum install repository:
https://downloads.mariadb.org/mariadb/repositories/#distro=CentOS&distro_release=centos7-amd64--centos7&mirror=coreix&version=10.4

MariaDB Galera Cluster部署实战
https://jeremyxu2010.github.io/2018/02/mariadb-galera-cluster%E9%83%A8%E7%BD%B2%E5%AE%9E%E6%88%98/

9 Tips for Going in Production with Galera Cluster for MySQL
https://severalnines.com/blog/9-tips-going-production-galera-cluster-mysql

HA for MySQL and MariaDB - Comparing Master-Master Replication to Galera Cluster
https://severalnines.com/database-blog/ha-mysql-and-mariadb-comparing-master-master-replication-galera-cluster

Galera Cluster for MySQL - Tutorial
https://severalnines.com/resources/tutorials/galera-cluster-mysql-tutorial










posted @ 2020-05-09 11:08 paulwong 阅读(31) | 评论 (0)编辑 收藏

How to disable IPv6 on CentOS / RHEL 7


https://www.thegeekdiary.com/centos-rhel-7-how-to-disable-ipv6/

https://linuxconfig.org/redhat-8-enable-disable-ipv6


posted @ 2020-05-06 12:42 paulwong 阅读(31) | 评论 (0)编辑 收藏

How To Count Files in Directory on Linux

https://devconnected.com/how-to-count-files-in-directory-on-linux/

posted @ 2020-05-05 17:01 paulwong 阅读(37) | 评论 (0)编辑 收藏

JENKINS TOURIAL

https://huongdanjava.com/jenkins-2

posted @ 2020-04-07 10:29 paulwong 阅读(57) | 评论 (0)编辑 收藏

Deploy artifacts into Maven Repository in Jenkins

https://huongdanjava.com/deploy-artifacts-into-maven-repository-in-jenkins.html

posted @ 2020-04-06 14:13 paulwong 阅读(37) | 评论 (0)编辑 收藏

MAVEN私服-Nexus Repository Manager


Nexus Repository Manager is a tool that allows us to store and use libraries we need in projects such as Maven project…

Nexus Repository ManagerIn this tutorial, I summarize the tutorials of Huong Dan Java on the Nexus Repository Manager for your reference.


Installation

In this tutorial, I will guide you how to install Nexus Repository Manager.


Configuration

In order to create a new Maven Repository in the Nexus Repository Manager, you can refer to this tutorial.

We need to define a Role to define User rights in the Nexus Repository Manager.

To be able to do anything in the Nexus Repository Manager, you need to create and use the User.


Manipulation

Nexus Repository Manager supports us UI to upload any artifact to the Repository.

In addition to the UI, we can also use the RESTful API to upload an artifact.

posted @ 2020-04-06 14:08 paulwong 阅读(49) | 评论 (0)编辑 收藏

List sessions / active connections on MariaDB server

Using a command

Option 1

show status where variable_name = 'threads_connected'; 

Columns

  • Variable_name - Name of the variable shown
  • Value - Number of active connections

Rows

  • One row: Only one row is displayed

Sample results

Option 2

show processlist; 

Columns

  • Id - The connection identifier
  • User - The MariaDB user who issued the statement
  • Host - Host name and client port of the client issuing the statement
  • db - The default database (schema), if one is selected, otherwise NULL
  • Command - The type of command the thread is executing
  • Time - The time in seconds that the thread has been in its current state
  • State - An action, event, or state that indicates what the thread is doing
  • Info - The statement the thread is executing, or NULL if it is not executing any statement
  • Progress - The total progress of the process (0-100%)

Rows

  • One row: represents one active connection
  • Scope of rows: total of active connections

Sample results

Using a query

Option 3

select id, user, host, db, command, time, state, 
info, progress from information_schema.processlist;

Columns

  • Id - The connection identifier
  • User - The MariaDB user who issued the statement
  • Host - Host name and client port of the client issuing the statement
  • db - The default database (schema), if one is selected, otherwise NULL
  • Command - The type of command the thread is executing
  • Time - The time in seconds that the thread has been in its current state
  • State - An action, event, or state that indicates what the thread is doing
  • Info - The statement the thread is executing, or NULL if it is not executing any statement
  • Progress - The total progress of the process (0-100%)
  • memory_used - Amount of memory used by the active connection

Rows

  • One row: represents one active connection
  • Scope of rows: total of active connections

Sample results

Using the GUI

Option 4

Click on the Client Connections option of the Management tab (left navigation pane)

This action will show the Client Connections screen containing the current active connections

posted @ 2020-04-02 15:38 paulwong 阅读(51) | 评论 (0)编辑 收藏

Finding slow queries in MongoDB

Database Profiling

MongoDB Profiler is a db profiling system that can help identify inefficient

or slow queries and operations.

Levels of profiles available are:

Level

Setting

0

Off. & No profiling

1

On & only includes slow operations

2

On & Includes all operations


We can enable it by setting the Profile level value using the following
command in mongo shell :

"db.setProfilingLevel(1)"

By default, mongod records slow queries to its log, as defined by slowOpThresholdMs.

NOTE

Enabling database profiler puts negative impact on MongoDB’s performance.

It’s better to enable it for specific intervals & minimal on Production Servers.

We can enable profiling on a mongod basis but This setting will not propagate
across a replica set and sharded cluster.

We can view the output in the system.profile collection in mongo shell using show profile command, or using following:

db.system.profile.find( { millis : { $gt : 200 } } )

Command returns operations that took longer than 200 ms. Similarly we
can change the values as per our need.

Enabling profile for an entire mongod instance.

For the purpose of development in testing, we can enable database profiling/settings for an 
entire mongod instance. The profiling level will be applied to all databases.

 

NOTE:

We can't enable the profiling settings on a mongos instance. To enable the profiling in

shard clusters, we have to enable/start profiling for each mongod instance in cluster.

 

Query for the recent 10 entries

db.system.profile.find().limit(10).sort( { ts : 1 } ).pretty()

 

Collection with the slowest queries(No. Of queries)

db.system.profile.group({key: {ns: true}, initial: {count: 0}, reduce: function(obj,prev){ prev.count++;}})

 

Collection with the slowest queries(No. Of millis spent)

db.system.profile.group({key: {ns: true}, initial: {millis: 0}, reduce: function(obj, prev){ prev.millis += obj.millis;}})

 

Most recent slow query

db.system.profile.find().sort({$natural: -1}).limit(1)

 

Single slowest query(Right now)

db.system.profile.find().sort({millis: -1}).limit(1)

posted @ 2020-03-27 23:35 paulwong 阅读(69) | 评论 (0)编辑 收藏

基于LINUX的分布式文件系统GlusterFS + NFS-Ganesha

基于LINUX的,也就是用yum install就可以使用。

GlusterFS是分布文件存储系统, 也即一个文件有三个备份,每个备份可以放到不同的节点(IP)上,这样某个节点CRASH后,会从其他节点取文件。

NFS-Ganesha则是用户层面非KERNAL层面的实现了NFS SERVER的功能,但双加了扩展,对外提供基于NFS协议的文件存取服务。

资源:
GlusterFS and NFS-Ganesha integration
https://staged-gluster-docs.readthedocs.io/en/release3.7.0beta1/Features/glusterfs_nfs-ganesha_integration/

Exporting and Unexporting Volumes through nfs-ganesha
https://access.redhat.com/documentation/en-US/Red_Hat_Storage/2.1/html/Administration_Guide/Starting_and_Stopping_nfs-ganesha.html

https://www.snia.org/sites/default/files/Poornima_NFS_GaneshaForClusteredNAS.pdf





posted @ 2020-03-22 11:46 paulwong 阅读(175) | 评论 (0)编辑 收藏

开机nfs自动挂载

1.echo "mount -t nfs -o nolock ${IP}:${remote_dir} ${local_dir}" >>  /etc/rc.local

2.echo "${IP}:/home/logs /home/logs nfs defaults 0 0" >> /etc/fstab

关于/etc/rc.local


rc.local也是我经常使用的一个脚本。该脚本是在系统初始化级别脚本运行之后再执行的,因此可以安全地在里面添加你想在系统启动之后执行的脚本。常见的情况是你可以再里面添加nfs挂载/mount脚本。此外,你也可以在里面添加一些调试用的脚本命令。例如,我就碰到过这种情况:samba服务总是无法正常运行,而检查发现,samba是在系统启动过程中就该启动执行的,也就是说,samba守护程序配置保证了这种功能本应该正确执行。碰到这种类似情况,一般我也懒得花大量时间去查为什么,我只需要简单的在/etc/rc.local脚本里加上这么一行:

/etc/init.d/samba start

这样就成功的解决了samba服务异常的问题。

posted @ 2020-03-21 19:44 paulwong 阅读(58) | 评论 (0)编辑 收藏

利用 Chef 在 Red Hat Enterprise Linux 上自动化部署 Mariadb Galera Cluster

https://www.ibm.com/developerworks/cn/linux/1611_chensz_mgc/index.html

posted @ 2020-03-21 10:55 paulwong 阅读(58) | 评论 (0)编辑 收藏

在SPRING BOOT中使用多JMS CONNECTION

使用自定义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((intthis.properties.getCloseTimeout().toMillis());
        }
        factory.setNonBlockingRedelivery(this.properties.isNonBlockingRedelivery());
        if (this.properties.getSendTimeout() != null) {
            factory.setSendTimeout((intthis.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);
    }
    
}


posted @ 2020-03-19 09:45 paulwong 阅读(57) | 评论 (0)编辑 收藏

Which is better: PooledConnectionFactory or CachingConnectionFactory?

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.

posted @ 2020-03-19 09:37 paulwong 阅读(75) | 评论 (0)编辑 收藏

Spring Boot Data Mongodb Starter自动配置那些坑

正好做Mongodb主从复制尝试使用Spring Boot Data Mongodb Starter插件链接访问Mongodb数据库集群。

遇到的坑:

  • spring.data.mongodb.host和spring.data.mongodb.port形式不适合集群配置,会报host无法识别异常
  • spring.data.mongodb.uri中经常抛出authentication failed异常


解决办法:

  1.  对于第一个坑,请使用spring.data.mongodb.uri。如果使用了uri,则其余的host/username/password/db/auth-db这些全部无效。
  2.  对于第二个坑,请在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={授权数据库}

posted @ 2020-03-17 09:39 paulwong 阅读(190) | 评论 (0)编辑 收藏

SPRING BATCH中STEP如果没加TRANSACTION MANAGER

这时发现如果处理总数足够大时,被处理的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.

如上面的总数是30006,但COMMIT的总数是29947,有59个被跳过了。

因此需在STEP上加TRANSACTION MANAGER.

https://stackoverflow.com/questions/42803941/spring-batch-incorrect-write-skip-count-issue

posted @ 2020-03-16 09:43 paulwong 阅读(88) | 评论 (0)编辑 收藏

mySQL Error 1040: Too Many Connection


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

posted @ 2020-03-10 20:20 paulwong 阅读(58) | 评论 (0)编辑 收藏

5 ways to customize Spring MVC JSON/XML output

https://mostafa-asg.github.io/post/customize-json-xml-spring-mvc-output/

posted @ 2020-03-08 15:55 paulwong 阅读(65) | 评论 (0)编辑 收藏

MongDB连接池参数serverSelectionTimeout、connectTimeout、maxWaitTime和socketTimeout介绍

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
#表示当没有手动关闭游标时,是否有一个自动释放游标对象的方法,如果你总是很小心的关闭游标,则可以将其设为false 推荐配置true

https://docs.mongodb.com/manual/reference/connection-string/#connections-connection-options
————————————————
版权声明:本文为CSDN博主「pursuer211」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/pursuer211/article/details/82994027

posted @ 2020-03-07 20:58 paulwong 阅读(126) | 评论 (0)编辑 收藏

使用 Spring Batch 构建企业级批处理应用-2

     摘要: 前言在本系列文章的第 1 部分,我们搭建了一个用户缴费通知的批处理任务。尽管这个简单的应用展现了 Spring Batch 的基本功能,但是它与真实的应用相去甚远。在实际应用中,我们的 Job 可能必须要包含多个 Step,为了提高性能,我们可能需要考虑 Job 的并发问题。Spring Batch 在这些方面又提供了哪些好的特性呢?让我们继续。Step Flow通过前文我们已经知道,Step 是...  阅读全文

posted @ 2020-03-06 13:47 paulwong 阅读(45) | 评论 (0)编辑 收藏

使用 Spring Batch 构建企业级批处理应用-1

     摘要: 引言总述本系列文章旨在通过示例搭建以及特性介绍,详细讲述如何利用 Spring Batch 开发企业批处理应用。本系列文章共分为三部分,第一部分初步介绍了批处理以及 Spring Batch 的相关概念,同时搭建了一个简单的基于 Spring Batch 的批处理应用。第二部分介绍了 Step Flow 以及并发支持。第三部分则主要介绍了 Spring Batch 对任务监控的支持。下面让我们进入...  阅读全文

posted @ 2020-03-06 13:45 paulwong 阅读(88) | 评论 (0)编辑 收藏

MobaXterm注册版

LINUX 的shell神器
https://www.upload.ee/files/11132815/MobaXterm_Pro_Portable_SetuP_v20_fu11.rar.html

posted @ 2020-02-27 17:44 paulwong 阅读(99) | 评论 (0)编辑 收藏

各种获取JVM DUMP的方法

JVM 的线程堆栈 dump 也称 core dump,内容为文本,主要包含当时 JVM 的线程堆栈,堆 dump 也称 heap dump,内容为二进制格式,主要包含当时 JVM 堆内存中的内容。由于各个操作系统、各个 JVM 实现不同,即使同一 JVM 实现,各个版本也有差异,本文描述的方法都基于 64 位 Linux 操作系统环境,Java 8 Oracle HotSpot JVM 实现。

堆栈和堆的内容在定位问题的时候,都是非常重要的信息。线程堆栈 dump 可以了解当时 JVM 中所有线程的运行情况,比如线程的状态和当前正在运行的代码行。堆 dump 可以了解当时堆的使用情况,各个类实例的数量及各个实例所占用的空间大小。

线程堆栈

使用 jstack

jstack 是 JDK 自带的工具,用于 dump 指定进程 ID(PID)的 JVM 的线程堆栈信息。

# 打印堆栈信息到标准输出 jstack PID  
# 打印堆栈信息到标准输出,会打印关于锁的信息 jstack -l PID  
强制打印堆栈信息到标准输出,如果使用 jstack PID 没有响应的情况下(此时 JVM 进程可能挂起),
加 -F 参数 jstack -F PID 

使用 jcmd

jcmd 是 JDK 自带的工具,用于向 JVM 进程发送命令,根据命令的不同,可以代替或部分代替 jstack、jmap 等。可以发送命令 Thread.print 来打印出 JVM 的线程堆栈信息。

# 下面的命令同等于 jstack PID 
jcmd PID Thread.print  

# 同等于 jstack -l PID 
jcmd PID Thread.print -l 

使用 kill -3

kill 可以向特定的进程发送信号(SIGNAL),缺省情况是发送终止(TERM) 的信号 ,即 kill PID 与 kill -15 PID 或 kill -TERM PID 是等价的。JVM 进程会监听 QUIT 信号(其值为 3),当收到这个信号时,会打印出当时的线程堆栈和堆内存使用概要,相比 jstack,此时多了堆内存的使用概要情况。但 jstack 可以指定 -l 参数,打印锁的信息。

kill -3 PID 
# 或 kill -QUIT PID 

-XX:+HeapDumpOnOutOfMemoryError

添加 JVM 参数 -XX:+HeapDumpOnOutOfMemoryError 后,当发生 OOM(OutOfMemory)时,自动堆 dump。缺省情况下,JVM 会创建一个名称为 java_pidPID.hprof 的堆 dump 文件在 JVM 的工作目录下。但可以使用参数 -XX:HeapDumpPath=PATH 来指定 dump 文件的保存位置。

# JVM 发生 OOM 时,会自动在 /var/log/abc 目录下产生堆 dump 文件 java_pidPID.hprof 
java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/abc/ 

jmap

jmap 也是 JDK 自带的工具,主要用于获取堆相关的信息。

堆 dump

# 将 JVM 的堆 dump 到指定文件,如果堆中对象较多,需要的时间会较长,子参数 format 只支持 b,
即二进制格式
jmap -dump:format=b,file=FILE_WITH_PATH

# 如果 JVM 进程未响应命令,可以加上参数 -F 尝试
jmap -F -dump:format=b,file=FILE_WITH_PATH

# 可以只 dump 堆中的存活对象,加上 live 子参数,但使用 -F 时不支持 live
jmap -dump:live,format=b,file=FILE_WITH_PATH

获取堆概要信息

# -heap 参数用于查看指定 JVM 进程的堆的信息,包括堆的各个参数的值,堆中新生代、年老代的内存大小、使用率等 
jmap -heap PID  

# 同样,如果 JVM 进程未响应命令,可以加上参数 -F 尝试 
jmap -F -heap PID 

一个实例输出如下:

Attaching to process ID 68322, please wait
Debugger attached successfully.
Server compiler detected.
JVM version is 25.112-b16

using thread-local object allocation.
Parallel GC with 4 thread(s)

Heap Configuration:
   MinHeapFreeRatio         = 0
   MaxHeapFreeRatio         = 100
   MaxHeapSize              = 268435456 (256.0MB)
   NewSize                  = 8388608 (8.0MB)
   MaxNewSize               = 89128960 (85.0MB)
   OldSize                  = 16777216 (16.0MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 41943040 (40.0MB)
   used     = 1701504 (1.6226806640625MB)
   free     = 40241536 (38.3773193359375MB)
   4.05670166015625% used
From Space:
   capacity = 4194304 (4.0MB)
   used     = 0 (0.0MB)
   free     = 4194304 (4.0MB)
   0.0% used
To Space:
   capacity = 5242880 (5.0MB)
   used     = 0 (0.0MB)
   free     = 5242880 (5.0MB)
   0.0% used
PS Old Generation
   capacity = 30408704 (29.0MB)
   used     = 12129856 (11.56793212890625MB)
   free     = 18278848 (17.43206787109375MB)
   39.889421134159484% used

16658 interned Strings occupying 1428472 bytes.

获取堆中的类实例统计
# 打印 JVM 堆中的类实例统计信息,以占用内存的大小排序,同样,如果 JVM 未响应命令,也可以使用 -F 参数 
jmap -histo PID  

# 也可以只统计堆中的存活对象,加上 live 子参数,但使用 -F 时不支持 live 
jmap -histo:live PID 

使用 jcmd

# 等同 jmap -dump:live,format=b,file=FILE_WITH_PATH
jcmd PID GC.heap_dump FILE_WITH_PATH

# 等同 jmap -dump:format=b,file=FILE_WITH_PATH
jcmd PID GC.heap_dump -all FILE_WITH_PATH

# 等同 jmap -histo:live PID
jcmd PID GC.class_histogram

# 等同 jmap -histo PID
jcmd PID GC.class_histogram -all

posted @ 2020-02-24 22:03 paulwong 阅读(212) | 评论 (0)编辑 收藏

Mongodb shell中select in 的实现


var bookIds = db.likes.find({userId:100}).map(function(like) { 
  return like.bookId; 
});
var books = db.books.find({_id:{$in:bookIds}});

db.REPORT_ITEM.count({REQUEST_ID : db.BATCH_CONTROL.find({ FILE_NAME : "20200218_100000.file" }).map(function(like) { 
  return like._id; 
})[0].str, JOB_TYPE_ENUM:"CHECK"})

posted @ 2020-02-21 23:10 paulwong 阅读(79) | 评论 (0)编辑 收藏

采用 jstatd 监控服务器


https://www.jianshu.com/p/97f09e1648a6

posted @ 2020-02-21 17:08 paulwong 阅读(73) | 评论 (0)编辑 收藏

mongodb错误记录

https://blog.csdn.net/wangxiaotongfan/article/details/81560463


posted @ 2020-02-21 08:50 paulwong 阅读(64) | 评论 (0)编辑 收藏

JAVA进程无故退出而没有留下LOG?

LINUX通常有个OOM KILLER进程,对于他认为吃内存的进程,会根据一定的算分,执行kill -9杀掉,查看日志如下:

#less /tmp/messages
Feb 20 03:55:09 ip kernel: Out of memory: Kill process 5974 (java) score 494 or sacrifice child
Feb 20 03:55:09 ip kernel: Killed process 5974 (java), UID 1001, total-vm:23674020kB, anon-rss:17503912kB, file-rss:0kB, shmem-rss:0kB

那如何屏蔽呢?

#/etc/cron.d/oom_disable
*/1**** root pgrep -f "java" | while read PID;do echo -17 > /proc/$PID/oom_adj;done

参考文档:
Linux内核OOM机制的详细分析
http://blog.chinaunix.net/uid-29242873-id-3942763.html


posted @ 2020-02-20 15:57 paulwong 阅读(238) | 评论 (0)编辑 收藏

!!21 MOST IMPORTANT JAVA 8 VM OPTIONS FOR SERVERS


  7 KOMMENTARE

In this video I explain some 21 JVM parameters which are suited for most server applications. If you have any questions, you can read those links below for more information or just ask in the comments section.

Java server flags video

I run several Java enterprise server applications. I often wondered – what are the best „default“ JVM settings for a server application to start with in production? I read a lot on the web and tried several things myself and wanted to share what I found out, so far. Links containing more information about JVM optimization can be found here:

http://blog.sokolenko.me/2014/11/javavm-options-production.html

http://www.petefreitag.com/articles/gctuning/

http://stas-blogspot.blogspot.de/2011/07/most-complete-list-of-xx-options-for.html

 

So let’s start:

-server

Use „-server“: All 64-bit JVMs use the server VM as default anyway. This setting generally optimizes the JVM for long running server applications instead of startup time. The JVM will collect more data about the Java byte code during program execution and generate the most efficient machine code via JIT.

-Xms=<heap size>[g|m|k] -Xmx=<heap size>[g|m|k]

The „-Xmx/-Xms“ settings specify the maximum and minimum values for the JVM heap memory. For servers, both params should have the same value to avoid heap resizing during runtime. I’ve applications running with 16GB heap sizes without an issue.

Depending on your application, you will have to try out how much memory will be best suited for your use case.

-XX:MaxMetaspaceSize=<metaspace size>[g|m|k]

Java 8 has no „Permanent Generation“ (PermGen) anymore but requires additional „Metaspace“ memory instead. This memory is used, in addition to the heap memory we specified before, for storing class meta data information.

The default size will be unlimited – I tend to limit MaxMetaspaceSize with a somewhat high value. Just in case something goes wrong with the application, the JVM will not hog all the memory of the server.

I suggest: Let your application run for a couple of days to get a feeling for how much Metaspace Size it uses normally. Upon next restart of the application set the limit to e.g. double the value.

-XX:+CMSClassUnloadingEnabled

Additionally, you might want to allow the JVM to unload classes which are held in memory but no code is pointing to them any more. If your application generates lots of dynamic classes, this is what you want.

-XX:+UseConcMarkSweepGC

This option makes the JVM use the ConcurrentMarkSweepGC – It can do much work in parallel to program execution but in some circumstances a „full GC“ with a „STW pause“ might still occur. I’ve read many articles and came to the conclusion that this GC is still the best one for server workloads.

-XX:+CMSParallelRemarkEnabled

The option CMSParallelRemarkEnabled means the remarking is done in parallel to program execution – which is what you want if your server has many cores (and most servers do).

 -XX:+UseCMSInitiatingOccupancyOnly  -XX:CMSInitiatingOccupancyFraction=<percent>

Normally the GC will use heuristics to know when it’s time to clear memory. GC might kick in too late with default settings (causing full-Gcs).
Some sources say it might be a good idea to disable heuristics altogether and just use generation occupancy to start a CMS collection cycle. Setting values around 70% worked fine for all of my applications and use cases.

-XX:+ScavengeBeforeFullGC

The first option tells the GC to first free memory by clearing out the „young generation“ or fairly new objects before doing a full GC.

-XX:+CMSScavengeBeforeRemark

CMSScavengeBeforeRemark does attempt a minor collection before the CMS remark phase – thus keeping the remark pause afterwards short.

-XX:+CMSClassUnloadingEnabled

The option „-XX:+CMSClassUnloadingEnabled“ here tells the JVM to unload classes, which are not needed any more by the running application. If you deploy war files to an application server like wildfly, tomcat or glassfish without restarting the server after the deployment, this flag is for you.

-XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses

The option „-XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses“ is especially important if your application uses RMI (remote method invocation). The usage of RMI will cause the JVM to do a FULL-GC EVERY HOUR! This might be a very bad idea for large heap sizes because the FULL-GC pause might take up to several seconds. It would be better to do a concurrent GC and try to unload unused classes to free up more memory – which is exactly what the second option does.

-XX:+PrintGCDateStamps -verbose:gc -XX:+PrintGCDetails -Xloggc:"<path to log>"

These options shown here will write out all GC related information to a specified log file. You can see how well your GC configuration works by looking into it.

I personally prefer to use the „Visual GC“ plug in for the „Visual VM“ tool to monitor the general JVM and GC behavior.

-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=<path to dump>`date`.hprof

When your JVM runs out of memory, you will want to know why. Since the OOM error might be hard to reproduce and you want to get your production server up and running again – you should specify a path for a heap dump. When things have settled down, you can analyze the dump afterwards.

-Djava.rmi.server.hostname=<external IP> -Dcom.sun.management.jmxremote.port=<port>

These options will help you to specify an IP and port for JMX – you will need those ports open to connect remotely to a JVM running on a server for tools like VisualVM. You can gain deep insights over cpu and memory usage, gc behaviour, class loading, thread count and usage of your application this way.

Visual VM
Lastly, I would like to recommend to you the VisualVM tool which is bundled with the Java 8 JDK. You can use it to gain more insights about your specific application behaviour on the JVM – like cpu and memory usage, thread utilisation and much more.

Visual GCVisualVM can be extended with a plug in called „Visual GC“. It will briefly show you VERY detailed information about the usage of the young and old generation object spaces. You can easily spot problems with garbage collection simply by analyzing these graphs during application runtime.

Thank you very much for watching! If you liked the video you might consider giving it a „thumbs up“. If you have any questions – just put them in the comments section. I will reply as quickly as possible.

-------------------------------------------------------

-XX:+UseCompressedOops [If Max Heap allocation is less than 32GB]
This can save a significant amount of memory and this option should already be enabled by default on recent java 8 versions. This option allowes object references to be stored as 32-bit values instead of 64-bit on 64-bit JVMs. This leads to before mentioned memory savings.

-XX:+AggressiveOpts
This option will enable performance options which are hoped to become enabled by default in upcoming released of the JVM. This option sets some performance settings but is marked as experimental! So you should only enable it, when you have to possibility to test your application thoroughly before enabling this flag on an production server.

-XX:+UseStringDeduplication
Since Java 8 update 20 you can use this option to reduce the memory usage of your application. The JVM will spot identical strings in memory, remove the duplicated and point all references to the remaining, single instance of the string.

-XX:+UseG1GC
Will tell the JVM to use the most recent G1 garbage collector. You are trading better application response times (due to shorter gc times with G1) against lower throughput (compared against good old ConcMarkSweepGC / CMS). If your application can deliver more value through short gc times, then G1 is definately better suited. Otherwise on Java 8, I’d recommend sticking with CMS.

Concerning your Tomcat 8 question, I’d suggest you have a look into it with the „VisualVM“ tool. Look at memory usage, GC times (visual GC plugin), pull and analyse stack traces or thread dumps to find the weak spot. You might also consider attaching a debugger to tomcat to find the bug.



https://www.maknesium.de/21-most-important-java-8-vm-options-for-servers

posted @ 2020-02-16 22:30 paulwong 阅读(77) | 评论 (0)编辑 收藏

windows下jenkins提示文件名太长

由于jenkins是调用windows的git取代码,因此是git的问题,进行如下配置即可:

git config --global core.longpaths true

posted @ 2020-02-14 14:37 paulwong 阅读(76) | 评论 (0)编辑 收藏

SPRING BOOT 环境下减少中间件依赖的UNIT测试

SPRING BOOT 环境下,测试有时会依赖于外部的中间件,如Mysql,Activemq,Mongodb等,那如何能减少这种依赖呢?
SPRING BOOT其实已经实现了自动化配置。

Mongodb

SPRING BOOT的自动化配置文件:org.springframework.boot.autoconfigure.mongo.embeddedEmbedded.MongoAutoConfiguration.java

在pom.xml中新增一test profile,并添加相应jar包,这样可防止对其他profile的影响,如果是在Eclipse跑测试,需在Project的属性中指定Active Profile为test,以覆盖pom.xml的定义。
这种方式即使是使用SPRING DATA MONGODB的REPOSITORY也是适用的。

    <profile>
        <id>test</id>
        <dependencies>
            <dependency>
                <groupId>de.flapdoodle.embed</groupId>
                <artifactId>de.flapdoodle.embed.mongo</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
        <activation>
            <activeByDefault>false</activeByDefault>
        </activation>
    </profile>
在application-test.yaml中添加端口,其他如IP那些信息都不需要
spring:
   data:
      mongodb:
         port: 27017

unit test config

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets;
import java.util.List;

import javax.annotation.PostConstruct;
import javax.sql.DataSource;

import org.bson.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
import org.springframework.jdbc.datasource.init.DatabasePopulatorUtils;
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
import org.springframework.util.FileCopyUtils;

@Configuration
@Profile({"test", "integrationTest"})
@EnableMongoRepositories(
        basePackages = {"paul.com.repository"
        }
)
public class EmbeddedDataSourceConfiguration {
    
    @Value("classpath:/initdata/USER.json")
    private Resource userResource;

    @Value("classpath:/initdata/MEMBERS.json")
    private Resource membersResource;
    
    @Autowired
    private ResourceLoader resourceLoader;
    
    @Autowired
    private DataSource dataSource;
    
    @Autowired
    private MongoTemplate  mongoTemplate;
    
    @PostConstruct
    protected void initialize() throws FileNotFoundException, IOException {
        this.initializeHsqldb();
        this.initializeMongodb();
    }
    
    private void initializeHsqldb() {
        ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
        populator.addScript(resourceLoader.getResource("classpath:/org/springframework/batch/core/schema-hsqldb.sql"));
        populator.setContinueOnError(true);
        DatabasePopulatorUtils.execute(populator , dataSource);
    }
    
    private void initializeMongodb() throws FileNotFoundException, IOException {
        this.saveResource(userResource, "USER");
        
        this.saveDocumentList(membersResource, "MEMBER");
    }
    
    private void saveResource(Resource resource, String collectionName) {
        String resourceJson = this.asString(resource);
        Document resourceDocument = Document.parse(resourceJson);
        this.mongoTemplate.save(resourceDocument, collectionName);
    }
    
    private void saveDocumentList(Resource resource, String collectionName) {
        String resourceJson = this.asString(resource);
        Document resourceDocument = Document.parse("{ \"list\":" + resourceJson + "}");
        List<Document> documentList = resourceDocument.get("list", List.class);
        documentList.forEach(document -> this.mongoTemplate.save(document, collectionName));
    }
    
    private String asString(Resource resource) {
        try (Reader reader = new InputStreamReader(resource.getInputStream(), StandardCharsets.UTF_8)) {
            return FileCopyUtils.copyToString(reader);
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }
    
//    @Bean(destroyMethod="close")
//    public DataSource dataSource() {
//        BasicDataSource dataSource = new BasicDataSource();
//        dataSource.setDriverClassName(environment.getProperty("batch.jdbc.driver"));
//        dataSource.setUrl(environment.getProperty("batch.jdbc.url"));
//        dataSource.setUsername(environment.getProperty("batch.jdbc.user"));
//        dataSource.setPassword(environment.getProperty("batch.jdbc.password"));
//        return dataSource;
//    }
}

ActiveMQ

只需更改application-test.yml中的brokerUrl为vm://embedded即可
spring:
   activemq:
      broker-url: vm://embedded?broker.persistent=false,useShutdownHook=false
      in-memory: true
      non-blocking-redelivery: true
      #packages:
        #trust-all: false
        #trusted: com.memorynotfound
      pool:
        block-if-full: true
        block-if-full-timeout: -1
        create-connection-on-startup: true
        enabled: false
        expiry-timeout: 0
        idle-timeout: 30000
        max-connections: 1
        maximum-active-session-per-connection: 500
        reconnect-on-exception: true
        time-between-expiration-check: -1
        use-anonymous-producers: true
        user: admin
        #password: ENC(hWJHuMyhydTqyF32neasTw==)
        password: admin

关系型数据库

将在application-test.yml中的数据库信息删除,同时在pom.xml中添加jar包依赖,这边是采用HSQL数据库
    <profile>
        <id>test</id>
        <dependencies>
            <dependency>
                <groupId>org.hsqldb</groupId>
                <artifactId>hsqldb</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
        <activation>
            <activeByDefault>false</activeByDefault>
        </activation>
    </profile>

非SPRING BOOT/SPRING的纯JDK环境可参考
https://github.com/yandex-qatools/embedded-services

https://github.com/flapdoodle-oss/de.flapdoodle.embed.mongo

https://github.com/jonyfs/spring-boot-data-embedded-mongodb/blob/master/src/main/java/br/com/jonyfs/spring/boot/data/embedded/mongodb/config/MongoConfig.java

ActiveMQ:
https://memorynotfound.com/spring-boot-embedded-activemq-configuration-example/

posted @ 2020-02-07 10:28 paulwong 阅读(268) | 评论 (0)编辑 收藏

配置SPRING BATCH中的JUNIT TEST

关键是JobLauncherTestUtils的配置:

    @Configuration
    public class BatchTestConfiguration {
        
        
        @Bean
        public JobLauncherTestUtils stoppedReportJobLauncherTestUtils(
                JobLauncher stoppedReportJobLauncher
        ) {
            return new JobLauncherTestUtils() {
                
                @Autowired
                public void setJobLauncher(JobLauncher stoppedReportJobLauncher) {
                    super.setJobLauncher(stoppedReportJobLauncher);
                }

                @Autowired
                public void setJob(Job stoppedReportJob) {
                    super.setJob(stoppedReportJob);
                }
                
            };
        }
    }


posted @ 2020-02-03 16:47 paulwong 阅读(129) | 评论 (0)编辑 收藏

Transform RemoteChunk to remote with json format in Spring Batch

Spring Batch Remote Chunk模式下,远程执行JOB时,传输的对象是ChunkRequest/ChunkResponse,无法转成JSON格式传输。

注意此处使用的是SPRING JACKSON,而不是JACKSON。一般是在SPRING INTEGRATIONA框架下转的。

需要自定义Transformer:

JsonToChunkRequestTransformer.java
package com.frandorado.springbatchawsintegrationslave.transformer;

import java.io.IOException;
import java.util.Collection;
import java.util.Map;
import java.util.stream.IntStream;

import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.integration.chunk.ChunkRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.aws.support.AwsHeaders;
import org.springframework.integration.json.JsonToObjectTransformer;
import org.springframework.messaging.Message;
import org.springframework.stereotype.Component;

import com.amazonaws.services.sqs.AmazonSQSAsync;
import com.fasterxml.jackson.databind.ObjectMapper;

@Component
public class JsonToChunkRequestTransformer extends JsonToObjectTransformer {
    
    private static final String MESSAGE_GROUP_ID_HEADER = "message-group-id";
    
    @Autowired
    AmazonSQSAsync amazonSQSAsync;
    
    @Override
    protected Object doTransform(Message<?> message) throws Exception {
        // ACK
        ack(message);
        
        return this.getMessageBuilderFactory().withPayload(buildChunkRequest(message)).setHeader(MESSAGE_GROUP_ID_HEADER, "unique").build();
    }
    
    private ChunkRequest buildChunkRequest(Message<?> message) throws IOException {
        Map map = new ObjectMapper().readValue(message.getPayload().toString(), Map.class);
        Map stepContributionMap = (Map) map.get("stepContribution");
        Map exitStatusMap = (Map) stepContributionMap.get("exitStatus");
        
        StepContribution stepContribution = new StepContribution(new StepExecution("null", null));
        ExitStatus exitStatus = new ExitStatus((String) exitStatusMap.get("exitCode"), (String) exitStatusMap.get("exitDescription"));
        
        IntStream.range(0, (Integer) stepContributionMap.get("readCount")).forEach(e -> stepContribution.incrementReadCount());
        stepContribution.incrementWriteCount((Integer) stepContributionMap.get("writeCount"));
        stepContribution.incrementFilterCount((Integer) stepContributionMap.get("filterCount"));
        stepContribution.incrementReadSkipCount((Integer) stepContributionMap.get("readSkipCount"));
        IntStream.range(0, (Integer) stepContributionMap.get("writeSkipCount")).forEach(e -> stepContribution.incrementWriteSkipCount());
        IntStream.range(0, (Integer) stepContributionMap.get("processSkipCount"))
                .forEach(e -> stepContribution.incrementProcessSkipCount());
        stepContribution.setExitStatus(exitStatus);
        
        return new ChunkRequest((Integer) map.get("sequence"), (Collection) map.get("items"), (Integer) map.get("jobId"), stepContribution);
    }
    
    private void ack(Message<?> message) {
        String receiptHandle = message.getHeaders().get(AwsHeaders.RECEIPT_HANDLE, String.class);
        String queue = message.getHeaders().get(AwsHeaders.QUEUE, String.class);
        String queueUrl = amazonSQSAsync.getQueueUrl(queue).getQueueUrl();
        
        amazonSQSAsync.deleteMessage(queueUrl, receiptHandle);
    }
}


JsonToChunkResponseTransformer.java
package com.frandorado.springbatchawsintegrationmaster.transformer;

import java.io.IOException;
import java.util.Map;

import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.integration.chunk.ChunkResponse;
import org.springframework.integration.json.JsonToObjectTransformer;
import org.springframework.messaging.Message;
import org.springframework.stereotype.Component;

import com.fasterxml.jackson.databind.ObjectMapper;

@Component
public class JsonToChunkResponseTransformer extends JsonToObjectTransformer {
    
    @Override
    protected Object doTransform(Message<?> message) throws Exception {
        return buildChunkResponse(message);
    }
    
    private ChunkResponse buildChunkResponse(Message<?> message) throws IOException {
        Map map = new ObjectMapper().readValue(message.getPayload().toString(), Map.class);
        
        Integer jobId = (Integer) map.get("jobId");
        Integer sequence = (Integer) map.get("sequence");
        String messageContent = (String) map.get("message");
        Boolean status = (Boolean) map.get("successful");
        
        StepContribution stepContribution = new StepContribution(new StepExecution("-", null));
        
        return new ChunkResponse(status, sequence, Long.valueOf(jobId), stepContribution, messageContent);
    }
}


还有一种方式,就是如果第三类不支持转JSON,即代码里没有JACKSON的注解,可以采用MIXIN的方式:

StepExecutionJacksonMixIn.java
package org.springframework.cloud.dataflow.rest.client.support;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

import org.springframework.batch.core.StepExecution;

/**
 * Jackson MixIn for {
@link StepExecution} de-serialization.
 *
 * 
@author Gunnar Hillert
 * 
@since 1.0
 
*/
@JsonIgnoreProperties({ "jobExecution", "jobParameters", "jobExecutionId", "skipCount", "summary" })
public abstract class StepExecutionJacksonMixIn {

    @JsonCreator
    StepExecutionJacksonMixIn(@JsonProperty("stepName") String stepName) {
    }

}

在配置文件中注册才能使用:
JacksonConfiguration.java
import java.util.Locale;
import java.util.TimeZone;

import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobInstance;
import org.springframework.batch.core.JobParameter;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.support.json.Jackson2JsonObjectMapper;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.novacredit.bmb.batchmonitor.springbatch.common.batch.jackson.ISO8601DateFormatWithMilliSeconds;
import com.novacredit.bmb.batchmonitor.springbatch.common.batch.jackson.mixin.ExecutionContextJacksonMixIn;
import com.novacredit.bmb.batchmonitor.springbatch.common.batch.jackson.mixin.ExitStatusJacksonMixIn;
import com.novacredit.bmb.batchmonitor.springbatch.common.batch.jackson.mixin.JobExecutionJacksonMixIn;
import com.novacredit.bmb.batchmonitor.springbatch.common.batch.jackson.mixin.JobInstanceJacksonMixIn;
import com.novacredit.bmb.batchmonitor.springbatch.common.batch.jackson.mixin.JobParameterJacksonMixIn;
import com.novacredit.bmb.batchmonitor.springbatch.common.batch.jackson.mixin.JobParametersJacksonMixIn;
import com.novacredit.bmb.batchmonitor.springbatch.common.batch.jackson.mixin.StepExecutionJacksonMixIn;

@Configuration
public class JacksonConfiguration {

    @Bean
    public Jackson2JsonObjectMapper jackson2JsonObjectMapper(ObjectMapper objectMapper) {
        return new Jackson2JsonObjectMapper(objectMapper);
    }
    
    @Bean
    public Jackson2ObjectMapperBuilderCustomizer dataflowObjectMapperBuilderCustomizer() {
        return (builder) -> {
            builder.dateFormat(new ISO8601DateFormatWithMilliSeconds(TimeZone.getDefault(), Locale.getDefault(), true));
            // apply SCDF Batch Mixins to
            
// ignore the JobExecution in StepExecution to prevent infinite loop.
            
// https://github.com/spring-projects/spring-hateoas/issues/333
            builder.mixIn(StepExecution.class, StepExecutionJacksonMixIn.class);
            builder.mixIn(ExecutionContext.class, ExecutionContextJacksonMixIn.class);
            builder.mixIn(JobExecution.class, JobExecutionJacksonMixIn.class);
            builder.mixIn(JobParameters.class, JobParametersJacksonMixIn.class);
            builder.mixIn(JobParameter.class, JobParameterJacksonMixIn.class);
            builder.mixIn(JobInstance.class, JobInstanceJacksonMixIn.class);
//            builder.mixIn(StepExecutionHistory.class, StepExecutionHistoryJacksonMixIn.class);
            builder.mixIn(ExecutionContext.class, ExecutionContextJacksonMixIn.class);
            builder.mixIn(ExitStatus.class, ExitStatusJacksonMixIn.class);
//            objectMapper.setDateFormat(new ISO8601DateFormatWithMilliSeconds());
            builder.modules(new JavaTimeModule(), new Jdk8Module());
        };
    }
}

    @Bean
    public IntegrationFlow flow4Contribution(
            ConnectionFactory connectionFactory, 
            JobProperties jobProperties,
            Jackson2JsonObjectMapper jackson2JsonObjectMapper
    ) {
        return IntegrationFlows
                    .from(request4ContributionMaster())
                    .enrichHeaders(headerEnricherConfigurer())
                    .transform(Transformers.toJson(jackson2JsonObjectMapper))
                    .handle(jmsOutboundGateway4Contribution(connectionFactory, jobProperties))
                    .transform(Transformers.fromJson(StepExecution.class, jackson2JsonObjectMapper))
                    .channel(replies4ContributionMaster(null))
                    .get();
    }


https://github.com/spring-cloud/spring-cloud-dataflow/tree/master/spring-cloud-dataflow-rest-client/src/main/java/org/springframework/cloud/dataflow/rest/client/support

https://frandorado.github.io/spring/2019/07/29/spring-batch-aws-series-introduction.html

https://github.com/frandorado/spring-projects/tree/master/spring-batch-aws-integration/spring-batch-aws-integration-master/src/main/java/com/frandorado/springbatchawsintegrationmaster/transformer


https://github.com/frandorado/spring-projects/tree/master/spring-batch-aws-integration/spring-batch-aws-integration-slave/src/main/java/com/frandorado/springbatchawsintegrationslave/transformer

posted @ 2020-01-21 16:44 paulwong 阅读(233) | 评论 (0)编辑 收藏

如何加CRON JOB到LINUX


https://linuxtools-rst.readthedocs.io/zh_CN/latest/tool/crontab.html

posted @ 2020-01-21 15:50 paulwong 阅读(85) | 评论 (0)编辑 收藏

Linux and Unix du command tutorial with examples

https://shapeshed.com/unix-du/

查询>100M的文件,倒序排列,文件大小以K/M/G等显示:
-h: 以可读方式显示,-t 排除小于100m的文件,sort排序,less: 分页
du -ah -t 100m / | sort -n -r | less

posted @ 2020-01-15 17:17 paulwong 阅读(88) | 评论 (0)编辑 收藏

SPRING INTEGRATION ERROR HANDLING

https://github.com/zakyalvan/spring-integration-java-dsl-learn

package com.jwebs.learn.errorhandling;

import java.util.Random;

import javax.jms.ConnectionFactory;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.integration.annotation.IntegrationComponentScan;
import org.springframework.integration.channel.PublishSubscribeChannel;
import org.springframework.integration.core.MessageSource;
import org.springframework.integration.dsl.IntegrationFlow;
import org.springframework.integration.dsl.IntegrationFlows;
import org.springframework.integration.dsl.core.Pollers;
import org.springframework.integration.dsl.jms.Jms;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.MessagingException;

/**
 * Show how to handle error in spring integration flow.
 * Please note, errorChannel in spring integration only applicable to
 * error thrown in asynch component.
 * 
 * 
@author zakyalvan
 
*/
@SpringBootApplication
@IntegrationComponentScan
public class ErrorHandlingApplication {
    public static void main(String[] args) throws Exception {
        ConfigurableApplicationContext applicationContext = new SpringApplicationBuilder(ErrorHandlingApplication.class)
                .web(false)
                .run(args);
        
        Runtime.getRuntime().addShutdownHook(new Thread(() -> applicationContext.close()));
        
        System.out.println("Pres enter key to exit");
        System.in.read();
        System.exit(0);
    }

    @Autowired
    private ConnectionFactory connectionFactory;
    
    @Bean
    public MessageSource<Integer> randomIntegerMessageSource() {
        return () -> MessageBuilder.withPayload(new Random().nextInt()).build();
    }
    
    @Bean
    public IntegrationFlow withErrorFlow() {
        return IntegrationFlows.from(randomIntegerMessageSource(), spec -> spec.poller(Pollers.fixedDelay(1000)))
                    .handle(Jms.outboundGateway(connectionFactory)
                    .requestDestination("processor.input")
                    .replyContainer(spec -> spec.sessionTransacted(true)))
                    .get();
    }
    
    @Autowired
    @Qualifier("errorChannel")
    private PublishSubscribeChannel errorChannel;
    
    @Bean
    public IntegrationFlow errorHandlingFlow() {
        return IntegrationFlows.from(errorChannel)
                .handle(message -> System.out.println("@@@@@@@@@@@@@@@@@@@@@" + ((MessagingException) message.getPayload()).getFailedMessage().getPayload()))
                .get();
    }
}

posted @ 2020-01-10 15:32 paulwong 阅读(94) | 评论 (0)编辑 收藏

APM资源

Spring Boot 2 实战:使用 Spring Boot Admin 监控你的应用
https://my.oschina.net/10000000000/blog/3119697

监控管理之Spring Boot Admin使用
https://my.oschina.net/xiedeshou/blog/2051625


https://my.oschina.net/janlle/blog/3040749

posted @ 2019-12-30 16:50 paulwong 阅读(77) | 评论 (0)编辑 收藏

微服务-网关

微服务开源最强网关Kong
https://felord.cn/kong-api-gateway.html

云原生网关 Kong 和Kong 管理UI Konga的完全安装攻略
https://felord.cn/kong-and-konga-install.html




posted @ 2019-12-30 16:44 paulwong 阅读(84) | 评论 (0)编辑 收藏

2020 年软件开发趋势预测有哪些?

基础设施:条条道路通云端

对于云厂商来说,2019 年是硕果累累的一年。不仅初创公司在使用云计算,那些很注重安全的“保守派”公司(如政府机构、医疗保健机构、银行、保险公司,甚至是美国五角大楼)也在迁移到云端。这种趋势在 2020 年将会继续,大大小小的公司都将(或者至少有计划)迁移到云端。Gartner 公司最近发布了一个数字:

image.png

如果你是一个还在考虑要不要迁移到云端的决策者,不妨重新审视一下你的策略。如果你是一个独立开发者,并且还没使用过云基础设施,那么完全可以在 2020 年尝试一下。很多大型的云厂商(如亚马逊、微软、谷歌)都提供了免费的体验机会。谷歌在这方面做得特别大方,它提供了价值 300 美元的一年免费服务。

image.png

策划注:阿里、腾讯、华为等国内云厂商同样有免费云服务试用产品。

云平台:亚马逊领头,其他跟上

作为第一大云厂商,亚马逊在 2019 年可谓风生水起。凭借其丰富的产品组合,亚马逊将把它的优势延续到 2020 年。Canalys 发布的 2019 年第三季度报告指出,大型云厂商(AWS、Azure、GCP)占据 56% 的市场份额,其中 AWS 独享 32.6%。

image.png

其他云厂商也在努力缩短与 AWS 之间的差距。微软把主要目标转向了大型企业。最近,微软打败了亚马逊,从美国五角大楼拿到了一个 100 亿美元的大单子。这个单子将提升 Azure 的声誉,同时削弱 AWS 的士气。

image.png

谷歌一直在推动 CNCF,实现云计算运维的标准化。谷歌的长期目标是让云迁移变得更容易,方便企业从 AWS 迁移到 GCP。IBM 之前斥资 360 亿美元收购了 RedHat,也想要在云计算市场占有一席之地。

image.png

在亚太地区,阿里云市场规模超过了 AWS、Azure 的总和,全球排名第三。中国国内腾讯云等企业的增长势头也十分迅猛。

2020 年将出现更多的并购。当然,很多初创公司将会带来新的想法和创新,例如多云服务。因为竞争激烈,这些公司只能从降价和推出更多的创新产品来获取利润。

容器化:Kubernetes 将会更酷

在容器编排领域,虽然一度出现了“三足鼎立”(Kubernetes、Docker Swarm 和 Mesos),但 Kubernetes 最终脱颖而出,成为绝对的赢家。云是一个分布式系统,而 Kubernetes 是它的 OS(分布式的 Linux)。2019 年北美 KubeCon+CloudNativeCon 大会的参会者达到了 12000 名,比 2018 年增长了 50%。以下是过去 4 年参会人数的增长情况。

image.png

在 2020 年,Kubernetes 不仅不会后退,只会变得越来越强,你完全可以把赌注压在 Kubernetes 身上。另外值得一提的是,Migrantis 最近收购了 Docker Enterprise,不过收购数额不详。

image.png

几年前,人们张口闭口说的都是 Docker,而现在换成了 Kubernetes。Docker 在它的全盛时期未能盈利,反而在优势渐退几年之后才尝试变现。这再次说明,在现代技术世界,时机就是一切。

软件架构:微服务将成为主流

谷歌趋势表明,微服务架构范式在 2019 年持续增长了一整年。

image.png

随着软件行业整体逐步迁移到云端,微服务也将成为占主导地位的架构范式。微服务架构崛起的一个主要原因是它与云原生完美契合,可以实现快速的软件开发。我在之前的一篇博文中解释了微服务架构的基本原则及其优势和劣势。

https://towardsdatascience.com/microservice-architecture-a-brief-overview-and-why-you-should-use-it-in-your-next-project-a17b6e19adfd

我假设现在也存在一种回归到单体架构的趋势,因为在很多情况下,微服务架构有点过头了,而且做好微服务架构设计其实很难。微服务架构有哪些好的实践?在之前的另一篇博文中,我也给出了一些大概,希望对读者有用。

https://towardsdatascience.com/effective-microservices-10-best-practices-c6e4ba0c6ee2

编程语言(整体):Python 将吞噬世界

机器学习、数据分析、数据处理、Web 开发、企业软件开发,甚至是拼接黑洞照片,Python 的影子无处不在。

在著名的编程语言排行榜网站 TIOBE 上,Python 位居最流行编程语言第三位,仅次于 Java 和 C 语言。

image.png

更有意思的是,在 2019 年,Python 的流行度翻了一番(从 5% 到 10%)。

Python 的崛起将在 2020 年延续,并缩短与 Java 和 C 语言之间的差距。另一门无所不在的编程语言 JavaScript 正面临下行的风险。为什么 Python 的势头会如此强劲?因为它的入手门槛低,有一个优秀的社区在支持,并受到数据科学家和新生代开发者的喜爱。

编程语言(企业方面):Java 将占主导

之前的 TIOBE 网站截图显示,Java 仍然是一门占主导地位的编程语言,并将在 2020 年继续保持这种地位。JVM 是 Java 的基石,其他编程语言(如 Kotlin、Scala、Clojure、Groovy)也将 JVM 作为运行时。最近,Oracle 修改了 JVM 的许可协议。

image.png

新的许可协议意味着使用 Java、Kotlin、Scala 或其他 JVM 编程语言的公司需要向 Oracle 支付大额费用。所幸的是,OpenJDK 让 JVM 继续免费。另外,还有其他一些公司为 JVM 提供企业支持。

image.png

因为体积和速度方面的问题,基于 JVM 的编程语言并不适合用在今天的无服务器环境中。Oracle 正在推动 GraalVM 计划,旨在让 Java 变得更加敏捷和快速,让它更适合用在无服务器环境中。因为除了 Java,没有其他编程语言可以提供企业级的稳定性和可靠性,所以 Java 将在 2020 年继续占主导地位。

企业版 Java:Spring 继续发力

曾几何时,在企业开发领域,Spring 和 JavaEE 之间存在着白热化的竞争。但因为 Oracle 在 JavaEE 方面没有作为,在竞争中惨败,这导致了“MicroProfile”计划的形成,并最终促成了 JakartaEE。

虽然所有的政策和活动都是围绕 JavaEE 展开,但 Spring 事实上已经赢得了这场企业 JVM 之争。2020 年,Spring 将成为 JVM 生态系统的头牌。

有两个正在进展中的项目,它们旨在减小 Java 的体积,让它更适合用在无服务器环境中。

其中一个是 Micronaut(https://micronaut.io/)。

image.png

另一个是 Quarkus(https://quarkus.io/)。

image.png

这两个项目都使用了 GraalVM,它们在 2020 年将会得到 Java 社区更多的关注。

编程语言:后起之秀的突破

2000 年代,编程语言的发展出现了停滞。大多数人认为没有必要再去开发新的编程语言,Java、C 语言、C++、JavaScript 和 Python 已经可以满足所有的需求。但是,谷歌的 Go 语言为新编程语言大门打开了一扇大门。在过去十年出现了很多有趣的编程语言,比如 Rust、Swift、Kotlin、TypeScript。导致这种情况的一个主要原因是已有的编程语言无法充分利用硬件优势(例如多核、更快的网络、云)。另一个原因是现代编程语言更加关注开发者经济,即实现更快速更容易的开发。在 Stackoverflow 提供的一份开发者报告中,排名靠前的现代编程语言如下所示(Rust 连续 4 年名列第一)。

image.png

在之前的一篇博文中,我深入探讨了现代编程语言,对比 Rust 和 Go 语言,并说明了为什么现在是采用这些语言的好时机。

https://towardsdatascience.com/back-to-the-metal-top-3-programming-language-to-develop-big-data-frameworks-in-2019-69a44a36a842

最近,微软宣布他们在探索使用 Rust 来开发更安全的软件。

image.png

亚马逊最近也宣布要赞助 Rust。

image.png

谷歌宣布将 Kotlin 作为 Android 官方开发语言,所以,在 JVM 领域,Kotlin 成了 Java 的主要竞争对手。

image.png

Angular 使用 TypeScript 代替 JavaScript,将其作为主要的编程语言,其他 JavaScript 框架(如 React 和 Vue)也开始为 TypeScript 提供更多的支持。

这种趋势将在 2020 年延续下去,很多巨头公司将会深入了解新一代编程语言(如 Rust、Swift、TypeScript、Kotlin),它们会站出来公开表示支持。

Web:JavaScript 继续占主导地位

曾几何时,JavaScript 并不被认为是一门强大的编程语言。在当时,前端内容主要通过后端框架在服务器端进行渲染。2014 年,AngularJS 的出现改变了这种局面。从那个时候开始,更多的 JavaScript 框架开始涌现(Angular 2+、React、Vue、Meteor),JavaScript 已然成为主流的 Web 开发语言。随着 JavaScript 框架不断创新以及微服务架构的崛起,JavaScript 框架在 2020 年将继续主导前端开发。

JavaScript 框架:React 闪耀

虽然 React 是在 AngularJS 之后出现的,但在过去十年对 Web 开发产生了巨大的影响,这也让 Facebook 在与 Google+ 的竞争中打了一场胜战。React 为前端开发带来了一些新的想法,比如事件溯源、虚拟 DOM、单向数据绑定、基于组件的开发,等等。它对开发者社区产生了重大影响,以至于谷歌放弃了 AngularJS,并借鉴 React 的想法推出了彻底重写的 Angular 2+。React 是目前为止最为流行的 JavaScript 框架,下图显示了相关的 NPM 下载统计信息。

image.png

为了获得更好的并发和用户体验,Facebook 宣布完全重写 React 的核心算法,推出了 React-Fiber 项目。

image.png

2020 年,React 仍然是你开发新项目的首选 Web 框架。其他框架(如 Angular/Angular 2+ 或 Vue)呢?Angular 仍然是一个不错的 Web 开发框架,特别适合企业开发。我敢肯定谷歌在未来几年会在 Angular 上加大投入。Vue 是另一个非常流行的 Web 框架,由中国的巨头公司阿里巴巴提供支持。如果你已经在使用 Angular 或 Vue,就没必要再迁移到 React 了。

App 开发:原生应用

在移动 App 开发方面,有关混合应用开发的炒作有所消停。混合开发提供了更快的开发速度,因为只需要一个开发团队,而不是多个。但原生应用提供了更好的用户体验和性能。另外,混合应用需要经过调整才能使用一些高级特性。对于企业来说,原生应用仍然是首选的解决方案,这种趋势将在 2020 年延续。Airbnb 在一篇博文中非常详细地说明了为什么他们要放弃混合应用开发平台 React Native。

https://medium.com/airbnb-engineering/sunsetting-react-native-1868ba28e30a

尽管 Facebook 尝试改进 React Native,谷歌也非常努力地推动混合 App 开发平台 Flutter,但它们仍然只适合用于原型、POC、MVP 或轻量级应用的开发。所以,原生应用在 2020 年仍将继续占主导地位。

在原生应用开发方面,谷歌和苹果分别将 Kotlin 和 Swift 作为各自平台主要的编程语言。谷歌最近再次重申了对 Kotlin 的支持,这对于 Kotlin 用户来说无疑是个好消息。

image.png

混合应用开发:React Native

在很多情况下,混合应用是个不错的选择。在这方面也有很多选择:Xamarin、Inoic、React Native 和 Flutter。Facebook 基于成熟的 React 框架推出了 React Native。就像 React 在 Web 框架领域占据主导地位一样,React Native 在混合应用领域也占据着主导地位,如下图所示。

image.png

React Native 和 React 有共同的基因,都提供了高度的代码重用性以及“一次开发,到处运行”的能力。React Native 的另一个优势是 Facebook 本身也用它来开发移动应用。谷歌在这个领域起步较晚,但在去年,谷歌的混合应用开发框架 Flutter 获得了不少关注。Flutter 提供了更好的性能,但需要使用另一门不是那么流行的编程语言 Dart。React Native 在 2020 年将继续占主导地位。

API:REST 将占主导地位

REST 是 API 领域事实上的标准,被广泛用在基于 API 的服务间通信上。当然,除了 REST,我们还有其他选择,比如来自谷歌的 gRPC 和来自 Facebook 的 GraphQL。

它们提供了不同的能力。谷歌开发的 gRPC 作为远程过程调用(如 SOAP)的化身,使用 Protobuf 代替 JSON 作为消息格式。Facebook 开发的 GraphQL 作为一个集成层,避免频繁的 REST 调用。gRPC 和 GraphQL 都在各自的领域取得了成功。2020 年,REST 仍然是占主导地位的 API 技术,而 GraphQL 和 gRPC 将作为补充技术。

人工智能:Tensorflow 2.0 将占主导地位

谷歌和 Facebook 也是深度学习 / 神经网络领域的主要玩家。谷歌基于深度学习框架 Theano 推出了 TensorFlow,它很快就成为深度学习 / 神经网络的主要开发库。谷歌还推出了特别设计的 GPU(TPU)来加速 TensorFlow 的计算。

Facebook 在深度学习领域也不甘落后,他们拥有世界上最大的图像和视频数据集合。Facebook 基于另一个深度学习库 Torch 推出了深度学习库 PyTorch。TensorFlow 和 PyTorch 之间有一些区别,前者使用的是静态图进行计算,而 PyTorch 使用的是动态图。使用动态图的好处是可以在运行时纠正自己。另外,PyTorch 对 Python 支持更好,而 Python 是数据科学领域的一门主要编程语言。

随着 PyTorch 变得越来越流行,谷歌也赶紧在 2019 年 10 月推出了 TensorFlow 2.0,也使用了动态图,对 Python 的支持也更好。

image.png

2020 年,TensorFlow 2.0 和 PyTorch 将齐头并进。考虑到 TensorFlow 拥有更大的社区,我估计 TensorFlow 2.0 将成为占主导地位的深度学习库。

数据库:SQL是王者,分布式SQL是王后

在炒作 NoSQL 的日子里,人们嘲笑 SQL,还指出了 SQL 的种种不足。有很多文章说 NoSQL 有多么的好,并将要取代 SQL。但等到炒作的潮水褪去,人们很快就意识到,我们的世界不能没有 SQL。以下是最流行的数据库的排名。

image.png

可以看到,SQL 数据库占据了前四名。SQL 之所以占主导地位,是因为它提供了 ACID 事务保证,而 ACID 是业务系统最潜在的需求。NoSQL 数据库提供了横向伸缩能力,但代价是不提供 ACID 保证。

互联网公司一直在寻找“大师级数据库”,也就是既能提供 ACID 保证又能像 NoSQL 那样可横向伸缩的数据库。目前有两个解决方案可以部分满足对“大师级数据库”的要求,一个是亚马逊的 Aurora,一个是谷歌的 Spanner。Aurora 提供了几乎所有的 SQL 功能,但不支持横向写伸缩,而 Spanner 提供了横向写伸缩能力,但对 SQL 支持得不好。

2020 年,但愿这两个数据库能够越走越近,或者有人会带来一个“分布式 SQL”数据库。如果真有人做到了,那一定要给他颁发图灵奖。

数据湖:MinIO 将要崛起

现代数据平台非常的复杂。企业一般都会有支持 ACID 事务的 OLTP 数据库(SQL),也会有用于数据分析的 OLAP 数据库(NoSQL)。除此之外,它们还有其他各种数据存储系统,比如用于搜索的 Solr、ElasticSearch,用于计算的 Spark。企业基于数据库构建自己的数据平台,将 OLTP 数据库的数据拷贝到数据湖中。各种类型的数据应用程序(比如 OLAP、搜索)将数据湖作为它们的事实来源。

HDFS 原本是事实上的数据湖,直到亚马逊推出了对象存储 S3。S3 可伸缩,价格便宜,很快就成为很多公司事实上的数据湖。使用 S3 唯一的问题是数据平台被紧紧地绑定在亚马逊的 AWS 云平台上。虽然微软 Azure 推出了 Blob Storage,谷歌也有类似的对象存储,但都不是 S3 的对手。

对于很多公司来说,MinIO 或许是它们的救星。MinIO 是一个开源的对象存储,与 S3 兼容,提供了企业级的支持,并专门为云原生环境而构建,提供了与云无关的数据湖。

image.png

微软在 Azure Marketplace 是这么描述 MinIO 的:“为 Azure Blog Storage 服务提供与亚马逊 S3 API 兼容的数据访问”。如果谷歌 GCP 和其他云厂商也提供 MinIO,那么我们将会向多云迈出一大步。

大数据批处理:Spark 将继续闪耀

现如今,企业通常需要基于大规模数据执行计算,所以需要分布式的批处理作业。Hadoop 的 Map-Reduce 是第一个分布式批处理平台,后来 Spark 取代了 Hadoop 的地位,成为真正的批处理之王。Spark 是怎样提供了比 Hadoop 更好的性能的?我之前写了另一篇文章,对现代数据平台进行了深入分析。

https://towardsdatascience.com/programming-language-that-rules-the-data-intensive-big-data-fast-data-frameworks-6cd7d5f754b0

Spark 解决了 Hadoop Map-Reduce 的痛点,它将所有东西放在内存中,而不是在完成每一个昂贵的操作之后把数据保存在存储系统中。尽管 Spark 重度使用 CPU 和 JVM 来执行批处理作业,但这并不妨碍它成为 2020 年批处理框架之王。我希望有人能够使用 Rust 开发出一个更加高效的批处理框架,取代 Spark,并为企业省下大量的云资源费用。

大数据流式处理:Flink 是未来

几年前,实现实时的流式处理几乎是不可能的事情。一些微批次处理框架(比如 Spark Streaming)可以提供“几近”实时的流式处理能力。不过,Flink 改变了这一状况,它提供了实时的流式处理能力。

2019 年之前,Flink 未能得到足够的关注,因为它无法撼动 Spark。直到 2019 年 1 月份,中国巨头公司阿里巴巴收购了 Data Artisan(Flink 背后的公司)。

image.png

在 2020 年,企业如果想要进行实时流式处理,Flink 应该是不二之选。不过,跟 Spark 一样,Flink 同样重度依赖 CPU 和 JVM,并且需要使用大量的云资源。

字节码:WebAssembly将被广泛采用

我从 JavaScript 作者 Brandon Eich 的一次访谈中知道了 WebAssembly 这个东西。现代 JavaScript(ES5 之后的版本)是一门优秀的编程语言,但与其他编程语言一样,都有自己的局限性。最大的局限性是 JavaScript 引擎在执行 JavaScript 时需要读取、解析和处理“抽象语法树”。另一个问题是 JavaScript 的单线程模型无法充分利用现代硬件(如多核 CPU 或 GPU)。正因为这些原因,很多计算密集型的应用程序(如游戏、3D 图像)无法运行在浏览器中。

一些公司(由 Mozilla 带领)开发了 WebAssembly,一种底层字节码格式,让任何一门编程语言都可以在浏览器中运行。目前发布的 WebAssembly 版本可以支持 C++、Rust 等。

image.png

WebAssembly 让计算密集型应用程序(比如游戏和 AutoCAD)可以在浏览器中运行。不过,WebAssembly 的目标不仅限于此,它还要让应用程序可以在浏览器之外运行。WebAssembly 可以被用在以下这些“浏览器外”的场景中。

  • 移动设备上的混合原生应用。
  • 没有冷启动问题的无服务器计算。
  • 在服务器端执行不受信任的代码。
    我预测,2020 年将是 WebAssembly 取得突破的一年,很多巨头公司(包括云厂商)和社区将会拥抱 WebAssembly。

代码:低代码 / 无代码将更进一步

快速的数字化和工业 4.0 革命意味着软件开发者的供需缺口巨大。由于缺乏开发人员,很多企业无法实现它们的想法。为了降低进入软件开发的门槛,可以尝试无代码(No Code)或低代码(Low Code)软件开发,也就是所谓的 LCNC(Low-Code No-Code)。它已经在 2019 年取得了一些成功。

image.png

LCNC 的目标是让没有编程经验的人也能开发软件,只要他们想要实现自己的想法。

虽然我对在正式环境中使用 LCNC 框架仍然心存疑虑,但它为其他公司奠定了良好的基础,像亚马逊和谷歌这样的公司可以基于这个基础构建出有用的产品,就像 AWS Lambda 的蓬勃发展是以谷歌 App Engine 为基础。

2020 年,LCNC 将会获得更多关注。

posted @ 2019-12-30 09:09 paulwong 阅读(128) | 评论 (0)编辑 收藏

How to provide a Spring Boot "fat JAR" with external dependencies

发现loader.path放在properties文件中时,不起作用,要以-Dloader.path=lib/的方式才行。

http://www.codevomit.xyz/bootlog/blog/how-to-provide-spring-boot-fat-jar


https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-executable-jar-format.html

posted @ 2019-12-27 15:47 paulwong 阅读(81) | 评论 (0)编辑 收藏

解决“/bin/bash^M: bad interpreter: No such file or directory”

在执行shell脚本时提示这样的错误主要是由于shell脚本文件是dos格式,即每一行结尾以\r\n来标识,而unix格式的文件行尾则以\n来标识。
查看脚本文件是dos格式还是unix格式的几种办法。
(1)cat -A filename 从显示结果可以判断,dos格式的文件行尾为^M$,unix格式的文件行尾为$。
(2)od -t x1 filename 如果看到输出内容中存在0d 0a的字符,那么文件是dos格式,如果只有0a,则是unix格式。
(3)vi filename打开文件,执行 : set ff,如果文件为dos格式在显示为fileformat=dos,如果是unxi则显示为fileformat=unix。

解决方法:
(1)使用linux命令dos2unix filename,直接把文件转换为unix格式
(2)使用sed命令sed -i -e 's/\r$//' filename 或者 sed -i "s/^M//" filename直接替换结尾符为unix格式
(3)vi filename打开文件,执行 : set ff=unix 设置文件为unix,然后执行:wq,保存成unix格式。

posted @ 2019-12-11 13:42 paulwong 阅读(101) | 评论 (0)编辑 收藏

防止在SPRING BOOT的配置文件中使用明文存储密码

Spring Boot how to hide passwords in properties file
https://stackoverflow.com/questions/37404703/spring-boot-how-to-hide-passwords-in-properties-file

工作随笔——jasypt-spring-boot使用
https://www.cnblogs.com/zz0412/p/jasypt-001.html

Get史上最优雅加密方式!没有之一!
https://www.jianshu.com/p/64ceda636e81

使用Jasypt对SpringBoot配置文件加密
https://www.jianshu.com/p/323ec96c46d2


posted @ 2019-11-26 15:13 paulwong 阅读(187) | 评论 (0)编辑 收藏

Git版本控制与工作流



https://www.jianshu.com/p/67afe711c731

posted @ 2019-11-26 11:21 paulwong 阅读(100) | 评论 (0)编辑 收藏

GIT TAG

GIT中的tag 相当于是一个快照,是不能更改它的代码的。
如果要在 tag 代码的基础上做修改,你需要一个分支: 

通常TAG与软件版本相对应,即TAG名称用软件版本号来表示。

软件版本的格式规范:
版本格式:主版本号.次版本号.修订号,版本号递增规则如下:

主版本号:当你做了不兼容的 API 修改,
次版本号:当你做了向下兼容的功能性新增,
修订号:当你做了向下兼容的问题修正。

先行版本号及版本编译信息可以加到“主版本号.次版本号.修订号”的后面,作为延伸。

Git打标签与版本控制规范
https://juejin.im/post/5b0531c6f265da0b7f44eb8c

git切换到某个tag
https://blog.csdn.net/DinnerHowe/article/details/79082769

git cherry-pick 把提交到A分支的部分commit 再提交到B分支上
http://yijiebuyi.com/blog/0e65f4a59a1cfa05c5b30ccb6c2f413d.html

posted @ 2019-11-26 10:57 paulwong 阅读(72) | 评论 (0)编辑 收藏

MAVEN打多环境包

通常配置文件是放在src/main/resources下,build完之后会放在classes文件夹下,最终会打进jar包中。

如果是SPRING BOOT工程,部署时,要求配置文件不打进jar包中,要有sh启动命令文件,最终产生一个ZIP包,包含所有需要的东西。这时就需要善用MAVEN的resource插件、assembly插件和jar插件了。

resource插件能重定义配置文件在output时的文件夹路径,用profile的变量值替换配置文件中的占位符。
但要更改点位符的默认定义:
<properties>
        <resource.delimiter>${}</resource.delimiter>
    </properties>


jar插件能排除生成在classes文件夹中不要的文件被打进jar包中。

assembly插件能定义ZIP包中需要包含哪些文件。

<resources>  
        <resource>  
            <directory>src/main/bin</directory>
            <!--表明此文件夹中如有占位符,则会取pom中的profile中的值代替-->
            <filtering>true</filtering>
            <includes>  
                <include>*.sh</include>
            </includes>
        </resource>  
</resources>  

<plugin>  
        <artifactId>maven-jar-plugin</artifactId>  
        <configuration>
            <!--bin/文件夹不会被打进jar包-->
            <excludes>  
                <exclude>bin/</exclude>
            </excludes>
        </configuration>  
</plugin>  

<fileSet>  
        <!--定义bin/文件夹被打进zip包-->
        <directory>${build.outputDirectory}/bin</directory>  
        <outputDirectory>bin</outputDirectory>
        <fileMode>0755</fileMode>  
</fileSet>  


maven打包加时间戳
https://blog.csdn.net/z410970953/article/details/50680603

posted @ 2019-11-21 16:40 paulwong 阅读(314) | 评论 (0)编辑 收藏

Maven打包时,环境变量替换,并解决spring-boot项目中${}无效的问题

https://www.jianshu.com/p/cf3bd9ddfe6f

posted @ 2019-11-20 16:12 paulwong 阅读(117) | 评论 (0)编辑 收藏

LOGBACK FOR SPRING

当程序中使用LOGGER.INFO("MESSAGE");要求打印日志时,LOGBACK会获取该行代码所在类的全名,和打印等级(INFO/DEBUG等),再在配置文件的<logger>中查找对应logger,使用其配置的appender组件打印日志,如无法找到对应的logger,则使用<root>对应的appender打印日志。

其中appender是用来输出日志,有file和console两个实现,console则是向控制台输出日志,而file则是向文件输出日志。
rolling file appender中,有rollingPolicy和triggerPolicy两个主要属性,rollingPolicy是确定如何处理日志文件,而triggerPolicy则是确定何时处理日志文件。

如果要使用SPRING针对LOGBACK的一些功能,如profile等,则要将logback.xml的配置文件命名为logback-spring.xml,并在SPRING中配置,logging.config= logback-spring.xml。

SPRING会将logging.file、logging.path这些配置转成系统变量LOG_FILE、LOG_PATH,可在配置文件中直接引用,如${LOG_FILE}。

如果logback配置文件要SPRING的其他属性,则要使用如下标签:
<springProperty scope="context" name="logLevel" source="log.level"/>

如果要使用LOGBACK的一些常用属性,可引入:
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml"/>
如CONSOLE APPENDER,此resource在spring-boot-version.jar中。


=========================================
看完这个不会配置 logback ,请你吃瓜!
https://juejin.im/post/5b51f85c5188251af91a7525

logback解析——Appender
https://juejin.im/post/5a39c91cf265da4327185d10

SpringBoot中logback.xml使用application.yml中属性
https://www.cnblogs.com/jianliang-Wu/p/8945343.html

springboot使用logback-spring.xml配置讲解
https://blog.csdn.net/heguiliang_123/article/details/80296745

Logback配置
https://www.cnblogs.com/cjsblog/p/9113131.html

Logback中如何自定义灵活的日志过滤规则
https://www.jianshu.com/p/d6360c517264

Spring Boot中的日志
http://loveshisong.cn/%E7%BC%96%E7%A8%8B%E6%8A%80%E6%9C%AF/2016-11-03-Spring-Boot%E4%B8%AD%E7%9A%84%E6%97%A5%E5%BF%97.html

Spring Boot与logback总结
https://blog.csdn.net/u014527058/article/details/79667458

SpringBoot Logback 配置参数迁移到配置中心 Apollo
https://blog.csdn.net/shuaizai88/article/details/83027262

posted @ 2019-11-19 15:14 paulwong 阅读(112) | 评论 (0)编辑 收藏

MONGODB安装


https://www.jianshu.com/p/d3b31b7aa182 

后续初始化用户及数据库
http://www.qianduan8.com/1786.html
https://zocada.com/setting-mongodb-users-beginners-guide/

如果要以认证的方式登录,需加以下内容至/etc/mongod.conf
security:
  authorization: enabled

如果用GUI连接数据库时,不显示数据库列表,要加权限:listDatabases
https://stackoverflow.com/questions/19458524/mongodb-show-dbs-and-show-log-without-clusteradmin-role

posted @ 2019-11-15 17:30 paulwong 阅读(103) | 评论 (0)编辑 收藏

Setting up ActiveMQ for HA-Load Balance


In a typical enterprise applications, we often need messaging and asynchronous processing.
To satisfy this need, we need a reliable as well as scalable messaging infrastructure. In currently available messaging infrastructures Apache ActiveMQ stands out in terms of features and simplicity.

Apache ActiveMQ comes with lot of features in built and also provides a way to configure or tweak as per the needs of an application.

In this post , we will explore how to enable network of activeMQ brokers so that we achieve HA(High Availability) as well as load balance between consumers & producers.

I carried out  my experiment on local machine with ACtiveMQ 5.8.0, but this can be easily upgraded to latest versions of ActiveMQ viz. 5.10.0

To have network of brokers, we need multiple brokers. So, I changed tcp and admin ports of brokers so that I can run multiple brokers on single machine.

To get brief background on network of broker, please visit this link

In this post we will setup below topology, we will mix failover and NOB to get work done,

1. Producer1 is configured to send messages to broker3 with failover to broker2
2. Producer2 is configured to send messages to broker2 with failover to broker3
3. Broker3, Broker2 are networked with Broker1as below


4. Broker1 is connected with broker4 with NOB.
5. Make sure you enable "advisorySupport" on the broker, which is essential for transparent routing of messages across brokers.
Dry Run:
1. Producer1 sends messages to queue "input.q" on broker3, where there are no active consumers, but it see subscriptions from broker1
2. Broker1 and broker 4 are has consumers which are looking at "input.q".
3. When broker3 receives a message it forwards it to broker1, as its in networked and has active consumers for "input.q" 
4. When broker1 receives a messages on "input.q", it gets load balanced between broker1 and broker4  as both has consumers looking for "input.q".
5. Whenever broker3 goes down, producer1 switches transparently to broker2, as its configured with failover.
6. I used prefetch size as 1, so that you can load balancing on consumers
Sample activemq configurations can be downloaded from here.

posted @ 2019-11-04 16:14 paulwong 阅读(119) | 评论 (0)编辑 收藏

ACTIVE MQ HA

组建ACTIVEMQ CLUSTER,使得其中一个ACTIVE MQ DOWN掉时,能自动切换到其他节点。

ACTIVEMQ 只有MASTER-SLAVE模式,集群中的多个节点共享消息的存储,多个节点同时启动时,竞争消息存储的锁,谁先取得,谁就是MASTER,当MASTER DOWN掉时,锁被释放,SALVE中马上又竞争锁,取得者成为MASTER。

方案:
  • 安装NFSV4
  • 修改消息存储路径
    <persistenceAdapter>
      <kahaDB directory="/sharedFileSystem/sharedBrokerData"/>
    </persistenceAdapter>
  • 客户端修改连接字符串
    failover://(tcp://master:61616,tcp://slave:61616)?randomize=false
--》


https://my.oschina.net/hzchenyh/blog/716424

https://www.iteye.com/blog/shift-alt-ctrl-2069250

https://stackoverflow.com/questions/53542928/activemq-ha-on-failover

https://activemq.apache.org/shared-file-system-master-slave

ActiveMQ(6)-基于networkConnector的Broker-Cluster方案
https://blog.csdn.net/jinjin603/article/details/78657387


Multi Data Centre Message Brokers with ActiveMQ
https://medium.com/thg-tech-blog/multi-data-centre-message-brokers-with-activemq-28495046370e

ActiveMQ中的NetworkConnector(网络连接器)详解
https://www.iteye.com/blog/manzhizhen-2116920

a

posted @ 2019-11-01 10:46 paulwong 阅读(108) | 评论 (0)编辑 收藏

ACTIVEMQ设置预取消息数目

当ACTIVEMQ的某个QUEUE有多个消费者,为避免某个消息者取了更多个消息处理,而造成其他消费者无消息可处理的情况,可以设置每个消费者不预取消息,即每个消费者消费完单个消息后,再去取消息,这样其他消费者就能平均的有消息可处理。


https://stackoverflow.com/questions/35928089/activemq-how-to-prevent-message-from-going-to-dispatched-queue


设置方法,在CONNECT STRING中设置:
tcp://localhost:61616?jms.prefetchPolicy.all=0 

tcp://localhost:61616?jms.prefetchPolicy.queuePrefetch=0 

queue = new ActiveMQQueue("TEST.QUEUE?consumer.prefetchSize=10"); 
consumer = session.createConsumer(queue);

http://activemq.apache.org/what-is-the-prefetch-limit-for.html

posted @ 2019-10-31 11:28 paulwong 阅读(211) | 评论 (0)编辑 收藏

EIP in SPRING INTEGRATION

  • idempotent receiver
幂等型,同一个MESSAGE,如MESSAGE ID都一样,在MESSAGING系统中不管运行多少次,结果都一样,为啥?因为重复的MESSAGE,都被忽略了。
方案:
消息被处理后,从消息中取出ID,放入META-DATA-STORE中,后续处理消息时,要从META-DATA-STORE中检查是否有值。

下面这个方案,ID的存储和判断是否重复消息都在一个INTERCEPTOR中搞定。
https://stackoverflow.com/questions/50401460/spring-integration-dsl-configure-idempotent-receiver-to-identify-duplicates
https://www.javacodegeeks.com/2015/09/monitoring-and-management.html


claim-check
将MESSAGE的PAYLOAD存在STORE中,返回一个ID,这个ID即claim-check,如果需要取MESSAGE的DETAIl时,可从STORE中取出MESSAGE。
https://github.com/spring-projects/spring-integration/blob/master/src/reference/asciidoc/claim-check.adoc


posted @ 2019-10-25 11:03 paulwong 阅读(244) | 评论 (0)编辑 收藏

SPRING INTEGRATION LESSONS

Lessons

Introduction to Enterprise Application Integration

In our first lesson, you will get introduced to the concepts of Enterprise Application Integration. You will learn about the and Enterprise integration patterns that can be applied to simplify integration between different platforms and the Integration strategies that can be followed for this purpose. Finally, we will discuss how and why to implement a Message driven architecture and how to achieve both Synchronous and asynchronous communication among nodes.

Spring Integration Fundamentals

In this lesson, you will get to understand how Spring Integration works under the hood. The core concepts of Spring Integration messaging system (like message channels and endpoints) will be introduced. Additionally, the components that build the framework will be discussed, including the channel adapters, transformers, filters, routers etc. Finally, the two distinct methods of communication (synchronous and asynchronous) are explained and the lesson ends with a discussion on error handling.

Spring Integration and Web Services

In this lesson, we will focus on the integration with external web services. Spring Integration comes with the necessary functionality (adapters, channels etc.) to support web services out of the box. A full example is built from scratch in order to better understand the topic.

Enterprise Messaging

In this lesson, we will focus on integrating our application with JMS messaging. For this purpose, we will use Active MQ, which will be our broker. We will show examples of sending and receiving JMS messages by using the Spring Integration JMS channel adapters. Following these examples, we will see some ways of customizing these invocations by configuring message conversion and destination resolution.

Spring Integration Full Example

In this lesson, we will wrap everything up by providing a complete application that uses several of the components provided by Spring Integration in order to provide a service to its users. We will discuss the system architecture, the actual implementation and the relevant error handling.

Monitoring and Management

In this lesson, we will examine different mechanisms of monitoring or gathering more information about what is going on within the messaging system. Some of these mechanisms consist of managing or monitoring the application through MBeans, which are part of the JMX specification. Another mechanism discussed in this chapter is how we will implement the EIP idempotent receiver pattern using a metadata store. Finally, the last mechanism described is the control bus. This will let us send messages that will invoke operations on components in the application context.

posted @ 2019-10-25 09:45 paulwong 阅读(79) | 评论 (0)编辑 收藏

SPRING INTEGRATION DSL DEMO

https://github.com/spring-projects/spring-integration/tree/master/src/reference/asciidoc

posted @ 2019-10-23 11:55 paulwong 阅读(88) | 评论 (0)编辑 收藏

Spring Integration 中文手册 - GOOD

Spring Integration 中文手册 (1)


Spring Integration 中文手册 (2)

posted @ 2019-10-11 10:28 paulwong 阅读(192) | 评论 (0)编辑 收藏

LINUX配置DNS

vi /etc/resolv.conf

nameserver 8.8.8.8

posted @ 2019-10-10 10:55 paulwong 阅读(115) | 评论 (0)编辑 收藏

在SPRING INTEGRATION中手动开始和停止JMS LISTENER

如果要对JMS BROKER生产和消费MESSAGE,一种方式是用JmsTemplate发送和消费消息,另一种方式是SPRING INTEGRATION。

SPRING INTEGRATION是实现了EIP模式的一种框架,即使用CHANNEL和JMS-INBOUND-ADAPTER、JMS-OUTBOUND-ADAPTER,完全脱离了JmsTemplate的API。

如果需要实现这种场景:从BROKER取一条消息,处理消息,且处理途中不要再从BROKER再取消息,处理完后再取消息,再处理。

这样要求手动开始和停止JMS LISTENER,即手动开始和停止JMS-INBOUND-ADAPTER、JMS-OUTBOUND-ADAPTER。

@Bean
@InboundChannelAdapter(value = "loaderResponseChannel")
public MessageSource loaderResponseSource() throws Exception {
    return Jms
            .inboundAdapter(oracleConnectionFactory())
            .configureJmsTemplate(
                    t -> t.deliveryPersistent(true)
                            .jmsMessageConverter(jacksonJmsMessageConverter())
            ).destination(jmsInbound).get();
}

当使用@InboundChannelAdapter时,会自动注册一个SourcePollingChannelAdapter ,但这个名字比较长:configrationName.loaderResponseSource.inboundChannelAdapter。

呼叫这个实例的start()和stop()方法即可。

@Bean
public IntegrationFlow controlBusFlow() {
    return IntegrationFlows.from("controlBus")
              .controlBus()
              .get();
}

Message operation = MessageBuilder.withPayload("@configrationName.loaderResponseSource.inboundChannelAdapter.start()").build();
operationChannel.send(operation)

https://stackoverflow.com/questions/45632469/shutdown-spring-integration-with-jms-inboundadapter

https://docs.spring.io/spring-integration/docs/5.0.7.RELEASE/reference/html/system-management-chapter.html#control-bus

https://github.com/spring-projects/spring-integration-java-dsl/blob/master/src/test/java/org/springframework/integration/dsl/test/jms/JmsTests.java

https://stackoverflow.com/questions/50428552/how-to-stop-or-suspend-polling-after-batch-job-fail

posted @ 2019-10-09 17:16 paulwong 阅读(119) | 评论 (0)编辑 收藏

CountDownLatch、CyclicBarrier和Semaphore

CountDownLatch、CyclicBarrier和Semaphore这三个并发辅助类,可以在线程中呼叫,使得线程暂停等,但各有不同。

  • CountDownLatch
1、初始化,并传入计数器
2、向不同的线程传入CountDownLatch实例
3、如果在某一线程中呼叫await(),则此线程被挂起,直到计数器为0,才往下执行
4、如果在某一线程中呼叫countDown(),计数器减1
5、最终如果计数器值为0时,则CountDownLatch实例不再起作用了,即为一次性的

  • CyclicBarrier
1、初始化,并传入计数器值,也可传入一个Runnable类,会在计数器为0时,被执行
2、向不同的线程传入CyclicBarrier实例
3、如果在某一线程中呼叫await(),则此线程被挂起,直到计数器为0,才往下执行
4、其他线程呼叫await(),则此线程被挂起,直到计数器为0,才往下执行
5、最终如果计数器值为0时,则CyclicBarrier实例会将计数器值恢复,又可重用

  • Semaphore
1、初始化,并传入计数器值
2、向不同的线程传入Semaphore实例
3、如果在某一线程中呼叫acquire(),则Semaphore实例会将计数器值减1,如果计数器值为-1,则将计数器值置为0,此线程被挂起,直到计数器值大于1时,才往下执行
4、此线程需呼叫release(),使得计数器值+1,以便其他线程在计数器值为0时不受阻


CountDownLatch 例子:
public class Test {
     public static void main(String[] args) {   
         final CountDownLatch latch = new CountDownLatch(2);
          
         new Thread(){
             public void run() {
                 try {
                     System.out.println("子线程"+Thread.currentThread().getName()+"正在执行");
                    Thread.sleep(3000);
                    System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕");
                    latch.countDown();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
             };
         }.start();
          
         new Thread(){
             public void run() {
                 try {
                     System.out.println("子线程"+Thread.currentThread().getName()+"正在执行");
                     Thread.sleep(3000);
                     System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕");
                     latch.countDown();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
             };
         }.start();
          
         try {
             System.out.println("等待2个子线程执行完毕");
            latch.await();
            System.out.println("2个子线程已经执行完毕");
            System.out.println("继续执行主线程");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
     }
}

结果:
线程Thread-0正在执行
线程Thread-1正在执行
等待2个子线程执行完毕
线程Thread-0执行完毕
线程Thread-1执行完毕
2个子线程已经执行完毕
继续执行主线程


CyclicBarrier例子:
public class Test {
    public static void main(String[] args) {
        int N = 4;
        CyclicBarrier barrier  = new CyclicBarrier(N,new Runnable() {
            @Override
            public void run() {
                System.out.println("当前线程"+Thread.currentThread().getName());   
            }
        });
         
        for(int i=0;i<N;i++)
            new Writer(barrier).start();
    }
    static class Writer extends Thread{
        private CyclicBarrier cyclicBarrier;
        public Writer(CyclicBarrier cyclicBarrier) {
            this.cyclicBarrier = cyclicBarrier;
        }
 
        @Override
        public void run() {
            System.out.println("线程"+Thread.currentThread().getName()+"正在写入数据");
            try {
                Thread.sleep(5000);      //以睡眠来模拟写入数据操作
                System.out.println("线程"+Thread.currentThread().getName()+"写入数据完毕,等待其他线程写入完毕");
                cyclicBarrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }catch(BrokenBarrierException e){
                e.printStackTrace();
            }
            System.out.println("所有线程写入完毕,继续处理其他任务");
        }
    }
}

执行结果:
线程Thread-0正在写入数据
线程Thread-1正在写入数据
线程Thread-2正在写入数据
线程Thread-3正在写入数据
线程Thread-0写入数据完毕,等待其他线程写入完毕
线程Thread-1写入数据完毕,等待其他线程写入完毕
线程Thread-2写入数据完毕,等待其他线程写入完毕
线程Thread-3写入数据完毕,等待其他线程写入完毕
当前线程Thread-3
所有线程写入完毕,继续处理其他任务
所有线程写入完毕,继续处理其他任务
所有线程写入完毕,继续处理其他任务
所有线程写入完毕,继续处理其他任务


Semaphore例子:
public class Test {
    public static void main(String[] args) {
        int N = 8;            //工人数
        Semaphore semaphore = new Semaphore(5); //机器数目
        for(int i=0;i<N;i++)
            new Worker(i,semaphore).start();
    }
     
    static class Worker extends Thread{
        private int num;
        private Semaphore semaphore;
        public Worker(int num,Semaphore semaphore){
            this.num = num;
            this.semaphore = semaphore;
        }
         
        @Override
        public void run() {
            try {
                semaphore.acquire();
                System.out.println("工人"+this.num+"占用一个机器在生产");
                Thread.sleep(2000);
                System.out.println("工人"+this.num+"释放出机器");
                semaphore.release();           
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

执行结果:
工人0占用一个机器在生产
工人1占用一个机器在生产
工人2占用一个机器在生产
工人4占用一个机器在生产
工人5占用一个机器在生产
工人0释放出机器
工人2释放出机器
工人3占用一个机器在生产
工人7占用一个机器在生产
工人4释放出机器
工人5释放出机器
工人1释放出机器
工人6占用一个机器在生产
工人3释放出机器
工人7释放出机器
工人6释放出机器

https://www.cnblogs.com/dolphin0520/p/3920397.html

https://juejin.im/post/5aeec3ebf265da0ba76fa327

posted @ 2019-09-24 10:18 paulwong 阅读(100) | 评论 (0)编辑 收藏

使用 Jenkins 部署 Spring Boot

https://mp.weixin.qq.com/s?__biz=MzI4NDY5Mjc1Mg==&mid=2247489278&idx=2&sn=a48342d706bfd1651e277e1c24e81e3e&chksm=ebf6ce81dc81479764d1e6ff7b207257a78d52bed5ef8c2f16c76f70660d1da9609167ed7bbb&mpshare=1&scene=1&srcid=&sharer_sharetime=1568861026830&sharer_shareid=24856bf403968a883e437b859be0a9b5&pass_ticket=qB9yWQbj%2FGo7PDZNogjBwishDCx5Suu%2BvBWnS1TpKmY%3D#rd

posted @ 2019-09-19 17:44 paulwong 阅读(109) | 评论 (0)编辑 收藏

CI/CD 资源

Continuous delivery tool landscape
http://www.jamesbowman.me/post/continuous-delivery-tool-landscape/

posted @ 2019-09-18 17:08 paulwong 阅读(134) | 评论 (0)编辑 收藏

ANSIBLE + SPRING BOOT

https://github.com/remyma/ansible-springboot

frontend+backend+jenkins+ansible
https://itnext.io/ci-cd-with-jenkins-and-ansible-f41ef2b33977

posted @ 2019-09-18 16:09 paulwong 阅读(107) | 评论 (0)编辑 收藏

Windows终端工具_MobaXterm

前言

  有人喜欢小而美的工具,有人喜欢大集成工具。这里推荐一款增强型的Windows终端工具MobaXterm,它提供所有重要的远程网络工具(SSH,X11,RDP,VNC,FTP,MOSH ......)和Unix命令(bash,ls,cat,sed,grep,awk,rsync等)。使用MobaXterm工具,可以替代SSH客户端工具(xshell、putty、securecrt等)、sftp/ftp工具(winscp、filezilla)、远程桌面访问工具(RDO等)等等,可以极大降低你windows系统上的软件安装数量。

MobaXterm使用体会&优点

  1、工具获取简单、免安装(绿色版)、免费(不用到处找license)、可个性化配置(字体、前景色、背景色、语法高亮等)。

  2、可以替代xshell、putty、securecrt等SSH客户端、winscp、filezilla等ftp传输工具、替代RDO远程桌面访问工具等。减少windows系统软件安装和资源占用。我的系统已经超负荷运转

  3、可以替代CMD窗口。CMD命令行字体太丑。。并且配置还麻烦。实在不想用。

  4、支持Unix/Linux常用命令使用。满足在windows上学习操作linux命令的需求以及利用linux命令快速处理文本文件。

  5、可以支持丰富的组件,减少部分软件的安装。如Cygwin。

  工具使用建议:每个人对工具使用的要求和场景不尽相同。需要了解MobaXterm特性可以进入MobaXterm官网或者阅读MobaXterm帮助手册(启动工具--> 菜单栏"help" -> "document")。

MobaXterm主要功能

  1、远程会话管理器:单个应用程序中的SSH,SFTP,telnet,VNC,Mosh,RDP连接

  2、Windows上的许多Unix/Linux命令:基本Cygwin命令(bash,grep,awk,sed,rsync,...)。

  3、丰富的组件和插件,可以自由选择。详情查看MobaXterm Plugins

  4、远程桌面:使用RDP,VNC或XDMCP在计算机上显示完整的远程桌面

  5、嵌入式Xserver:在Windows计算机上显示远程应用程序

  6、....

MobaXterm工具下载

  MobaXterm工具分为便携版(绿色免安装版,免费)和专业版(收费)。对于大部分开发测试人员,免费的绿色免安装版本就可以满足日常工作的需求。下载路径:MobaXterm

MobaXterm使用技巧

1、执行cmd命令快速切换执行DOS指令。可以执行exit退回原界面。

2、MobaXterm界面风格、主体、字体以及相关快捷方式设置。MobaXterm --> Settings --> Configuration

3、MobaXterm取消自动断开SSH会话。

posted @ 2019-09-11 13:19 paulwong 阅读(141) | 评论 (0)编辑 收藏

nohup命令不输出OUTPUT到实体文件

https://stackoverflow.com/questions/10408816/how-do-i-use-the-nohup-command-without-getting-nohup-out

posted @ 2019-09-06 10:15 paulwong 阅读(144) | 评论 (0)编辑 收藏

MONGODB拓展操作

MongoTemplate使用Cursor处理大数量的数据
https://blog.csdn.net/ClementAD/article/details/55210973

Spring Data MongoDB系列之三:数据库批量操作
https://blog.csdn.net/sinat_24044957/article/details/80646292

Distinct in Spring Data MongoDB
https://stackoverflow.com/questions/19203724/distinct-in-spring-data-mongodb

MONGODB SQL语句
http://www.runoob.com/mongodb/mongodb-indexing.html


posted @ 2019-09-03 15:52 paulwong 阅读(110) | 评论 (0)编辑 收藏

MAVEN中SCOPE为SYSTEM的JAR包如何打包?

https://stackoverflow.com/questions/2065928/maven-2-assembly-with-dependencies-jar-under-scope-system-not-included

在pom.xml中加入REPOSITRY:
<repositories>
  <repository>
    <id>my</id>
    <url>file://${basedir}/my-repo</url>
  </repository>
</repositories>

file://${basedir}/my-repo 中放JAR包的结构要和MAVEN库.m2中保持一致。

DEPENDENCY还是按正常的来,不加SYSTEM SCOPE:
<dependency>
  <groupId>sourceforge.jchart2d</groupId>
  <artifactId>jchart2d</artifactId>
  <version>3.1.0</version>
</dependency>


这里涉及到一个问题:如何将JAR比较优雅地放到file://${basedir}/my-repo 中,可以使用deploy,如果JAR包不是由MAVEN打出来的,命令如下:
mvn deploy:deploy-file 
-DgroupId={yourProject} \ 
-DartifactId={yourProject}\
-Dfile={yourFile}\ // jar 包路径
-Durl={URL} \// 私服URL
-DrepositoryId=releases\
-Dpackaging=jar\ // 指定格式,如果不写,一句 pom 文件中
-DpomFile=pom.xml // 指定该 jar 包的 pom 文件,如不指定,将生成一个默认的 pom——导致不可用

如果JAR包是由MAVEN打出来的,命令如下:
mvn deploy:deploy-file 
-Dfile={yourFile}\ // jar 包路径
-Durl=file://${basedir}/my-repo
-DpomFile=pom.xml // 指定该 jar 包的 pom 文件,如不指定,将生成一个默认的 pom——导致不可用

MAVEN DEPLOY PLUGIN:
<plugin>
   <groupId>org.apache.maven.plugins</groupId>
   <artifactId>maven-deploy-plugin</artifactId>
   <version>2.8.2</version>
   <executions>
      <execution>
         <id>default-cli</id>
         <phase>package</phase>
         <goals>
            <goal>deploy-file</goal>
         </goals>
         <configuration>
            <file>target/COMPOSANT-A-1.0.tar.gz</file>
            <repositoryId>nexus</repositoryId>
            <groupId>COMPOSANTS</groupId>
            <artifactId>COMPOSANT-A</artifactId>
            <version>1.0</version>
            <generatePom>false</generatePom>
            <packaging>tar.gz</packaging>
            <url>http://urlRepo:8080/nexus/content/repositories/snapshots</url>
         </configuration>
      </execution>
   </executions>
</plugin>

posted @ 2019-08-27 17:32 paulwong 阅读(500) | 评论 (0)编辑 收藏

淘宝千万级并发分布式架构的14次演进

一、概述

本文以淘宝作为例子,介绍从一百个并发到千万级并发情况下服务端的架构的演进过程,同时列举出每个演进阶段会遇到的相关技术,让大家对架构的演进有一个整体的认知,文章最后汇总了一些架构设计的原则。

二、基本概念

在介绍架构之前,为了避免部分读者对架构设计中的一些概念不了解,下面对几个最基础的概念进行介绍:

1)分布式

系统中的多个模块在不同服务器上部署,即可称为分布式系统,如Tomcat和数据库分别部署在不同的服务器上,或两个相同功能的Tomcat分别部署在不同服务器上。

2)高可用

系统中部分节点失效时,其他节点能够接替它继续提供服务,则可认为系统具有高可用性。

3)集群

一个特定领域的软件部署在多台服务器上并作为一个整体提供一类服务,这个整体称为集群。如Zookeeper中的Master和Slave分别部署在多台服务器上,共同组成一个整体提供集中配置服务。

在常见的集群中,客户端往往能够连接任意一个节点获得服务,并且当集群中一个节点掉线时,其他节点往往能够自动的接替它继续提供服务,这时候说明集群具有高可用性。

4)负载均衡

请求发送到系统时,通过某些方式把请求均匀分发到多个节点上,使系统中每个节点能够均匀的处理请求负载,则可认为系统是负载均衡的。

5)正向代理和反向代理

  • 系统内部要访问外部网络时,统一通过一个代理服务器把请求转发出去,在外部网络看来就是代理服务器发起的访问,此时代理服务器实现的是正向代理;
  • 当外部请求进入系统时,代理服务器把该请求转发到系统中的某台服务器上,对外部请求来说,与之交互的只有代理服务器,此时代理服务器实现的是反向代理。

简单来说,正向代理是代理服务器代替系统内部来访问外部网络的过程,反向代理是外部请求访问系统时通过代理服务器转发到内部服务器的过程。

三、架构演进

单机架构

淘宝千万级并发分布式架构的14次演进

以淘宝作为例子。在网站最初时,应用数量与用户数都较少,可以把Tomcat和数据库部署在同一台服务器上。浏览器往www.taobao.com发起请求时,首先经过DNS服务器(域名系统)把域名转换为实际IP地址10.102.4.1,浏览器转而访问该IP对应的Tomcat。

随着用户数的增长,Tomcat和数据库之间竞争资源,单机性能不足以支撑业务。

第一次演进:Tomcat与数据库分开部署

淘宝千万级并发分布式架构的14次演进

Tomcat和数据库分别独占服务器资源,显著提高两者各自性能。

随着用户数的增长,并发读写数据库成为瓶颈。

第二次演进:引入本地缓存和分布式缓存

淘宝千万级并发分布式架构的14次演进

在Tomcat同服务器上或同JVM中增加本地缓存,并在外部增加分布式缓存,缓存热门商品信息或热门商品的html页面等。通过缓存能把绝大多数请求在读写数据库前拦截掉,大大降低数据库压力。

其中涉及的技术包括:使用memcached作为本地缓存,使用Redis作为分布式缓存,还会涉及缓存一致性、缓存穿透/击穿、缓存雪崩、热点数据集中失效等问题。

缓存抗住了大部分的访问请求,随着用户数的增长,并发压力主要落在单机的Tomcat上,响应逐渐变慢。

第三次演进:引入反向代理实现负载均衡

淘宝千万级并发分布式架构的14次演进

在多台服务器上分别部署Tomcat,使用反向代理软件(Nginx)把请求均匀分发到每个Tomcat中。

此处假设Tomcat最多支持100个并发,Nginx最多支持50000个并发,那么理论上Nginx把请求分发到500个Tomcat上,就能抗住50000个并发。

其中涉及的技术包括:Nginx、HAProxy,两者都是工作在网络第七层的反向代理软件,主要支持http协议,还会涉及session共享、文件上传下载的问题。

反向代理使应用服务器可支持的并发量大大增加,但并发量的增长也意味着更多请求穿透到数据库,单机的数据库最终成为瓶颈。

第四次演进:数据库读写分离

淘宝千万级并发分布式架构的14次演进

把数据库划分为读库和写库,读库可以有多个,通过同步机制把写库的数据同步到读库,对于需要查询最新写入数据场景,可通过在缓存中多写一份,通过缓存获得最新数据。

其中涉及的技术包括:Mycat,它是数据库中间件,可通过它来组织数据库的分离读写和分库分表,客户端通过它来访问下层数据库,还会涉及数据同步,数据一致性的问题。

业务逐渐变多,不同业务之间的访问量差距较大,不同业务直接竞争数据库,相互影响性能。

第五次演进:数据库按业务分库

淘宝千万级并发分布式架构的14次演进

把不同业务的数据保存到不同的数据库中,使业务之间的资源竞争降低,对于访问量大的业务,可以部署更多的服务器来支撑。

这样同时导致跨业务的表无法直接做关联分析,需要通过其他途径来解决,但这不是本文讨论的重点,有兴趣的可以自行搜索解决方案。

随着用户数的增长,单机的写库会逐渐会达到性能瓶颈。

第六次演进:把大表拆分为小表

淘宝千万级并发分布式架构的14次演进

比如针对评论数据,可按照商品ID进行hash,路由到对应的表中存储;针对支付记录,可按照小时创建表,每个小时表继续拆分为小表,使用用户ID或记录编号来路由数据。

只要实时操作的表数据量足够小,请求能够足够均匀的分发到多台服务器上的小表,那数据库就能通过水平扩展的方式来提高性能。其中前面提到的Mycat也支持在大表拆分为小表情况下的访问控制。

这种做法显著的增加了数据库运维的难度,对DBA的要求较高。数据库设计到这种结构时,已经可以称为分布式数据库,但是这只是一个逻辑的数据库整体,数据库里不同的组成部分是由不同的组件单独来实现的。

如分库分表的管理和请求分发,由Mycat实现,SQL的解析由单机的数据库实现,读写分离可能由网关和消息队列来实现,查询结果的汇总可能由数据库接口层来实现等等,这种架构其实是MPP(大规模并行处理)架构的一类实现。

目前开源和商用都已经有不少MPP数据库,开源中比较流行的有Greenplum、TiDB、Postgresql XC、HAWQ等,商用的如南大通用的GBase、睿帆科技的雪球DB、华为的LibrA等等。

不同的MPP数据库的侧重点也不一样,如TiDB更侧重于分布式OLTP场景,Greenplum更侧重于分布式OLAP场景。

这些MPP数据库基本都提供了类似Postgresql、Oracle、MySQL那样的SQL标准支持能力,能把一个查询解析为分布式的执行计划分发到每台机器上并行执行,最终由数据库本身汇总数据进行返回。

也提供了诸如权限管理、分库分表、事务、数据副本等能力,并且大多能够支持100个节点以上的集群,大大降低了数据库运维的成本,并且使数据库也能够实现水平扩展。

数据库和Tomcat都能够水平扩展,可支撑的并发大幅提高,随着用户数的增长,最终单机的Nginx会成为瓶颈。

第七次演进:使用LVS或F5来使多个Nginx负载均衡

淘宝千万级并发分布式架构的14次演进

由于瓶颈在Nginx,因此无法通过两层的Nginx来实现多个Nginx的负载均衡。

图中的LVS和F5是工作在网络第四层的负载均衡解决方案,其中LVS是软件,运行在操作系统内核态,可对TCP请求或更高层级的网络协议进行转发,因此支持的协议更丰富,并且性能也远高于Nginx,可假设单机的LVS可支持几十万个并发的请求转发;F5是一种负载均衡硬件,与LVS提供的能力类似,性能比LVS更高,但价格昂贵。

由于LVS是单机版的软件,若LVS所在服务器宕机则会导致整个后端系统都无法访问,因此需要有备用节点。可使用keepalived软件模拟出虚拟IP,然后把虚拟IP绑定到多台LVS服务器上,浏览器访问虚拟IP时,会被路由器重定向到真实的LVS服务器,当主LVS服务器宕机时,keepalived软件会自动更新路由器中的路由表,把虚拟IP重定向到另外一台正常的LVS服务器,从而达到LVS服务器高可用的效果。

此处需要注意的是,上图中从Nginx层到Tomcat层这样画并不代表全部Nginx都转发请求到全部的Tomcat。

在实际使用时,可能会是几个Nginx下面接一部分的Tomcat,这些Nginx之间通过keepalived实现高可用,其他的Nginx接另外的Tomcat,这样可接入的Tomcat数量就能成倍的增加。

由于LVS也是单机的,随着并发数增长到几十万时,LVS服务器最终会达到瓶颈,此时用户数达到千万甚至上亿级别,用户分布在不同的地区,与服务器机房距离不同,导致了访问的延迟会明显不同。

第八次演进:通过DNS轮询实现机房间的负载均衡

淘宝千万级并发分布式架构的14次演进

在DNS服务器中可配置一个域名对应多个IP地址,每个IP地址对应到不同的机房里的虚拟IP。

当用户访问www.taobao.com时,DNS服务器会使用轮询策略或其他策略,来选择某个IP供用户访问。此方式能实现机房间的负载均衡,至此,系统可做到机房级别的水平扩展,千万级到亿级的并发量都可通过增加机房来解决,系统入口处的请求并发量不再是问题。

随着数据的丰富程度和业务的发展,检索、分析等需求越来越丰富,单单依靠数据库无法解决如此丰富的需求。

第九次演进:引入NoSQL数据库和搜索引擎等技术

淘宝千万级并发分布式架构的14次演进

当数据库中的数据多到一定规模时,数据库就不适用于复杂的查询了,往往只能满足普通查询的场景。

对于统计报表场景,在数据量大时不一定能跑出结果,而且在跑复杂查询时会导致其他查询变慢,对于全文检索、可变数据结构等场景,数据库天生不适用。

因此需要针对特定的场景,引入合适的解决方案。如对于海量文件存储,可通过分布式文件系统HDFS解决,对于key value类型的数据,可通过HBase和Redis等方案解决,对于全文检索场景,可通过搜索引擎如ElasticSearch解决,对于多维分析场景,可通过Kylin或Druid等方案解决。

当然,引入更多组件同时会提高系统的复杂度,不同的组件保存的数据需要同步,需要考虑一致性的问题,需要有更多的运维手段来管理这些组件等。

引入更多组件解决了丰富的需求,业务维度能够极大扩充,随之而来的是一个应用中包含了太多的业务代码,业务的升级迭代变得困难。

第十次演进:大应用拆分为小应用

淘宝千万级并发分布式架构的14次演进

按照业务板块来划分应用代码,使单个应用的职责更清晰,相互之间可以做到独立升级迭代。这时候应用之间可能会涉及到一些公共配置,可以通过分布式配置中心Zookeeper来解决。

不同应用之间存在共用的模块,由应用单独管理会导致相同代码存在多份,导致公共功能升级时全部应用代码都要跟着升级。

第十一次演进:复用的功能抽离成微服务

淘宝千万级并发分布式架构的14次演进

如用户管理、订单、支付、鉴权等功能在多个应用中都存在,那么可以把这些功能的代码单独抽取出来形成一个单独的服务来管理,这样的服务就是所谓的微服务。

应用和服务之间通过HTTP、TCP或RPC请求等多种方式来访问公共服务,每个单独的服务都可以由单独的团队来管理。此外,可以通过Dubbo、SpringCloud等框架实现服务治理、限流、熔断、降级等功能,提高服务的稳定性和可用性。

不同服务的接口访问方式不同,应用代码需要适配多种访问方式才能使用服务,此外,应用访问服务,服务之间也可能相互访问,调用链将会变得非常复杂,逻辑变得混乱。

第十二次演进:引入企业服务总线ESB屏蔽服务接口的访问差异

淘宝千万级并发分布式架构的14次演进

通过ESB统一进行访问协议转换,应用统一通过ESB来访问后端服务,服务与服务之间也通过ESB来相互调用,以此降低系统的耦合程度。这种单个应用拆分为多个应用,公共服务单独抽取出来来管理,并使用企业消息总线来解除服务之间耦合问题的架构,就是所谓的SOA(面向服务)架构,这种架构与微服务架构容易混淆,因为表现形式十分相似。

个人理解,微服务架构更多是指把系统里的公共服务抽取出来单独运维管理的思想,而SOA架构则是指一种拆分服务并使服务接口访问变得统一的架构思想,SOA架构中包含了微服务的思想。

业务不断发展,应用和服务都会不断变多,应用和服务的部署变得复杂,同一台服务器上部署多个服务还要解决运行环境冲突的问题,此外,对于如大促这类需要动态扩缩容的场景,需要水平扩展服务的性能,就需要在新增的服务上准备运行环境,部署服务等,运维将变得十分困难。

第十三次演进:引入容器化技术实现运行环境隔离与动态服务管理

淘宝千万级并发分布式架构的14次演进

目前最流行的容器化技术是Docker,最流行的容器管理服务是Kubernetes(K8S),应用/服务可以打包为Docker镜像,通过K8S来动态分发和部署镜像。

Docker镜像可理解为一个能运行你的应用/服务的最小的操作系统,里面放着应用/服务的运行代码,运行环境根据实际的需要设置好。把整个“操作系统”打包为一个镜像后,就可以分发到需要部署相关服务的机器上,直接启动Docker镜像就可以把服务起起来,使服务的部署和运维变得简单。

在大促的之前,可以在现有的机器集群上划分出服务器来启动Docker镜像,增强服务的性能,大促过后就可以关闭镜像,对机器上的其他服务不造成影响(在3.14节之前,服务运行在新增机器上需要修改系统配置来适配服务,这会导致机器上其他服务需要的运行环境被破坏)。

使用容器化技术后服务动态扩缩容问题得以解决,但是机器还是需要公司自身来管理,在非大促的时候,还是需要闲置着大量的机器资源来应对大促,机器自身成本和运维成本都极高,资源利用率低。

第十四次演进:以云平台承载系统

淘宝千万级并发分布式架构的14次演进

系统可部署到公有云上,利用公有云的海量机器资源,解决动态硬件资源的问题,在大促的时间段里,在云平台中临时申请更多的资源,结合Docker和K8S来快速部署服务,在大促结束后释放资源,真正做到按需付费,资源利用率大大提高,同时大大降低了运维成本。

所谓的云平台,就是把海量机器资源,通过统一的资源管理,抽象为一个资源整体。在之上可按需动态申请硬件资源(如CPU、内存、网络等),并且之上提供通用的操作系统,提供常用的技术组件(如Hadoop技术栈,MPP数据库等)供用户使用,甚至提供开发好的应用。用户不需要关系应用内部使用了什么技术,就能够解决需求(如音视频转码服务、邮件服务、个人博客等)。在云平台中会涉及如下几个概念:

  • IaaS:基础设施即服务。对应于上面所说的机器资源统一为资源整体,可动态申请硬件资源的层面;
  • PaaS:平台即服务。对应于上面所说的提供常用的技术组件方便系统的开发和维护;
  • SaaS:软件即服务。对应于上面所说的提供开发好的应用或服务,按功能或性能要求付费。

至此,以上所提到的从高并发访问问题,到服务的架构和系统实施的层面都有了各自的解决方案,但同时也应该意识到,在上面的介绍中,其实是有意忽略了诸如跨机房数据同步、分布式事务实现等等的实际问题,这些问题以后有机会再拿出来单独讨论。

四、 架构设计总结

架构的调整是否必须按照上述演变路径进行?

不是的,以上所说的架构演变顺序只是针对某个侧面进行单独的改进,在实际场景中,可能同一时间会有几个问题需要解决,或者可能先达到瓶颈的是另外的方面,这时候就应该按照实际问题实际解决。

如在政府类的并发量可能不大,但业务可能很丰富的场景,高并发就不是重点解决的问题,此时优先需要的可能会是丰富需求的解决方案。

对于将要实施的系统,架构应该设计到什么程度?

对于单次实施并且性能指标明确的系统,架构设计到能够支持系统的性能指标要求就足够了,但要留有扩展架构的接口以便不备之需。对于不断发展的系统,如电商平台,应设计到能满足下一阶段用户量和性能指标要求的程度,并根据业务的增长不断的迭代升级架构,以支持更高的并发和更丰富的业务。

服务端架构和大数据架构有什么区别?

所谓的“大数据”其实是海量数据采集清洗转换、数据存储、数据分析、数据服务等场景解决方案的一个统称。

在每一个场景都包含了多种可选的技术,如数据采集有Flume、Sqoop、Kettle等,数据存储有分布式文件系统HDFS、FastDFS,NoSQL数据库HBase、MongoDB等,数据分析有Spark技术栈、机器学习算法等。

总的来说大数据架构就是根据业务的需求,整合各种大数据组件组合而成的架构,一般会提供分布式存储、分布式计算、多维分析、数据仓库、机器学习算法等能力。而服务端架构更多指的是应用组织层面的架构,底层能力往往是由大数据架构来提供。

有没有一些架构设计的原则?

1)N+1设计

系统中的每个组件都应做到没有单点故障。

2)回滚设计

确保系统可以向前兼容,在系统升级时应能有办法回滚版本。

3)禁用设计

应该提供控制具体功能是否可用的配置,在系统出现故障时能够快速下线功能。

4)监控设计

在设计阶段就要考虑监控的手段。

5)多活数据中心设计

若系统需要极高的高可用,应考虑在多地实施数据中心进行多活,至少在一个机房断电的情况下系统依然可用

6)采用成熟的技术

刚开发的或开源的技术往往存在很多隐藏的bug,出了问题没有商业支持可能会是一个灾难。

7)资源隔离设计

应避免单一业务占用全部资源。

8)架构应能水平扩展

系统只有做到能水平扩展,才能有效避免瓶颈问题

9)非核心则购买

非核心功能若需要占用大量的研发资源才能解决,则考虑购买成熟的产品。

10)使用商用硬件

商用硬件能有效降低硬件故障的机率。

11)快速迭代

系统应该快速开发小功能模块,尽快上线进行验证,早日发现问题大大降低系统交付的风险。

12)无状态设计

服务接口应该做成无状态的,当前接口的访问不依赖于接口上次访问的状态。

设计到此结束,其实,解决方案有很多,但是这个只是我采用的,觉得最轻便的一个。

原创文章,转载请注明: 转载自并发编程网 – ifeve.com本文链接地址: 淘宝千万级并发分布式架构的14次演进

posted @ 2019-08-16 10:05 paulwong 阅读(212) | 评论 (0)编辑 收藏