innerpeacez

innerpeacez 查看完整档案

填写现居城市  |  填写毕业院校  |  填写所在公司/组织填写个人主网站
编辑

日拱一足,不期速成

个人动态

innerpeacez 发布了文章 · 1月13日

特殊密码锁

题目

描述
有一种特殊的二进制密码锁,由n个相连的按钮组成(n<30),按钮有凹/凸两种状态,用手按按钮会改变其状态。

然而让人头疼的是,当你按一个按钮时,跟它相邻的两个按钮状态也会反转。当然,如果你按的是最左或者最右边的按钮,该按钮只会影响到跟它相邻的一个按钮。

当前密码锁状态已知,需要解决的问题是,你至少需要按多少次按钮,才能将密码锁转变为所期望的目标状态。

输入
两行,给出两个由0、1组成的等长字符串,表示当前/目标密码锁状态,其中0代表凹,1代表凸。
输出
至少需要进行的按按钮操作次数,如果无法实现转变,则输出impossible。
样例输入
011
000
样例输出
1

解答

public class Test

    public static void main(String[] args) {
        String concurrent = "011";
        String spec = "000";
        two(concurrent, spec);
    }

    public static void two(String concurrent, String spec) {
        Assert.assertTrue("Cannot be greater than 30", concurrent.length() <= 30);
        String[] one = toArray(concurrent);
        String[] two = toArray(spec);
        if (one.length != two.length) {
            System.out.println("impossible");
            return;
        }

        Integer[] concurrentInts = toIntegers(one);
        Integer[] specInts = toIntegers(two);
        Integer count = 0;

        for (int i = 0; i < concurrentInts.length - 1; i++) {
            // 最后
            if (i == concurrentInts.length - 2) {
                if (concurrentInts[i] != specInts[i]) {
                    count++;
                    concurrentInts[concurrentInts.length - 2] = swap(concurrentInts[concurrentInts.length - 2]);
                    concurrentInts[concurrentInts.length - 1] = swap(concurrentInts[concurrentInts.length - 1]);
                }
            }

            // 第一个位置和中间位置
            if (concurrentInts[i] != specInts[i]) {
                count++;
                concurrentInts[i] = swap(concurrentInts[i + 1]);
                concurrentInts[i+1] = swap(concurrentInts[i + 1]);
                concurrentInts[i+2] = swap(concurrentInts[i + 2]);
            }
        }

        if (Arrays.toString(concurrentInts).equals(Arrays.toString(specInts))) {
            System.out.println(count);
        } else {
            System.out.println("impossible");
        }
    }

    public static Integer[] toIntegers(String[] strs) {
        Integer[] num = new Integer[strs.length];
        for (int i = 0; i < num.length; i++) {
            num[i] = Integer.parseInt(strs[i]);
        }
        return num;
    }

    public static Integer swap(Integer num) {
        if (num == 0) {
            num = 1;
        }
        if (num == 1) {
            num = 0;
        }
        return num;
    }

    public static String[] toArray(String str) {
        return str.split("");
    }
}
本文由博客一文多发平台 OpenWrite 发布!
查看原文

赞 0 收藏 0 评论 0

innerpeacez 发布了文章 · 2019-12-30

Helm Chart 一键部署 Jenkins

Jenkins

Jenkins是一款开源 CI&CD 软件,用于自动化各种任务,包括构建、测试和部署软件。目前提供超过1000个插件来支持构建、部署、自动化, 满足任何项目的需要。

Jenkins 支持各种运行方式,可通过系统包、Docker 或者通过一个独立的 Java 程序。本文记录通过使用 Helm Chart 将 Jenkins 部署到 Kubernetes 集群中

本文环境

  • jenkins 2.190.3
  • Kubernetes v1.14.8
  • helm 3.0 release

部署

  1. 部署 Kubernetes
  2. 配置 helm 及 kubectl 环境
  3. 部署 Jenkins
部署 Kubernetes

本文不再阐述 kubernetes 部署细节,有需要可以参考一步步部署 Kubernetes.

配置 helm 及 kubectl 环境

本文不再阐述 helm 及 kubectl 环境配置,需要自行安装.

部署 Jenkins
  1. 添加 helm repo

    helm repo add stable https://kubernetes-charts.storage.googleapis.com/
  2. 查看目前的最近版本

    $ helm search repo stable/jenkins -l
    NAME              CHART VERSION    APP VERSION    DESCRIPTION
    stable/jenkins    1.9.11           lts            Open source continuous integration server. It s...
    stable/jenkins    1.9.10           lts            Open source continuous integration server. It s...
    stable/jenkins    1.9.9            lts            Open source continuous integration server. It s...
    stable/jenkins    1.9.8            lts            Open source continuous integration server. It s...
    stable/jenkins    1.9.7            lts            Open source continuous integration server. It s...
    stable/jenkins    1.9.6            lts            Open source continuous integration server. It s...
    stable/jenkins    1.9.5            lts            Open source continuous integration server. It s...
    stable/jenkins    1.9.4            lts            Open source continuous integration server. It s...
  3. 部署 jenkins

    chart 中对应的镜像版本为 lts ,这个版本的镜像 jenkins 一直有在更新。所以我们只需要部署目前最新的 chart 即可,默认是没有指定 storageClass 的,需要指定。

    $ helm install jenkins stable/jenkins -n <namespace> \
                --set persistence.storageClass=<storageClass>

    注意:namespace 为命名空间, storageClass 为存储类

    只需这一条命令 jenkins 就已经部署完成了,并且 chart 中默认安装了一些常用插件,比如 kubernetes 插件,并且生成了对应的配置,chart 真香:)

总结

本文只是简单介绍了使用 chart 的部署过程。

本文由博客一文多发平台 OpenWrite 发布!
查看原文

赞 0 收藏 0 评论 0

innerpeacez 发布了文章 · 2019-10-29

使用 Helm Chart 部署及卸载 istio

部署 istio

1.添加 istio 官方的 helm 仓库

helm repo add istio   https://storage.googleapis.com/istio-release/releases/1.3.3/charts/

2.是否添加成功

helm search repo istio
NAME CHART VERSION APP VERSION DESCRIPTION
istio/istio 1.3.3 1.3.3 Helm chart for all istio components
istio/istio-cni 1.3.3 1.3.3 Helm chart for istio-cni components
istio/istio-init 1.3.3 1.3.3 Helm chart to initialize Istio CRDs

3.创建 istio-system 命名空间

kubectl create ns istio-system

4.创建 istio 所需的 crd 文件

helm install istio-init istio/istio-init -n istio-system

5.检查 CRD 文件是否创建完成,输出为23

kubectl get crds | grep 'istio.io' | wc -l
23

6.部署 istio

helm install istio istio/istio -n istio-system

NAME: istio
LAST DEPLOYED: Thu Oct 24 12:05:06 2019
NAMESPACE: istio-system
STATUS: deployed
REVISION: 1
NOTES:
Thank you for installing Istio.

Your release is named Istio.

To get started running application with Istio, execute the following steps:

  1. Label namespace that application object will be deployed to by the following command (take default namespace as an example)

kubectl label namespace default istio-injection=enabled
​ kubectl get namespace -L istio-injection

  1. Deploy your applications

$ kubectl apply -f <your-application>.yaml

For more information on running Istio, visit:
https://istio.io/

卸载 istio

1.卸载 istio

helm -n istio-system uninstall istio

2.删除 istio crd 文件

helm -n istio-system uninstall istio-init

kubectl delete crd `kubectl get crd | grep istio| awk '{print $1}'`

一键部署及卸载 istio 的脚本

部署脚本

#!/bin/bash

# Add istio official repo
add_repo(){
  VERSION=$1
  REPO="https://storage.googleapis.com/istio-release/releases/${VERSION}/charts/"
  helm repo add istio $REPO

  STATUS_CMD=`echo $?`
  CHECK_REPO_CMD=`helm repo list | grep $REPO | wc -l`
  echo "$STATUS_CMD"
  echo "$CHECK_REPO_CMD"
  while [[ $STATUS_CMD != 0 && $CHECK_REPO_CMD -ge 1 ]]
  do
    sleep 5
    helm repo add istio $REPO

    STATUS_CMD=`echo $?`
    CHECK_REPO_CMD=`helm repo list | grep $REPO | wc -l`
  done
}

# Create istio-system namespace
create_namespace() {
  NAMESPACE=$1
  kubectl create ns ${NAMESPACE}

  STATUS_CMD=`echo $?`
  while [[ $STATUS_CMD != 0 ]]
  do
    sleep 5
    kubectl create ns ${NAMESPACE}
    STATUS_CMD=`echo $?`
  done
}

# Create CRD need for istio
create_crd() {
  NAMESPACE=$1
  helm install istio-init istio/istio-init -n ${NAMESPACE}
  CRD_COUNT=`kubectl get crds | grep 'istio.i' | wc -l`

  while [[ ${CRD_COUNT} != 23 ]]
  do
    sleep 5
    CRD_COUNT=`kubectl get crds | grep 'istio.io' | wc -l`
  done

  echo 'Istio crd create successful'
}

# Deploy istio related components
deploy_istio() {
  NAMESPACE=$1
  VERSION=$2
  helm install istio istio/istio -n ${NAMESPACE}

  check() {
     kubectl -n ${NAMESPACE}  get deploy | grep istio | awk '{print "deployment/"$1}' | while read line ;
     do
       kubectl rollout status $line -n ${NAMESPACE};
     done
  }
  check

  echo "Istio is deployed successful"
}

main(){
  ISTIO_VERSION="1.3.3"
  ISTIO_NAMESPACE="istio-system"
  add_repo $ISTIO_VERSION
  if [[ `kubectl get ns | grep $ISTIO_NAMESPACE | wc -l ` == 0 && `kubectl get ns $ISTIO_NAMESPACE | grep -v NAME | wc -l` == 0 ]] ;then
    create_namespace $ISTIO_NAMESPACE
  fi
  create_crd $ISTIO_NAMESPACE
  deploy_istio $ISTIO_NAMESPACE $ISTIO_VERSION
}

main

卸载脚本

#!/bin/bash

helm -n istio-system uninstall istio 
helm -n istio-system uninstall istio-init
kubectl delete crd `kubectl get crd | grep istio | awk '{print $1}'` 
kubectl delete ns istio-system

注意:卸载需谨慎,删除了 istio-system 的命名空间

总结

上述步骤使用的是 istio 官方提供的默认配置,如果你想要自定配置,可以阅读 values.yaml 文件后,通过 --set 的方式修改,或者直接修改 chart。

本文由博客一文多发平台 OpenWrite 发布!
查看原文

赞 0 收藏 0 评论 0

innerpeacez 发布了文章 · 2019-10-08

使用 chart 部署 skywalking

使用 chart 部署 skywalking

本文主要讲述的是如何使用 Helm Charts 将 SkyWalking 部署到 Kubernetes 集群中,相关文档可以参考skywalking-kubernetes backend-k8s 文档

目前推荐的四种方式:

  • 使用 helm 2 提供的 helm serve 启动本地 helm repo
  • 使用本地 chart 文件部署
  • 使用 harbor 提供的 repo 功能
  • 直接从官方 repo 进行部署

注意:目前 skywalking 的 chart 还没有提交到官方仓库,请先参照前两种方式进行部署

Helm 2 提供的 helm serve

打包对应版本的 skywalking chart

1.配置 helm 环境,参考 Helm 环境配置 ,如果你要部署helm2 相关chart 可以直接配置 helm2 的相关环境

2.克隆/下载ZIP skywalking-kubernetes 这个仓库,仓库关于chart的目录结构如下

helm-chart

  • helm2

    • 6.0.0-GA
    • 6.1.0
  • helm3

    • 6.3.0
    • 6.4.0

克隆/下载ZIP 完成后进入指定目录打包对应版本的chart

cd skywalking-kubernetes/helm-chart/<helm-version>/<skywalking-version>

注意:helm-version 为对应的helm版本目录,skywalking-version 为对应的 skywalking 版本目录,下面以helm3 和 skywalking 6.3.0 为例

cd skywalking-kubernetes/helm-chart/helm3/6.3.0

