kubernets下使用cert-manage签发免费证书

Install cert-manager

  1. 安装helm
  2. 创建cert-manager命名空间
kubectl create namespace cert-manager
  1. 添加Jetstack helm仓库
helm repo add jetstack https://charts.jetstack.io
  1. Update heml repo
helm repo update
  1. Install cert-manager
helm install \
  cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --version v1.10.0 \
  --set installCRDs=true

使用阿里webhook签发泛域名证书

  1. install alidns-webhook
# Install alidns-webhook to cert-manager namespace. 
kubectl apply -f https://raw.githubusercontent.com/pragkent/alidns-webhook/master/deploy/bundle.yaml
  1. 创建RAM用户。
    1. 使用阿里云账号登录RAM控制台。
      在访问控制控制台左侧导航栏,选择身份管理 > 用户。
      在用户页面,单击创建用户
      在创建用户页面输入登录名称和显示名称,选中OpenAPI调用访问,然后单击确定。
      记录RAM用户的AccessKey ID和AccessKey Secret。
    2. 授予RAM用户AliyunDNSFullAccess策略
      在用户页面单击上文创建的RAM用户右侧操作列下的添加权限。
      在添加权限面板系统策略下输入AliyunDNSFullAccess,单击AliyunDNSFullAccess,单击确定。
    3. 授予RAM用户自定义策略。
      在访问控制控制台左侧导航栏,选择权限管理 > 权限策略。
      在权限策略页面单击创建权限策略。
      在创建权限策略页面单击脚本编辑页签,然后输入以下内容,单击下一步。
{
    "Version": "1",
    "Statement": [
        {
            "Action": "*",
            "Resource": "acs:alidns:*:*:domain/#domain-name",
            "Effect": "Allow"
        },
        {
            "Action": [
                "alidns:DescribeSiteMonitorIspInfos",
                "alidns:DescribeSiteMonitorIspCityInfos",
                "alidns:DescribeSupportLines",
                "alidns:DescribeDomains",
                "alidns:DescribeDomainNs",
                "alidns:DescribeDomainGroups"
            ],
            "Resource": "acs:alidns:*:*:*",
            "Effect": "Allow"
        }
    ]
}

输入权限策略的名称,单击确定
4. 在Base64执行以下命令,为步骤1中的AccessKey ID和AccessKey Secret进行Base64编码。

echo -n <AccessKey ID> | base64
echo -n <AccessKey Secret>  | base64
  1. Create secret contains alidns credentials
    apiVersion: v1
    kind: Secret
    metadata:
      name: alidns-secret
      namespace: cert-manager
    data:
      access-key: ****base64
      secret-key: ****base64
    

    注意这里需要对AccessKey ID和AccessKey Secret进行Base64编码。

  2. Create ClusterIssuer

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-staging
spec:
  acme:
    # Change to your letsencrypt email
    email: 11@qq.com
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: letsencrypt-staging-account-key
    solvers:
    - dns01:
        webhook:
          groupName: acme.yourcompany.com
          solverName: alidns
          config:
            region: ""
            accessKeySecretRef:
              name: alidns-secret
              key: access-key
            secretKeySecretRef:
              name: alidns-secret
              key: secret-key
  1. Issue a certificate
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: abc-lts
spec:
  secretName: letsencrypt-staging
  dnsNames:
  - "*.abc.com"
  issuerRef:
    name: letsencrypt-staging
    kind: ClusterIssuer
  1. Verify certificate
$ kubectl get certificate -n namespace

READY 为 False:则表示签发失败,可以通过 describe 命令查看 event 来排查失败原因。
READY 为 True:则表示签发成功,证书将保存在所指定的 Secret 中。例如,default/test-mydomain-com-tls。

  1. Create ingress
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: abc
  annotations:
    # 务必添加以下两个注解, 指定 ingress 类型及使用哪个 cluster-issuer
    kubernetes.io/ingress.class: "nginx"
    cert-manager.io/cluster-issuer:"letsencrypt-staging"

    # 如果你使用 issuer, 使用以下注解 
    # cert-manager.io/issuer: "letsencrypt-staging"
spec:
  tls:
  - hosts:
    - example.example.com                # TLS 域名
    secretName: example-tls   # 用于存储证书的 Secret 对象名字 
  rules:
  - host: example.example.com
    http:
      paths:
      - path: /
        backend:
          serviceName: abc
          servicePort: 80

Reference

  1. 使用cert-manager管理网关的证书
  2. 使用cert-manager签发免费证书
  3. 使用 cert-manager 签发免费证书
  4. kubernetes使用cert-manager自动签发letsencrypt证书
Posted in 架构运维 | Leave a comment

Letsencrypt和cerbot自制免费证书,使用docker compose部署

生成证书

docker-compose run --rm certbot certonly --manual --preferred-challenges=dns --email service@qq.com --agree-tos --expand -d abc.com,*.abc.com --server https://acme-v02.api.letsencrypt.org/directory

在生成证书过程中,会提示你设置一个域名(TXT)解析,来验证域名所有权,根据提示操作即可。还有一种 webroot的方式,不需要通过域名解析来验证域名所有权,但是不支持泛域名解析,在此不采用。

生成后会有如下提示,可以看到证书的存储路径

生成Perfect Forward Security(PFS)键值

PFS(perfect forward secrecy),中文可叫做完全前向保密。要求一个密钥只能访问由它所保护的数据;用来产生密钥的元素一次一换,不能再产生其他的密钥;一个密钥被破解,并不影响其他密钥的安全性。

#创建目录
mkdir /etc/ssl/private/ -p
#执行命令
cd /etc/ssl/private/
openssl dhparam 2048 -out dhparam.pem

nginx配置文件修改

server {
    listen 80;
    listen [::]:80;

    server_name example.org www.example.org;
    server_tokens off;

    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }

    location / {
        return 301 https://example.org$request_uri;
    }
}

docker compose文件修改

version: '3'
services:
 nginx:
     image: nginx:1.8.1
     ports:              #端口映射
         - "80:80"
         - "443:443"
     command: [ "/bin/sh", "-c", "while :;do sleep 24h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"" ]
     volumes:
         - "${PWD}/nginx/nginx.conf:/etc/nginx/nginx.conf"
         - "${PWD}/nginx/conf.d:/etc/nginx/conf.d"   #将宿主机上nginx配置文件映射到容器中
         - "${PWD}/dataweb/abc:/var/www/html/abc" #映射网站根目录
         - "${PWD}/nginx/log:/var/log/nginx"
         - ./certbot/conf:/etc/letsencrypt
         - ./certbot/www:/var/www/certbot
         - /etc/ssl:/etc/ssl
     networks:
         - app_net
     container_name: "compose-nginx"  #容器名称
 certbot:
     container_name: certbot
     image: certbot/certbot:latest
     # command: certonly --webroot --webroot-path=/var/www/certbot --agree-tos --email service@qq.com -d  abc.com
     #entrypoint: ["/bin/sh", "-c", "trap exit TERM;while :; do certbot renew --webroot -w /var/www/certbot; sleep 24h & wait $${!}; done;"]
     volumes:
       - ./certbot/conf:/etc/letsencrypt
       - ./certbot/logs:/var/log/letsencrypt
       - ./certbot/www:/var/www/certbot

nginx的ssl配置

首先增加443端口的监听,然后在conf文件中增加一个server指令,以前写在80的server中的设置都写在这个server中,并且在80端口的server中设置跳转,将http请求跳转到https

server {
    listen       80;
    server_name  abc.com;

    return 301 https://abc.com$request_uri;

     location ~ /.well-known/acme-challenge {
         allow all;
         root /var/www/certbot;
     }
}

server {
       listen 443 ssl default_server;
       server_name abc.com;
       server_tokens off;

       access_log  /var/log/nginx/abc.com.access.log  main;
       index   index.php index.html index.htm;

       ssl_session_timeout 5m;
       ssl_prefer_server_ciphers on;
       ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5;
       ssl_protocols TLSv1.2 TLSv1.1 TLSv1;

       ssl_stapling on;
       ssl_stapling_verify on;

       ssl_certificate /etc/letsencrypt/live/xiaocilao.com/fullchain.pem;
       ssl_certificate_key /etc/letsencrypt/live/xiaocilao.com/privkey.pem;

       ssl_dhparam /etc/ssl/private/dhparam.pem;

        #charset koi8-r;
        #access_log  logs/host.gaoxueping.log  main;
        root   /var/www/html/abc;
        location / {
            try_files $uri $uri/ /index.php?q=$uri&$args;
            #if (!-e $request_filename){
                #rewrite ^(.*)$ /index.php?s=$1 last;
                #break;
            #}
            index  index.php index.html index.htm;
        }

        error_page  404              /404.html;
        location = /404.html {
            root   /usr/share/nginx/html;
        }
        # redirect server error pages to the static page /50x.html
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   /var/www/html/abc;
        }

        location ~ \.php$ {
            fastcgi_split_path_info  ^(.+\.php)(.*)$;
            fastcgi_pass   php7:9000;
            fastcgi_index  index.php;
            fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
            include        fastcgi_params;
        }

        location ~ /\. {
            deny  all;
        }

        location ~ /\.ht {
            deny  all;
        }
}

