paulwong

2019年8月1日 #

keycloak 资源

Keycloak为现代应用和服务提供开源的认证和访问管理,即通常所说的认证和授权。

Keycloak支持OpenID、OAuth 2.0和SAML 2.0协议;支持用户注册、用户管理、权限管理;支持OTP,支持代理OpenID、SAML 2.0 IDP,支持GitHub、LinkedIn等第三方登录,支持整合LDAP和Active Directory;支持自定义认证流程、自定义用户界面,支持国际化。

有用户管理界面,可用于API的认证和用户的认证,用户认证需人为输入用户名与密码,API则凭BARE TOKEN即可认证。

Spring Boot/Angular整合Keycloak实现单点登录
https://blog.51cto.com/7308310/2446368

SpringBoot整合KeyCloak权限管理
https://qianmoq.com/fuwuduan/springboot/springbootzhenghekeycloakquanxianguanli.html

使用Spring Gateway和KeyCloak构建一个OIDC认证系统
https://zhuanlan.zhihu.com/p/138578359

A Quick Guide to Using Keycloak with Spring Boot
https://www.baeldung.com/spring-boot-keycloak


posted @ 2020-09-25 15:46 paulwong 阅读(2) | 评论 (0)编辑 收藏

linux shell 中检查文件夹是否存在

To check if a directory exists in a shell script, you can use the following:
if [ -d "$DIRECTORY" ]; then
  # Control will enter here if $DIRECTORY exists.
fi

Or to check if a directory doesn't exist:
if [ ! -d "$DIRECTORY" ]; then
  # Control will enter here if $DIRECTORY doesn't exist.
fi

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

linux shell 中函数的调用

function fun1(){
  return 34
}

function fun2(){
  local res=$(fun1)
  echo $res
}

上面调用fun1时,打印结果却不返回34,这是为何?原来函数只是返回结果成功与否的值,并不能自定义。因此要改成下面这种写法

function fun1(){
  echo 34
}

function fun2(){
  local res=$(fun1)
  echo $res
}

https://stackoverflow.com/questions/17336915/return-value-in-a-bash-function

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

在ECLIPSE中切换到新建的分支

如果已经在ECLIPSE中CLONE了GIT的项目,这时当GIT中又新建了项目,ECLIPSE无法切换到这新建的项目,解决办法:


In the Git Repositories view:

  1. Right-click the repository and choose Fetch from Upstream
  2. If the new branch will not shown up below Branches/Remote Tracking, you have to configure fetch:
    1. Right-click the fetch node below Remotes/origin and choose Configure Fetch...
    2. In the Configure Fetch make sure there is only the single Ref mapping (assuming the remote is named origin+refs/heads/*:refs/remotes/origin/*:
      Configure fetch

这时再次Fetch from upstream,则新建的项目再次重现:Git Repositories View-->Branches-->Remote Checking中。
双击新的分支,选:Check out as New Local Branch即可。

https://stackoverflow.com/questions/47390703/how-do-i-get-a-new-branch-to-show-up-in-eclipse-git-remote-tracking/47391183

posted @ 2020-09-24 15:16 paulwong 阅读(32) | 评论 (0)编辑 收藏

向所有服务器发送相同命令

先在主控机执行ssh-keygen,再向被控机传输key,
ssh-copy-id -i ~/.ssh/id_rsa.pub user1@ip

样例脚本transfer-artemis.sh如下:
#!/bin/bash

loop_server(){
    for ((i=2; i<=8; i++))
    do
      ipd=10.10.31.1${i}2
      echo ${ipd}
      $1 ${ipd}
    done
    
    for ((i=1; i<=2; i++))
    do
      ipd=10.20.31.1${i}2
      echo ${ipd}
      $1 ${ipd}
    done
    
}

start_artemis_cmd(){
    echo "ssh user1@${1} '/opt/myapp/artemis/apache-artemis-2.15.0/instance/bin/artemis-service start'"
    ssh user1@${1} '/opt/myapp/artemis/apache-artemis-2.15.0/instance/bin/artemis-service start'
}

stop_artemis_cmd(){
    echo "ssh user1@${1} '/opt/myapp/apache-activemq-5.15.10/bin/activemq stop'"
    echo "ssh user1@${1} '/opt/myapp/artemis/apache-artemis-2.15.0/instance/bin/artemis-service stop'"
    ssh user1@${1} '/opt/myapp/apache-activemq-5.15.10/bin/activemq stop'
    ssh user1@${1} '/opt/myapp/artemis/apache-artemis-2.15.0/instance/bin/artemis-service stop'
}

scp_artemis_cmd(){
    echo "ssh user1@${1} 'rm -rf /opt/myapp/artemis'"
    echo "scp -r /opt/myapp/artemis user1@${1}:/opt/myapp/"
    ssh user1@${1} 'rm -rf /opt/myapp/artemis'
    scp -r /opt/myapp/artemis user1@${1}:/opt/myapp/
}

stop_artemis(){
    loop_server stop_artemis_cmd
}

start_artemis(){
    loop_server start_artemis_cmd
}

scp_artemis(){ 
    loop_server scp_artemis_cmd
}

#start_artemis "Hello start_artemis"

$1

执行命令:
./transfer-artemis.sh start_artemis

posted @ 2020-09-24 10:52 paulwong 阅读(44) | 评论 (0)编辑 收藏

How do I set directory permissions in maven output?

pom.xml:
        <plugin>
            <artifactId>maven-assembly-plugin</artifactId>
            <version>3.1.1</version>

            <executions>
                <execution>
                    <id>make-assembly</id>
                    <phase>package</phase>

                    <goals>
                        <goal>single</goal>
                    </goals>
                </execution>
            </executions>

            <configuration>
                <descriptors>
                    <descriptor>src/main/assembly/descriptor.xml</descriptor>
                </descriptors>
                <archiverConfig>
                    <directoryMode>0755</directoryMode>
                    <defaultDirectoryMode>0755</defaultDirectoryMode>
                    <fileMode>0644</fileMode>
                </archiverConfig>
            </configuration>
        </plugin>

src/main/assembly/descriptor.xml,在此文件覆盖:
<fileSets>
    <fileSet>
        <directory>src/conf</directory>
        <outputDirectory>conf</outputDirectory>
        <fileMode>0600</fileMode>
        <directoryMode>0700</directoryMode>
    </fileSet>
    <fileSet>
        <directory>src/db</directory>
        <outputDirectory>db</outputDirectory>
    </fileSet>
    <fileSet>
        <directory>src/bin</directory>
        <outputDirectory>bin</outputDirectory>
        <fileMode>0755</fileMode>
    </fileSet>


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

深入浅出 Retrofit,这么牛逼的框架你们还不来看看?

https://segmentfault.com/a/1190000005638577

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

Spring Retry框架——看这篇就够了

https://my.oschina.net/marvelcode/blog/4563352

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

linux nohup命令详解

nohup命令及其输出文件

nohup命令:如果你正在运行一个进程,而且你觉得在退出帐户时该进程还不会结束,那么可以使用nohup命令。该命令可以在你退出帐户/关闭终端之后继续运行相应的进程。nohup就是不挂起的意思( n ohang up)。

一般都是在linux下nohup格式:  

nohup command

或者

nohup command &

这之间的差别是带&的命令行,即使terminal(终端)关闭,或者电脑死机程序依然运行(前提是你把程序递交到服务器上);

它把标准输出(STDOUT)和标准错误(STDERR)结果输出到nohup.txt文件这个看似很方便,但是当输出很大的时候,nohup.txt文件会非常大,或者多个后台命令的时候大家都会输出到nohup.txt文件,不利于查找结果和调试程序。

所以能够重定向输出会非常方便。下面要介绍标准输出,标准输入 和标准错误了。

其实我们一直都在用,只是没有注意到, 

比如

>./command.sh > output

#这其中的>就是标准输出符号,其实是 1>output 的缩写

>./command.sh 2> output

#这里的2>就是将标准错误输出到output文件里。

而0< 则是标准输入了。

下面步入正题,重定向后台命令

>nohup ./command.sh > output 2>&1 &

解释:前面的nohup 和后面的&我想大家都能明白了把。

主要是中间的 2>&1的意思

这个意思是把标准错误(2)重定向到标准输出中(1),而标准输出又导入文件output里面,   www.2cto.com

所以结果是标准错误和标准输出都导入文件output里面了。

至于为什么需要将标准错误重定向到标准输出的原因,那就归结为标准错误没有缓冲区,而stdout有。

这就会导致 >output 2>output 文件output被两次打开,而stdout和stderr将会竞争覆盖,这肯定不是我门想要的.

这就是为什么有人会写成:

nohup ./command.sh >output 2>output
出错的原因了

##########################

最后谈一下/dev/null文件的作用

这是一个无底洞,任何东西都可以定向到这里,但是却无法打开。

所以一般很大的stdou和stderr当你不关心的时候可以利用stdout和stderr定向到这里

>./command.sh >/dev/null 2>&1

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

Scheduling a task in Java within a CompletableFuture

When we want to do something later in our Java code, we often turn to the ScheduledExecutorService. This class has a method called schedule(), and we can pass it some code to be run later like this:

ScheduledExecutorService executor =
    Executors.newScheduledThreadPool(4);
executor.schedule(
    () -> {System.out.println("..later");},
    1,
    TimeUnit.SECONDS
);
System.out.println("do");
// (Don't forget to shut down the executor later)

The above code prints “do…” and then one second later it prints “…later”.

We can even write code that does some work and returns a result in a similar way:

// (Make the executor as above.)
ScheduledFuture future = executor.schedule(
    () -> 10 + 25, 1, TimeUnit.SECONDS);
System.out.println("answer=" + future.get())


The above code prints “answer=35”. When we call get() it blocks waiting for the scheduler to run the task and mark the ScheduledFuture as complete, and then returns the answer to the sum (10 + 25) when it is ready.

This is all very well, but you may note that the Future returned from schedule() is a ScheduledFuture, and a ScheduledFuture is not a CompletableFuture.

Why do you care? Well, you might care if you want to do something after the scheduled task is completed. Of course, you can call get(), and block, and then do something, but if you want to react asynchronously without blocking, this won’t work.

The normal way to run some code after a Future has completed is to call one of the “then*” or “when*” methods on the Future, but these methods are only available on CompletableFuture, not ScheduledFuture.

Never fear, we have figured this out for you. We present a small wrapper for schedule that transforms your ScheduledFuture into a CompletableFuture. Here’s how to use it:

CompletableFuture<Integer> future =
    ScheduledCompletable.schedule(
        executor,
        () -> 10 + 25,
        1,
        TimeUnit.SECONDS
     );
future.thenAccept(
    answer -> {System.out.println(answer);}
);
System.out.println("Answer coming")


The above code prints “Answer coming…” and then “35”, so we can see that we don’t block the main thread waiting for the answer to come back.

So far, we have scheduled a synchronous task to run in the background after a delay, and wrapped its result in a CompletableFuture to allow us to chain more tasks after it.

In fact, what we often want to do is schedule a delayed task that is itself asynchronous, and already returns a CompletableFuture. In this case it feels particularly natural to get the result back as a CompletableFuture, but with the current ScheduledExecutorService interface we can’t easily do it.

It’s OK, we’ve figured that out too:

Supplier<CompletableFuture<Integer>> asyncTask = () ->
    CompletableFuture.completedFuture(10 + 25);
CompletableFuture<Integer> future =
    ScheduledCompletable.scheduleAsync(
        executor, asyncTask, 1, TimeUnit.SECONDS);
future.thenAccept(
    answer -> {System.out.println(answer);}
);
System.out.println("Answer coming")


The above code prints “Answer coming…” and then “35”, so we can see that our existing asynchronous task was scheduled in the background, and we didn’t have to block the main thread waiting for it. Also, under the hood, we are not blocking the ScheduledExecutorService‘s thread (from its pool) while the async task is running – that task just runs in whatever thread it was assigned when it was created. (Note: in our example we don’t really run an async task at all, but just immediately return a completed Future, but this does work for real async tasks.)

I know you’re wondering how we achieved all this. First, here’s how we run a simple blocking task in the background and wrap it in a CompletableFuture:

public static <T> CompletableFuture<T> schedule(
    ScheduledExecutorService executor,
    Supplier<T> command,
    long delay,
    TimeUnit unit
) {
    CompletableFuture<T> completableFuture = new CompletableFuture<>();
    executor.schedule(
        (() -> {
            try {
                return completableFuture.complete(command.get());
            } catch (Throwable t) {
                return completableFuture.completeExceptionally(t);
            }
        }),
        delay,
        unit
    );
    return completableFuture;
}


And here’s how we delay execution of an async task but still return its result in a CompletableFuture:

public static <T> CompletableFuture<T> scheduleAsync(
    ScheduledExecutorService executor,
    Supplier<CompletableFuture<T>> command,
    long delay,
    TimeUnit unit
) {
    CompletableFuture<T> completableFuture = new CompletableFuture<>();
    executor.schedule(
        (() -> {
            command.get().thenAccept(
                t -> {completableFuture.complete(t);}
            )
            .exceptionally(
                t -> {completableFuture.completeExceptionally(t);return null;}
            );
        }),
        delay,
        unit
    );
    return completableFuture;
}


Note that this should all work to run methods like exceptionally()thenAccept()whenComplete() etc.

Feedback and improvements welcome!


https://www.artificialworlds.net/blog/2019/04/05/scheduling-a-task-in-java-within-a-completablefuture/

posted @ 2020-08-24 09:06 paulwong 阅读(26) | 评论 (0)编辑 收藏

如何找出LINUX中消耗CPU最大的应用的PID

有时查看LINUX时,会发现当前CPU消耗连续保持80%,如何找出是哪个应用呢?
top -i //输出应用列表,并隐藏IDEL的应用
P //在列表时,按P,则按CPU的使用排序


How To Check CPU Utilization In Linux With Command Line
https://phoenixnap.com/kb/check-cpu-usage-load-linux

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

CompletableFuture

很久以前多线程是这样创建:
Thread t1 = new Thread();
Thread t2 = new Thread();
t1.start(); // 启动新线程
t2.start(); // 启动新线程

由于创建和销毁线程是非常耗资源,因此改成线程建好后不销毁,可以重用,用户只需提供run方法的具体实现:
public static void main(String[] args) throws Exception {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Future<String> stringFuture = executor.submit(new Callable<String>() {
            @Override
            public String call() throws Exception {
                Thread.sleep(2000);
                return "async thread";
            }
        });
        Thread.sleep(1000);
        System.out.println("main thread");
        System.out.println(stringFuture.get());

    }

但如果很多线程被创建,并且线程间有依赖,即按流程和条件执行线程,实现起来就有点复杂,于是CompletableFuture横空出世。一共有50各方法可供使用。
CompletableFuture.supplyAsync(),相当于创建了ExecutorService,同时也创建了Callable,然后提交到线程池中执行。
CompletableFuture<String> futureA = CompletableFuture.supplyAsync(() -> "任务A");
CompletableFuture<String> futureB = CompletableFuture.supplyAsync(() -> "任务B");
CompletableFuture<String> futureC = futureB.thenApply(b -> {
      System.out.println("执行任务C.");
      System.out.println("参数:" + b);//参数:任务B
      return "a";
});


!!How to use CompletableFuture and Callable in Java
https://ducmanhphan.github.io/2020-02-10-How-to-use-CompletableFuture-Callable-in-Java/

使用CompletableFuture优化你的代码执行效率
https://www.cnblogs.com/fingerboy/p/9948736.html

CompletableFuture 使用详解
https://www.jianshu.com/p/6bac52527ca4

使用CompletableFuture
https://www.liaoxuefeng.com/wiki/1252599548343744/1306581182447650


https://github.com/eugenp/tutorials/blob/master/core-java-modules/core-java-concurrency-basic/src/test/java/com/baeldung/completablefuture/CompletableFutureLongRunningUnitTest.java

posted @ 2020-08-14 11:46 paulwong 阅读(30) | 评论 (0)编辑 收藏

Springboot+proguard+maven 混淆

https://blog.csdn.net/qq_35981283/article/details/78529929

posted @ 2020-08-13 10:53 paulwong 阅读(56) | 评论 (0)编辑 收藏

MYSQL经典配置

/etc/my.cnf

[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
#symbolic-links=0

#vish start
open_files_limit = 8000
max_connections = 500
#set-variable=max_connections=500
thread_concurrency = 8
#concurrent_insert=2
thread_cache_size=2000

interactive_timeout=180
wait_timeout=180

max_allowed_packet = 32M
key_buffer_size = 3000M
read_buffer_size = 16M
read_rnd_buffer_size = 4M
bulk_insert_buffer_size = 256M
myisam_sort_buffer_size = 16M
myisam_max_sort_file_size = 256M
myisam_repair_threads = 1
#myisam_recover = 4
max_heap_table_size = 2048M
tmp_table_size = 1024M
table_open_cache = 2000
table_cache = 2000
sort_buffer_size = 128M 
join_buffer_size = 128M 
query_cache_size = 128M 
query_cache_limit = 128M 

#slow_query_log=1
log-slow-queries
long_query_time=2
#log_queries_not_using_indexes=1
slow_query_log_file = /var/log/mariadb/host_name-slow.log

[mysqldump]
user=root
password=*********
#max_allowed_packet = 1024M 

[myisamchk] 
key_buffer_size = 2500M 
sort_buffer_size = 1024M 
read_buffer = 32M
write_buffer = 32M

[innodb]
innodb_buffer_pool_size=3G
innodb_buffer_pool_instance=2
innodb_additional_mem_pool_size=20M
innodb_log_file_size= 512M
innodb_log_buffer_size=16M
innodb_flush_log_at_trx_commit=1
innodb_thread_concurrency=16
#innodb_read_io_threads=16
#innodb_write_io_threads=16
#innodb_io_capacity=2000
#innodb_lock_wait_timeout=120

innodb_flush_method=O_DIRECT
#vish end

[mysqld_safe]
log-error=/var/log/mariadb/mariadb.log
pid-file=/var/run/mariadb/mariadb.pid
#!includedir /etc/my.cnf.d

posted @ 2020-08-07 18:01 paulwong 阅读(28) | 评论 (0)编辑 收藏

shell 脚本替换文件中某个字符串

1、将当前目录下包含jack串的文件中,jack字符串替换为tom
sed -i "s/jack/tom/g" `grep "jack" -rl ./`

2、将某个文件中的jack字符串替换为tom

sed -i "s/jack/tom/g" test.txt

参考连接:http://blog.csdn.net/lizhi200404520/article/details/7968483

posted @ 2020-08-07 18:01 paulwong 阅读(55) | 评论 (0)编辑 收藏

JMETER连接数据库做压力测试

JMETER作为一个压力测试工具,可模拟上千用户进行登录之类的操作。
但一般图形界面只用来制作测试计划,真正运行是在LINUX上。
同时也可以作为压测MYSQL数据库等。

配置基本流程:添加测试计划-->添加线程组,在添加的线程组中-->添加JDBC配置组件-->添加JDBC访问的SQL语句的REQUEST组件-->配置REQUEST和配置组件相关联的变量值-->添加查看结果组件-->设定要运行的线程数-->执行。

执行期间如有问题,查看DOS窗口。

【JMeter】JMeter完成一个MySql压力测试
https://www.cnblogs.com/zhaoxd07/p/4895681.html

JMeter 如何与 MySQL 进行整合测试
https://juejin.im/post/6844903728215162887

JMeter JDBC连接MySql配置
https://www.jianshu.com/p/cde2729421d1

jmeter(二十五)linux环境运行jmeter并生成报告
https://www.cnblogs.com/imyalost/p/9808079.html

posted @ 2020-08-07 17:59 paulwong 阅读(31) | 评论 (0)编辑 收藏

TCL 的连接TIME_WAIT太多的处理

TCP TIME_WAIT 详解
https://www.zhuxiaodong.net/2018/tcp-time-wait-instruction/

mysql服务器,大量tcp连接状态TIME_WAIT
https://www.cnblogs.com/zhjh256/p/6363312.html

linux下释放TIME_WAIT方法
https://blog.csdn.net/u013488847/article/details/46421867

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

Different ways to create objects in Java

https://www.geeksforgeeks.org/different-ways-create-objects-java/


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

RHEL\CentOS 7 下 MySQL 连接数被限制为214个

MARIADB的默认最大连接数是100,当试图更改配置文件/etc/my.conf追加max_connections=1000,并重启服务时,发现最大连接数被重置为214,这是由于系统问题,open_files数为1024,如何正确地更改是个复杂的问题。

MARIADB中一些查当前连接数/最大连接数的命令为:

MariaDB [(none)]
show variables like "max_connections";
show processlist;
show status where variable_name = 'threads_connected';
show status where variable_name = 'max_used_connections';
show variables like 'max_user_connections';
show variables like '%connection%';

正确方案:
RHEL\CentOS 7 下 MySQL 连接数被限制为214个
https://waylau.com/rhel-mysql-connections-limit-214/

Centos7.4下面mysql的max_connections不生效的问题。
https://blog.51cto.com/wangqh/2131340

为何要在SERVICE中添加允许打开的文件数?
Centos7下修改/etc/security/limits.conf文件只在用户登录后打开的进程有效,系统服务或通过rc.local启动的无效,系统服务修改文件/usr/lib/systemd/system/SOME_SERVICE.service添加:
[Service]
LimitNOFILE=65535


关于LINUX系统的最大打开文件数:
https://blog.51cto.com/as007012/1956222

https://www.haiyun.me/archives/linux-file-limit.html

posted @ 2020-08-05 20:55 paulwong 阅读(33) | 评论 (0)编辑 收藏

统计文件行数或LIST目录的条数

https://www.thegeekdiary.com/how-to-count-lines-in-a-file-in-unix-linux/

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

21道系统设计+面向对象的面试题


事不宜迟,我们马上来看这21道最受欢迎,提问率最高的系统设计+面向对象的面试题,这些问题的分析能让你轻松应对项目设计的面试。

1. 如何用Java设计自动售货机? (解决方案)

你需要编写代码来设计自动售货机,该自动售货机售卖如巧克力,糖果,冷饮之类的产品,需要投入硬币进行购买,例如5分、10分、25分,50分、1元等。确保插入硬币获得产品 ,然后退回零钱。 另外,编写单元测试以证明这些常见用例有效。 如果遇到困难,可以阅读有关解决这些经典系统设计问题的两部分文章(第1部分和第2部分)。

2. 如何设计类似Goo.gl或Bit.Ly的URL缩短服务?(解决方案)

这是一个常见的系统设计问题。 你给了一个普通长度的URL,你将如何设计为其生成较短且唯一的别名的服务? 如果你不熟悉URL缩短程序服务,请查看一些常用的例子,例如Google的goo.gl和Twitter使用的bit.ly。确保在一些设计决策后提供数据库模式和基本原理,例如保留数据多长时间,如何获取统计信息和分析等。如果遇到问题,可以参照关于Educative上“系统设计面试课程”中给出的解决方案。

3.你如何设计交通控制系统?

经典系统设计问题仍然提及频率。 确保你知道如何从一种状态过渡到另一种状态,例如从红色过渡到绿色,从绿色过渡到橙色再过渡到红色等。

4. 如何设计限价单? (解决方案)

限价订单簿在证券交易所中用于根据价格和时间优先级将买入订单与卖出订单进行匹配。 你会怎么做? 你将使用哪种数据结构? 请记住,匹配的速度是关键,也是可靠的。 如果你需要复习数据结构,则可以查看Java课程中的数据结构和算法,如果你遇到困难,可以在这里查看我的解决方案。

5. 你如何设计类似Pastebin的网站?

Pastebin允许你粘贴文本或代码,然后在任意位置共享指向该代码的链接。它不是在线代码编辑器,但是你可以使用它来存储任何类型的文本。

6. 你将如何创建自己的Instagram? (解决方案)

Instagram是一个照片共享应用程序,它提供了一些自定义滤镜以提高照片质量。你的应用程序应该具有照片上传功能,为搜索标记照片以及一些基本的过滤器。如果你可以添加共享或社交网络,那就太好不过了。顺便说一句,如果你遇到困难,还可以在Educative的如何准备系统设计面试课程中看到免费的解决方案。

7. 你如何设计像Google Drive或Dropbox这样的全局文件共享和存储应用程序?

这些用于存储和共享文件,照片和其他媒体。你如何设计诸如允许用户上传/查看/搜索/共享文件或照片之类的东西?跟踪文件共享的权限,并允许多个用户编辑同一文档?

8. 你如何设计类似Whatsapp或Facebook Messenger的聊天应用程序?

你肯定使用过WhatsApp和Facebook?没有?如果没有,我告诉你聊天应用程序允许你向你的朋友发送消息。这是点对点的连接。你保留一个朋友列表,查看他们的状态并聊天。在WhatsApp中,你也可以连接组,但适用于高级和经验丰富的开发人员。至少,你应该提供一种设计来保留好友列表并从中发送和接收消息。

9. 你如何设计Twitter克隆?

Twitter是一种流行的消息服务,可让你向所有关注者广播消息。你发布推文,你的关注者可以看到这些消息,他们可以喜欢或转发。确保实现了诸如粉丝关注,主题标签,发博,删除等常用功能。如果你觉得自己有点困难,则可以按照如何应对系统设计面试的课程解决方案进行操作。

10. 如何设计像YouTube或Netflix这样的全球视频流服务?

设计像NetFlix或YouTube这样的视频流服务时,关键是顺畅和缓冲以及在低带宽连接上的功能,如何应对这些挑战?

11. 如何设计ATM机?

ATM机允许用户存入和提取现金。它还允许用户查看其余额。你如何设计这样的系统?你面临的主要挑战是什么?

12. 如何设计API速率限制器?

13. 你如何设计Twitter搜索?

14. 如何设计类似于Google的网络爬虫?

网络搜寻器可以访问网站并像Google一样搜寻所有链接并将它们编入索引,以便它们以后可以出现在搜索结果中。爬网程序还可用于在一组目录中搜索特定文件,你如何设计此类文件?主要挑战是什么?

15. 如何设计Facebook的Newsfeed?你将使用哪种算法?

新闻源是Facebook的重要组成部分,它使用户可以查看他的世界正在发生的事情,包括朋友和家人,他喜欢的页面,他关注的组以及Facebook广告。

Newsfeed算法的作用是显示对用户来说最重要的消息,并且可以产生很高的参与度。显然,来自朋友和家人的消息应该优先处理。如果你觉得陷入困境,则可以按照如何做好系统设计面试的课程所提出解决答案。

16. 如何设计Yelp或Nearby Friends?

17. 如何设计全球乘车服务E.G. Uber,Grab或Ola后端?

优步(Uber)和奥拉(Ola)是最受欢迎的两种乘车服务,它将驾驶员和乘客都组合到一起。你如何设计让乘客看到附近的出租车并进行预订?

18. 如何设计BookMyShow?

一个允许你预订电影院和活动门票的网站。这实际上是一家运转良好的印度创业公司。

19. 如何设计Quora,Reddit或Hackernews等社交网络+留言板服务网站?

Reddit,Quora和HackerNews是一些最受欢迎的社交网站,用户可以在其中发布问题或共享链接。其他用户可以回答问题或对共享链接发表评论。

20. 你如何设计类似Airbnb的应用程序?

它允许某些用户上传房间出租,而其他用户则出租。某些功能仅适用于管理员,发布者和订阅者。

21. 你如何设计电梯系统?

我们大多数人都使用电梯,你在一些大型办公大楼中,你可以看到3到4部电梯。你需要为此设计软件,以便可以在不同楼层快速使用。你可以假设你有两部电梯和一栋10层楼的建筑。

如果你需要一些帮助,那么我建议你可在Udemy上学习如何做好系统设计面试课程,Rajat Mehta在那里有一个完整的案例研究来解决这个问题。你还将在那里学习高级系统设计,低级系统设计和数据库设计。

posted @ 2020-08-02 16:54 paulwong 阅读(42) | 评论 (0)编辑 收藏

如何在JENKINS中使用HTTP下载 NEXUS上的ZIP

需添加如下PLUGIN:File Operations

添加如下STEP:File Download Operation

NEXUS上的下载地址按如下模式:http://localhost:8081/service/rest/v1/search/assets/download?group=org.osgi&name=org.osgi.core&version=4.3.1&maven.extension=jar&maven.classifier

posted @ 2020-07-31 20:18 paulwong 阅读(54) | 评论 (0)编辑 收藏

使用ssh-keygen和ssh-copy-id三步实现SSH无密码登录 和ssh常用命令

ssh-keygen  产生公钥与私钥对.
ssh-copy-id 将本机的公钥复制到远程机器的authorized_keys文件中,ssh-copy-id也能让你有到远程机器的home, ~./ssh , 和 ~/.ssh/authorized_keys的权利

第一步:在本地机器上使用ssh-keygen产生公钥私钥对
  1. zhz@zhz:~/$ ssh-keygen
    Generating public/private rsa key pair.
    Enter file in which to save the key (/home/zhz/.ssh/id_rsa):
  2. Enter passphrase (empty for no passphrase): [Press enter key
  3. same passphrase again: [Pess enter key]
  4. Your identification has been saved in /home/zhz/.ssh/id_rsa.
  5. Your public key has been saved in /home/zhz/.ssh/id_rsa.pub.
  6. The key fingerprint is:
  7. 用cat命令查看是否生成产生公钥私钥对
  8. zhz@zhz:~$ cat .ssh/id_rsa.pub
    ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCl9N5+xboqSIagBx02rdZ2fkROCPW8iW7hl6Gu+2hkBYYy/b1qcOm8RF/AMyas3i0QEK7Hcu9H51l2lulVbS5n9M9FaWIyYzssaS012x2mg9iA6MxPMlaXFsZ5jnVrGicndzf3VUu9kCErp5q0OzzMjsG3PKQevzWZJSBaFgc8NF5ZJ+VT54BN8ktMTHVwOo15I2Uai+bs4eP0NsuwIJmGyYIUOuvTuUtJxGV3hZ+tcjhupupqVCwYOE+cDz8VkFBGtnKsdE69hWoY2VUfEOAfHZptra7Ce9dXfDgx9jxuuNiJYtGo/bZDfe+UJ5HUv8wrL+hFeRIihdmP2CKJD8j5 zhz@zhz


第二步:用ssh-copy-id将公钥复制到远程机器中
  1. zhz@zhz:~$  ssh-copy-id -i .ssh/id_rsa.pub  用户名字@192.168.x.xxx

注意: ssh-copy-id 将key写到远程机器的 ~/ .ssh/authorized_key.文件中

第三步: 登录到远程机器不用输入密码
  1. zhz@zhz:~$  ssh 用户名字@192.168.x.xxx

  2. Last login: Sun Nov 16 17:22:33 2008 from 192.168.1.2

常见问题:

  1. ssh-copy-id -u eucalyptus -i ~eucalyptus/.ssh/id_rsa.pub ssh 用户名字@192.168.x.xxx
  2. 第一次需要密码登录

上述是给eucalyptus用户赋予无密码登陆的权利

  1. /usr/bin/ssh-copy-id: ERROR: No identities found

使用选项 -i ,当没有值传递的时候或者 如果 ~/.ssh/identity.pub 文件不可访问(不存在), ssh-copy-id 将显示上述的错误信息  ( -i选项会优先使用将ssh-add -L的内容)



ssh命令

1、复制SSH密钥到目标主机,开启无密码SSH登录

ssh-copy-id user@host

如果还没有密钥,请使用ssh-keygen命令生成。

2、从某主机的80端口开启到本地主机2001端口的隧道

ssh -N -L2001:localhost:80 somemachine

现在你可以直接在浏览器中输入http://localhost:2001访问这个网站。

3、将你的麦克风输出到远程计算机的扬声器

dd if=/dev/dsp | ssh -c arcfour -C username@host dd of=/dev/dsp

这样来自你麦克风端口的声音将在SSH目标计算机的扬声器端口输出,但遗憾的是,声音质量很差,你会听到很多嘶嘶声。

4、比较远程和本地文件

ssh user@host cat /path/to/remotefile | diff /path/to/localfile –

在比较本地文件和远程文件是否有差异时这个命令很管用。

5、通过SSH挂载目录/文件系统

sshfs name@server:/path/to/folder /path/to/mount/point

http://fuse.sourceforge.net/sshfs.html下载sshfs,它允许你跨网络安全挂载一个目录。

6、通过中间主机建立SSH连接

ssh -t reachable_host ssh unreachable_host

Unreachable_host表示从本地网络无法直接访问的主机,但可以从reachable_host所在网络访问,这个命令通过到reachable_host的“隐藏”连接,创建起到unreachable_host的连接。

7、将你的SSH公钥复制到远程主机,开启无密码登录 – 简单的方法

ssh-copy-id username@hostname

8、直接连接到只能通过主机B连接的主机A

ssh -t hostA ssh hostB

当然,你要能访问主机A才行。

9、创建到目标主机的持久化连接

ssh -MNf <user>@<host>

在后台创建到目标主机的持久化连接,将这个命令和你~/.ssh/config中的配置结合使用:

Host host ControlPath ~/.ssh/master-%r@%h:%p ControlMaster no

所有到目标主机的SSH连接都将使用持久化SSH套接字,如果你使用SSH定期同步文件(使用rsync/sftp/cvs/svn),这个命令将非常有用,因为每次打开一个SSH连接时不会创建新的套接字。

10、通过SSH连接屏幕

ssh -t remote_host screen –r

直接连接到远程屏幕会话(节省了无用的父bash进程)。

11、端口检测(敲门)

knock <host> 3000 4000 5000 && ssh -p <port> user@host && knock <host> 5000 4000 3000

在一个端口上敲一下打开某个服务的端口(如SSH),再敲一下关闭该端口,需要先安装knockd,下面是一个配置文件示例。

[options] logfile = /var/log/knockd.log [openSSH] sequence = 3000,4000,5000 seq_timeout = 5 command = /sbin/iptables -A INPUT -i eth0 -s %IP% -p tcp –dport 22 -j ACCEPT tcpflags = syn [closeSSH] sequence = 5000,4000,3000 seq_timeout = 5 command = /sbin/iptables -D INPUT -i eth0 -s %IP% -p tcp –dport 22 -j ACCEPT tcpflags = syn

12、删除文本文件中的一行内容,有用的修复

ssh-keygen -R <the_offending_host>

在这种情况下,最好使用专业的工具。

13、通过SSH运行复杂的远程shell命令

ssh host -l user $(<cmd.txt)

更具移植性的版本:

ssh host -l user “`cat cmd.txt`”

14、通过SSH将MySQL数据库复制到新服务器

mysqldump –add-drop-table –extended-insert –force –log-error=error.log -uUSER -pPASS OLD_DB_NAME | ssh -C user@newhost “mysql -uUSER -pPASS NEW_DB_NAME”

通过压缩的SSH隧道Dump一个MySQL数据库,将其作为输入传递给mysql命令,我认为这是迁移数据库到新服务器最快最好的方法。

15、删除文本文件中的一行,修复“SSH主机密钥更改”的警告

sed -i 8d ~/.ssh/known_hosts

16、从一台没有SSH-COPY-ID命令的主机将你的SSH公钥复制到服务器

cat ~/.ssh/id_rsa.pub | ssh user@machine “mkdir ~/.ssh; cat >> ~/.ssh/authorized_keys”

如果你使用Mac OS X或其它没有ssh-copy-id命令的*nix变种,这个命令可以将你的公钥复制到远程主机,因此你照样可以实现无密码SSH登录。

17、实时SSH网络吞吐量测试

yes | pv | ssh $host “cat > /dev/null”

通过SSH连接到主机,显示实时的传输速度,将所有传输数据指向/dev/null,需要先安装pv。

如果是Debian:

apt-get install pv

如果是Fedora:

yum install pv

(可能需要启用额外的软件仓库)。

18、如果建立一个可以重新连接的远程GNU screen

ssh -t user@some.domain.com /usr/bin/screen –xRR

人们总是喜欢在一个文本终端中打开许多shell,如果会话突然中断,或你按下了“Ctrl-a d”,远程主机上的shell不会受到丝毫影响,你可以重新连接,其它有用的screen命令有“Ctrl-a c”(打开新的shell)和“Ctrl-a a”(在shell之间来回切换),请访问http://aperiodic.net/screen/quick_reference阅读更多关于screen命令的快速参考。

19、继续SCP大文件

rsync –partial –progress –rsh=ssh $file_source $user@$host:$destination_file

它可以恢复失败的rsync命令,当你通过VPN传输大文件,如备份的数据库时这个命令非常有用,需要在两边的主机上安装rsync。

rsync –partial –progress –rsh=ssh $file_source $user@$host:$destination_file local -> remote

rsync –partial –progress –rsh=ssh $user@$host:$remote_file $destination_file remote -> local

20、通过SSH W/ WIRESHARK分析流量

ssh root@server.com ‘tshark -f “port !22″ -w -' | wireshark -k -i –

使用tshark捕捉远程主机上的网络通信,通过SSH连接发送原始pcap数据,并在wireshark中显示,按下Ctrl+C将停止捕捉,但也会关闭wireshark窗口,可以传递一个“-c #”参数给tshark,让它只捕捉“#”指定的数据包类型,或通过命名管道重定向数据,而不是直接通过SSH传输给wireshark,我建议你过滤数据包,以节约带宽,tshark可以使用tcpdump替代:

ssh root@example.com tcpdump -w – ‘port !22′ | wireshark -k -i –

21、保持SSH会话永久打开

autossh -M50000 -t server.example.com ‘screen -raAd mysession’

打开一个SSH会话后,让其保持永久打开,对于使用笔记本电脑的用户,如果需要在Wi-Fi热点之间切换,可以保证切换后不会丢失连接。

22、更稳定,更快,更强的SSH客户端

ssh -4 -C -c blowfish-cbc

强制使用IPv4,压缩数据流,使用Blowfish加密。

23、使用cstream控制带宽

tar -cj /backup | cstream -t 777k | ssh host ‘tar -xj -C /backup’

使用bzip压缩文件夹,然后以777k bit/s速率向远程主机传输。Cstream还有更多的功能,请访问http://www.cons.org/cracauer/cstream.html#usage了解详情,例如:

echo w00t, i’m 733+ | cstream -b1 -t2

24、一步将SSH公钥传输到另一台机器

ssh-keygen; ssh-copy-id user@host; ssh user@host

这个命令组合允许你无密码SSH登录,注意,如果在本地机器的~/.ssh目录下已经有一个SSH密钥对,ssh-keygen命令生成的新密钥可能会覆盖它们,ssh-copy-id将密钥复制到远程主机,并追加到远程账号的~/.ssh/authorized_keys文件中,使用SSH连接时,如果你没有使用密钥口令,调用ssh user@host后不久就会显示远程shell。

25、将标准输入(stdin)复制到你的X11缓冲区

ssh user@host cat /path/to/some/file | xclip

你是否使用scp将文件复制到工作用电脑上,以便复制其内容到电子邮件中?xclip可以帮到你,它可以将标准输入复制到X11缓冲区,你需要做的就是点击鼠标中键粘贴缓冲区中的内容。

如果你还有其它SSH命令技巧,欢迎在本文评论中帖出。

原文:http://blog.urfix.com/25-ssh-commands-tricks/





posted @ 2020-07-31 14:19 paulwong 阅读(48) | 评论 (0)编辑 收藏

一次删除前5个以外的其他文件夹的SHELL

子文件夹按字母排倒序,只保留前5个,其他的删除。
find path/to/folder/ -mindepth 1 -maxdepth 1 -type d | head -n -5 | xargs rm -rf

-find: the unix command for finding files / directories / links etc.
-/path/to/base/dir: the directory to start your search in.
-mindepth 1 -maxdepth 1 only search the first sub folder
-type d: only find directories
-head -n -5: Filter out all lines except the last 5
-xargs rm -rf: remove each given folder.

posted @ 2020-07-27 11:35 paulwong 阅读(119) | 评论 (0)编辑 收藏

一次删除10天前的文件夹的SHELL

find /path/to/base -mindepth 1 -maxdepth 1 -type d -ctime +10 | xargs rm -rf

-find: the unix command for finding files / directories / links etc.
/path/to/base/dir: the directory to start your search in.
-type d: only find directories
-ctime +10: only consider the ones with modification time older than 10 days
-exec  \;: for each such result found, do the following command in 
-rm -rf dir1 dir2 dir3 


https://stackoverflow.com/questions/13868821/shell-script-to-delete-directories-older-than-n-days

posted @ 2020-07-27 10:48 paulwong 阅读(133) | 评论 (0)编辑 收藏

Git Cherry Pick

https://www.atlassian.com/git/tutorials/cherry-pick


posted @ 2020-07-23 20:21 paulwong 阅读(36) | 评论 (0)编辑 收藏

Spring Cloud异步场景分布式事务怎样做?试试RocketMQ

例如有如下场景,新增一订单,同时为此订单的用户增加积分。场景对应场景表,积分对应积分表,如果要防止订单增加成功而积分增加不成功,则要将此两种操作放在一个事务下。

分布式的场景下,订单服务在一个JVM下,积分服务在另一个JVM下,两者要如何才能达到数据一致(原子)性?


https://zlt2000.gitee.io/2019-09-23-rocketmq-transaction/

posted @ 2020-07-15 10:33 paulwong 阅读(51) | 评论 (0)编辑 收藏

!!zlt-microservices-platform

1. 总体架构图

mark


2. 功能介绍

mark




https://github.com/zlt2000/microservices-platform 

https://zlt2000.gitee.io/

产品文档(付费,微信登录):
https://www.kancloud.cn/zlt2000/microservices-platform


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

LINUX SHELL

!!
https://tecadmin.net/tutorial/bash-scripting/

Check existence of input argument in a Bash shell script
https://stackoverflow.com/questions/6482377/check-existence-of-input-argument-in-a-bash-shell-script

How to Check if a File or Directory Exists in Bash
https://linuxize.com/post/bash-check-if-file-exists/

bash string compare to multiple correct values
https://stackoverflow.com/questions/21157435/bash-string-compare-to-multiple-correct-values

Bash – Check If Two Strings are Equal
https://tecadmin.net/tutorial/bash/examples/check-if-two-strings-are-equal/




posted @ 2020-07-06 09:23 paulwong 阅读(61) | 评论 (0)编辑 收藏

如何优雅地停止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 阅读(185) | 评论 (0)编辑 收藏

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

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

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

GIT资源


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

posted @ 2020-06-04 10:38 paulwong 阅读(38) | 评论 (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 阅读(41) | 评论 (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 阅读(71) | 评论 (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 阅读(49) | 评论 (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 阅读(49) | 评论 (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 阅读(49) | 评论 (0)编辑 收藏

JENKINS TOURIAL

https://huongdanjava.com/jenkins-2

posted @ 2020-04-07 10:29 paulwong 阅读(69) | 评论 (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 阅读(58) | 评论 (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 阅读(63) | 评论 (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 阅读(64) | 评论 (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 阅读(90) | 评论 (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 阅读(267) | 评论 (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 阅读(90) | 评论 (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 阅读(79) | 评论 (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 阅读(87) | 评论 (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 阅读(102) | 评论 (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 阅读(363) | 评论 (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 阅读(123) | 评论 (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 阅读(86) | 评论 (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 阅读(76) | 评论 (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 阅读(252) | 评论 (0)编辑 收藏

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

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

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

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

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

posted @ 2020-03-06 13:45 paulwong 阅读(110) | 评论 (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 阅读(126) | 评论 (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 阅读(348) | 评论 (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 阅读(100) | 评论 (0)编辑 收藏

采用 jstatd 监控服务器


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

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

mongodb错误记录

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


posted @ 2020-02-21 08:50 paulwong 阅读(74) | 评论 (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 阅读(276) | 评论 (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 阅读(90) | 评论 (0)编辑 收藏

windows下jenkins提示文件名太长

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

git config --global core.longpaths true

posted @ 2020-02-14 14:37 paulwong 阅读(89) | 评论 (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 阅读(303) | 评论 (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 阅读(196) | 评论 (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 阅读(257) | 评论 (0)编辑 收藏

如何加CRON JOB到LINUX


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

posted @ 2020-01-21 15:50 paulwong 阅读(101) | 评论 (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 阅读(97) | 评论 (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 阅读(110) | 评论 (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 阅读(88) | 评论 (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 阅读(99) | 评论 (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 阅读(145) | 评论 (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 阅读(96) | 评论 (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 阅读(116) | 评论 (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 阅读(261) | 评论 (0)编辑 收藏

Git版本控制与工作流



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

posted @ 2019-11-26 11:21 paulwong 阅读(118) | 评论 (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 阅读(89) | 评论 (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 阅读(338) | 评论 (0)编辑 收藏

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

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

posted @ 2019-11-20 16:12 paulwong 阅读(163) | 评论 (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 阅读(128) | 评论 (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 阅读(121) | 评论 (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 阅读(130) | 评论 (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 阅读(133) | 评论 (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 阅读(252) | 评论 (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 阅读(267) | 评论 (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 阅读(91) | 评论 (0)编辑 收藏

SPRING INTEGRATION DSL DEMO

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

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

Spring Integration 中文手册 - GOOD

Spring Integration 中文手册 (1)


Spring Integration 中文手册 (2)

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

LINUX配置DNS

vi /etc/resolv.conf

nameserver 8.8.8.8

posted @ 2019-10-10 10:55 paulwong 阅读(132) | 评论 (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 阅读(143) | 评论 (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 阅读(110) | 评论 (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 阅读(125) | 评论 (0)编辑 收藏

CI/CD 资源

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

posted @ 2019-09-18 17:08 paulwong 阅读(159) | 评论 (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 阅读(120) | 评论 (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 阅读(158) | 评论 (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 阅读(164) | 评论 (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 阅读(127) | 评论 (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 阅读(638) | 评论 (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 阅读(233) | 评论 (0)编辑 收藏

Guide to the Most Important JVM Parameters

https://www.baeldung.com/jvm-parametersa

Optimising Your Minecraft: Jvm Arguments
https://xealgaming.net/threads/optimising-your-minecraft-jvm-arguments.4758/

posted @ 2019-08-01 16:55 paulwong 阅读(108) | 评论 (0)编辑 收藏

JVM内存配置

JVM内存主要分为两个部分,分别是PermanentSapce和HeapSpace。

PermantSpace主要负责存放加载的Class类级对象如class本身,method,field等反射对象,一般不用配置。

JVM的Heap区可以通过-X参数来设定。HeapSpace= {Old + NEW {= Eden , from, to } }

当一个URL被访问时,内存申请过程如下:

  1. JVM会试图为相关Java对象在Eden中初始化一块内存区域 
  2. 当Eden空间足够时,内存申请结束。否则到下一步 
  3. JVM试图释放在Eden中所有不活跃的对象(这属于1或更高级的垃圾回收), 释放后若Eden空间仍然不足以放入新对象,则试图将部分Eden中活跃对象放入Survivor区
  4.  Survivor区被用来作为Eden及OLD的中间交换区域,当OLD区空间足够时,Survivor区的对象会被移到Old区,否则会被保留在Survivor区 
  5. 当OLD区空间不够时,JVM会在OLD区进行完全的垃圾收集(0级) 
  6. 完全垃圾收集后,若Survivor及OLD区仍然无法存放从Eden复制过来的部分对象,导致JVM无法在Eden区为新对象创建内存区域,则出现”out of memory错误”

Xms/Xmx:定义NEW+OLD段的总尺寸,ms为JVM启动时NEW+OLD的内存大小;mx为最大可占用的NEW+OLD内存大小。。在用户生产环境上一般将这两个值设为相同,以减少运行期间系统在内存申请上所花的开销; 

NewSize/MaxNewSize:定义单独NEW段的尺寸,NewSize为JVM启动时NEW的内存大小;MaxNewSize为最大可占用的NEW的内存大小。在用户生产环境上一般将这两个值设为相同,以减少运行期间系统在内存申请上所花的开销;

Xms/Xmx和NewSize/MaxNewSize定义好后,OLD区间也自然定义完毕了,即OLD区初始大小=(Xms-NewSize),OLD区最大可占用大小=(Xmx-MaxNewSize); 

PermSize/MaxPermSize:定义Perm段的尺寸,PermSize为JVM启动时Perm的内存大小;MaxPermSize为最大可占用的Perm内存大小。在用户生产环境上一般将这两个值设为相同,以减少运行期间系统在内存申请上所花的开销。

posted @ 2019-08-01 16:44 paulwong 阅读(155) | 评论 (0)编辑 收藏

使用 Awaitility 测试异步代码

自动化工具 异步校验工具 awaitility 快速入门
https://testerhome.com/topics/7408


https://yanbin.blog/test-asyn-call-with-awaitility/

Introduction to Awaitlity
https://www.baeldung.com/awaitlity-testing




posted @ 2019-08-01 10:06 paulwong 阅读(233) | 评论 (0)编辑 收藏