3.由于skywalking 依赖 elasticsearch 作为存储库,执行以下命令更新依赖,默认会从官方repo进行拉取

helm dep up skywalking
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "stable" chart repository
Update Complete. ⎈Happy Helming!⎈
Saving 1 charts
Downloading elasticsearch from repo https://kubernetes-charts.sto...
Deleting outdated charts

如果官方 repo 不存在,请先添加官方仓库

helm repo add stable https://kubernetes-charts.storage.googleapis.com
"stable" has been added to your repositories

4.打包 skywalking , 执行以下命令

helm package skywalking/
Successfully packaged chart and saved it to: C:codeinnerpeacez_githubskywalking-kuberneteshelm-charthelm36.3.0skywalking-0.1.0.tgz

打包完成后会在当前目录的同级目录生成 .tgz 文件

 ls
skywalking/ skywalking-0.1.0.tgz
启动 helm serve

由于上文配置的 helm 为 helm3 ,但是helm 3中移除了 helm serve 的相关命令,所以需要另外一个环境配置helm2 的相关环境,下载 helm 2.14.3 的二进制文件,配置基本上没有大的差别,不在赘述

初始化 helm

helm init

将上文生成的 skywalking-0.1.0.tgz 文件复制到 helm 相关目录 /root/.helm/repository/local,启动 serve

helm serve --address <ip>:8879 --repo-path /root/.helm/repository/local

注意: ip 为要能够被上文配置 helm 3 环境的机器访问到

可以访问一下看看服务 serve 是否启动成功

curl ip:8879
部署 skywalking

1.在helm3 环境中添加启动的本地 repo

helm repo add local http://<ip>:8879

2.查看 skywalking chart 是否存在于本地仓库中

helm search skywalking
NAME CHART VERSION APP VERSION DESCRIPTION
local/skywalking 0.1.5 6.3.0 Apache SkyWalking APM System

3.部署

helm -n test install skywalking local/skywalking

这样 skywalking 就部署到了 k8s 集群中的 test 命名空间了,至此本地安装skywalking 就完成了。

本地文件部署

如果你不想存储到 chart 到仓库中也可以直接使用本地文件部署 skywalking,按照上面的步骤将skywalking chart 打包完成之后,直接使用以下命令进行部署

helm -n test install skywalking skywalking-0.1.0.tgz

harbor 作为 repo 存储 charts

harbor 目前已经提供了,charts repo 的能力,这样就可以将 docker 镜像和 chart 存储在一个仓库中了,方便维护,具体harbor 的部署方法参考 Harbor 作为存储仓库存储 chart

官方 repo 部署

目前没有发布到官方 repo 中,后续发布完成后,只需要执行下面命令即可

helm install -n test stable/skywalking

总结

四种方式都可以进行部署,如果你想要自定义chart ,需要使用上述两种本地方法及harbor 存储的方式来进行部署.

查看原文

赞 0 收藏 0 评论 0

innerpeacez 发布了文章 · 2019-09-24

豆瓣电影TOP250和书籍TOP250爬虫

豆瓣电影 TOP250 和书籍 TOP250 爬虫

最近开始玩 Python , 学习爬虫相关知识的时候,心血来潮,爬取了豆瓣电影TOP250 和书籍TOP250, 这里记录一下自己玩的过程。

电影 TOP250 爬虫
import requests
from bs4 import BeautifulSoup
import time


def getlist(list_url):
    time.sleep(2)
    res = requests.get(list_url)
    soup = BeautifulSoup(res.text, 'html.parser')
    movie_list = soup.select('.grid_view li')
    for m in movie_list:
        rank = m.select('em')[0].text
        score = m.select('.rating_num')[0].text
        title = m.select('.title')[0].text
        direct = m.select('.info .bd p')[0].text.strip()
        actor = '\n主演:'.join(direct.split('   主演:'))
        director = '年代:'.join(actor.split('                           '))
        if m.select('.inq'):
            comments = m.select('.inq')[0].text.strip()
        else:
            comments = 'None'
        movie.append(
            '排名: ' + rank + '\n'
            + '评分: ' + score + '\n'
            + '片名: ' + title + '\n'
            + director + '\n'
            + '评论: ' + comments + '\n'
            + '\n')
    if soup.select('.next a'):
        asoup = soup.select('.next a')[0]['href']
        next_page = seed_url + asoup
        getlist(next_page)
    else:
        print('结束')
    return movie


def write(movies):
    with open('movie.txt', 'w', encoding='utf8') as m:
        for a in movies:
            m.write(a)


def main():
    write(getlist(seed_url))
    pass


if __name__ == '__main__':
    seed_url = 'https://movie.douban.com/top250'
    movie = []
    main()
书籍 TOP250 爬虫
import bs4
import requests
import re
from bs4 import BeautifulSoup
from operator import itemgetter


def getHtmlText(url):
    try:
        r = requests.get(url)
        r.raise_for_status()
        r.encoding = r.apparent_encoding
        return r.text
    except:
        return ""


def parserText(text, book_list):
    soup = BeautifulSoup(text, 'html.parser')
    for table in soup('table', {'width': '100%'}):
        if isinstance(table, bs4.element.Tag):
            tds = table.find('tr')('td')
            divs = tds[1]('div')
            content = {}
            for div in divs:
                if isinstance(div, bs4.element.Tag):
                    if div.find('a'):
                        name = div.find('a').attrs['title']
                        content.update({"书名": name})
                    if div.select('.rating_nums'):
                        score = div.select('.rating_nums')[0].text
                        content.update({"评分": score})
                    if div.select('.pl'):
                        people_num = div.select('.pl')[0].text
                        regex = re.compile(r'[\d]{1,10}')
                        content.update({"评价人数": regex.findall(people_num)[0]})

            ps = tds[1]('p')
            for p in ps:
                if isinstance(p, bs4.element.Tag):
                    if p.attrs['class'][0] == 'quote':
                        description = p.find('span').string
                        content.update({"介绍": description})
                    if p.attrs['class'][0] == 'pl':
                        author = p.string
                        content.update({"作者信息": author})

            book_list.append(content)

    next_books = soup.find('span', {'class': 'next'})
    if next_books.find('a'):
        a = next_books.find('a').attrs['href']
        text = getHtmlText(a)
        parserText(text, books)

    return book_list


def sortedBookTop250(book_list):
    tmp = sorted(book_list, key=itemgetter('评分'), reverse=True)
    for i in range(len(tmp)):
        tmp[i].update({"排名": i + 1})
    return tmp


def writeToFile(book_list):
    with open('good_books.txt', 'w', encoding='utf8') as book_file:
        for book in book_list:
            for key, value in book.items():
                book_file.write(f'{key}:{value}\n')
            book_file.write('\n')
    pass


def main():
    text = getHtmlText(seed_url)
    book_list = parserText(text, books)
    writeToFile(sortedBookTop250(book_list))
    pass


if __name__ == '__main__':
    seed_url = "https://book.douban.com/top250"
    books = []
    main()

总结

以上直接贴出了代码,这是很简单的两段代码,主要用到了 requests 库和 beautifulsoup 库,需要的可以直接拿去,或者直接去我的 GIthub上拿 movies.txtgood_books.txt

查看原文

赞 0 收藏 0 评论 0

innerpeacez 发布了文章 · 2019-08-30

如何使用 Skywalking Agent ?

如何使用 Skywalking Agent ?

如果你还不知道 Skywalking agent 是什么,请点击这里查看 Probe 或者这里查看快速了解agent,由于我这边大部分都是 JAVA 服务,所以下文以 Java 中使用 agent 为例,提供了以下三种方式供你选择

三种方式:

  • 使用官方提供的基础镜像
  • 将 agent 包构建到已经存在的基础镜像中
  • sidecar 模式挂载 agent
1.使用官方提供的基础镜像

查看官方 docker hub 提供的基础镜像,只需要在你构建服务镜像是 From 这个镜像即可,直接集成到 Jenkins 中可以更加方便

2.将 agent 包构建到已经存在的基础镜像中