自动更新

可以在docker compose中设置commond来自动定时更新。

Reference

Boilerplate for nginx with Let’s Encrypt on docker-compose
HTTPS using Nginx and Let’s encrypt in Docker

Posted in 架构运维 | Leave a comment

ISC20 Students Cluster Competition 国际超算比赛参赛心得

ISC20 Student Cluster Competition结束了,冠军是中国科大,亚军是南非超算,第三名是清华,第四名是南阳理工,我们爱丁堡大学(TeamEPCC)得到了第五名,结果差强人意,我负责的项目Elmer/Ice也是第五名,西班牙小哥负责的BERT得到第四,另外一个西班牙小哥负责的coding challenge和tinker也是第四,其他两位同学成绩不是很理想,那就简单写一下本次比赛的一些心得吧。先上一张我们的团队图片以及疫情期间在家打比赛的照片。



首先简单介绍一下这个比赛,这个比赛全称是国际超算比赛(ISC),每年举办一次,基本都是在德国举办,2020年的比赛应该是在法兰克福举办,但是突然爆发的COVID-19让这场线下比赛改为了远程比赛,这就意味着每支队伍不再需要单独构建自己的超算集群,这样的话比赛的质量就打了一点折扣了,因为这个比赛的一个很重要的task就是构建自己的超算集群,然后在集群上跑Benchmarking和HPC Applications,谁的速度快,准确性高便取胜。

远程比赛也就意味着committee会提供一个shared cluster,我原本以为committee会自己搭建比赛集群或者使用德国或欧洲的超算集群来作为比赛的平台,出乎我的意料,作为德国的超算比赛,居然用了一个新加坡的集群(NSCC),不知道是不是商业赞助,更加坑爹的是,这个比赛集群是个巨坑,细节后续再讲。

因为是shared cluster,LINPACK, HPCG, and HPCC等这些Benchmarks就不会再作为比赛项了,于是组委会将浙这些项目改成了Gromacs,Tinker-HP等,同时今年新增了一个coding challenge项目。我们比赛的队伍有五个人,不同于其他队伍都是本科生(其他队伍都比我们人多一点),我们的队伍都是HPC/HPC-DS专业的研究生,每个人负责一个单独的项目,我负责的是Elmer/Ice,一个冰川建模软件。疫情期间,学校关闭了,我们五个人只有我和保加利亚的小哥留在爱丁堡,两个西班牙的小哥回国了,塞浦路斯的小姐姐也回去了,因此我们的交流时间并不多,基本上是每个人做自己的东西。

比赛开始后,我们都要通过VPN连上新加披的集群NSCC,这个是南阳理工和新国立两间学校在使用的集群,VPN连接上之后,速度真的不敢恭维,很多时候速度会巨慢,然后提交了PBS jobs后就会进入无穷等待队列,我的一个任务等了3天。。然后等来的结果是job killed walltime exceeded limit,我的walltime设置的是2小时,后来我将其不断调整甚至到12小时,依旧报这个错误,我确信这是集群的问题,因为我在archer上最多20几分钟就可以跑完这个程序。比赛过半之后,我的几个任务可以又报出错误:job killed: mem job total exceeded limit,我确定和我的脚本没有关系(select=4:ncpus=24:mpiprocs=24:ompthreads=1:mem=96gb),是集群资源不够,没办法,除了傻等,其他都做不了。比赛过了大半,我的三个节点的程序可以跑出结果了,感谢党,这个集群居然还有资源够用的时候,但是四个节点的任务一直还是不行的,我尝试过跑GPU+CPU节点(但是组委会说过,Elmer/Ice不需要寻找GPU节点方案,我这里只是尝试),或者通过申请四个节点,但是每个节点不跑满的方式,可是全部fail,除了等待真的没有其他办法,当时我基本上决定要提交三个节点的成绩,可是在最后一天的时候,我看到邮件通知,我的四个节点任务正常结束,我看了下时间,比三个节点的要好些,于是保存了一份,作为提交结果,我不清楚NSCC的队列调度方式和资源分配方式,反正我的大部分时间都在等待上,真的无力吐槽。

再讲一下Elmer/Ice的编译和优化,这个项目比较大,主要使用C/C++/Fortran来编写,需要交叉编译(使用的Cmake编译),同时依赖大量的第三方类库例如MMG,HDF5,NetCDF等,在比赛之前我们需要在自己学校的超算平台Cirrus编译练习的,但是这平台被黑客攻击用来挖矿,只能选择另外一个架构比较老旧的平台Archer,不巧的是,它也被攻击了,好吧,只能等待,好在比赛开始前几天,Archer修好了,开始在Archer上编译,我尝试过GCC,Intel compiler,PGI等,只有GCC成功,Archer上Intel的版本太低,程序有个BUG必须要高一点版本,本打算自己装一个Intel compiler,但是时间太紧,直接上比赛集群练习了。到了NSCC上,我使用Intel编译的,NCSS的Intel最新版本是19版本,安装编译虽然遇到了很多坑,这里有个很诡异的事情,就是环境变量的问题,当我加载了众多模块后

    module load scotch    
    module unload composerxe/2016.1.150    
    module load intel/19.0.0.117    
    module load netcdf/intel19/netcdf-f-4.4.5    
    module load openmpi/intel/1.10.2    
    module load cmake/3.14.4    
    module load hdf5/1.10.5/intel19/parallel    
    module load netcdf/4.4.0/xe_2016/parallel    
    module load netcdf-fortran/4.4.3/xe_2016/parallel 

这里Intel的版本因该是19,可是需要手动修改一下环境变量,否则Inte的版本会变成16

    source  /app/intel/xe2019/compilers_and_libraries_2019.0.117/linux/bin/compilervars.sh -arch intel64 -platform linux

编译的时候有还能多编译选项可以优化比如

-Ofast -fPIC -ipo -finline -align -xCORE-AVX2 -axAVX,SSE4.2 -mtune=haswell -L/opmpath/lib -lipmf -lipm

当然还有一个很重要的选项可以使用-pgo,这个会生成一个优化报告,编译的时候compiler会根据这个进行针对性优化。
比赛要求使用IPM来进行profiling,我当时不知道NSCC有安装这个,我还自己手动安装了IPM和ploticus。

对于优化来讲,其实能优化的地方不多,除了代码和编译优化,还有一个地方就是SIF文件,也就是Elmer的配置文件

!%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
! You are allowed to change any EXISTING parameter entry 
! in this file to tune your run
! just leave the residual output numbers as they are in
! order that log-file remains readable 
!%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
! tolerance of block solutions (if you choose iterative)
  $blocktol = 0.001
! max amount of block iterations (if you choose iterative)
  $blockiter = 2000
! pre-conditioner of block matrices   
  $preblock = "ILU0"

! Nonlinear system settings
  Nonlinear System Max Iterations = 50
  Nonlinear System Newton After Iterations = 5
  Nonlinear System Newton After Tolerance = 1.0e-02
  !Nonlinear System Relaxation Factor = #2.0/3.0

