eclipse离线安装activiti designer

根据activiti官网上介绍说明的,在eclipse安装activiti designer居然失败??!!
报错:https://www.activiti.org/designer/update/content.xml 找不到。

没有办法,只能通过离线方式来安装activiti designer,通过 https://www.activiti.org/designer/archived/activiti-designer-5.18.0.zip 下载,但是想下载其他版本的怎么办?对不起,没有目录列表,只能靠猜了。同时这个地址下载网速很慢,所以依靠我们伟大的迅雷,一会时间就下载完了,迅雷牛X。

下载好后,在eclipse中,help -> install new software -> archive,选中下载的文件,然后name中随便输入一个activiti即可,在 contact all update sites during install to find requied software 前面的checkbox要打上勾,因为可能会对你的eclipse进行升级必要的图形组件。选好后然后点击 finish,根据后面提示安装。

安装的时候,假如报错的话,那么可能你的eclipse版本过低了,最好下载一个最新的eclipse。我用的是一个比较新的eclipse,所以在安装的时候,没有其他额外的组件需要安装(都有了),所以安装的速度非常快,没有任何问题。

网上说的其他各种各样的安装法,看了都头痛,但是我这种办法是最有效最便捷最高效的。

e.printStackTrace()没有打印出完整的堆栈

单位里的一个很老的应用,在分析一个问题的时候,查看日志,看到打印异常的地方仅仅出现了一句:java.lang.NullPointerException。

该应用的源代码都已经找不到了(真的太老了),只能反编译class文件看,发现就是一句 e.printStackTrace(), 那也应该打印出堆栈啊,为什么堆栈没有打出来?

找到网上的几篇文章:
java.lang.NullPointerException:null 没有打印出任何堆栈信息的解决办法
NullPointerException in Java with no StackTrace

需要在java的启动命令中加入:
-XX:-OmitStackTraceInFastThrow

假如是 tomcat 的应用,那么需要在 bin/catalina.sh 中,大致如下位置,加入:

# $Id: catalina.sh 1146097 2011-07-13 15:25:05Z markt $
# -----------------------------------------------------------------------------

#在这个位置加入下面这一行
JAVA_OPTS=" -XX:-OmitStackTraceInFastThrow"

# OS specific support.  $var _must_ be set to either true or false.
cygwin=false
os400=false

重启后,堆栈出现了!

对这篇文章进行3个补充:
1,堆栈被丢弃,不单单出现在 e.printStackTrace() 中,还出现在 log4j,logback 中,比如打印日志如下:
logger.error(e.getMessage(), e);
这样,往往e的堆栈在上述情况下会不打印完整。
2,到目前为止,我只看到过 java.lang.NullPointerException:null 这样的异常,其他的异常发生堆栈被丢弃没有遇到过。
3,堆栈被丢弃,不仅仅发生在老版本的jdk上,新版本的jdk都会出现,也就是说jdk5,jdk6,jdk7,jdk8都会出现。

代理那些事

本文只涉及代理的一些技术性原理和术语解释。

我在工作中经常用到代理:比如nginx的反向代理,使用littleproxy来作为程序可控的正向代理,公司内作为正向代理访问外网一些资料。

什么是代理

那么什么是代理,其实看下面的图,很简单,不需要用过多的文字来描述:

用户要访问app,因为各种原因直接不可达,那么在用户和app之间放一层代理:
1. 用户传入的请求流到代理
2. 代理将流转到目标app
3. app的响应流到代理
4. 代理将响应流转给用户
说白了,代理的作用就是你给我什么流,我转就是了。

那么假如让你来写一个代理软件,是不是很简单,只需要一个socket接收,另一个socket转发就可以了?理论上是这样的,比如HAPROXY就是这样的。但是实际上还是有很多地方需要考虑的,更多的是协议!

代理和协议转换

假如代理实现的是http/https的协议,那么用户传入的请求必须是http/https;假如代理实现的是mysql的协议,那么用户传入的请求必须是mysql协议;假如代理实现的是socks的协议,那么用户传入的请求必须是socks协议。

下面以socks代理服务器来举例说明:

等一下,有几个情况先看一下:
1. firefox中设置好了socks代理服务器后,居然能够使得socks代理http协议的流量?也就是说上面图中,发出http协议的请求,居然socks代理能够接受?原因是:firefox事先将http转换成了socks协议然后再发送,同理在响应的时候将socks协议转成了http协议了。
2. curl中,通过 --socks 来访问socks代理,那么curl的收发代码中肯定有对 http 互转 socks 的代码。
3. 在git中,我们可以设置如下来进行git的代理访问:

git config --global http.proxy 'socks5://127.0.0.1:1080'
git config --global https.proxy 'socks5://127.0.0.1:1080'

但是在git中,非常奇怪的能看到 http.proxy 中,居然可以设置 socks的协议。这个不像上2个情况,比如第1个firefox是对http和socks进行分别设置,然后由firefox来转换协议;第2个的curl通过--socks和-x来区分,由curl来转换协议。那么git怎么弄了个在 http 中设置成 socks 的协议呢?
这是因为只有HTTP_PROXY这种约定俗成的环境变量定义,而没有SOCKS_PROXY这个环境变量。git采用了一个取巧的办法(尽管我没有查git的源代码,但是根据原理肯定能猜出来),就是判断HTTP_PROXY的内容假如是socks协议的就转socks。

上面3个情况,都可以通过设置环境变量进行全局代理,详见HTTP_PROXY,HTTPS_PROXY,NO_PROXY,ALL_PROXY来自什么协议?

通过上面3个情况可以看到,对于 http互转socks这个过程,都是在客户端本身内部完成的,而和socks代理没有任何关系。

根据上面的理论,再来看一个现象,分析一下为什么会出现这样的情况:
minikube start
然后会去下载一些文件,因为一些原因下载不下来就会卡在那里,这个时候,假如用HTTP_PROXY=socks://127.0.0.1:1080,那么也还是下载不下来,为什么原因呢?因为minikube start会调用curl去下载文件,而curl不同于git,curl是http_proxy和socks是分开的。问题就出在这里。那怎么办呢?有几种解决办法:1,采用privoxy对http和socks进行互换;2,直接用nginx来作为http的代理,详见我的另一篇博文玩转nginx:用nginx搭建正向代理,支持http和https,当然假如就minikube这个来说,建议采用阿里修改的minikube,非常好用,阿里minikube

正向代理和认证

从我的那篇关于nginx代理的文章中知道,nginx或者tinyproxy从http的原理上来说,因为是短连接,所以天生对于认证不方便或者实现不了(注意,这里的认证不是说nginx带的安全认证,那个安全认证在代理中没有用,你总不可能让你所访问的网站上带上你的认证信息吧)。

所以,对于socks来说,在用户和代理之间再加一个代理客户端,这个代理客户端就是和代理进行长连接,当然也具有认证的功能。

反向代理和正向代理


反向代理:关键点是用户在外网,代理在内网。

正向代理:关键点是用户和代理都在内网中。

玩转nginx:用nginx搭建正向代理,支持http和https

什么是代理,什么是正向代理,什么是反向代理,详见代理那些事

用nginx搭建反向代理,这个是nginx最大的功能,网上资料一大堆,这里不再复述。

用nginx搭建正向代理,假如只是http的代理,那么网上也是一大堆,也都是正确的;但是https的代理,网上所讲得一大堆,绝大部分是错误的。(吐槽一下,明明是很明显错误的内容,难道作者都没有验证一下就发出来吗?)希望读者能通过搜索引擎找到我这一篇文章,而不是先找到那些错误的文章,耗时耗精力。

nginx的正向代理支持https,其实也是很简单,只要 nginx 加入 ngx_http_proxy_connect_module 这个模块即可。(所以网上的文章假如说支持https的nginx正向代理没有提到 ngx_http_proxy_connect_module 这个模块,看都不用看,是错误的)

下面是安装步骤:
#所有操作在 /opt 目录下
cd /opt
git clone https://github.com/chobits/ngx_http_proxy_connect_module.git
wget http://nginx.org/download/nginx-1.14.2.tar.gz
tar xzvf nginx-1.14.2.tar.gz
cd nginx-1.14.2
patch -p1 < /opt/ngx_http_proxy_connect_module/patch/proxy_connect_xxxx.patch
#注意这里选用的补丁文件proxy_connect_xxxx.patch需要参照 https://github.com/chobits/ngx_http_proxy_connect_module 中的nginx不同版本来决定,如下所示:

因为我们的nginx是1.14版本,参照上面,所以上面的命令应该是:
patch -p1 < /opt/ngx_http_proxy_connect_module/patch/proxy_connect_1014.patch
假如不成功,会显示错误信息。成功后,进行编译nginx
./configure --add-module=/path/to/ngx_http_proxy_connect_module
make && make install

然后nginx的配置如下:

server {
        listen 6281;
        resolver 114.114.114.114;

        proxy_connect;
        proxy_connect_allow            443 563;
        proxy_connect_connect_timeout  10s;
        proxy_connect_read_timeout     10s;
        proxy_connect_send_timeout     10s;

        location / {
                #下面的居然是 http 而不是 https,很奇怪的设置,但是事后验证确实没有问题
                proxy_pass      http://$host;
                proxy_set_header        Host $host;
        }
}

然后启动nginx,这样即可支持 https 的正向代理。

另外多说两句:
1. 一定要知道这样一件很重要的事情,nginx的 http/https的代理,是不支持认证的(不是说nginx不支持认证,而是这种机制下认证是没有用的),所以该代理只能放在内网里,否则你就等着哪一天突然你的外网nginx被别人使用了。
2. 支持 http/https 的代理,还可以用 tinyproxy。

linux中dns的设置

linux查看dns的顺序是依照:
1. /etc/hosts
2. /etc/sysconfig/network-scripts/ifcfg-eth0
3. /etc/resolve.conf

  1. /etc/hosts:仅在特定情况下使用,这个如何使用大家都知道。
  2. /etc/sysconfig/network-scripts/ifcfg-eth0,(ifcfg-eth0 这个每个人的网卡接口不一样,名字也不一样):在其中写上比如:
    DNS1=114.114.114.114
    DNS2=8.8.8.8
    DNS3=xxx.xxx.xxx.xxx

    按照顺序往下进行解析。
  3. /etc/resove.conf:上面2个都没有设定,则最后找到这个文件,这个文件中的内容如下:
    nameserver 114.114.114.114
    nameserver 8.8.8.8
    nameserver xxx.xxx.xxx.xxx

在国内,最好用 114.114.114.114
在国外,最好用8.8.8.8或者8.8.4.4

HTTP_PROXY,HTTPS_PROXY,NO_PROXY,ALL_PROXY来自什么协议?

我们在使用 curl 的时候,假如需要用到代理,则我们常常会这么写:

curl --socks5 xxx.xxx.xxx.xxx:xxxx http://example.com/
或者
curl -x "http_proxy://xxx.xxx.xxx.xxx:xxxx" http://example.com/

参考:Curl使用socks5 or http代理命令

假如使用频繁嫌麻烦,我们可以在环境变量中设置代理,比如:

export HTTP_PROXY=xxx.xxx.xxx.xxx:xxxx
export HTTPS_PROXY=xxx.xxx.xxx.xxx:xxxx
export NO_PROXY=xxx.xxx.xxx.xxx,qq.com,baidu.com

然后只需简单的 curl http://example.com 即可默认走代理了。类似curl的有很多,大家好像不约而同的都认识 HTTP_PROXY,HTTPS_PROXY,NO_PROXY,ALL_PROXY这些环境变量,这个时候我就纳闷了,这些环境变量究竟出自什么协议使得大家都认识,为什么以前我都不知道?带着这样的问题,我去查了资料,看下面这个链接:
Are HTTP_PROXY, HTTPS_PROXY and NO_PROXY environment variables standard?,里面说得很清楚,原来是:

This is more a convention than a standard. “这更像是一种惯例而非标准。”

下面稍微解释一下这4个惯例的意思:

HTTP_PROXY: 不用解释了,看名字就明白了
HTTPS_PROXY: 不用解释了,看名字就明白了
ALL_PROXY: 假如上面2个是同一个,那么可以用这一个来定义就够了,但是惯例更多的是用上面2个
NO_PROXY: 可以是IP,可以是域名,多个以逗号分隔,比如:xxx.xxx.xxx.xxx,baidu.com,凡是这个列表中的都不进行代理

box-sizing用在哪里和怎么用

很惭愧,才第一次看到 box-sizing 这个CSS属性,查找了 box-sizing 相关知识点,很多都是讲述 box-sizing 这个属性是什么意思,但是这个属性用在哪里?为什么要用这个属性?怎么用好这个属性?这样的资料找了半天才找到一篇,不过这一篇文章写得是非常不错,我这里给上链接:css3 box-sizing属性详解,这篇文章是转自W3CPLUS的,可是原始的文章现在居然是要付费才能看了,唉,文章确实不错,但是这个付费就不太好了。

所以在这里,我并不进一步讲 box-sizing 这个属性怎么回事,看上面的文章写得清清楚楚了,这篇文章只是对其中的一些做一些解释。

首先看一段示例:

<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="utf-8" />
<title>box-sizing</title>
</head>
<body>
<div style="margin: 0 auto;width: 700px;font-size: 30px;">在chrome浏览器下,出现了2种盒子模型</div>
<form>
1,<input style="width: 100px;">
<pre style="display: inline-block;font-weight: bold;margin-left: 10px">
&lt;input style="width: 100px;"&gt;:W3C盒子模型
</pre>
<br><br>
2,<input style="width: 100px; border: 20px solid black;">
<pre style="display: inline-block;font-weight: bold;margin-left: 10px">
&lt;input style="width: 100px; border: 20px solid black;"&gt;:W3C盒子模型
</pre>
<br><br>
3,<input style="width: 100px;" type="submit" value="aaa">
<pre style="display: inline-block;font-weight: bold;margin-left: 10px">
&lt;input style="width: 100px;" type="submit" value="aaa"&gt;:IE盒子模型
</pre>
<br><br>
4,<input style="width: 100px; border: 10px solid black;" type="submit" value="aaa">
<pre style="display: inline-block;font-weight: bold;margin-left: 10px">
&lt;input style="width: 100px; border: 10px solid black;" type="submit" value="aaa"&gt;:IE盒子模型
</pre>
<br><br>
5,<select style="width: 100px; border: 10px solid black;">
    <option>aaa</option>
</select>
<pre style="display: inline-block;font-weight: bold;margin-left: 10px">
&lt;select style="width: 100px; border: 10px solid black;"&gt;
    &lt;option&gt;aaa&lt;/option&gt;
&lt;/select&gt;
:IE盒子模型
</pre>
</form>
</body>
</html>

上面代码块中的html拷贝后,需要将其中全角的&转为半角的&,下面是截图显示:

可以看到,在同一个浏览器下面居然同时出现了2种盒子模型,从上面的文章中可以看到,form表单中的有些元素比如

<submit>,<select>

默认还是IE盒子模型,所以第一个用法:在表单的某些元素中指定 box-sizing 是什么,明确指定,如此不会混淆。第二个用法:用在布局中,上文中的例子已经说得非常仔细易懂了,这里不再重复。

再接着上面那个例子,加入 bootstrap.css ,如:

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css">

截图显示如下:

因为bootstrap.css中包含如下定义:

* {
  -webkit-box-sizing: border-box;
     -moz-box-sizing: border-box;
          box-sizing: border-box;
}

所以,bootstrap从一开始就约束 boxmodel 为IE的盒子模式,如此就统一了标准,非常棒。

另外,不用 box-sizing 也是可以的,就是在调整大小的时候,就是要麻烦一些,但是也能做到。

集群下的quartz出现重复调度的问题解决

我们采用 xxl-job 来作为我们的调度任务中心,而 xxl-job 采用的是 quartz 作为它的调度内核。

我们在部署的时候,采用了2台服务器集群的方式,有一天在分析一个工单的时候发现这2台服务器同时执行了某个时间段的调度2次,于是开始了这个问题查找分析过程。

首先我们找到了这篇文章:多个xxl-job-admin节点,任务重复执行问题,这篇文章描述了为什么会发生这个问题,以及该如何解决,但是这篇文章写得比较简短,还是不明白。