提供这种方式的原因是:官方的镜像属于精简镜像,并且是 openjdk ,可能很多命令没有,需要自己二次安装,以下是我构建的过程

  • 下载 oracle jdk

    这个现在 oracle 有点恶心了,wget 各种不行,然后我放弃了,直接从官网下载了

  • 下载 skywalking 官方发行包,并解压(以6.3.0为例)

    wget https://www.apache.org/dyn/closer.cgi/skywalking/6.3.0/apache-skywalking-apm-6.3.0.tar.gz && tar -zxvf apache-skywalking-apm-6.3.0.tar.gz
  • 通过以下 dockerfile 构建基础镜像

    FROM alpine:3.8 
    
    ENV LANG=C.UTF-8
    
    RUN set -eux && \
        apk update && apk upgrade && \
        wget -q -O /etc/apk/keys/sgerrand.rsa.pub https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub &&\
            wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.30-r0/glibc-2.30-r0.apk &&\
            apk --no-cache add unzip vim curl git bash ca-certificates glibc-2.30-r0.apk file && \
        rm -rf /var/lib/apk/* &&\
            mkdir -p /usr/skywalking/agent/
    
    # A streamlined jre
    ADD jdk1.8.0_221/ /usr/java/jdk1.8.0_221/
    ADD apache-skywalking-apm-bin/agent/ /usr/skywalking/agent/
    
    # set env
    ENV JAVA_HOME /usr/java/jdk1.8.0_221
    ENV PATH ${PATH}:${JAVA_HOME}/bin
    
    # run container with base path:/
    WORKDIR /
    
    CMD bash

这里由于 alpine 是基于mini lib 的,但是 java 需要 glibc ,所以加入了 glibc 相关的东西,最后构建出的镜像大小在 490M 左右,应为加了挺多命令还是有点大,仅供参考,同样构建出的镜像也可以直接配置到 jenkins 中。

3.sidecar 模式挂载 agent

如果你们的服务是部署在 Kubernetes 中,你还可以使用这种方式来使用 Skywalking Agent ,这种方式的好处在与不需要修改原来的基础镜像,也不用重新构建新的服务镜像,而是以sidecar 模式,通过共享volume的方式将agent 所需的相关文件挂载到已经存在的服务镜像中

构建 skywalking agent sidecar 镜像的方法

  • 下载skywalking 官方发行包,并解压

    wget https://www.apache.org/dyn/closer.cgi/skywalking/6.3.0/apache-skywalking-apm-6.3.0.tar.gz && tar -zxvf apache-skywalking-apm-6.3.0.tar.gz
  • 通过以下 dockerfile 进行构建

    FROM busybox:latest 
    
    ENV LANG=C.UTF-8
    
    RUN set -eux && mkdir -p /usr/skywalking/agent/
    
    ADD apache-skywalking-apm-bin/agent/ /usr/skywalking/agent/
    
    WORKDIR /

注意:这里我没有在dockerfile中下载skywalking 发行包是因为保证构建出的 sidecar 镜像保持最小,bosybox 只有700 k左右,加上 agent 最后大小小于20M

如何使用 sidecar 呢?

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    name: demo-sw
  name: demo-sw
spec:
  replicas: 1
  selector:
    matchLabels:
      name: demo-sw
  template:
    metadata:
      labels:
        name: demo-sw
    spec:
      initContainers:
      - image: innerpeacez/sw-agent-sidecar:latest
        name: sw-agent-sidecar
        imagePullPolicy: IfNotPresent
        command: ['sh']
        args: ['-c','mkdir -p /skywalking/agent && cp -r /usr/skywalking/agent/* /skywalking/agent']
        volumeMounts:
        - mountPath: /skywalking/agent
          name: sw-agent
      containers:
      - image: nginx:1.7.9
        name: nginx
        volumeMounts:
        - mountPath: /usr/skywalking/agent
          name: sw-agent
        ports:
        - containerPort: 80
      volumes:
      - name: sw-agent
        emptyDir: {}

以上是挂载 sidecar 的 deployment.yaml 文件,以nginx 作为服务为例,主要是通过共享 volume 的方式挂载 agent,首先 initContainers 通过 sw-agent 卷挂载了 sw-agent-sidecar 中的 /skywalking/agent ,并且将上面构建好的镜像中的 agent 目录 cp 到了 /skywalking/agent 目录,完成之后 nginx 启动时也挂载了 sw-agent 卷,并将其挂载到了容器的 /usr/skywalking/agent 目录,这样就完成了共享过程。

总结

这样除去 ServiceMesh 以外,我能想到的方式就介绍完了,希望可以帮助到你。最后给 Skywalking 一个 Star 吧,国人的骄傲。

查看原文

赞 1 收藏 1 评论 0

innerpeacez 发布了文章 · 2019-07-30

Windows 使用 helm3 和 kubectl

简介:

主要原因是,我不会 vim ,在 linux 上修改 charts 的很蹩脚,所以就想着能不能再 windows 上执行 helm 命令,将 charts install linux 上搭建的 kubernetes 集群上,答案当然是可以的。本文将告诉大家怎么在 windows 上执行 helm 命令和 kubectl 命令,本文的先决条件是需要有一个 kubernetes 集群环境,如果没有请自行部署。

本文环境

  • kubernetes 1.15
  • helm 3 alpha.2
  • kubectl

Helm 3 是什么?

helm 是 kubernetes 官方的包管理工具, 通过 helm 将发布在 kubenetes 环境的多个 yaml 以软件包(charts)的形式打包,简化了 kubernetes 集群环境中应用的部署及更新,helm 支持应用的部署,升级,回滚等操作。helm 3 是针对于 helm 来说的升级版,做了一个功能上的调整,以其 github 发布的版本号命名,目前最新的版本为 Helm v3.0.0-alpha.2。个人认为helm 必定会成为未来 CICD 链中的关键一环。

kubectl 是什么?

熟悉 kubernetes 的同学肯定非常熟悉 kubectl ,甚至于每天都会使用到。kubectl是用于针对Kubernetes集群运行命令的命令行接口。

调教经历

简单的了解的 helm 和 kubectl 之后,我们开始实现在 windows 上执行 helm 命令。

安装helm3

1.下载 helm3 windows 安装包,解压之后,你会看到有个 windows-amd64 的文件夹,其中有个 helm.exe 的可执行文件,我将这个文件夹直接放在了 C:\windows-amd64

2.设置 path

1564151212023

1564151483086

1564151916623

  1. helm init

打开 CMD,进行 helm 的初始化操作, 如果你如法访问 google 的 stable 仓库 ,将以下内容保存到$HOMEhelmrepositoryrepositories.yaml 文件中。

apiVersion: v1
generated: "2019-07-23T09:39:54.849920932+08:00"
repositories:
- caFile: ""
  cache: C:\Users\innerpeacez\.helm\repository\cache\stable-index.yaml
  certFile: ""
  keyFile: ""
  name: stable
  password: ""
  url: https://kubernetes-charts.storage.googleapis.com
  username: ""

出现下图所示,就说明初始化完成了

1564152036983

安装 kubectl

1.下载 kubectl windows 安装包

2.设置path

这里为了方便,我将 kubectl.exe 文件和 helm.exe 文件放在了同一个目录,就不用设置两次path了

3.配置 kubernetes config 文件,这个文件是你在 linux 服务器上使用 kubectl 与kubernetes 交互的配置文件,我们只要在 linux 上找到 $HOME/.kube/config 文件,将其复制到windows 的$HOME/.kube/config ,即可,我的位置是 C:\Users\innerpeacez\.kube\config

4.测试 kubectl

1564213477567

通过 helm 部署应用到 kubernetes 中

配置好 helm 和 kuberctl 之后,我们还需要给helm 设置 conext ,因为 helm 3,的访问 k8s 集群权限是与 我们在 kubeconfig 设置的权限是一致的

查看当前 context

kubectl config current-context

设置 context 指定我们需要部署应用对应的 namespace ,不指定使用的是 default

kubectl config set-context <current-context> --namespace test

部署应用

helm -n test install promethues .\prometheus\

出现以下内容就说明应用已经开始部署了

NAME: promethues
LAST DEPLOYED: 2019-07-27 15:53:30.4257149 +0800 CST m=+0.775545401
NAMESPACE: test
STATUS: deployed

NOTES:
The Prometheus server can be accessed via port 80 on the following DNS name from within your cluster:

.......

总结

本文主要讲述了 windows 中我们怎么通过 helm 与 kubernetes 进行交互,写这篇文章是因为目前网上大部分介绍 windows 上使用 k8s 的都是在 windows 上使用 minikube 来玩的,还有就是为了方便调试 charts。希望可以帮助到你。

查看原文

赞 1 收藏 1 评论 0

innerpeacez 发布了文章 · 2019-07-30

Windows 使用 helm3 和 kubectl

简介:

主要原因是,我不会 vim ,在 linux 上修改 charts 的很蹩脚,所以就想着能不能再 windows 上执行 helm 命令,将 charts install linux 上搭建的 kubernetes 集群上,答案当然是可以的。本文将告诉大家怎么在 windows 上执行 helm 命令和 kubectl 命令,本文的先决条件是需要有一个 kubernetes 集群环境,如果没有请自行部署。

本文环境

  • kubernetes 1.15
  • helm 3 alpha.2
  • kubectl

Helm 3 是什么?

helm 是 kubernetes 官方的包管理工具, 通过 helm 将发布在 kubenetes 环境的多个 yaml 以软件包(charts)的形式打包,简化了 kubernetes 集群环境中应用的部署及更新,helm 支持应用的部署,升级,回滚等操作。helm 3 是针对于 helm 来说的升级版,做了一个功能上的调整,以其 github 发布的版本号命名,目前最新的版本为 Helm v3.0.0-alpha.2。个人认为helm 必定会成为未来 CICD 链中的关键一环。

kubectl 是什么?

熟悉 kubernetes 的同学肯定非常熟悉 kubectl ,甚至于每天都会使用到。kubectl是用于针对Kubernetes集群运行命令的命令行接口。

调教经历

简单的了解的 helm 和 kubectl 之后,我们开始实现在 windows 上执行 helm 命令。

安装helm3

1.下载 helm3 windows 安装包,解压之后,你会看到有个 windows-amd64 的文件夹,其中有个 helm.exe 的可执行文件,我将这个文件夹直接放在了 C:\windows-amd64

2.设置 path

1564151212023

1564151483086

1564151916623

  1. helm init

打开 CMD,进行 helm 的初始化操作, 如果你如法访问 google 的 stable 仓库 ,将以下内容保存到$HOMEhelmrepositoryrepositories.yaml 文件中。

apiVersion: v1
generated: "2019-07-23T09:39:54.849920932+08:00"
repositories:
- caFile: ""
  cache: C:\Users\innerpeacez\.helm\repository\cache\stable-index.yaml
  certFile: ""
  keyFile: ""
  name: stable
  password: ""
  url: https://kubernetes-charts.storage.googleapis.com
  username: ""

出现下图所示,就说明初始化完成了

1564152036983

安装 kubectl

1.下载 kubectl windows 安装包

2.设置path

这里为了方便,我将 kubectl.exe 文件和 helm.exe 文件放在了同一个目录,就不用设置两次path了

3.配置 kubernetes config 文件,这个文件是你在 linux 服务器上使用 kubectl 与kubernetes 交互的配置文件,我们只要在 linux 上找到 $HOME/.kube/config 文件,将其复制到windows 的$HOME/.kube/config ,即可,我的位置是 C:\Users\innerpeacez\.kube\config

4.测试 kubectl

1564213477567

通过 helm 部署应用到 kubernetes 中

配置好 helm 和 kuberctl 之后,我们还需要给helm 设置 conext ,因为 helm 3,的访问 k8s 集群权限是与 我们在 kubeconfig 设置的权限是一致的

查看当前 context

kubectl config current-context

设置 context 指定我们需要部署应用对应的 namespace ,不指定使用的是 default

kubectl config set-context <current-context> --namespace test

部署应用

helm -n test install promethues .\prometheus\

出现以下内容就说明应用已经开始部署了

NAME: promethues
LAST DEPLOYED: 2019-07-27 15:53:30.4257149 +0800 CST m=+0.775545401
NAMESPACE: test
STATUS: deployed

NOTES:
The Prometheus server can be accessed via port 80 on the following DNS name from within your cluster:

.......

总结

本文主要讲述了 windows 中我们怎么通过 helm 与 kubernetes 进行交互,写这篇文章是因为目前网上大部分介绍 windows 上使用 k8s 的都是在 windows 上使用 minikube 来玩的,还有就是为了方便调试 charts。希望可以帮助到你。

查看原文

赞 1 收藏 1 评论 0

innerpeacez 发布了文章 · 2019-07-26

Helm 3 使用 harbor 作为仓库存储 charts

简介

本文讲述的是调教 Helm 3 和 harbor 1.6+ 的经验,从 helm2 更新到 helm 3 并且将 charts 推送到 harbor 中进行存储,移除了原先的 helm serve,在讲述怎么操作之前先来看一下Helm 3 和 Harbor 1.6+ 的新特性。

Helm 3 新特性

  • 移除了 Tiller
  • 不同的 namespace 可以使用相同的 Release Name
  • 简化模板对象 .Capabilities
  • 使用JSONSchema验证 charts 的 Values
  • requirements.yaml合并到Chart.yaml
  • helm install 时需要指定 Release Name,开启自动生成需要 --generate-name 参数
  • 支持 push 到远端 registry (如:harbor)
  • 移除 helm serve
  • 命令行变化(将原先的命令保留为别名Aliases)

    • helm delete --> helm uninstall
    • helm inspect -> helm show
    • helm fetch -> helm pull
  • go 导入路径改变 k8s.io/helm --> helm.sh/helm

具体新特性可以参考Helm 3 新特性,或者参考Helm 官方文档

Harbor v1.6.0 新特性

  • 支持存储 helm charts
  • ...

这里没什么好说的,想要了解更多关于Harbor的,可以参考官方Github

调教开始

在了解了以上新的特性之后,让我着手来操作吧

环境
  • kubernetes 1.10+
  • helm 3
  • harbor 1.6+
调教步骤
  1. 确保kubernetes环境可用
  2. 下载并初始化 helm 3
  3. 安装 harbor 1.6+
  4. 添加 harbor 中的 chartrepo 到 helm 3 中
  5. 安装使用 helm-push 插件
1.确保kubernetes环境可用

这里就不多说 kubernetes 环境的具体搭建过程了,搭建步骤随处可见。

2.下载并初始化 helm 3

首先执行一下命令,下载并解压安装包

wget https://get.helm.sh/helm-v2.14.2-linux-amd64.tar.gz
tar zxvf helm-v2.14.2-linux-amd64.tar.gz
cd linux-amd64
cp helm /usr/local/bin

解压之后,你如果使用过helm 2 你会发现里面tiller的二进制文件不见了,前文的新特性中已经说了,helm 3 已经移除了 tiller

接下来初始化 helm

helm init

1563862747766

默认添加官方 repo stable https://kubernetes-charts.storage.googleapis.com

3.安装 harbor 1.6+

这里我使用的是 harbor 官方提供的 charts repo,好奇心驱使我打开了这个网站 https://helm.goharbor.io/

1563863645436

这熟悉的界面让我严重怀疑是使用的 helm serve 启的 repo(滑稽脸)

添加harbor repo

helm repo add goharbor https://helm.goharbor.io

这个 repo 只有一个charts harbor ,对应的 harbor 版本为1.8.1

在安装之前我们需要配置一下 kube config context

查看当前的context

kubectl config current-context

设置 context 指定对应的 namespace ,不指定使用的是 default

kubectl config set-context <current-context> --namespace test

这里是因为,helm 3 开始helm 3 的执行权限和kubectl config 的权限是一致的,通过kubectl config的方式来控制helm 3 的执行权限。

按时安装harbor ,这里为了简化测试操作,我关闭了数据卷的挂载并使用的是 NodePort 方式进行访问。

helm -n test install harbor goharbor/harbor --set persistence.enabled=false --set expose.type=nodePort --set expose.tls.enabled=false --set externalURL=http://192.168.10.196:30002

参数说明:

  • persistence.enabled=false 关闭存储,为了方便操作,真实使用时需要挂在存储
  • expose.type=nodePort 使用 NodePort 访问
  • expose.tls.enabled=false 关闭tls
  • externalURL=http://192.168.10.196:30002 设置登录 harbor 的外部链接

出现以下返回,就证明已经开始安装了

NAME: harbor
LAST DEPLOYED: 2019-07-23 11:00:38.525597536 +0800 CST m=+0.690703892
NAMESPACE: test
STATUS: deployed

NOTES:
Please wait for several minutes for Harbor deployment to complete.
Then you should be able to visit the Harbor portal at https://core.harbor.domain. 
For more details, please visit https://github.com/goharbor/harbor.
4.添加 harbor 中的 chartrepo 到 helm 3 中

harbor 装好之后,我们访问 http://192.168.10.196:30002 进行登录 harbor, harbor 的默认账号密码是 admin/Harbor12345

1564133342050

新建一个chart repo

1564133389826

创建一个 test 用户

1564134352276

添加 repo 到 helm 中

helm repo add test http://192.168.10.76:30002/chartrepo/chart_repo
5.安装使用 helm-push 插件
helm plugin install https://github.com/chartmuseum/helm-push

这里最好本地配置一下 github 的 dns 地址,不然可能会出现链接超时的现象

安装好插件之后,就可以push charts 到 harbor 里面了

helm push grafana-0.0.2.tgz test --username test --password xxx

1564135632193

出现以上就说明 push 成功了 ,恭喜!!!

参考


查看原文

赞 3 收藏 3 评论 0

innerpeacez 发布了文章 · 2019-07-26

Helm 3 使用 harbor 作为仓库存储 charts

简介

本文讲述的是调教 Helm 3 和 harbor 1.6+ 的经验,从 helm2 更新到 helm 3 并且将 charts 推送到 harbor 中进行存储,移除了原先的 helm serve,在讲述怎么操作之前先来看一下Helm 3 和 Harbor 1.6+ 的新特性。

Helm 3 新特性

  • 移除了 Tiller
  • 不同的 namespace 可以使用相同的 Release Name
  • 简化模板对象 .Capabilities
  • 使用JSONSchema验证 charts 的 Values
  • requirements.yaml合并到Chart.yaml
  • helm install 时需要指定 Release Name,开启自动生成需要 --generate-name 参数
  • 支持 push 到远端 registry (如:harbor)
  • 移除 helm serve
  • 命令行变化(将原先的命令保留为别名Aliases)

    • helm delete --> helm uninstall
    • helm inspect -> helm show
    • helm fetch -> helm pull
  • go 导入路径改变 k8s.io/helm --> helm.sh/helm

具体新特性可以参考Helm 3 新特性,或者参考Helm 官方文档

Harbor v1.6.0 新特性

  • 支持存储 helm charts
  • ...

这里没什么好说的,想要了解更多关于Harbor的,可以参考官方Github

调教开始

在了解了以上新的特性之后,让我着手来操作吧

环境
  • kubernetes 1.10+
  • helm 3
  • harbor 1.6+
调教步骤
  1. 确保kubernetes环境可用
  2. 下载并初始化 helm 3
  3. 安装 harbor 1.6+
  4. 添加 harbor 中的 chartrepo 到 helm 3 中
  5. 安装使用 helm-push 插件
1.确保kubernetes环境可用

这里就不多说 kubernetes 环境的具体搭建过程了,搭建步骤随处可见。

2.下载并初始化 helm 3

首先执行一下命令,下载并解压安装包

wget https://get.helm.sh/helm-v2.14.2-linux-amd64.tar.gz
tar zxvf helm-v2.14.2-linux-amd64.tar.gz
cd linux-amd64
cp helm /usr/local/bin

解压之后,你如果使用过helm 2 你会发现里面tiller的二进制文件不见了,前文的新特性中已经说了,helm 3 已经移除了 tiller

接下来初始化 helm

helm init

1563862747766

默认添加官方 repo stable https://kubernetes-charts.storage.googleapis.com

3.安装 harbor 1.6+

这里我使用的是 harbor 官方提供的 charts repo,好奇心驱使我打开了这个网站 https://helm.goharbor.io/

1563863645436

这熟悉的界面让我严重怀疑是使用的 helm serve 启的 repo(滑稽脸)

添加harbor repo

helm repo add goharbor https://helm.goharbor.io

这个 repo 只有一个charts harbor ,对应的 harbor 版本为1.8.1

在安装之前我们需要配置一下 kube config context

查看当前的context

kubectl config current-context

设置 context 指定对应的 namespace ,不指定使用的是 default

kubectl config set-context <current-context> --namespace test

这里是因为,helm 3 开始helm 3 的执行权限和kubectl config 的权限是一致的,通过kubectl config的方式来控制helm 3 的执行权限。

按时安装harbor ,这里为了简化测试操作,我关闭了数据卷的挂载并使用的是 NodePort 方式进行访问。

helm -n test install harbor goharbor/harbor --set persistence.enabled=false --set expose.type=nodePort --set expose.tls.enabled=false --set externalURL=http://192.168.10.196:30002

参数说明:

  • persistence.enabled=false 关闭存储,为了方便操作,真实使用时需要挂在存储
  • expose.type=nodePort 使用 NodePort 访问
  • expose.tls.enabled=false 关闭tls
  • externalURL=http://192.168.10.196:30002 设置登录 harbor 的外部链接

出现以下返回,就证明已经开始安装了

NAME: harbor
LAST DEPLOYED: 2019-07-23 11:00:38.525597536 +0800 CST m=+0.690703892
NAMESPACE: test
STATUS: deployed

NOTES:
Please wait for several minutes for Harbor deployment to complete.
Then you should be able to visit the Harbor portal at https://core.harbor.domain. 
For more details, please visit https://github.com/goharbor/harbor.
4.添加 harbor 中的 chartrepo 到 helm 3 中

harbor 装好之后,我们访问 http://192.168.10.196:30002 进行登录 harbor, harbor 的默认账号密码是 admin/Harbor12345

1564133342050

新建一个chart repo

1564133389826

创建一个 test 用户

1564134352276

添加 repo 到 helm 中

helm repo add test http://192.168.10.76:30002/chartrepo/chart_repo
5.安装使用 helm-push 插件
helm plugin install https://github.com/chartmuseum/helm-push

这里最好本地配置一下 github 的 dns 地址,不然可能会出现链接超时的现象

安装好插件之后,就可以push charts 到 harbor 里面了

helm push grafana-0.0.2.tgz test --username test --password xxx

1564135632193

出现以上就说明 push 成功了 ,恭喜!!!

参考


查看原文

赞 3 收藏 3 评论 0

innerpeacez 发布了文章 · 2019-07-19

Spring Boot 配置多源的 RabbitMQ

简介

MQ 是开发中很平常的中间件,本文讲述的是怎么在一个Spring Boot项目中配置多源的RabbitMQ,这里不过多的讲解RabbitMQ的相关知识点。如果你也有遇到需要往多个RabbitMQ中发送消息的需求,希望本文可以帮助到你。

环境

  • rabbitmq 3.7.12
  • spring boot 2.1.6.RELEASE

当然软件的版本不是硬性要求,只是我使用的环境而已,唯一的要求是需要启动两个RabbitMQ,我这边是在kubernetes集群中使用helm 官方提供的charts包快速启动的两个rabbitmq-ha高可用rabbitmq集群。

想要了解 kubernetes或者helm,可以参看以下 github仓库:

SpringBoot中配置两个RabbitMQ源

在springboot 中配置单个RabbitMQ是极其简单的,我们只需要使用Springboot为我们自动装配的RabbitMQ相关的配置就可以了。但是需要配置多个源时,第二个及其以上的就需要单独配置了,这里我使用的都是单独配置的。

代码:
/**
 * @author innerpeacez
 * @since 2019/3/11
 */