! Default is [1 2 3 4]
! Block Structure(4) = Integer 1 1 1 2
!  Block Order(4) = Integer 1 2 3 4

! Linear System Scaling = False
! Linear system solver for outer loop
!-----------------------------------------
  Outer: Linear System Solver = "Iterative"
  Outer: Linear System Iterative Method = GCR
  Outer: Linear System GCR Restart =  250
  Outer: Linear System Residual Output = 10 ! please, leave that one to keep output readable
  Outer: Linear System Max Iterations =  500
  Outer: Linear System Abort Not Converged = True
  Outer: Linear System Convergence Tolerance = 1e-05

  block 11: Linear System Convergence Tolerance = $blocktol
  block 11: Linear System Solver = "iterative"
  block 11: Linear System Scaling = false
  block 11: Linear System Preconditioning = $preblock
  block 11: Linear System Residual Output = 100 ! please, leave that one to keep output readable
  block 11: Linear System Max Iterations = $blockiter
  block 11: Linear System Iterative Method = idrs

  block 22: Linear System Convergence Tolerance = $blocktol
  block 22: Linear System Solver = "iterative"
  block 22: Linear System Scaling = false
  block 22: Linear System Preconditioning = $preblock
  block 22: Linear System Residual Output = 100 ! please, leave that one to keep output readable
  block 22: Linear System Max Iterations = $blockiter
  block 22: Linear System Iterative Method = idrs

  block 33: Linear System Convergence Tolerance = $blocktol
  block 33: Linear System Solver = "iterative"
  block 33: Linear System Scaling = false
  block 33: Linear System Preconditioning = $preblock
  block 33: Linear System Residual Output = 100 ! please, leave that one to keep output readable
  block 33: Linear System Max Iterations = $blockiter
  block 33: Linear System Iterative Method = idrs

  block 44: Linear System Convergence Tolerance = $blocktol
  block 44: Linear System Solver = "iterative"
  block 44: Linear System Scaling = true
  block 44: Linear System Preconditioning = $preblock
  block 44: Linear System Residual Output = 100 ! please, leave that one to keep output readable
  block 44: Linear System Max Iterations = $blockiter
  block 44: Linear System Iterative Method = idrs

这里面主要能改的就是一下tolerance,pre-conditioner,Iterative Method,对于pre-conditioner来讲可以使用ILU0,ILU1,ILU2,但是精度越高意味着内存和时间去求更大,虽然迭代次数可能会减少,因为选择ILU0。

ILU(0)分解预处理技术,对原系数矩阵做无任何额外非零元填充的ILU的分解,预处理速度较快。大规模的数矩阵一般具有稀疏性,ILU(0)分解不注入非零元, 能够有效保持系数矩阵的稀疏性,因此这一预处理方法适用于系统系数矩阵。而不完全LU分解中的其他两类方法 ILU(1)和 ILU(2),虽然相较于 ILU(0)能够一定程度上提高预处理效果、减少方程求解的迭代次数,但是这两种预处理方法速度较慢,且均需要注入非零元,破坏系数矩阵的稀疏性,在稀疏矩阵的乘法、内积等运算中显著加大计算量,降低计算的效率。

对于Iterative Method有很多选择比如
* CG
* BiCGSTAB(Biconjugate gradient stabilized method)
* Jacobi
* IDR(s)

对于IDR和BiCGSTAB的比较:
IDR(1) ≈ IDR is equally fast but preferable to the closely related Bi-CGSTAB, and that IDR(s) with s > 1 may be much faster than Bi-CGSTAB. It also turned out that when s > 1, IDR(s) is related to ML(s)BiCGSTAB of Yeung and Chan, and that there is quite some flexibility in the IDR approach

对于共轭梯度:
共轭梯度法收敛的快慢依赖于系数矩阵的谱分布情况,当特征值比较集中,系数矩阵的条件数很小,共轭梯度方法收敛得就快。“超线性收敛性”告诉我们,实际当中,我们往往需要更少的步数就能得到所需的精度的解。

我这里对主要的方法做了一张图来分析其执行时间,最终选择IDR

最终运行后得到的profiling截图如下



最终结果图

给出建议
We can see from the graph that MPI_Recv took a lot of time. First, it is a blocking method and receive a message in a blocking fashion.Also, for the the cross-node communication, it would bring the addtional costs.
In this case, MPI_Irecv could be employed to reduce the waiting time. It does not blocked until the message is received.
As for the sending message, the MPI_Bsend had been used, which is is the asynchronous blocking send. For a performance perspective, we could consider using non-blocking communication methods such as MPI_Ibsend, it is the asynchronous non-blocking send.

当然这个项目的可优化之处很多,我这个只是敷衍面试写的几句话,首先这个项目可以使用GPU版本,同时对于进程的分配其实可以考虑单进程多任务。当然还可以加入ML的东西,不过貌似开发者已经开始考虑这一点了。

说一下面试,面试其实有两次,比赛期间有一次主要是meet team,视频在YouTube上,后一次面试是技术面试,是一个presentation,我们自己每个人大约讲5分钟关于自己的项目,评委会针对性的问技术问题,问题大都是和项目相关的,最终我们的面试成绩是第六,也不是很理想。

Posted in 架构运维 | 2 Comments

Ubuntu下编译OpenJDK12

本文是在虚拟机VMware下的Ubuntu编译OpenJDK12
* 首先是获取OpenJDK源码

hg clone https://hg.openjdk.java.net/jdk/jdk12

当然需要首先安装mercurial,这个是比SVN还古老的版本管理软件

sudo apt-get install mercurial meld
  • 依赖库
    需要GCC,FreeType、CUPS等若干第三方库
sudo apt-get install build-essential
sudo apt-get install libfreetype6-dev
sudo apt-get install libcups2-dev
sudo apt-get install libx11-dev libxext-dev libxrender-dev libxrandr-dev sudo libxtst-dev libxt-dev
sudo apt-get install libasound2-dev
sudo apt-get install libffi-dev
sudo apt-get install autoconf
sudo apt-get install ccache
sudo apt-get install systemtap-sdt-dev
  • 构建配置
bash configure --with-debug-level=slowdebug --enable-dtrace --with-jvm-variants=server --with-target-bits=64 --enable-ccache --with-num-cores=8 --with-memory-size=8000  --disable-warnings-as-errors

配置参数说明:

```bash
--with-debug-level=slowdebug 启用slowdebug级别调试
--enable-dtrace 启用dtrace
--with-jvm-variants=server 编译server类型JVM
--with-target-bits=64 指定JVM为64位
--enable-ccache 启用ccache,加快编译
--with-num-cores=8 编译使用CPU核心数
--with-memory-size=8000 编译使用内存
--disable-warnings-as-errors 忽略警告
```
</code></pre>

构建成功后会显示如下:

<pre><code>```bash
Configuration summary:
* Debug level:    slowdebug
* HS debug level: debug
* JVM variants:   server
* JVM features:   server: 'aot cds cmsgc compiler1 compiler2 dtrace epsilongc g1gc graal jfr jni-check jvmci jvmti management nmt parallelgc serialgc services vm-structs' 
* OpenJDK target: OS: macosx, CPU architecture: x86, address length: 64
* Version string: 12-internal+0-adhoc.wangdong.openjdk (12-internal)

Tools summary:
* Boot JDK:       java version "10.0.2" 2018-07-17 Java(TM) SE Runtime Environment 18.3 (build 10.0.2+13) Java HotSpot(TM) 64-Bit Server VM 18.3 (build 10.0.2+13, mixed mode)  (at /Library/Java/JavaVirtualMachines/jdk-10.0.2.jdk/Contents/Home)
* Toolchain:      clang (clang/LLVM from Xcode 9.4.1)
* C Compiler:     Version 9.1.0 (at /usr/bin/clang)
* C++ Compiler:   Version 9.1.0 (at /usr/bin/clang++)

Build performance summary:
* Cores to use:   7
* Memory limit:   8000 MB
* ccache status:  Active (3.4.2)
```