接着我们找到了这篇文章:记一次Quartz重复调度(任务重复执行)的问题排查,这篇文章的作者花了很多心思写这篇文章,写得很透彻。我们通过一边参照他的文章,一边看源代码,一边再进行不同阶段跟踪测试,证实了这篇文章所写得完全正确,也证实了他的解决办法是正确的。

下面就这个问题的出现原因再啰嗦几句,希望能更进一步的解惑:
下面这张图非常重要,我这边摘抄过来(链接地址没法用,所以我摘抄过来,希望作者理解一下这里用来说明问题):

  1. 上面图中出现的那几个Lock,都是基于数据库的分布式锁,这个分布式锁采用了数据库的for update行级锁的机制来实现。这个机制保证了在一开始的时候只有1个调度服务才能获取到调度任务。

  2. 上图中的第一个Lock,非常关键,这个Lock是出现重复调度的关键所在,但是 quartz 默认这个 Lock 是空的!!!这就会出现在上面文中所说的问题,2个调度服务同时发出请求,第1个非常快速的完成,会导致第2个也能在瞬间拿到。具体可以参见这个过程:

  3. 上图中的作者,有部分语言描述有丁点漏洞,上图中的“线程A”,“线程B”,会让读者认为只是在一个进程中会出现重复调度的问题,其实不然,应该改成“进程A”,”进程B”

springboot下报错“MethodInvokingJobDetailFactoryBean is not serializable”

quartz是在java语言中用得最为广泛的任务调度框架,spring对quartz进行了集成,方便了开发和使用。

在springboot下,我们对一个采用quartz的app进行多个集群部署的时候,对于quartz的集群采用数据库集群方式,报了“MethodInvokingJobDetailFactoryBean is not serializable”的错误。

原因是spring为了让开发更方便,耦合性更低,对quartz中 JobDetailFactoryBean 用 MethodInvokingJobDetailFactoryBean 进行了封装,假如quartz是单机运行那是没有关系的,但是当采用数据库方式进行集群的时候,quartz会把 JobDetailFactoryBean 序列化存储到数据库中,假如用 MethodInvokingJobDetailFactoryBean 的话,就会报错,说不能序列化。

解决的办法参考这篇文章:使用Spring + quartz集群持久化时注意事项,但是这篇文章中只讲了在spring下如何修改。

下面讲一下在 springboot 中如何修改:
一、需要将原先定义的pojo 的 Job 改成实现 Job 接口:

@Component("myJobHandler")
public class MyJobHandler implements Job {

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        System.out.println("do it ......");
    }

}

二、在配置中如下设置:

@Bean(name = "myJobDetailFactoryBean")
    public JobDetailFactoryBean getMyJobDetailFactoryBean() {
        JobDetailFactoryBean jobDetail = new JobDetailFactoryBean();
        jobDetail.setJobClass(com.champbay.quartztest.MyJobHandler.class);
        jobDetail.setDurability(true);
        return jobDetail;
    }

    @Bean(name = "myCronTriggerFactoryBean")
    public CronTriggerFactoryBean getMyCronTriggerFactoryBean(
            @Qualifier("myJobDetailFactoryBean") JobDetailFactoryBean jobDetail) {
        CronTriggerFactoryBean tigger = new CronTriggerFactoryBean();
        tigger.setJobDetail(jobDetail.getObject());
        // 每1分钟     
        tigger.setCronExpression("0 0/1 * * * ?");
        return tigger;
    }

本文结束。

linux免密码登录

在测试机上,通过 ssh 登录,需要键入密码,有时候会觉得特别麻烦,测试机无所谓安全,所以有没有“免密码登录”的功能(对于公钥方式的那种免密登录不是本篇文章所涉及的)

可以按照如下步骤进行:
1. vi /etc/ssh/sshd_config

PermitEmptyPasswords yes

也就是允许空密码登录。
2. systemctl restart sshd,然后再登录发现还是需要输入密码。原因是该用户本来就有密码,那么:
3. passwd -d someuser-name,也就是把某个用户的密码删除即可。

本文结束。