@Data
public abstract class AbstractRabbitConfiguration {

    protected String host;
    protected int port;
    protected String username;
    protected String password;

    protected ConnectionFactory connectionFactory() {
        CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
        connectionFactory.setHost(host);
        connectionFactory.setPort(port);
        connectionFactory.setUsername(username);
        connectionFactory.setPassword(password);
        return connectionFactory;
    }
}

第一个源的配置代码

package com.zhw.study.springbootmultirabbitmq.config;

import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.amqp.SimpleRabbitListenerContainerFactoryConfigurer;
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;

/**
 * @author innerpeacez
 * @since 2019/3/8
 */

@Configuration
@ConfigurationProperties("spring.rabbitmq.first")
public class FirstRabbitConfiguration extends AbstractRabbitConfiguration {

    @Bean(name = "firstConnectionFactory")
    @Primary
    public ConnectionFactory firstConnectionFactory() {
        return super.connectionFactory();
    }

    @Bean(name = "firstRabbitTemplate")
    @Primary
    public RabbitTemplate firstRabbitTemplate(@Qualifier("firstConnectionFactory") ConnectionFactory connectionFactory) {
        return new RabbitTemplate(connectionFactory);
    }

    @Bean(name = "firstFactory")
    public SimpleRabbitListenerContainerFactory firstFactory(SimpleRabbitListenerContainerFactoryConfigurer configurer,
                                                             @Qualifier("firstConnectionFactory") ConnectionFactory connectionFactory) {
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        configurer.configure(factory, connectionFactory);
        return factory;
    }

    @Bean(value = "firstRabbitAdmin")
    public RabbitAdmin firstRabbitAdmin(@Qualifier("firstConnectionFactory") ConnectionFactory connectionFactory) {
        return new RabbitAdmin(connectionFactory);
    }
}

第二个源的配置代码

package com.zhw.study.springbootmultirabbitmq.config;

import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.amqp.SimpleRabbitListenerContainerFactoryConfigurer;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author innerpeacez
 * @since 2019/3/8
 */

@Configuration
@ConfigurationProperties("spring.rabbitmq.second")
public class SecondRabbitConfiguration extends AbstractRabbitConfiguration {

    @Bean(name = "secondConnectionFactory")
    public ConnectionFactory secondConnectionFactory() {
        return super.connectionFactory();
    }

    @Bean(name = "secondRabbitTemplate")
    public RabbitTemplate secondRabbitTemplate(@Qualifier("secondConnectionFactory") ConnectionFactory connectionFactory) {
        return new RabbitTemplate(connectionFactory);
    }

    @Bean(name = "secondFactory")
    public SimpleRabbitListenerContainerFactory secondFactory(SimpleRabbitListenerContainerFactoryConfigurer configurer,
                                                             @Qualifier("secondConnectionFactory") ConnectionFactory connectionFactory) {
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        configurer.configure(factory, connectionFactory);
        return factory;
    }

    @Bean(value = "secondRabbitAdmin")
    public RabbitAdmin secondRabbitAdmin(@Qualifier("secondConnectionFactory") ConnectionFactory connectionFactory) {
        return new RabbitAdmin(connectionFactory);
    }
}

配置信息

spring:
  application:
    name: multi-rabbitmq
  rabbitmq:
    first:
      host: 192.168.10.76
      port: 30509
      username: admin
      password: 123456
    second:
      host: 192.168.10.76
      port: 31938
      username: admin
      password: 123456
测试

这样我们的两个RabbitMQ源就配置好了,接下来我们进行测试使用,为了方便使用,我写了一个MultiRabbitTemplate.class 方便我们使用不同的源。

/**
 * @author innerpeacez
 * @since 2019/3/8
 */
@Component
public abstract class MultiRabbitTemplate {

    @Autowired
    @Qualifier(value = "firstRabbitTemplate")
    public AmqpTemplate firstRabbitTemplate;

    @Autowired
    @Qualifier(value = "secondRabbitTemplate")
    public AmqpTemplate secondRabbitTemplate;
}

第一个消息发送者类 TestFirstSender.class

/**
 * @author innerpeacez
 * @since 2019/3/11
 */
@Component
@Slf4j
public class TestFirstSender extends MultiRabbitTemplate implements MessageSender {