编译:

make images

编译成功后如下:大概可能需要几分钟到十几分钟不等:

    Note: Recompile with -Xlint:unchecked for details.
    Creating support/demos/image/jfc/CodePointIM/CodePointIM.jar
    Creating support/demos/image/jfc/FileChooserDemo/FileChooserDemo.jar
    Creating support/demos/image/jfc/Font2DTest/Font2DTest.jar
    Creating support/demos/image/jfc/SwingSet2/SwingSet2.jar
    Creating support/demos/image/jfc/J2Ddemo/J2Ddemo.jar
    Creating support/demos/image/jfc/Metalworks/Metalworks.jar
    Creating support/demos/image/jfc/Notepad/Notepad.jar
    Creating support/demos/image/jfc/Stylepad/Stylepad.jar
    Creating support/demos/image/jfc/SampleTree/SampleTree.jar
    Creating support/demos/image/jfc/TableExample/TableExample.jar
    Creating support/demos/image/jfc/TransparentRuler/TransparentRuler.jar
    Creating support/classlist.jar
    Creating images/jmods/jdk.jlink.jmod
    Creating images/jmods/java.base.jmod
    Creating jdk image
    Stopping sjavac server
    Finished building target 'images' in configuration 'macosx-x86_64-normal-server-slowdebug'
  • 设置环境变量
vim /etc/profile

然后添加下面几行到文件最后

export JAVA_HOME=$HOME/jdk12/build/linux-x86_64-server-slowdebug/images/jdk
export PATH=$JAVA_HOME/bin:$PATH

然后

source /etc/profile

这时候可以查看是否安装成功

java -version

接下来就可以使用Clion来进行调试了
OpenJDK本身也没有为任何IDE提供支持,但如
果只是为了能够在CLion中跟踪、阅读源码,而不需要修改重新编译的话,那直接在Run/Debug
Configurations中增加一个CMake Application,然后Executable选择我们刚才编译出来的FastDebug或者
SlowDebug版的java命令,运行参数加上-version或者某个Class文件的路径,再把Before launch里面的
Build去掉,就可以开始运行调试了

Posted in Java编程语言, 编程语言 | Leave a comment

基于NB (Naive Bayes) 和SVM的自然语言处理 (NLP)

本文主要讲述使用NB和SVM算法来实现NLP的小例子,当然目前使用neural network等深度学习的算法会比较多。本文文件集: https://www.kaggle.com/hellogxp/corpus#corpus.csv

1. 类库

import pandas as pd
import numpy as np
from nltk.tokenize import word_tokenize
from nltk import pos_tag
from nltk.corpus import stopwords
from nltk.stem import WordNetLemmatizer
from sklearn.preprocessing import LabelEncoder
from collections import defaultdict
from nltk.corpus import wordnet as wn
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn import model_selection, naive_bayes, svm
from sklearn.metrics import accuracy_score

2. 设置随机种子,保证每次运行结果相同

np.random.seed(500)

3. Add the Corpus 添加语料库

Corpus = pd.read_csv(r"../input/corpus.csv",encoding='latin-1', nrows=5)

4. Data pre-processing 数据预处理

主要包括Tokenization和Word Stemming/Lemmatization,Tokenization可以叫令牌化,把字符串拆分成单词,标点符号,短语等元素,称之为tokens.
Word Stemming和Lemmatization,两者差不多,但是词干stemmer操作一个单词,是不知道其上下文的,所以就不能区分不同含义的词,但是它实现简单切比较快,有的应用就效率换质量。

预处理的主要步骤如下:
1. 除去空行
2. 字母改小写
3. 单词令牌化
4. 删除stop words, 主要是针对英文,也叫common words,比如a,the,or等使用频率很多的字或词
5. 除去字符不全是字母的单词
6. Word Lemmatization 将单词的不同形式简化为一种形式的过程,例如,”builds”, “building”, or “built” to the lemma “build”

#删除空白行
Corpus['text'].dropna(inplace=True)


#全部小写
for index, row in Corpus.iterrows():
    Corpus.loc[index]['text'] = Corpus.loc[index]['text'].lower()

# Step - b : 全部小写
for label, row in Corpus.iterrows():
    Corpus.loc[[label], ['text']] = Corpus.loc[label]['text'].lower()
# Step - c : 令牌化操作,会将一个字符串拆分成单个单词的集合,包括标点符号
Corpus['text']= [word_tokenize(entry) for entry in Corpus['text']]

# Step - d : 删除停止符号, 非数值,对单词进行Stemming/Lemmenting,也就是词干和词法化

# WordNetLemmatizer 需要 Pos tags来了解单词是名词,动词还是其它,默认是名词
tag_map = defaultdict(lambda : wn.NOUN)
tag_map['J'] = wn.ADJ
tag_map['V'] = wn.VERB
tag_map['R'] = wn.ADV

for index,entry in enumerate(Corpus['text']):
    # 声明一个数组来存储最终处理过的单词
    Final_words = []
    # 初始化 WordNetLemmatizer()
    word_Lemmatized = WordNetLemmatizer()
    # pos_tag 函数将给每个单词打一个'tag',比如说单词是Noun(N)或者Verb(V)抑或其他
    for word, tag in pos_tag(entry):
        # 检查是否是Stop words或者全是字符的字符
        if word not in stopwords.words('english') and word.isalpha():
            word_Final = word_Lemmatized.lemmatize(word,tag_map[tag[0]])
            Final_words.append(word_Final)
    # 在DF种在新加一列text_final来存储最终处理好的字符串
    Corpus.loc[index,'text_final'] = str(Final_words)

5. 准备训练集和测试集

我们会将词库分成训练集和测试集两部分,训练集是为了拟合模型,预测将在测试集上进行,我们可以使用sklearn库的train_test_spli函数来实现词库的拆分,训练集将占70%,测试集将占据30%,也就是说我们将test_size参数设置为0.3

6. Encoding

主要是将目标变量标签编码,将字符串类型的类别数据转换成数字类型,以使得模型可以理解

7. 词向量化

这一步将文本文档集合转化为数字特征向量,有很多种方法可供选择比如TF-IDF,是一种用于信息检索与数据挖掘的常用加权技术,常用于挖掘文章中的关键词,而且算法简单高效,常被工业用于最开始的文本数据清洗。

假设我们现在有一片长文叫做《量化系统架构设计》词频高在文章中往往是停用词,“的”,“是”,“了”等,这些在文档中最常见但对结果毫无帮助、需要过滤掉的词,用TF可以统计到这些停用词并把它们过滤。当高频词过滤后就只需考虑剩下的有实际意义的词。

但这样又会遇到了另一个问题,我们可能发现”量化”、”系统”、”架构”这三个词的出现次数一样多。这是不是意味着,作为关键词,它们的重要性是一样的?事实上系统应该在其他文章比较常见,所以在关键词排序上,“量化”和“架构”应该排在“系统”前面,这个时候就需要IDF,IDF会给常见的词较小的权重,它的大小与一个词的常见程度成反比。

当有TF(词频)和IDF(逆文档频率)后,将这两个词相乘,就能得到一个词的TF-IDF的值。某个词在文章中的TF-IDF越大,那么一般而言这个词在这篇文章的重要性会越高,所以通过计算文章中各个词的TF-IDF,由大到小排序,排在最前面的几个词,就是该文章的关键词。

Tfidf_vect = TfidfVectorizer(max_features=5000)
Tfidf_vect.fit(Corpus['text_final'])

Train_X_Tfidf = Tfidf_vect.transform(Train_X)
Test_X_Tfidf = Tfidf_vect.transform(Test_X)

可以通过

print(Tfidf_vect.vocabulary_)

来打印,有类似的输出

{'stun': 141, 'even': 39, 'sound': 134, 'track': 153, 'beautiful': 9, 'paint': 102, 'senery': 129, 'mind': 91, 'well': 164......}

也可通过

print(Train_X_Tfidf)

直接打印向量数据

(0, 175)    0.11054398869597175
(0, 163)    0.1783723880767145
(0, 160)    0.11054398869597175
(0, 158)    0.11054398869597175

以第一行为例
0是‘Train_X_Tfidf’的行号,175是第一行中单词的唯一的整数值,后面的小数值是TF-IDF Vectorizer计算的分数。
现在数据处理好了,可以输入到不同的算法种了

7. 使用机器学习算法预测输出

首先我们使用朴素贝叶斯算法

# 使得训练集合适合NB classifier
Naive = naive_bayes.MultinomialNB()
Naive.fit(Train_X_Tfidf,Train_Y)
# 预测测试数据集的标签
predictions_NB = Naive.predict(Test_X_Tfidf)
# 使用accuracy_score函数获得准确度
print("Naive Bayes Accuracy Score -> ",accuracy_score(predictions_NB, Test_Y)*100)

输出

Naive Bayes Accuracy Score -> 83.1%

我们还可以使用SVM,也就是支持向量机

# 使训练数据集适合分类器
SVM = svm.SVC(C=1.0, kernel='linear', degree=3, gamma='auto')
SVM.fit(Train_X_Tfidf,Train_Y)
# 预测验证数据集上的标签
predictions_SVM = SVM.predict(Test_X_Tfidf)
# 使用accuracy_score函数获得准确性
print("SVM Accuracy Score -> ",accuracy_score(predictions_SVM, Test_Y)*100)

输出

SVM Accuracy Score -> 84.7%
Posted in AI | Leave a comment

简单理解朴素贝叶斯分类算法

贝叶斯分类是一类分类算法的总称,这类算法均以贝叶斯定理为基础,故统称为贝叶斯分类。而朴素朴素贝叶斯分类是贝叶斯分类中最简单,也是常见的一种分类方法。

既然是贝叶斯分类算法,那么分类的数学描述又是什么呢?

从数学角度来说,分类问题可做如下定义:已知集合$C=y_1,y_2…y_n$和$I=x_1, x_2,x_3…x_n$,确定映射规则y = f(),使得任意$x_i \in I$有且仅有一个$y_i \in C$,使得$y_i \in f(x_i)$成立。

其中C叫做类别集合,其中每一个元素是一个类别,而I叫做项集合(特征集合),其中每一个元素是一个待分类项,f叫做分类器。分类算法的任务就是构造分类器f。

分类算法的内容是要求给定特征,让我们得出类别,这也是所有分类问题的关键。那么如何由指定特征,得到我们最终的类别,也是我们下面要讲的,每一个不同的分类算法,对应着不同的核心思想。

贝叶斯公式:

$$P(B|A) = \frac{P(A|B)P(B)}{P(A)}$$

换个表达,火汁萌
$$P(类别|特征) = \frac{P(特征|类别)P(类别)}{P(特征)}$$

我们最终求的p(类别|特征)即可!就相当于完成了我们的任务。

Posted in AI | Leave a comment

pandas docs

  • 根据某一列的值进行筛选axis=0
train = pd.read_csv('../input/train.csv')
train.head()

train = pd.read_csv(‘../input/train.csv’)

id name
0 harry
1 william

我们筛选name=”harry”的行

train.loc[data['name'] = 'harry']
  • 使用max()查找索引轴上的最大值
import pandas as pd 
df = pd.DataFrame({"A":[12, 4, 5, 44, 1], 
                   "B":[5, 2, 54, 3, 2], 
                   "C":[20, 16, 7, 3, 8],  
                   "D":[14, 3, 17, 2, 6]}) 
df 
A B C D
0 12 5 20 14
1 4 2 16 3
2 5 54 7 17
3 44 3 3 2
4 1 2 8 6
默认axis = 0
df.max(axis = 0) 
A 44
B 54
C 20
D 17
  • 根据某一列的值进行筛选axis=1
df = pd.DataFrame({"A":[12, 4, 5, None, 1],  
                   "B":[7, 2, 54, 3, None], 
                   "C":[20, 16, 11, 3, 8], 
                   "D":[14, 3, None, 2, 6]}) 
//skip the Na values while finding the maximum 
df.max(axis = 1, skipna = True) 
0 20.0
1 16.0
2 54.0
3 3.0
4 8.0
  • 按照某一列的值,画直方图,统计各个值的分布情况
import matplotlib.pyplot as plt
train['none'].value_counts().plot.bar()
plt.show()
Posted in AI | Leave a comment

B/B+tree

  • B/B+ tree,如果是p阶,每个节点就有最多p个指针,也就意味着可以有最多P个孩子。
    那么最小应该有ceil(p/2)个指针,因此也就意味着最少有ceil(p/2)个孩子。
  • B/B+ tree,如果是p阶,那么每个节点最大可以用p-1个key,最小有ceil(p/2)-1个key。
Posted in 架构运维 | Leave a comment

Tensorflow和jupyter使用问题

  • 在新版本的tensorflow中,会话函数Session()已经抛弃,要这样使用
import tensorflow as tf
tf.compat.v1.disable_eager_execution()
a = tf.constant([1, 2], name="a")
b = tf.constant([3, 4], name="b")
result = a + b
sess = tf.compat.v1.Session()
sess.run(result)
  • coanda activate env_name报错,比如有时候安装了Git可能会报错这个
CommandNotFoundError: Your shell has not been properly configured to use 'conda activate'

这时候可以在Git下面使用下面这个命令解决

source activate
  • conda创建一个虚拟环境
conda create -n myenv python=3.6
  • Jupyter导入conda创建的环境
conda install -c anaconda ipykernel
```
python -m ipykernel install --user --name=envname
```

* To remove an environment, in your terminal window or an Anaconda Prompt, run:

conda env remove --name myenv

get the paths of all your kernels

jupyter kernelspec list

uninstall your unwanted-kernel,删除jupyter中的虚拟环境

jupyter kernelspec uninstall unwanted-kernel
  • 在jupyter中使用Tensorflow和PyTorch
    比如通过conda建立了一个Tensorflow的环境,如果要在jupyter中使用,则有如下步骤:

    1. 激活这个环境,在这个环境下安装ipython和jupyter
    conda install ipython 
    conda install jupyter
    
    1. 安装python kernel for Tensroflow或者PyTorch
    ipython kernelspec install-self --user  
    
    1. 在jupyter中添加conda的environment
    python -m ipykernel install --user --name=envname
    
    1. 运行
    Jupyter notebook 
    

    现在就可以在jupyter下面运行Tensorflow了,这样其实相当于在每个环境下都单独安装一个jupyter。

Posted in AI | Leave a comment

Linux命令汇总

AWK

AWK是文本处理语言,是很强大的文本分析工具,请注意,这个名字和它的作用没一分钱关系,这么骚的名字,是取了三位创始人名字的首字母。这个骚指令常用于一整行的处理,倾向于把一整行分成好多列

awk '条件类型1{动作1} 条件类型2{动作2} ...' filename

基本使用格式为AWK后面跟上成对单引号,然后使用大括号{}来配置数据的处理动作,它可以处理给定文件的数据,也可以读取来自前面命令的输出standard output。因为awk主要是处理一行中不同列的数据,那默认的列的分隔符是空格键或者tab键。例如

[root@xyz harry]# last -n 5
root     pts/0        188.74.64.40     Mon Feb  3 04:32   still logged in   
root     pts/0        192.41.125.249   Fri Jan 31 19:04 - 19:45  (00:41)    
root     pts/0        192.41.131.250   Thu Jan 30 20:21 - 20:59  (00:38)    
root     pts/0        188.74.64.40     Thu Jan 30 02:18 - 08:14  (05:56)    
root     pts/0        192.41.125.254   Wed Jan 29 19:06 - 19:53  (00:47)

我们可以打印第一列和第三列,也就是用户名和ip,同时使用tab键隔开

[root@xyz harry]# last -n 5 | awk '{print $1 "\t" $3}'
root    188.74.64.40
root    192.41.125.249
root    192.41.131.250
root    188.74.64.40
root    192.41.125.254

查看磁盘文件du

查看当前文件夹大小

du -sh

output:

7.3G    .

查看当前文件夹下,子文件夹大小以及当前文件夹总大小

du -h --max-depth=1

output:

4.0K    ./Pictures
156K    ./.config
34M ./.mozilla
7.3G    .

Linux查看物理CPU个数、核数、逻辑CPU个数

总核数 = 物理CPU个数 X 每颗物理CPU的核数
总逻辑CPU数 = 物理CPU个数 X 每颗物理CPU的核数 X 超线程数
* 查看物理CPU个数

cat /proc/cpuinfo| grep "physical id"| sort| uniq| wc -l
  • 查看每个物理CPU中core的个数(即核数)
cat /proc/cpuinfo| grep "cpu cores"| uniq
  • 查看逻辑CPU的个数
cat /proc/cpuinfo| grep "processor"| wc -l
  • 查看CPU信息(型号)
cat /proc/cpuinfo | grep name | cut -f2 -d: | uniq -c
  • Ubuntu截图
#保存到图片文件夹
Print Screen  #截取整个桌面
Alt + Print Screen #截取选中的窗口
Shift + Print Screen #自由选区
#复制到剪贴板
Ctrl + Print Screen  #整个桌面
Ctrl + Alt + Print Screen #选中的窗口
Shift + Ctrl + Print Screen #自由选区
Posted in 架构运维 | Tagged , | Leave a comment

wireshark抓包android APP

本文简单讲一下如何通过wireshark对android的APP进行抓包,方式和工具很多,这里只是简单介绍两种。

一 通过wifi的模式

首先在PC上开一个热点,可以自己命令行设置或者360wifi等,然后手机通过wifi连入这个热点,打开wireshark抓包这个无线网卡

二 通过wireshar+tcpdump进行抓包

这个一般来说实时性不强,不过也有方法实时抓包,今天暂且不讲

1 手机root,开启开发者模式,启动USB debug模式

2 本地安装android SDK

3 tcpdump一般手机上自带,如果没有的话,下载后上传到手机

adb shell

su

chmod 777 /data/local

退出shell

adb root

adb push D:\tcpdump /data/local/tcpdump

启动并运行dump,并且保存信息文件,命令是(如果不是自带的tcpdump,需要加上tcpdump的路径):

adb shell tcpdump -i any -p -s 0 -w /sdcard/my.pcap
5.将my.pcap文件pull到电脑上,命令是: 
adb pull /sdcard/my.pcap D:/capture

最后在电脑上用Wireshark打开my.pcap文件就可以分析了。

Posted in 架构运维 | Leave a comment

MySQL issues

mysql Invalid default value for

建表的时候,如果设置datetime字段为not null,默认值为0000-00-00 00 00:00:00,就会报错mysql Invalid default value for,这是因为在mysql5.6+的版本中,在strict模式下,不允在日期中出现类似这种值:‘0000-00-00’。你可以运行这条命令 show variables like ‘sql_mode’; 来查看sql_mode,如果出现NO_ZERO_IN_DATE,那么就是不允许上述情况了。粗暴的解决办法可以设置sql_mode允许出现这种零值日期的出现,或者在IGNORE 模式下建表,但是不建议。有几个解决方案

去掉默认值,改为`add_time` datetime DEFAULT NULL,