    @Override
    public void send(Object msg) {
        log.info("rabbitmq1 , msg: {}", msg);
        firstRabbitTemplate.convertAndSend("rabbitmq1", msg);
    }

    public void rabbitmq1sender() {
        this.send("innerpeacez1");
    }
}

第二个消息发送者类 TestSecondSender.class

/**
 * @author innerpeacez
 * @since 2019/3/11
 */
@Component
@Slf4j
public class TestSecondSender extends MultiRabbitTemplate implements MessageSender {

    @Override
    public void send(Object msg) {
        log.info("rabbitmq2 , msg: {}", msg);
        secondRabbitTemplate.convertAndSend("rabbitmq2", msg);
    }

    public void rabbitmq2sender() {
        this.send("innerpeacez2");
    }
}

动态创建Queue的消费者

/**
 * @author innerpeacez
 * @since 2019/3/11
 */

@Slf4j
@Component
public class TestFirstConsumer implements MessageConsumer {

    @Override
    @RabbitListener(bindings = @QueueBinding(value = @Queue("rabbitmq1")
            , exchange = @Exchange("rabbitmq1")
            , key = "rabbitmq1")
            , containerFactory = "firstFactory")
    public void receive(Object obj) {
        log.info("rabbitmq1 , {}", obj);
    }

}
/**
 * @author innerpeacez
 * @since 2019/3/11
 */

@Slf4j
@Component
public class TestSecondConsumer implements MessageConsumer {

    @Override
    @RabbitListener(bindings = @QueueBinding(value = @Queue("rabbitmq2")
            , exchange = @Exchange("rabbitmq2")
            , key = "rabbitmq2")
            , containerFactory = "secondFactory")
    public void receive(Object obj) {
        log.info("rabbitmq2 , {}", obj);
    }

}

测试类