把datetime改为timestamp,`add_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP。

如果要允许这种零日期的值为默认值,可以禁用no_zero_date

set GLOBAL sql_mode =’STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION ‘;

有时候通过命令行设置不生效,可以通过配置文件设置

sql_mode=ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION



Posted in 架构运维 | Leave a comment

docker 发布 spring boot

# docker issues
## docker部署发布 spring boot
1. 先mvn打包
    > mvn package
    在target生成一个类似appname-0.0.1-SNAPSHOT.jar的文件
2. 将src,pom,mvn打包的jar包文件上传到服务器的某个目录例如docker
3. 在此目录下执行
    > mvn package docker:build
    如果报错“No plugin found for prefix ‘docker’ in the current project and in the plugin groups ”
    则在maven的conf/setting文件中加入
    <pluginGroups>  
        <pluginGroup>com.spotify</pluginGroup>  
    </pluginGroups>

    详细原因:https://github.com/spotify/docker-maven-plugin/issues/322

    同时记得在pom文件中加入docker-maven-plugin插件

     <!–加入maven插件“docker-maven-plugin”–>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>0.4.14</version>
<configuration>
<imageName>${docker.image.prefix}/${project.artifactId}</imageName>
<!–<dockerDirectory>${project.basedir}/src/main/docker</dockerDirectory>–>
<dockerDirectory>${project.basedir}</dockerDirectory>
<resources>
<resource>
<targetPath>/</targetPath>
<directory>${project.build.directory}</directory>
<include>${project.build.finalName}.jar</include>
</resource>
</resources>
</configuration>
</plugin>

4. 打包结束后使用docker查看镜像文件

    > docker images    

5 运行

docker run -it -d -p 443:8080 springboot/programe
 
 ## docker命令
* 停止所有镜像:  docker stop $(docker ps -a -q)
* docker ps -a | grep "Exited" | awk '{print $1 }'|xargs docker stop
* docker ps -a | grep "Exited" | awk '{print $1 }'|xargs docker rm
* 删除所有none的镜像: docker images|grep none|awk '{print $3 }'| xargs docker rmi,如果删不掉则先
> 先查询记录 docker ps -a
  把该镜像的记录全部删除掉,如果删除所有镜像的记录,可以使用:
  docker ps -a|awk '{print $1}'|xargs docker rm   
  然后再删除即可 

Posted in 架构运维 | Leave a comment

spring boot 笔记

Failed to configure a DataSource: ‘url’ attribute is not specified and no embedded datasource could be configured

spring boot需要配置好datasource才可以正常运行,在properties中

spring.datasource.url=jdbc:mysql://localhost:3306/mysql 
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
 

@RestController

这个标签是@Controller 和@ResponseBody的组合

如果遇到8080端口被占用,The Tomcat connector configured to listen on port 8080 failed to start intellij idea

1 netstat -ao |find /i “listening”,查看8080端口被哪个进程占用

TCP 0.0.0.0:7981 machinename:0 LISTENING 2428 

TCP 0.0.0.0:7982 machinename:0 LISTENING 2428 

TCP 0.0.0.0:8080 machinename:0 LISTENING 12704 

TCP 0.0.0.0:8500 machinename:0 LISTENING 2428

2 Taskkill /F /IM 12704 (Note: Mention correct Process Id),杀掉此进程

Posted in 架构运维 | Leave a comment

小程序领取微信卡券“签名错误”解决小结

在开发一个小程序,需要在小程序中领取卡券,因为官方文档写的比较粗糙,遇到一些小问题,特此记录。首选简述开发流程。

1 开通公众号的卡券功能,通过微信公众平台的卡券api创建卡券。

2 申请开放平台账号,将小程序与公众号绑定在同一个开放平台下

3 在小程序中通过wx.addCard添加领取卡券接口,重点说下接口的扩展字段参数

cardExt: ‘{“timestamp”:”‘ + dataInfo.timestamp + ‘”,”signature”:”‘ + dataInfo.signature + ‘”,”nonce_str”:”‘ + dataInfo.nonceStr + ‘”}’

如果你有自定义code,并且指定了领取人的openid,那么也要传入这个两个字段,这些字段都是从后端获取的,并这些字段都是用于签名的字段,重点说下签名,调用微信卡券需要签名,签名规则官方文档有,这里给出简单示例

public String ByteToHexString(byte[] data)
    {
        StringBuilder str = new StringBuilder();
        for (byte b : data)
        {
            String hv = Integer.toHexString(b & 0xFF);
            if( hv.length() < 2 )
                str.append("0");
            str.append(hv);
        }
        return str.toString();
    }

    public String getTheSignature(ArrayList<String> param_sign)
    {
        Collections.sort(param_sign);
        StringBuilder string_to_sign = new StringBuilder();
        for (String str : param_sign)
        {
            string_to_sign.append(str);
        }

        try
        {
            MessageDigest hasher = MessageDigest.getInstance("SHA-1");
            byte[] digest = hasher.digest(string_to_sign.toString().getBytes());
            return ByteToHexString(digest);
        } catch (NoSuchAlgorithmException e)
        {
            e.printStackTrace();
            return e.getMessage();
        }
    }

这里主要一定要排序,否则会有问题,这里有个坑,就是官方给出的签名验证工具没有对参数进行排序,导致正确的签名验证不通过,误导了许多人,这里注意了。

Posted in 移动开发 | Leave a comment

Spring Boot Application Secured by Let’s Encrypt Certificate(docker+spring boot+letsencrypt)

首先介绍一下项目,基础spring boot,docker部署,使用letsencrypt申请证书。

1 首先,安装一个工具

$ git clone https://github.com/certbot/certbot
$ cd certbot
$ ./certbotauto help

2 用这个工具生成证书和秘钥

./certbot-auto certonly -a standalone -d example.com -d www.example.com

3 打开/etc/letsencrypt/live/example.com目录


4 openssl pkcs12 -export -in fullchain.pem -inkey privkey.pem -out keystore.p12 -name tomcat -CAfile chain.pem -caname root


文件 keystore.p12生成在 /etc/letsencrypt/live/example.com目录下


证书申请完毕,开始配置sping boot项目,因为spring boot是运行在docker中的,默认服务器是Tomcat等,

server.port: 443
security.require-ssl=true
server.ssl.key-store:/etc/letsencrypt/live/example.com/keystore.p12
server.ssl.key-store-password: <your-password>
server.ssl.keyStoreType: PKCS12
server.ssl.keyAlias: tomcat


配置完成后重启container,然后重新运行项目

> docker run -it -d -v /etc/letsencrypt/live/example.com/keystore.p12:/etc/letsencrypt/live/example.com/keystore.p12 -p 443:443 springboot/application -Dspring.profiles.active=dev 

这个默认有效期是90天,到期要重新验证,可以设置自动更新,可以去官方文档查看。


Posted in 架构运维 | Leave a comment

单向链表反转迭代算法实现

1 首先创建一个链表

package com.alibaba.LinkedNode;

/**
 * Created by think.
 * Date: 12/11/2018
 * Time: 13:06
 */
public class Node {
    //数据域
    private int date;
    //指针域
    private Node next;

    public Node(int date) {
        this.date = date;
    }

    public int getDate() {
        return date;
    }

    public void setDate(int date) {
        this.date = date;
    }

    public Node getNext() {
        return next;
    }

    public void setNext(Node next) {
        this.next = next;
    }
}

然后从最后一个节点开始反转:

package com.alibaba.LinkedNode;

/**
 * Created by think.
 * Date: 12/11/2018
 * Time: 13:10
 */
public class Test {
    public static void main(String[] args) {
        Node head = new Node(0);
        Node node1 = new Node(1);
        Node node2 = new Node(2);
        Node node3 = new Node(3);

        head.setNext(node1);
        node1.setNext(node2);
        node2.setNext(node3);

        Node h = head;
        System.out.println("反转前的节点");
        while (h != null) {
            System.out.println(h.getDate() + " ");
            h = h.getNext();
        }

        System.out.println("反转后的节点");
        head = reverse(head);
        while (head != null) {
            System.out.println(head.getDate() + "");
            head = head.getNext();
        }
    }

    public static Node reverse(Node head) {
        // head为上一节点,如果为空链或者当前节点在尾节点,则直接返回
        if (head == null || head.getNext() == null) {
            return head;
        }

        Node reverseHead = reverse(head.getNext());
        //将当前节点的指针域指向前一节点
        head.getNext().setNext(head);
        //将前一节点的指针域设置为null
        head.setNext(null);
        return reverseHead;
    }
}

Posted in Java编程语言 | Leave a comment

Java实现快速排序算法


package com.alibaba;

import java.util.Arrays;

/**
 * Created by think.
 * Date: 08/11/2018
 * Time: 11:08
 */
public class QuickSortExample
{
    public static void main(String[] args)
    {
        // This is unsorted array
        Integer[] array = new Integer[] { 12, 13, 24, 10, 3, 6, 90, 70 };

        // Let's sort using quick sort
        quickSort( array, 0, array.length - 1 );

        // Verify sorted array
        System.out.println(Arrays.toString(array));
    }

    public static void quickSort(Integer[] arr, int low, int high)
    {
        //check for empty or null array
        if (arr == null || arr.length == 0){
            return;
        }

        if (low >= high){
            return;
        }

        //Get the pivot element from the middle of the list
        int middle = low + (high - low) / 2;
        int pivot = arr[middle];

        // make left < pivot and right > pivot
        int i = low, j = high;
        while (i <= j)
        {
            //Check until all values on left side array are lower than pivot
            while (arr[i] < pivot)
            {
                i++;
            }
            //Check until all values on left side array are greater than pivot
            while (arr[j] > pivot)
            {
                j--;
            }
            //Now compare values from both side of lists to see if they need swapping
            //After swapping move the iterator on both lists
            if (i <= j)
            {
                swap (arr, i, j);
                i++;
                j--;
            }
        }
        //Do same operation as above recursively to sort two sub arrays
        if (low < j){
            quickSort(arr, low, j);
        }
        if (high > i){
            quickSort(arr, i, high);
        }
    }

    public static void swap (Integer array[], int x, int y)
    {
        int temp = array[x];
        array[x] = array[y];
        array[y] = temp;
    }
}

Posted in Java编程语言, 架构运维 | Leave a comment

搭建基于dubbo,zookeeper的spring boot分布式系统示例

目前越来越多的系统采用分布式,微服务的结构,将业务根据模块进行拆分,然后分布式部署,采用RPC的方式调用服务,这样的架构灵活,高效,这里就简单介绍一下如何搭建基于dubbo,zookeeper的spring boot分布式系统,过程很简单,具体的复杂实践还需要在企业级应用中练习。

首先介绍一下用到的各个组件和框架。

spring boot,首先引用官方的介绍,最为准确,Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can “just run”.spring boot让你快速搭建一个技术spring的应用,大大简化了传统spring项目的配置,使用起步依赖和自动配置简化配置流程,快速搭建一个模块或者应用。

dubbo,引用官方介绍,Apache Dubbo™ (incubating) is a high-performance, java based open source RPC framework高性能的RPC框架,比如多台服务器之间服务互相调用,就可以使用它,性能很高,就好比从本地调用,其实我们在蚂蚁没有用这个框架,我们用的自主开发的分布式开发框架也集成了此功能,当然具体应用看自己需求了。

zkoopeer,同样引用官方介绍,Apache ZooKeeper is an effort to develop and maintain an open-source server which enables highly reliable distributed coordination.主要是一个服务协调的软件。具体功能包括了很多,ZooKeeper is a centralized service for maintaining configuration information, naming, providing distributed synchronization, and providing group services。在本教程中,我们将其作为注册中心使用。对于生产者,可以在zk上注册服务地址,消费者通过在注册中心获取注册地址来获取服务。

首先预览一下项目的整个目录结构


这是一个多模块的maven项目,主项目是dubbo-zookeeper,下面有三个子模块,分别是interface,provider,consumer,作用分别是定义服务接口,生产者,消费者。


首先,新建一个spring boot的项目,使用idea搭建很快,细节不表,然后分别建立三个子模块,子模块建立好之后,注意设置一下pom文件,讲parent设置为主项目,比如给出一个provider的配置示例

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.gaoxueping</groupId>
    <artifactId>dubbo-zookeeper-provider</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>dubbo-zookeeper-provider</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>com.gaoxueping</groupId>
        <artifactId>dubbo-zookeeper</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.2</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.gaoxueping</groupId>
            <artifactId>duboo-zookeeper-interface</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.alibaba/dubbo -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>dubbo</artifactId>
            <version>2.5.3</version>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>com.github.sgroschupf</groupId>
            <artifactId>zkclient</artifactId>
            <version>0.1</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


</project>

同时在父项目中设置一下pom,加入一下modules标签,讲子模块加入进去

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.gaoxueping</groupId>
    <artifactId>dubbo-zookeeper</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
    <modules>
        <module>duboo-zookeeper-interface</module>
        <module>dubbo-zookeeper-provider</module>
        <module>dubbo-zookeeper-consumer</module>
    </modules>

    <name>dubbo-zookeeper</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.0.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.2</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


</project>

三个子模块建立好,分别配置resource文件,配置zk的注册信息,这样,通过zk的注册中心,生产者和消费者可以实现通。这里给出provider的配置信息。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://code.alibabatech.com/schema/dubbo
    http://code.alibabatech.com/schema/dubbo/dubbo.xsd
    ">

    <description>Dubbo Demo Service</description>
    <!-- 提供方应用信息,用于计算依赖关系 -->
    <dubbo:application name="dubbo-zookeeper-provider" />

    <!-- 使用zookeeper注册中心暴露服务地址 -->
    <dubbo:registry protocol="zookeeper" address="127.0.0.1:2181"
                    client="zkclient" />

    <!-- dubbo缓存 -->
    <dubbo:protocol id="dubbo" name="dubbo" port="20880"
                    threadpool="cached" threads="100" />

    <!-- Greeting服务 -->
    <bean id="userService" class="com.gaoxueping.dubbozookeeperprovider.UserServiceImpl" />

    <dubbo:service protocol="dubbo"
                   interface="com.gaoxueping.dubooookeeperinterface.UserService" ref="userService" />
</beans>

同样的方式配置完consumer,然后分别启动zookeeper,provider,consumer。就可以实现功能了。

项目的github地址为:https://github.com/hellogxp/dubbo-zookeeper,大家可以现在下来参考

Posted in Java编程语言, 架构运维, 编程语言 | Leave a comment

密码保护:Week 7

这是一篇受密码保护的文章,您需要提供访问密码:

Posted in 胡言乱语 | 要查看留言请输入您的密码。

git使用小结

首先

修改完成之后

git add.

git commit

这个时候是将修改提交到了本地,此时先不要push,先做一下远端代码与本地代码的合并(如果有冲突,push也会失败的)

git pull –rebase

这个表示拉取远端代码与本地代码合并,合并过程中你会发现可能有冲突,

对于有冲突的代码,进行修改,修改结束之后需要再次将修改过的文件执行add,表示修改完毕

git add modified_file

然后继续进行远端代码和本地代码的合并

git rebase –continue

此时合并成功,本地代码与远端代码一致,此时可以通过push进行提交,将本地的代码提交到远端

git push

如果提交的时候有一些本地的修改不想提交,比如一些测试代码,这时候提交的时候会报错,提示有modified文件没有提交,这时候可以先隐藏这些修改

git statsh

然后提交成功后再放开这些隐藏

git stash pop

  • 删除跟踪某个文件或者文件夹
    如果是文件
git rm --cached file

如果是文件夹

git rm -f --cached directory

如果你想同时删除本地文件那么去掉参数 –cached

git commit -m "comment"
git push origin master
  • git pull and overwrite local files
git fetch -all
git reset --hard origin/branch_name
  • 本地新建一个分支并且推送到远程
git checkout -b feature_branch_name
git push -u origin feature_branch_name
  • 回退到上一个版本
    • 查看旧版本
    git log --oneline
    e2f9a78 Revert "Try something crazy"
    872fa7e Try something crazy
    a1e8fb5 Make some important changes to hello.txt
    435b61d Create hello.txt
    9773e52 Initial import
    

    你可以checkout到某个具体的commit

    git checkout a1e8fb5
    

    你当前的工作目录与a1e8fb5这个commit内容一致,你可以查看文件,编辑文件,编译项 目,运行测试等,不要担心,你所做的更改不会被保存。如果要继续开发,可以切换回相应分支,比如master

    git checkout master
    
    • 使用git checkout进行回退
      首先我可以checkout到a1e8fb5这个commit
    git checkout a1e8fb5
    

    checkout到一个具体commit会使得repo处于 “detached HEAD” 状态。这意味着你不在任何分支上。当你切换到具体分支的时候,你做的任何commits都会成为孤儿,也就是Orphaned commit,这些孤儿会被Git’s garbage collector删除,这与JVM的辣鸡收机器道理相通,不可达队形最终会被GC回收。

    在detached HEAD状态下,执行

    git checkout -b new_branch_without_crazy_commit
    

    现在这个repo有一个新的时间线,872fa7e不在其中。我们可以继续这个分支上工作,但是如果你需要在一条不变的分支上工作,比如master,这方式就不太合适,我们可以用revet

    • git revert回退

    我们使用

    git revert HEAD
    

    来退回到上一次提交。这个命令其实是创建了一次新的commit,这个commit内容是回退到上一次提交,我们看一下时间线

    git log --oneline
    e2f9a78 Revert "Try something crazy"
    872fa7e Try something crazy
    a1e8fb5 Make some important changes to hello.txt
    435b61d Create hello.txt
    9773e52 Initial import
    
    • Force “git pull” to overwrite local files
    git fetch --all
    

    and then

    git reset --hard origin/master
    

    OR If you are on some other branch:

    git reset --hard origin/<branch_name>
    

    Explanation:
    git fetch downloads the latest from remote without trying to merge or rebase anything.

    Then the git reset resets the master branch to what you just fetched. The --hard option changes all the files in your working tree to match the files in origin/master

    • 将本地已有的仓库推动到远程仓库
    git init
    git add -A
    git commit -m 'Added my project'
    git remote add origin git@github.com:scotch-io/my-new-project.git
    git push -u -f origin master
    
Posted in 架构运维 | Leave a comment

mac os下安装nginx,php-fpm

换了mac后,稍微有点不熟悉,mac自带了php和apache,不过php是的GD不支持freetype,在使用验证码功能是后有问题,还是建议删掉自带的php个apche,删除的时候建议先find / -name php或者find / -name php5等,找到所有的文件,都删掉,这里给出php个apche的安装文件夹,不过不一定全,至少依赖包是没有删除的,不过不影响我们的后续安装和使用

sudo rm -rf /etc/apache2

sudo rm -rf /usr/libexec/apache2

sudo rm -rf sr/local/lib/php

sudo rm -rf /usr/share/php

sudo rm -rf /usr/bin/php-config

sudo rm -rf /usr/bin/phpize

sudo rm -rf usr/local/etc/php

sudo rm -rf usr/local/include/php

删除文件很多会报错

Operation not permitted
这时候可以重启,并且按住command + R进入recovery模式,然后在终端输入
csrutil disable,这样就不会包权限错误了,如果要恢复可以输入
csrutil enable即可。

卸载之后可以使用brew安装php和nginx,安装目录都会在/usr/local/Cellar,例如你的安装的php56会在/usr/local/Cellar/php56,brew和centos下面的yum差不多,方便

关于brewphp的文档可以查看https://github.com/Homebrew/homebrew-php

brew的文档可查看https://docs.brew.sh/Installation.html

brew tap homebrew/homebrew-php

brew install php56

安装完成之后,做一下相关配置

php的配置文件在/usr/local/etc/php/5.6/php.ini,如果你没有卸载mac自带版本或者你安装过其他版本,为了能够正确使用到你使用brew安装的版本,你需要在你的环境变量中设置一下,让/usr/local/bin优先于/usr/sbin,执行,可以 vim ~/.profile,写入PATH=”/usr/local/bin:$PATH”,完成后source ~/.profile生效,当然,如果你只有一个版本,可以略过

mkdir -p ~/Library/LaunchAgents

cp /usr/local/opt/php56/homebrew.mxcl.php56.plist ~/Library/LaunchAgents/

launchctl load -w ~/Library/LaunchAgents/homebrew.mxcl.php56.plist

对于php-fpm的启动在这里/usr/local/opt/php56/sbin/php56-fpm

安装好之后最好设置一下php的session.save_path,一般都是/tmp,进入php.in中打开注释即可,注意这个文件的读写权限改成777,不然很多用到文件保存session的文件会报错

Posted in PHP服务器脚本 | Leave a comment