@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class SpringBootMultiRabbitmqApplicationTests extends MultiRabbitTemplate {

    @Autowired
    private TestFirstSender firstSender;
    @Autowired
    private TestSecondSender secondSender;

    /**
     * 一百个线程向 First Rabbitmq 的 rabbitmq1 queue中发送一百条消息
     */
    @Test
    public void testFirstSender() {
        for (int i = 0; i < 100; i++) {
            new Thread(() ->
                    firstSender.rabbitmq1sender()
            ).start();
        }
        try {
            Thread.sleep(1000 * 10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 一百个线程向 Second Rabbitmq 的 rabbitmq2 queue中发送一百条消息
     */
    @Test
    public void testSecondSender() {
        for (int i = 0; i < 100; i++) {
            new Thread(() ->
                    secondSender.rabbitmq2sender()
            ).start();
        }
        try {
            Thread.sleep(1000 * 10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}

测试结果:

总结

这样配置好之后我们就可向两个RabbitMQ中发送消息啦。这里只配置了两个源,当然如果你需要更多的源,仅仅只需要配置*RabbitConfiguration.class就可以啦。本文没有多说关于RabbitMQ的相关知识,如果未使用过需要自己了解一下相关知识。


查看原文

赞 1 收藏 0 评论 0

innerpeacez 发布了文章 · 2019-07-19

linux 定时任务 crontabs 安装及使用方法

图片描述

安装 crontab

yum install crontabs

centos7 自带了我没有手动去装

启动/关闭

service crond start // 启动服务
service crond stop // 关闭服务
service crond restart // 重启服务
service crond reload // 重新载入配置

查看 crontab 服务是否已设置为开机启动

systemctl list-unit-files | grep enable | grep crond

将 crontab 加入开机自动启动

chkconfig crond on
// 或者
systemctl enable crond.service

查看 crontab 状态

service crond status // 查看crontab服务状态

编写定时任务

  • 命令格式
min hour day month dayofweek command
 分  时   天    月    星期几      命令
​ min:每个小时的第几分钟执行该任务;取值范围0-59

​ hour:每天的第几个小时执行该任务;取值范围0-23

​ day:每月的第几天执行该任务;取值范围1-31

​ month:每年的第几个月执行该任务;取值范围1-12

​ dayofweek:每周的第几天执行该任务;取值范围0-6,0表示周末

​ command:指定要执行的命令

  • 编辑命令两种方式

    1. 在命令行输入: crontab -e 然后添加相应的任务,wq存盘退出
    2. 直接编辑/etc/crontab 文件,即vi /etc/crontab,添加相应的任务
  • 时间格式
​ * :表示任意的时刻;如小时位 * 则表示每个小时

​ n :表示特定的时刻;如小时位 5 就表示5时

​ n,m :表示特定的几个时刻;如小时位 1,10 就表示1时和10时

​ n-m :表示一个时间段;如小时位 1-5 就表示1到5点

/n : 表示每隔多少个时间单位执行一次;如小时位 /1 就表示每隔1个小时执行一次命令,也可以写成 1-23/1

小栗子

* 1 * * * ~/clear_cache.sh :从 1:00 到 1:59 每隔1分钟执行一次脚本
0 * * * * ~/clear_cache.sh :每个小时的 0 分钟执行一次脚本
*/10 * * * * ~/clear_cache.sh :每隔10分执行一次脚本

清理系统cache的脚本

代码:
vim ~/clear_cache_logs.txt
sudo sysctl -w vm.drop_caches=3
sudo sysctl -w vm.drop_caches=1
echo `date -R` >> ~/clear_cache_logs.txt
free -lh >> ~/clear_cache_logs.txt

清理内存 cache ,并将清理时间和内存剩余情况日志输入到~/clear_cache_logs.txt文件中,方便查看,可以结合crontab做定时清理内存cache的定时任务。

查看原文

赞 1 收藏 1 评论 0

innerpeacez 发布了文章 · 2019-07-11

lombok

相识

lombok想必已经有很多人已经使用了很长时间了,而我却是第一次接触到,有点呆。lombok主要是用于减少重复代码,通过一组简单的注释取代一些重复的Java代码。对于lombok的评价褒贬不一,有的人觉得特别方便,有的人觉得改变了一成不变的代码结构,增加了代码维护成本(有的人没有用过lombok),我是觉得每一个工具诞生肯定是有他诞生的价值的,多学一个是一个啊,小老弟,用不用再说。:)

官方文档地址

官方API地址

官方注解介绍地址

准备

1、下载idea插件

我这里已经安装好了,没有安装的时候按钮应该是Install

这里写图片描述

2、pom文件引入project lombok的maven依赖

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.2</version>
    <scope>provided</scope>
</dependency>

lombok maven版本

注解介绍

@Getter和@Setter

顾名思义,生成get和set方法的注解,@Getter和@Setter发生在编译阶段,编译之后,@Getter和@Setter相关的注解就消失了,取而代之的是相应的get和set方法。

public class LombokTest {
    @Getter @Setter
    private boolean flag;
}

lombok遵循了boolean类型的get方法的约定,我们来看一下编译后的代码(等效Java代码)吧。

public class LombokTest {
    private boolean flag;

    public LombokTest() {
    }

    public boolean isFlag() {
        return this.flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}

可以看到boolean类型的get方法是isFlag()而不是getFlag()。我们还可定义生成get和set方法的访问级别。

public class LombokTest {
    @Getter(AccessLevel.PUBLIC)
    @Setter(AccessLevel.PROTECTED)
    private String name;
}

编译后的代码(等效Java代码)

public class LombokTest {
    private String name;

    public LombokTest() {
    }

    public String getName() {
        return this.name;
    }

    protected void setName(String name) {
        this.name = name;
    }
}

可以看到setName的访问级别是protected。lombok提供了下面几种级别。

这里写图片描述

AccessLevel枚举类的源代码。

public enum AccessLevel {
    PUBLIC, // public
    MODULE, // 编译后相当于 default
    PROTECTED, // protected
    PACKAGE, // default
    PRIVATE, // private
    NONE; // 不生成

    private AccessLevel() {
    }
}

最后@Getter和@Setter注解是可以写在类级别的,作用于所有的成员变量,无法细粒度的控制访问级别。

@Getter(AccessLevel.MODULE)
@Setter(AccessLevel.PROTECTED)
public class LombokTest {
    private String name;
    private boolean flag;
}

编译后的代码(等效Java代码)

public class LombokTest {
    private String name;
    private boolean flag;

    public LombokTest() {
    }

    String getName() {
        return this.name;
    }

    boolean isFlag() {
        return this.flag;
    }

    protected void setName(String name) {
        this.name = name;
    }

    protected void setFlag(boolean flag) {
        this.flag = flag;
    }
}

@NonNull

变量空检查,如果@NonNull注解使用在构造函数的字段时,构造函数也会进行空变量检查。不可用于类级别。

public class LombokTest {
    @NonNull
    @Setter
    @Getter
    private String name;
    
    public LombokTest(@NonNull String name) {
        this.name = name;
    }
}

编译后的代码(等效Java代码)

public class LombokTest {
    @NonNull
    private String name;

    public LombokTest(@NonNull String name) {
        if (name == null) {
            throw new NullPointerException("name is marked @NonNull but is null");
        } else {
            this.name = name;
        }
    }

    public void setName(@NonNull String name) {
        if (name == null) {
            throw new NullPointerException("name is marked @NonNull but is null");
        } else {
            this.name = name;
        }
    }

    @NonNull
    public String getName() {
        return this.name;
    }
}

@ToString

用于生成toString()方法。只能用于类级别。static修饰的变量是不能生成在toString()方法中的,原因是static修饰的变量在类创建的时候就生成了,只有一个;而普通的变量属于对象,每创建一个新的对象都会有一个。可以理解为static修饰的变量属于类,而普通变量数据对象,这样可能好理解一点。

@ToString
public class LombokTest {
    private String name;
    private static String phone;
}

编译后的代码(等效Java代码)

public class LombokTest {
    private String name;
    private static String phone;

    public LombokTest() {
    }

    public String toString() {
        return "YmlProperties(name=" + this.name + ")";
    }
}

默认情况下出了static修饰的字段都将以name-value的形式生成在toString()方法中。同时toString注解中还可以设置一些属性来细粒度的控制toString()方法。

下面是ToString注解的源码和解释:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface ToString {
    boolean includeFieldNames() default true; // 生成toString()方法时默认为 name-value的形式,设置为false时 则 value的形式。

    String[] exclude() default {}; // 不包含指定字段

    String[] of() default {}; // 包含指定字段

    boolean callSuper() default false; // 是否调用父类的toString()方法

    boolean doNotUseGetters() default false; // 生成toString()方法是否直接访问字段,设置为true时直接访问字段(如:this.name),false时通过getter()方法进行访问(如:this.getName())。当然如果没有生成getter()方法,无论是否设置都直接访问字段

    boolean onlyExplicitlyIncluded() default false; // 是否仅仅包含添加了@ToString.Include的字段,默认为false。

    @Target({ElementType.FIELD})
    @Retention(RetentionPolicy.SOURCE)
    public @interface Exclude { // 使用@ToString.Exclude修饰字段,需要和@ToString注解一起使用,单独使用无效。
    }

    @Target({ElementType.FIELD, ElementType.METHOD})
    @Retention(RetentionPolicy.SOURCE)
    public @interface Include {// 使用@ToString.Include修饰字段,需要和@ToString注解一起使用,单独使用无效。
        int rank() default 0;

        String name() default "";
    }
}

注意:

  • of和exclude属性设置多个字段时,以字符串数组的形式,如:
@ToString(of = {"name","phone"}) // 包含name和phone字段
@ToString(exclude = {"name","phone"}) // 不包含name和phone字段
  • 使用@ToString.Include或者@ToString.Exclude注解时也需要使用@ToString注解,否者无效。
@ToString
public class LombokTest {
    @ToString.Include
    private String name;
    @ToString.Exclude
    private String phone;
}

@EqualsAndHashCode

生成equals()和hashCode()方法。默认使用所有的非static和非transient字段生成这两个方法。

@EqualsAndHashCode
public class LombokTest {
    private String name;
    private String phone;
    private boolean flag;
}

编译后的代码(等效Java代码)

public class LombokTest {
    private String name;
    private String phone;
    private boolean flag;

    public LombokTest() {
    }

    public boolean equals(Object o) {
        if (o == this) {
            return true;
        } else if (!(o instanceof LombokTest)) {
            return false;
        } else {
            LombokTest other = (LombokTest)o;
            if (!other.canEqual(this)) {
                return false;
            } else {
                label39: {
                    Object this$name = this.name;
                    Object other$name = other.name;
                    if (this$name == null) {
                        if (other$name == null) {
                            break label39;
                        }
                    } else if (this$name.equals(other$name)) {
                        break label39;
                    }

                    return false;
                }

                Object this$phone = this.phone;
                Object other$phone = other.phone;
                if (this$phone == null) {
                    if (other$phone != null) {
                        return false;
                    }
                } else if (!this$phone.equals(other$phone)) {
                    return false;
                }

                if (this.flag != other.flag) {
                    return false;
                } else {
                    return true;
                }
            }
        }
    }

    protected boolean canEqual(Object other) {
        return other instanceof LombokTest;
    }

    public int hashCode() {
        int PRIME = true;
        int result = 1;
        Object $name = this.name;
        int result = result * 59 + ($name == null ? 43 : $name.hashCode());
        Object $phone = this.phone;
        result = result * 59 + ($phone == null ? 43 : $phone.hashCode());
        result = result * 59 + (this.flag ? 79 : 97);
        return result;
    }
}

和ToString注解相似也可以通过设置一些属性来控制决堤生成的hashCode()和equals()方法。下面是@EqualsAndHashCode注解的源码和解释,和@ToString一样的属性就没有写注释了。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface EqualsAndHashCode {
    String[] exclude() default {};

    String[] of() default {};

    boolean callSuper() default false;// 是否使用父类的equals和toString方法,注意:父类为Object时无法设置为true。

    boolean doNotUseGetters() default false;

    EqualsAndHashCode.AnyAnnotation[] onParam() default {};

    boolean onlyExplicitlyIncluded() default false;

    /** @deprecated */
    @Deprecated
    @Retention(RetentionPolicy.SOURCE)
    @Target({})
    public @interface AnyAnnotation { // 过时的方法(deprecated)
    }

    @Target({ElementType.FIELD})
    @Retention(RetentionPolicy.SOURCE)
    public @interface Exclude {
    }

    @Target({ElementType.FIELD, ElementType.METHOD})
    @Retention(RetentionPolicy.SOURCE)
    public @interface Include {
        String replaces() default "";
    }
}

@Data

这是lombok中使用的最多的注解,相当于@ToString@EqualsAndHashCode@RequiredArgsConstructor@Getter@Setter五个注解的功能。虽然使用起来方法,但是失去了细粒度的控制。如果需要细粒度控制,则需要进行注解的覆盖。

@Data
public class LombokTest {
    private String name;
    private String phone;
    private boolean flag;
}

编译后的代码(等效Java代码)

public class LombokTest {
    private String name;
    private String phone;
    private boolean flag;

    public LombokTest() {
    }

    public String getName() {
        return this.name;
    }

    public String getPhone() {
        return this.phone;
    }

    public boolean isFlag() {
        return this.flag;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }

    public boolean equals(Object o) {
        if (o == this) {
            return true;
        } else if (!(o instanceof LombokTest)) {
            return false;
        } else {
            LombokTest other = (LombokTest)o;
            if (!other.canEqual(this)) {
                return false;
            } else {
                label39: {
                    Object this$name = this.getName();
                    Object other$name = other.getName();
                    if (this$name == null) {
                        if (other$name == null) {
                            break label39;
                        }
                    } else if (this$name.equals(other$name)) {
                        break label39;
                    }

                    return false;
                }

                Object this$phone = this.getPhone();
                Object other$phone = other.getPhone();
                if (this$phone == null) {
                    if (other$phone != null) {
                        return false;
                    }
                } else if (!this$phone.equals(other$phone)) {
                    return false;
                }

                if (this.isFlag() != other.isFlag()) {
                    return false;
                } else {
                    return true;
                }
            }
        }
    }

    protected boolean canEqual(Object other) {
        return other instanceof LombokTest;
    }

    public int hashCode() {
        int PRIME = true;
        int result = 1;
        Object $name = this.getName();
        int result = result * 59 + ($name == null ? 43 : $name.hashCode());
        Object $phone = this.getPhone();
        result = result * 59 + ($phone == null ? 43 : $phone.hashCode());
        result = result * 59 + (this.isFlag() ? 79 : 97);
        return result;
    }

    public String toString() {
        return "LombokTest(name=" + this.getName() + ", phone=" + this.getPhone() + ", flag=" + this.isFlag() + ")";
    }
}

下面看一下@Data注解的源码。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface Data {
    String staticConstructor() default "";
}

可以看到提供了一个,staticConstructor(),这个方法的作用是私有化构造器,并生成一个静态的获取实例的方法,这样就可通过类名来获取实例(如:LombokTest.getLombokTest())

@Data(staticConstructor = "getLombokTest")
private LombokTest() {
}

public static LombokTest getLombokTest() {
    return new LombokTest();
}
// ...省略其他代码

@Cleanup

自动释放资源,主要用于IO流的操作,不需要手动编写释放资源的try/finally代码块。

public class LombokTest {
    public static void main(String[] args) throws Exception {
        @Cleanup InputStream is = new FileInputStream(args[0]);
        @Cleanup OutputStream os = new FileOutputStream(args[0]);
    }
}

编译后的代码(等效Java代码)

public class LombokTest {
    public static void main(String[] args) throws Exception {
        FileInputStream is = new FileInputStream(args[0]);

        try {
            OutputStream os = new FileOutputStream(args[0]);
            if (Collections.singletonList(os).get(0) != null) {
                os.close();
            }
        } finally {
            if (Collections.singletonList(is).get(0) != null) {
                is.close();
            }
        }
    }
}

还是挺方便的。再来看看@Cleanup注解的源代码

@Target({ElementType.LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface Cleanup {
    String value() default "close";
}

可以看到默认调用的是close方法,也就是说我们可以设置finally方法体具体调用的流处理方法。如:

public static void main(String[] args) throws Exception {
    @Cleanup(value = "available") InputStream is = new FileInputStream(args[0]);
    @Cleanup OutputStream os = new FileOutputStream(args[0]);
}

编译后的代码(等效Java代码)

这里写图片描述

具体可以指定哪些字段,需要流对象支持哪些方法了。如InputStream:

这里写图片描述

@Synchronized

同步代码块,只能使用在类方法(static)或者对象方法(普通方法)上,synchronized关键字锁住的是this,@Synchronized注解默认使用的锁为$lock,静态的锁为$LOCK,也可以自己指定锁对象,如示例中的:world

public class LombokTest {
    @Synchronized
    public void hello() {

    }

    @Synchronized
    public static void helloWorld() {

    }

    private final Object world = new Object();
    
    @Synchronized("world")
    public void world() {

    }
}

编译后的代码(等效Java代码)

public class LombokTest {
    private final Object $lock = new Object[0];
    private static final Object $LOCK = new Object[0];
    private final Object world = new Object();

    public LombokTest() {
    }

    public void hello() {
        Object var1 = this.$lock;
        synchronized(this.$lock) {
            ;
        }
    }

    public static void helloWorld() {
        Object var0 = $LOCK;
        synchronized($LOCK) {
            ;
        }
    }

    public void world() {
        Object var1 = this.world;
        synchronized(this.world) {
            ;
        }
    }
}

@SneakyThrows

个人觉得没有啥意义,唯一的作用可能就是不用手动捕获异常或者向上抛出,而是通过lombok帮你捕获。。。可以指定捕获的异常类型,默认捕获的是Throwable。

public class LombokTest {
    @SneakyThrows()
    public void testSneakyThrows() {
       throw new IllegalAccessException();
    }

    @SneakyThrows(IllegalAccessException.class)
    public void testSneakyThrows2() {
        throw new IllegalAccessException();
    }
}

编译后的代码(等效Java代码)

public class LombokTest {
    public LombokTest() {
    }

    public void testSneakyThrows() {
        try {
            throw new IllegalAccessException();
        } catch (Throwable var2) {
            throw var2;
        }
    }

    public void testSneakyThrows2() {
        try {
            throw new IllegalAccessException();
        } catch (IllegalAccessException var2) {
            throw var2;
        }
    }
}

感觉有点呆。。。官方建议在没有深思熟虑之前不要使用这个注解。。。下面是官网上的一句话:

  • You can pass any number of exceptions to the @SneakyThrows annotation. If you pass no exceptions, you may throw any exception sneakily.

@NoArgsConstructor,@ RequiredArgsConstructor,@ AllArgsConstructor

@NoArgsConstructor 生成一个无参构造器。

@NoArgsConstructor注解源代码,另外两个注解的源码和这个很类似,就不贴出来了。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface NoArgsConstructor {
    String staticName() default ""; // 如果指定了相应字段,则私有化构造并生成一个静态的获取实例方法。

    NoArgsConstructor.AnyAnnotation[] onConstructor() default {};

    AccessLevel access() default AccessLevel.PUBLIC;

    boolean force() default false;

    /** @deprecated */
    @Deprecated
    @Retention(RetentionPolicy.SOURCE)
    @Target({})
    public @interface AnyAnnotation { // 过时的(deprecated)
    }
}

@ RequiredArgsConstructor 生成包含必须处理的字段的构造器,如:final修饰(必须初始化)、@NonNull注解修饰的。

@RequiredArgsConstructor
public class LombokTest {
    private String name;
    private String phone;
    private transient boolean flag;
    private final String finalFiled;
    private static int num;
}

编译后的代码(等效Java代码)

public class LombokTest {
    private String name;
    private String phone;
    private transient boolean flag;
    private final String finalFiled;
    private static int num;

    public LombokTest(String finalFiled) {
        this.finalFiled = finalFiled;
    }
}

@ AllArgsConstructor 生成一个全参构造器,除static修饰的字段。@NonNull可以组合使用。

@AllArgsConstructor
public class LombokTest {
    private String name;
    @NonNull
    private String phone;
    private transient boolean flag;
    private final String finalFiled;
    private static int num;
}

编译后的代码(等效Java代码)

public class LombokTest {
    private String name;
    @NonNull
    private String phone;
    private transient boolean flag;
    private final String finalFiled;
    private static int num;

    public LombokTest(String name, @NonNull String phone, boolean flag, String finalFiled) {
        if (phone == null) {
            throw new NullPointerException("phone is marked @NonNull but is null");
        } else {
            this.name = name;
            this.phone = phone;
            this.flag = flag;
            this.finalFiled = finalFiled;
        }
    }
}

小结

学到这里常用的Lombok注解基本上都有了,如果想要根据深入的学习,建议去官网看。官网地址文章开始已经列出来了,加油!!!

查看原文

赞 1 收藏 0 评论 0

innerpeacez 发布了文章 · 2019-07-11

lombok

相识

lombok想必已经有很多人已经使用了很长时间了,而我却是第一次接触到,有点呆。lombok主要是用于减少重复代码,通过一组简单的注释取代一些重复的Java代码。对于lombok的评价褒贬不一,有的人觉得特别方便,有的人觉得改变了一成不变的代码结构,增加了代码维护成本(有的人没有用过lombok),我是觉得每一个工具诞生肯定是有他诞生的价值的,多学一个是一个啊,小老弟,用不用再说。:)

官方文档地址

官方API地址

官方注解介绍地址

准备

1、下载idea插件

我这里已经安装好了,没有安装的时候按钮应该是Install

这里写图片描述

2、pom文件引入project lombok的maven依赖

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.2</version>
    <scope>provided</scope>
</dependency>

lombok maven版本

注解介绍

@Getter和@Setter

顾名思义,生成get和set方法的注解,@Getter和@Setter发生在编译阶段,编译之后,@Getter和@Setter相关的注解就消失了,取而代之的是相应的get和set方法。

public class LombokTest {
    @Getter @Setter
    private boolean flag;
}

lombok遵循了boolean类型的get方法的约定,我们来看一下编译后的代码(等效Java代码)吧。

public class LombokTest {
    private boolean flag;

    public LombokTest() {
    }

    public boolean isFlag() {
        return this.flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}

可以看到boolean类型的get方法是isFlag()而不是getFlag()。我们还可定义生成get和set方法的访问级别。

public class LombokTest {
    @Getter(AccessLevel.PUBLIC)
    @Setter(AccessLevel.PROTECTED)
    private String name;
}

编译后的代码(等效Java代码)

public class LombokTest {
    private String name;

    public LombokTest() {
    }

    public String getName() {
        return this.name;
    }

    protected void setName(String name) {
        this.name = name;
    }
}

可以看到setName的访问级别是protected。lombok提供了下面几种级别。

这里写图片描述

AccessLevel枚举类的源代码。

public enum AccessLevel {
    PUBLIC, // public
    MODULE, // 编译后相当于 default
    PROTECTED, // protected
    PACKAGE, // default
    PRIVATE, // private
    NONE; // 不生成

    private AccessLevel() {
    }
}

最后@Getter和@Setter注解是可以写在类级别的,作用于所有的成员变量,无法细粒度的控制访问级别。

@Getter(AccessLevel.MODULE)
@Setter(AccessLevel.PROTECTED)
public class LombokTest {
    private String name;
    private boolean flag;
}

编译后的代码(等效Java代码)

public class LombokTest {
    private String name;
    private boolean flag;

    public LombokTest() {
    }

    String getName() {
        return this.name;
    }

    boolean isFlag() {
        return this.flag;
    }

    protected void setName(String name) {
        this.name = name;
    }

    protected void setFlag(boolean flag) {
        this.flag = flag;
    }
}

@NonNull

变量空检查,如果@NonNull注解使用在构造函数的字段时,构造函数也会进行空变量检查。不可用于类级别。

public class LombokTest {
    @NonNull
    @Setter
    @Getter
    private String name;
    
    public LombokTest(@NonNull String name) {
        this.name = name;
    }
}

编译后的代码(等效Java代码)

public class LombokTest {
    @NonNull
    private String name;

    public LombokTest(@NonNull String name) {
        if (name == null) {
            throw new NullPointerException("name is marked @NonNull but is null");
        } else {
            this.name = name;
        }
    }

    public void setName(@NonNull String name) {
        if (name == null) {
            throw new NullPointerException("name is marked @NonNull but is null");
        } else {
            this.name = name;
        }
    }

    @NonNull
    public String getName() {
        return this.name;
    }
}

@ToString

用于生成toString()方法。只能用于类级别。static修饰的变量是不能生成在toString()方法中的,原因是static修饰的变量在类创建的时候就生成了,只有一个;而普通的变量属于对象,每创建一个新的对象都会有一个。可以理解为static修饰的变量属于类,而普通变量数据对象,这样可能好理解一点。

@ToString
public class LombokTest {
    private String name;
    private static String phone;
}

编译后的代码(等效Java代码)

public class LombokTest {
    private String name;
    private static String phone;

    public LombokTest() {
    }

    public String toString() {
        return "YmlProperties(name=" + this.name + ")";
    }
}

默认情况下出了static修饰的字段都将以name-value的形式生成在toString()方法中。同时toString注解中还可以设置一些属性来细粒度的控制toString()方法。

下面是ToString注解的源码和解释:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface ToString {
    boolean includeFieldNames() default true; // 生成toString()方法时默认为 name-value的形式,设置为false时 则 value的形式。

    String[] exclude() default {}; // 不包含指定字段

    String[] of() default {}; // 包含指定字段

    boolean callSuper() default false; // 是否调用父类的toString()方法

    boolean doNotUseGetters() default false; // 生成toString()方法是否直接访问字段,设置为true时直接访问字段(如:this.name),false时通过getter()方法进行访问(如:this.getName())。当然如果没有生成getter()方法,无论是否设置都直接访问字段

    boolean onlyExplicitlyIncluded() default false; // 是否仅仅包含添加了@ToString.Include的字段,默认为false。

    @Target({ElementType.FIELD})
    @Retention(RetentionPolicy.SOURCE)
    public @interface Exclude { // 使用@ToString.Exclude修饰字段,需要和@ToString注解一起使用,单独使用无效。
    }

    @Target({ElementType.FIELD, ElementType.METHOD})
    @Retention(RetentionPolicy.SOURCE)
    public @interface Include {// 使用@ToString.Include修饰字段,需要和@ToString注解一起使用,单独使用无效。
        int rank() default 0;

        String name() default "";
    }
}

注意:

  • of和exclude属性设置多个字段时,以字符串数组的形式,如:
@ToString(of = {"name","phone"}) // 包含name和phone字段
@ToString(exclude = {"name","phone"}) // 不包含name和phone字段
  • 使用@ToString.Include或者@ToString.Exclude注解时也需要使用@ToString注解,否者无效。
@ToString
public class LombokTest {
    @ToString.Include
    private String name;
    @ToString.Exclude
    private String phone;
}

@EqualsAndHashCode

生成equals()和hashCode()方法。默认使用所有的非static和非transient字段生成这两个方法。

@EqualsAndHashCode
public class LombokTest {
    private String name;
    private String phone;
    private boolean flag;
}

编译后的代码(等效Java代码)

public class LombokTest {
    private String name;
    private String phone;
    private boolean flag;

    public LombokTest() {
    }

    public boolean equals(Object o) {
        if (o == this) {
            return true;
        } else if (!(o instanceof LombokTest)) {
            return false;
        } else {
            LombokTest other = (LombokTest)o;
            if (!other.canEqual(this)) {
                return false;
            } else {
                label39: {
                    Object this$name = this.name;
                    Object other$name = other.name;
                    if (this$name == null) {
                        if (other$name == null) {
                            break label39;
                        }
                    } else if (this$name.equals(other$name)) {
                        break label39;
                    }

                    return false;
                }

                Object this$phone = this.phone;
                Object other$phone = other.phone;
                if (this$phone == null) {
                    if (other$phone != null) {
                        return false;
                    }
                } else if (!this$phone.equals(other$phone)) {
                    return false;
                }

                if (this.flag != other.flag) {
                    return false;
                } else {
                    return true;
                }
            }
        }
    }

    protected boolean canEqual(Object other) {
        return other instanceof LombokTest;
    }

    public int hashCode() {
        int PRIME = true;
        int result = 1;
        Object $name = this.name;
        int result = result * 59 + ($name == null ? 43 : $name.hashCode());
        Object $phone = this.phone;
        result = result * 59 + ($phone == null ? 43 : $phone.hashCode());
        result = result * 59 + (this.flag ? 79 : 97);
        return result;
    }
}

和ToString注解相似也可以通过设置一些属性来控制决堤生成的hashCode()和equals()方法。下面是@EqualsAndHashCode注解的源码和解释,和@ToString一样的属性就没有写注释了。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface EqualsAndHashCode {
    String[] exclude() default {};

    String[] of() default {};

    boolean callSuper() default false;// 是否使用父类的equals和toString方法,注意:父类为Object时无法设置为true。

    boolean doNotUseGetters() default false;

    EqualsAndHashCode.AnyAnnotation[] onParam() default {};

    boolean onlyExplicitlyIncluded() default false;

    /** @deprecated */
    @Deprecated
    @Retention(RetentionPolicy.SOURCE)
    @Target({})
    public @interface AnyAnnotation { // 过时的方法(deprecated)
    }

    @Target({ElementType.FIELD})
    @Retention(RetentionPolicy.SOURCE)
    public @interface Exclude {
    }

    @Target({ElementType.FIELD, ElementType.METHOD})
    @Retention(RetentionPolicy.SOURCE)
    public @interface Include {
        String replaces() default "";
    }
}

@Data

这是lombok中使用的最多的注解,相当于@ToString@EqualsAndHashCode@RequiredArgsConstructor@Getter@Setter五个注解的功能。虽然使用起来方法,但是失去了细粒度的控制。如果需要细粒度控制,则需要进行注解的覆盖。

@Data
public class LombokTest {
    private String name;
    private String phone;
    private boolean flag;
}

编译后的代码(等效Java代码)

public class LombokTest {
    private String name;
    private String phone;
    private boolean flag;

    public LombokTest() {
    }

    public String getName() {
        return this.name;
    }

    public String getPhone() {
        return this.phone;
    }

    public boolean isFlag() {
        return this.flag;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }

    public boolean equals(Object o) {
        if (o == this) {
            return true;
        } else if (!(o instanceof LombokTest)) {
            return false;
        } else {
            LombokTest other = (LombokTest)o;
            if (!other.canEqual(this)) {
                return false;
            } else {
                label39: {
                    Object this$name = this.getName();
                    Object other$name = other.getName();
                    if (this$name == null) {
                        if (other$name == null) {
                            break label39;
                        }
                    } else if (this$name.equals(other$name)) {
                        break label39;
                    }

                    return false;
                }

                Object this$phone = this.getPhone();
                Object other$phone = other.getPhone();
                if (this$phone == null) {
                    if (other$phone != null) {
                        return false;
                    }
                } else if (!this$phone.equals(other$phone)) {
                    return false;
                }

                if (this.isFlag() != other.isFlag()) {
                    return false;
                } else {
                    return true;
                }
            }
        }
    }

    protected boolean canEqual(Object other) {
        return other instanceof LombokTest;
    }

    public int hashCode() {
        int PRIME = true;
        int result = 1;
        Object $name = this.getName();
        int result = result * 59 + ($name == null ? 43 : $name.hashCode());
        Object $phone = this.getPhone();
        result = result * 59 + ($phone == null ? 43 : $phone.hashCode());
        result = result * 59 + (this.isFlag() ? 79 : 97);
        return result;
    }

    public String toString() {
        return "LombokTest(name=" + this.getName() + ", phone=" + this.getPhone() + ", flag=" + this.isFlag() + ")";
    }
}

下面看一下@Data注解的源码。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface Data {
    String staticConstructor() default "";
}

可以看到提供了一个,staticConstructor(),这个方法的作用是私有化构造器,并生成一个静态的获取实例的方法,这样就可通过类名来获取实例(如:LombokTest.getLombokTest())

@Data(staticConstructor = "getLombokTest")
private LombokTest() {
}

public static LombokTest getLombokTest() {
    return new LombokTest();
}
// ...省略其他代码

@Cleanup

自动释放资源,主要用于IO流的操作,不需要手动编写释放资源的try/finally代码块。

public class LombokTest {
    public static void main(String[] args) throws Exception {
        @Cleanup InputStream is = new FileInputStream(args[0]);
        @Cleanup OutputStream os = new FileOutputStream(args[0]);
    }
}

编译后的代码(等效Java代码)

public class LombokTest {
    public static void main(String[] args) throws Exception {
        FileInputStream is = new FileInputStream(args[0]);

        try {
            OutputStream os = new FileOutputStream(args[0]);
            if (Collections.singletonList(os).get(0) != null) {
                os.close();
            }
        } finally {
            if (Collections.singletonList(is).get(0) != null) {
                is.close();
            }
        }
    }
}

还是挺方便的。再来看看@Cleanup注解的源代码

@Target({ElementType.LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface Cleanup {
    String value() default "close";
}

可以看到默认调用的是close方法,也就是说我们可以设置finally方法体具体调用的流处理方法。如:

public static void main(String[] args) throws Exception {
    @Cleanup(value = "available") InputStream is = new FileInputStream(args[0]);
    @Cleanup OutputStream os = new FileOutputStream(args[0]);
}

编译后的代码(等效Java代码)

这里写图片描述

具体可以指定哪些字段,需要流对象支持哪些方法了。如InputStream:

这里写图片描述

@Synchronized

同步代码块,只能使用在类方法(static)或者对象方法(普通方法)上,synchronized关键字锁住的是this,@Synchronized注解默认使用的锁为$lock,静态的锁为$LOCK,也可以自己指定锁对象,如示例中的:world

public class LombokTest {
    @Synchronized
    public void hello() {

    }

    @Synchronized
    public static void helloWorld() {

    }

    private final Object world = new Object();
    
    @Synchronized("world")
    public void world() {

    }
}

编译后的代码(等效Java代码)

public class LombokTest {
    private final Object $lock = new Object[0];
    private static final Object $LOCK = new Object[0];
    private final Object world = new Object();

    public LombokTest() {
    }

    public void hello() {
        Object var1 = this.$lock;
        synchronized(this.$lock) {
            ;
        }
    }

    public static void helloWorld() {
        Object var0 = $LOCK;
        synchronized($LOCK) {
            ;
        }
    }

    public void world() {
        Object var1 = this.world;
        synchronized(this.world) {
            ;
        }
    }
}

@SneakyThrows

个人觉得没有啥意义,唯一的作用可能就是不用手动捕获异常或者向上抛出,而是通过lombok帮你捕获。。。可以指定捕获的异常类型,默认捕获的是Throwable。

public class LombokTest {
    @SneakyThrows()
    public void testSneakyThrows() {
       throw new IllegalAccessException();
    }

    @SneakyThrows(IllegalAccessException.class)
    public void testSneakyThrows2() {
        throw new IllegalAccessException();
    }
}

编译后的代码(等效Java代码)

public class LombokTest {
    public LombokTest() {
    }

    public void testSneakyThrows() {
        try {
            throw new IllegalAccessException();
        } catch (Throwable var2) {
            throw var2;
        }
    }

    public void testSneakyThrows2() {
        try {
            throw new IllegalAccessException();
        } catch (IllegalAccessException var2) {
            throw var2;
        }
    }
}

感觉有点呆。。。官方建议在没有深思熟虑之前不要使用这个注解。。。下面是官网上的一句话:

  • You can pass any number of exceptions to the @SneakyThrows annotation. If you pass no exceptions, you may throw any exception sneakily.

@NoArgsConstructor,@ RequiredArgsConstructor,@ AllArgsConstructor

@NoArgsConstructor 生成一个无参构造器。

@NoArgsConstructor注解源代码,另外两个注解的源码和这个很类似,就不贴出来了。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface NoArgsConstructor {
    String staticName() default ""; // 如果指定了相应字段,则私有化构造并生成一个静态的获取实例方法。

    NoArgsConstructor.AnyAnnotation[] onConstructor() default {};

    AccessLevel access() default AccessLevel.PUBLIC;

    boolean force() default false;

    /** @deprecated */
    @Deprecated
    @Retention(RetentionPolicy.SOURCE)
    @Target({})
    public @interface AnyAnnotation { // 过时的(deprecated)
    }
}

@ RequiredArgsConstructor 生成包含必须处理的字段的构造器,如:final修饰(必须初始化)、@NonNull注解修饰的。

@RequiredArgsConstructor
public class LombokTest {
    private String name;
    private String phone;
    private transient boolean flag;
    private final String finalFiled;
    private static int num;
}

编译后的代码(等效Java代码)

public class LombokTest {
    private String name;
    private String phone;
    private transient boolean flag;
    private final String finalFiled;
    private static int num;

    public LombokTest(String finalFiled) {
        this.finalFiled = finalFiled;
    }
}

@ AllArgsConstructor 生成一个全参构造器,除static修饰的字段。@NonNull可以组合使用。

@AllArgsConstructor
public class LombokTest {
    private String name;
    @NonNull
    private String phone;
    private transient boolean flag;
    private final String finalFiled;
    private static int num;
}

编译后的代码(等效Java代码)

public class LombokTest {
    private String name;
    @NonNull
    private String phone;
    private transient boolean flag;
    private final String finalFiled;
    private static int num;

    public LombokTest(String name, @NonNull String phone, boolean flag, String finalFiled) {
        if (phone == null) {
            throw new NullPointerException("phone is marked @NonNull but is null");
        } else {
            this.name = name;
            this.phone = phone;
            this.flag = flag;
            this.finalFiled = finalFiled;
        }
    }
}

小结

学到这里常用的Lombok注解基本上都有了,如果想要根据深入的学习,建议去官网看。官网地址文章开始已经列出来了,加油!!!

查看原文

赞 1 收藏 0 评论 0

innerpeacez 关注了标签 · 2019-01-11

java

Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的 Java 程序设计语言和 Java 平台(即 JavaSE, JavaEE, JavaME)的总称。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

Java编程语言的风格十分接近 C++ 语言。继承了 C++ 语言面向对象技术的核心,Java舍弃了 C++ 语言中容易引起错误的指針,改以引用取代,同时卸载原 C++ 与原来运算符重载,也卸载多重继承特性,改用接口取代,增加垃圾回收器功能。在 Java SE 1.5 版本中引入了泛型编程、类型安全的枚举、不定长参数和自动装/拆箱特性。太阳微系统对 Java 语言的解释是:“Java编程语言是个简单、面向对象、分布式、解释性、健壮、安全与系统无关、可移植、高性能、多线程和动态的语言”。

版本历史

重要版本号版本代号发布日期
JDK 1.01996 年 1 月 23 日
JDK 1.11997 年 2 月 19 日
J2SE 1.2Playground1998 年 12 月 8 日
J2SE 1.3Kestrel2000 年 5 月 8 日
J2SE 1.4Merlin2002 年 2 月 6 日
J2SE 5.0 (1.5.0)Tiger2004 年 9 月 30 日
Java SE 6Mustang2006 年 11 月 11 日
Java SE 7Dolphin2011 年 7 月 28 日
Java SE 8JSR 3372014 年 3 月 18 日
最新发布的稳定版本:
Java Standard Edition 8 Update 11 (1.8.0_11) - (July 15, 2014)
Java Standard Edition 7 Update 65 (1.7.0_65) - (July 15, 2014)

更详细的版本更新查看 J2SE Code NamesJava version history 维基页面

新手帮助

不知道如何开始写你的第一个 Java 程序?查看 Oracle 的 Java 上手文档

在你遇到问题提问之前,可以先在站内搜索一下关键词,看是否已经存在你想提问的内容。

命名规范

Java 程序应遵循以下的 命名规则,以增加可读性,同时降低偶然误差的概率。遵循这些命名规范,可以让别人更容易理解你的代码。

  • 类型名(类,接口,枚举等)应以大写字母开始,同时大写化后续每个单词的首字母。例如:StringThreadLocaland NullPointerException。这就是著名的帕斯卡命名法。
  • 方法名 应该是驼峰式,即以小写字母开头,同时大写化后续每个单词的首字母。例如:indexOfprintStackTraceinterrupt
  • 字段名 同样是驼峰式,和方法名一样。
  • 常量表达式的名称static final 不可变对象)应该全大写,同时用下划线分隔每个单词。例如:YELLOWDO_NOTHING_ON_CLOSE。这个规范也适用于一个枚举类的值。然而,static final 引用的非不可变对象应该是驼峰式。

Hello World

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}

编译并调用:

javac -d . HelloWorld.java
java -cp . HelloWorld

Java 的源代码会被编译成可被 Java 命令执行的中间形式(用于 Java 虚拟机的字节代码指令)。

可用的 IDE

学习资源

常见的问题

下面是一些 SegmentFault 上在 Java 方面经常被人问到的问题:

(待补充)

关注 104719

innerpeacez 关注了标签 · 2019-01-11

程序员

一种近几十年来出现的新物种,是工业革命的产物。英文(Programmer Monkey)是一种非常特殊的、可以从事程序开发、维护的动物。一般分为程序设计猿和程序编码猿,但两者的界限并不非常清楚,都可以进行开发、维护工作,特别是在中国,而且最重要的一点,二者都是一种非常悲剧的存在。

国外的程序员节

国外的程序员节,(英语:Programmer Day,俄语:День программи́ста)是一个俄罗斯官方节日,日期是每年的第 256(0x100) 天,也就是平年的 9 月 13 日和闰年的 9 月 12 日,选择 256 是因为它是 2 的 8 次方,比 365 少的 2 的最大幂。

1024程序员节,中国程序员节

1024是2的十次方,二进制计数的基本计量单位之一。程序员(英文Programmer)是从事程序开发、维护的专业人员。程序员就像是一个个1024,以最低调、踏实、核心的功能模块搭建起这个科技世界。1GB=1024M,而1GB与1级谐音,也有一级棒的意思。

从2012年,SegmentFault 创办开始我们就从网络上引导社区的开发者,发展成中国程序员的节日 :) 计划以后每年10月24日定义为程序员节。以一个节日的形式,向通过Coding 改变世界,也以实际行动在浮躁的世界里,固执地坚持自己对于知识、技术和创新追求的程序员们表示致敬。并于之后的最为临近的周末为程序员们举行了一个盛大的狂欢派对。

2015的10月24日,我们SegmentFault 也在5个城市同时举办黑客马拉松这个特殊的形式,聚集开发者开一个编程大爬梯。

特别推荐:

【SF 黑客马拉松】:http://segmentfault.com/hacka...
【1024程序员闯关秀】小游戏,欢迎来挑战 http://segmentfault.com/game/

  • SF 开发者交流群:206236214
  • 黑客马拉松交流群:280915731
  • 开源硬件交流群:372308136
  • Android 开发者交流群:207895295
  • iOS 开发者交流群:372279630
  • 前端开发者群:174851511

欢迎开发者加入~

交流群信息


程序员相关问题集锦:

  1. 《程序员如何选择自己的第二语言》
  2. 《如何成为一名专业的程序员?》
  3. 《如何用各种编程语言书写hello world》
  4. 《程序员们最常说的谎话是什么?》
  5. 《怎么加入一个开源项目?》
  6. 《是要精于单挑,还是要善于合作?》
  7. 《来秀一下你屎一般的代码...》
  8. 《如何区分 IT 青年的“普通/文艺/二逼”属性?》
  9. 程序员必读书籍有哪些?
  10. 你经常访问的技术社区或者技术博客(IT类)有哪些?
  11. 如何一行代码弄崩你的程序?我先来一发
  12. 编程基础指的是什么?
  13. 后端零起步:学哪一种比较好?
  14. 大家都用什么键盘写代码的?

爱因斯坦

程序猿崛起

关注 110087

innerpeacez 关注了标签 · 2019-01-11

vue.js

Reactive Components for Modern Web Interfaces.

Vue.js 是一个用于创建 web 交互界面的。其特点是

  • 简洁 HTML 模板 + JSON 数据,再创建一个 Vue 实例,就这么简单。
  • 数据驱动 自动追踪依赖的模板表达式和计算属性。
  • 组件化 用解耦、可复用的组件来构造界面。
  • 轻量 ~24kb min+gzip,无依赖。
  • 快速 精确有效的异步批量 DOM 更新。
  • 模块友好 通过 NPM 或 Bower 安装,无缝融入你的工作流。

官网:https://vuejs.org
GitHub:https://github.com/vuejs/vue

关注 96502

innerpeacez 关注了标签 · 2019-01-11

react.js

React (sometimes styled React.js or ReactJS) is an open-source JavaScript library for creating user interfaces that aims to address challenges encountered in developing single-page applications. It is maintained by Facebook, Instagram and a community of individual developers and corporations.

关注 36362

innerpeacez 关注了标签 · 2019-01-11

python

Python(发音:英[ˈpaɪθən],美[ˈpaɪθɑ:n]),是一种面向对象、直译式电脑编程语言,也是一种功能强大的通用型语言,已经具有近二十年的发展历史,成熟且稳定。它包含了一组完善而且容易理解的标准库,能够轻松完成很多常见的任务。它的语法非常简捷和清晰,与其它大多数程序设计语言不一样,它使用缩进来定义语句。

Python支持命令式程序设计、面向对象程序设计、函数式编程、面向切面编程、泛型编程多种编程范式。与Scheme、Ruby、Perl、Tcl等动态语言一样,Python具备垃圾回收功能,能够自动管理存储器使用。它经常被当作脚本语言用于处理系统管理任务和网络程序编写,然而它也非常适合完成各种高级任务。Python虚拟机本身几乎可以在所有的作业系统中运行。使用一些诸如py2exe、PyPy、PyInstaller之类的工具可以将Python源代码转换成可以脱离Python解释器运行的程序。

Python的主要参考实现是CPython,它是一个由社区驱动的自由软件。目前由Python软件基金会管理。基于这种语言的相关技术正在飞快的发展,用户数量快速扩大,相关的资源非常多。

关注 102700