0%

DeepEXI 脚手架集成 TX-LCN

TX-LCN定位于一款事务协调性框架,框架其本身并不操作事务,而是基于对事务的协调从而达到事务一致性的效果。

为什么选择 TX-LCN?

  • 性能优秀
  • 可靠性强
  • 支持LCN、TCC、TXC等事务模式,代码侵入性低

如下演示简单的LCN分布式事务:

步骤引导

  • 安装 DeepEXI 脚手架及相关依赖
    • 准备依赖环境服务
      JDK1.8+,Mysql5.6+,Redis3.2+,Consul(SpringCloud),ZooKeeper(Dubbo),Git,Maven
    • 初始化数据
    • 启动TxManager(TM)
    • 配置微服务模块

安装 DeepEXI 脚手架及相关依赖

安装 yeoman

$ npm install -g yo

安装 DeepEXI 脚手架

$ npm install -g generator-deepexi-spring-cloud

下载脚手架代码

1
2
3
4
mkdir {your folder}
cd {your folder}
git clone https://github.com/deepexi/generator-deepexi-spring-cloud.git
git checkout develop

调试模式启动脚手架

1
npm link

即可将本地项目代替 npm module 中对应的包

初始化数据

  • TM数据初始化
    TxManager(TM) 依赖 tx-manager 数据库 (MariaDB 、MySQL) 建表语句如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    DROP TABLE IF EXISTS `t_tx_exception`;
    CREATE TABLE `t_tx_exception` (
    `id` bigint(20) NOT NULL AUTO_INCREMENT,
    `group_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
    `unit_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
    `mod_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
    `transaction_state` tinyint(4) NULL DEFAULT NULL,
    `registrar` tinyint(4) NULL DEFAULT NULL COMMENT '-1 未知 0 Manager 通知事务失败, 1 client询问事务状态失败2 事务发起方关闭事务组失败',
    `ex_state` tinyint(4) NULL DEFAULT NULL COMMENT '0 待处理 1已处理',
    `create_time` datetime(0) NULL DEFAULT NULL,
    PRIMARY KEY (`id`) USING BTREE
    ) ENGINE = InnoDB AUTO_INCREMENT = 967 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

TC 数据初始化, 微服务(服务A,服务B)演示 Demo 依赖 txlcn-dem o数据库 (MariaDB 、MySQL) 建表语句如下:

1
2
3
4
5
6
7
8
9
10
11
DROP TABLE IF EXISTS `t_demo`;
CREATE TABLE `t_demo` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`kid` varchar(45) DEFAULT NULL,
`demo_field` varchar(255) DEFAULT NULL,
`group_id` varchar(64) DEFAULT NULL,
`unit_id` varchar(32) DEFAULT NULL,
`app_name` varchar(32) DEFAULT NULL,
`create_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;

启动 TxManager(TM)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
spring.application.name=tx-manager
server.port=7970

spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/tx-manager?characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=root

mybatis.configuration.map-underscore-to-camel-case=true
mybatis.configuration.use-generated-keys=true

#tx-lcn.logger.enabled=true
# TxManager Host Ip
#tx-lcn.manager.host=127.0.0.1
# TxClient连接请求端口
#tx-lcn.manager.port=8070
# 心跳检测时间(ms)
#tx-lcn.manager.heart-time=15000
# 分布式事务执行总时间
#tx-lcn.manager.dtx-time=30000
#参数延迟删除时间单位ms
#tx-lcn.message.netty.attr-delay-time=10000
#tx-lcn.manager.concurrent-level=128
# 开启日志
#tx-lcn.logger.enabled=true
#logging.level.com.codingapi=debug
#redisIp
#spring.redis.host=127.0.0.1
#redis\u7AEF\u53E3
#spring.redis.port=6379
#redis\u5BC6\u7801
#spring.redis.password=

TM 编译与启动

  • 编译
    进入到txlcn-tm路径下。 执行 mvn clean package ‘-Dmaven.test.skip=true’
  • 启动
    进入target文件夹下。执行 java -jar txlcn-tm-5.0.0.jar
  • 启动TxManager

配置微服务模块

代码清单

服务A

利用脚手架创建服务A

1
2
3
mkdir tx-client-a
$ cd tx-client-a
$ yo deepexi-spring-cloud

根据交互任务调度类型选择 TX-LCN

1
2
3
spring:
application:
name: txlcn-demo-spring-service-a
1
2
3
4
5
6
7
8
9
10
11
spring:
cloud:
consul:
discovery:
register: true
enabled: true
register-health-check: false
datasource:
username: root
password: root
url: 'jdbc:mysql://127.0.0.1:3306/txlcn-demo'
1
2
3
4
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@RestController
@Payload
public class DemoController {

private final DemoService demoService;

@Autowired
public DemoController(DemoService demoService) {
this.demoService = demoService;
}

@RequestMapping("/txlcn")
public String execute(@RequestParam("value") String value, @RequestParam(value = "ex", required = false) String exFlag) {
return demoService.execute(value, exFlag);
}


}
1
2
3
4
5
6
7
8
9
10
11
12
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Demo {
private Long id;
private String kid = RandomUtils.randomKey();
private String demoField;
private String groupId;
private Date createTime;
private String appName;

}
1
2
3
4
5
@Mapper
public interface DemoMapper {
@Insert("insert into t_demo(kid, demo_field, group_id, create_time,app_name) values(#{kid}, #{demoField}, #{groupId}, #{createTime},#{appName})")
void save(Demo demo);
}
1
2
3
public interface DemoService {
String execute(String value, String exFlag);
}

可以发现的是,想要开启TX-LCN的LCN事务功能,在想要开启的地方加上 @LcnTransaction 注解即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@Service
@Slf4j
public class DemoServiceImpl implements DemoService {
@Autowired
private DemoMapper demoMapper;

@Autowired
private RestTemplate restTemplate;

@LcnTransaction//分布式事务
@Transactional //本地事务
@Override
public String execute(String value, String exFlag) {
// step1. call remote service B
String bResp = restTemplate.getForObject("http://127.0.0.1:8083/rpc?value=" + value, String.class);

// step2. local store operate. DTX commit if save success, rollback if not.
Demo demo = new Demo();
demo.setGroupId(TracingContext.tracing().groupId());
demo.setDemoField(value);
demo.setCreateTime(new Date());
demo.setAppName(Transactions.getApplicationId());
demoMapper.save(demo);

// 置异常标志,DTX 回滚
if (Objects.nonNull(exFlag)) {
throw new IllegalStateException("by exFlag");
}

return bResp + " > " + "ok-A";
}
}

服务B

利用脚手架创建服务B

1
2
3
mkdir tx-client-b
$ cd tx-client-b
$ yo deepexi-spring-cloud

根据交互任务调度类型选择 TX-LCN

1
2
3
4
5
6
7
8
9
spring:
application:
name: txlcn-demo-spring-service-b
server:
port: 8083

management:
server:
port: 8086
1
2
3
4
5
6
7
8
9
10
11
spring:
cloud:
consul:
discovery:
register: true
enabled: true
register-health-check: false
datasource:
username: root
password: root
url: 'jdbc:mysql://127.0.0.1:3306/txlcn-demo'
1
2
3
4
5
6
7
8
9
10
11
12
@RestController
@Payload
public class DemoController {

@Autowired
private DemoService demoService;

@GetMapping("/rpc")
public String rpc(@RequestParam("value") String value) {
return demoService.rpc(value);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Demo {
private Long id;
private String kid = RandomUtils.randomKey();
private String demoField;
private String groupId;
private Date createTime;
private String appName;

}
1
2
3
4
5
@Mapper
public interface DemoMapper{
@Insert("insert into t_demo(kid, demo_field, group_id, create_time,app_name) values(#{kid}, #{demoField}, #{groupId}, #{createTime},#{appName})")
void save(Demo demo);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Service
@Slf4j
public class DemoServiceImpl implements DemoService {

private final DemoMapper demoMapper;

@Autowired
public DemoServiceImpl(DemoMapper demoMapper) {
this.demoMapper = demoMapper;
}

@Override
@LcnTransaction//分布式事务
@Transactional //本地事务
public String rpc(String value) {
Demo demo = new Demo();
demo.setGroupId(TracingContext.tracing().groupId());
demo.setDemoField(value);
demo.setAppName(Transactions.getApplicationId());
demo.setCreateTime(new Date());
demoMapper.save(demo);
return "ok-service-b";
}
}
1
2
3
public interface DemoService {
String rpc(String value);
}

启动模块与测试

正常提交事务

访问服务A提供的Rest接口 http://localhost:8080/txlcn?value=111
发现事务全部提交。

回滚事务

访问服务A提供的接口 http://localhost:8080/txlcn?value=111&ex=1
在返回结果前抛出了异常,发现由于本地事务回滚,而参与方B也被TX-LCN回滚数据

DeepEXI 脚手架集成 Consul

consul 是一个用Go语言编写的可以自动化网络配置,服务发现并连接云容器的软件

为什么选择 Consul ?
✅服务发现 | ✅储存数据元空间 | ✅集成Kubernetes | ✅健康检查 | ✅动态负载均衡

安装 generator-deepexi-spring-cloud相关依赖

安装 yeoman

$ npm install -g yo

安装 generator-deepexi-spring-cloud

$ npm install -g generator-deepexi-spring-cloud

下载 Consul 到本地并启动 Consul:

1
2
docker pull consul:latest 
docker run -d -p 8500:8500/tcp consul agent -server -ui -bootstrap-expect=1 -client=0.0.0.0

下载脚手架代码

1
2
3
4
mkdir {your folder}
cd {your folder}
git clone https://github.com/deepexi/generator-deepexi-spring-cloud.git
git checkout develop

调试模式启动脚手架

1
npm link

即可将本地项目代替 npm module 中对应的包

利用脚手架创建生产者项目

1
2
3
mkdir consul-producer
$ cd consul-producer
$ yo deepexi-spring-cloud
  • 根据交互任务调度类型选择 consul,生成 demo 选择 y.

  • 修改applicaition.yml

    • spring.application.name 是consul首页上显示的service name,为了便于识别修改为spring-cloud-consul-producer

    • 由于consul的健康检查实际上依赖的是actuator,所以还要配置actuator。脚手架默认引入了actuator的依赖及配置, 所以我们只需设置其服务端口即可

      1
      2
      3
      4
      5
      6
      7
      8
      9
       spring:
      application:
      name: spring-cloud-consul-producer

      server:
      port: 8086
      management:
      server:
      port: 8083
    • 修改applicaition-local.yml
      • 注意的是,如果想要management-port生效,服务需和consul处于同一集群下
        1
        2
        3
        4
        5
        6
        7
        8
          spring:
        cloud:
        consul:
        discovery:
        register-health-check: true
        register: true
        enabled: true
        management-port: 8083
属性名 说明
register-health-check 注册健康检查
register 注册为consul服务
enabled 是否启用服务发现
management-port 端口注册管理服务(即为actuator端口)
  • 新增代码清单:
    用于给生产者调用的接口

    1
    2
    3
    4
    5
    6
    7
    8
    9
     @RestController
    @Payload
    public class ProducerController {

    @GetMapping("/hello")
    public String hello() {
    return "hello consul from producer";
    }
    }

    利用脚手架创建消费者项目

    1
    2
    3
    mkdir consul-consumer
    $ cd consul-consumer
    $ yo deepexi-spring-cloud
  • 根据交互任务调度类型选择 consul,生成 demo 选择 y.

  • 修改applicaition.yml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    spring:
    application:
    name: spring-cloud-consul-consumer

    server:
    port: 8080
    management:
    server:
    port: 8081
    • 修改applicaition-local.yml

      1
      2
      3
      4
      5
      6
      7
      8
      spring:
      cloud:
      consul:
      discovery:
      register-health-check: true
      register: true
      enabled: true
      management-port: 8081
    • 新增代码清单:
      远程调用生产者接口的接口

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
       @Payload
      @RestController
      class ConsumerController {
      @Autowired
      private LoadBalancerClient loadBalancer;

      @Bean
      @LoadBalanced
      public RestTemplate restTemplate() {
      return new RestTemplate();
      }

      @Autowired
      private RestTemplate restTemplate;

      @RequestMapping("/call")
      public String call() {
      ServiceInstance serviceInstance = loadBalancer.choose("spring-cloud-consul-producer");
      System.out.println("Hostname:" + serviceInstance.getUri());
      System.out.println("service name:" + serviceInstance.getServiceId());

      String serviceResult1 =
      new RestTemplate().getForObject(serviceInstance.getUri().toString() + "/hello", String.class);
      String serviceResult2 =
      restTemplate.getForObject("http://spring-cloud-consul-producer/hello", String.class);
      return serviceResult1;
      }
      }

服务发现相关接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Payload
@RestController
public class ServiceController {

@Autowired
private LoadBalancerClient loadBalancerClient;

@Autowired
private DiscoveryClient discoverClient;

@RequestMapping("/services")
public Object services() {
return discoverClient.getInstances("spring-cloud-consul-producer");
}

@RequestMapping("/discover")
public Object discover() {
return loadBalancerClient.choose("spring-cloud-consul-producer").getUri().toString();
}
}

运行项目并测试

ok,当走到这一步已经差不多要大功告成了:)

  • 将两个项目启动,并打开 http://localhost:8500/ui
    consul首页
    首页出现了绿色的打钩即说明健康检查正常

  • 测试远程调用接口

    1
    curl -X GET "http://localhost:8080/call" -H "accept: */*"

    返回以下信息即说明调用链路没有问题

    1
    2
    3
    4
    5
    {
    "code": "1",
    "payload": "{\"code\":\"1\",\"payload\":\"hello consul from producer\",\"success\":true}",
    "success": true
    }
  • 测试服务发现接口

    1
    curl -X GET "http://localhost:8080/services" -H "accept: */*"

    返回以下信息则说明服务发现成功:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    {
    "code": "1",
    "payload": [
    {
    "serviceId": "spring-cloud-consul-producer",
    "host": "DESKTOP-J4VTE9I",
    "port": 8086,
    "secure": false,
    "metadata": {
    "secure": "false"
    },
    "uri": "http://DESKTOP-J4VTE9I:8086",
    "scheme": null
    }
    ],
    "success": true
    }

Ansible 系列:

  • (一):[快速上手 Ansible](./Ansible 快速上手.md)
  • (二):[Ansible 命令](./Ansible 命令.md)
  • (三):[Ansible 主机清单配置文件](Ansible 主机配置清单.md)
  • (四):[Ansible Playbook 剧本语法](Playbook 剧本语法.md)

快速上手 Ansible

Ansible 是 Paramiko 开发的一种自动化运维工具,基于模块化工作,集合了众多运维工具的优点,实现了批量系统配置,批量程序部署、批量运行命令等功能。

Ansible 它本身没有批量部署的能力,真正执行这些操作的是 Ansible 的模块,它只是提供了一种框架。直到目前为止,Ansible 已有 800 多个模块可以使用。

Ansible 的另一个优势在于,它不需要在远程主机上安装任何东西,因为它是基于 SSH 来和远程主机进行通信的。

安装 Ansible

在 Ansible 里,有两种角色 Control Machine 与 Managed Node,它们通过 SSH 和 Python 进行沟通:

  • Control Machine(主控端):操作 Ansible 的机器,用于操纵 Managed Node
  • Managed Node(被控端):被 Ansible 操纵的机器

在一般情况下,我们只需在 Control Machine 里安装 Ansible 即可,因为 Linux 和 macOS 系统早已预载了 Python 2.5 以上的版本,且支持 SSH 连接。

而使用 Ansible 来管理 Window 的话,则需要较多的设置(此处不介绍,可自寻谷歌/百度)。

macOS 安装 Ansible

1
2
3
4
# 请先安装 homebrew,已安装者请略过
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
# 安装 Ansible
brew install ansible

Linux 安装 Ansible

CentOS(Yum)

1
2
3
4
# 新增 epel-release 第三方套件来源。
sudo yum install -y epel-release
# 安装 Ansible
sudo yum install -y ansible

Ubuntu(Apt)

1
2
3
4
5
6
# 安装 add-apt-repository 必要套件
sudo apt-get install -y python-software-properties software-properties-common
# 使用 Ansible 官方的 PPA 套件来源
sudo add-apt-repository -y ppa:ansible/ansible; sudo apt-get update
# 安装 Ansible
sudo apt-get install -y ansible

使用 Pip 安装 Ansible

1
2
3
4
5
6
7
8
9
10
11
12
# 1、首先安装 pip
# Debian, Ubuntu
$ sudo apt-get install -y python-pip
# CentOS
$ sudo yum install -y python-pip
# macOS
$ sudo easy_install pip

# 2、升级 pip
sudo pip install -U pip
# 3、安装 Ansible
sudo pip install ansible

Ansible 文件

在安装 Ansible 之后,你可以访问以下 Ansible 文件:

  • /usr/bin/ansible :Ansible 命令行工具
  • /usr/bin/ansible-doc :Ansible 帮助文档工具
  • /usr/bin/ansible-playbook :Ansible 剧本执行工具
  • /etc/ansible/ansible.cfg :主配置文件
  • /etc/ansible/hosts :管理的主机清单
  • /etc/ansible/roles :角色存放处

Ansible 操作之 Ad-Hoc Command 和 Playbook

Ad-Hoc Commands

第一种方式是通过向 Ansible 发送 Ad-Hoc Commands(指令)来操作 Managed Node。

以常见的 pingecho 操作为例:

  • ping

    1
    2
    3
    4
    5
    ansible all -m ping
    server1 | SUCCESS => {
    "changed": false,
    "ping": "pong"
    }
  • echo

    1
    2
    3
    ansible all -m command -a "echo Hello World"
    server1 | SUCCESS | rc=0 >>
    Hello World

Playbook

另外一种方式即通过 Playbook (剧本)让各个 Managed Node 进行指定的动作(Plays)和任务(Tasks),你可以理解为 Shell Script。

Playbook 支持两种写法 :

  • YAML:简单易读
  • Jinja2:模板化文件,支持变量、条件、循环语法

在一份 Playbook 中,可以有多个 Play、Task 与 Module:

  • Play:目的
  • Task:要执行的 Play 这个目的所需做的步骤
  • Module:Ansible 所提供的模块来执行各种操作

下面是一个 Hello World 的 Playbook 剧本,剧本后缀名应为 .yml:

1
2
3
4
5
6
7
8
9
10
11
- name: say 'hello world'
hosts: all
tasks:

- name: echo 'hello world'
command: echo 'hello world'
register: result

- name: print stdout
debug:
msg: ""

执行剧本

1
ansible-playbook hello_world.yml

查看 Ansible 的 Modules

模块是 Ansible 的核心,也是操作真正的执行者,只要掌握了如何使用模块就可以快速上手 Ansible,其余都只是延伸使用罢了。

例如我们经常使用的 Command Modules 模块,你可以进入 Ansible 的 Module 帮助文档中找到它的使用文档。

automate_with_ansible_basic-20.jpg

文档中 Options 选项表会列出模块参数,参数的预设值等信息。

automate_with_ansible_basic-22.jpg

Setup 模块

在使用 Playbook 时,Ansible 会自动执行 Setup Modules 以收集各个 Managed Node 的 facts(系统信息),如 IP、作业系统、CPU 信息等。

我们也可以通过 Ad-Hoc Commands 指令模式使用 setup,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ansible all -m setup | less

--- 结果如下
server1 | SUCCESS => {
"ansible_facts": {
"ansible_all_ipv4_addresses": [
"172.19.0.2"
],
"ansible_all_ipv6_addresses": [
"fe80::42:acff:fe13:2"
],
"ansible_architecture": "x86_64",
"ansible_bios_date": "03/14/2014",
:

通过 filter 参数还可以过滤结果

1
2
3
4
5
6
7
8
9
10
11
12
ansible all -m setup -a "filter=ansible_distribution*"

--- 结果如下
server1 | SUCCESS => {
"ansible_facts": {
"ansible_distribution": "Ubuntu",
"ansible_distribution_major_version": "14",
"ansible_distribution_release": "trusty",
"ansible_distribution_version": "14.04"
},
"changed": false
}

通常在 Playbook 中我们会将主机信息结合条件判断 when 使用,例如下面针对 Debian, Ubuntu, CentOS 安装 Vim 的 playbook:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
- name: Setup the vim 
hosts: all
become: true
tasks:

# Debian, Ubuntu.
- name: install apt packages
apt: name=vim state=present
when: ansible_pkg_mgr == "apt"

# CentOS.
- name: install yum packages
yum: name=vim-minimal state=present
when: ansible_pkg_mgr == "yum"

Ansible 系列:

  • (一):[快速上手 Ansible](./Ansible 快速上手.md)
  • (二):[Ansible 命令](./Ansible 命令.md)
  • (三):[Ansible 主机清单配置文件](./Ansible 主机配置清单.md)
  • (四):[Ansible Playbook 剧本语法](./Playbook 剧本语法.md)

Ansible 命令

ansible

Ad-Hoc 即单条命令,指需要快速执行并且不需要保存的命令。默认不指定模块时,使用的是 Command 模块。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
Usage: ansible <host-pattern> [options]

命令选项
-a # 模块的参数。
-B # 异步运行时,多长时间超时。
-P # 如果使用-B,则设置轮询间隔。
-C # 只是测试一下会改变什么内容,不会真正去执行;相反,试图预测一些可能发生的变化。
-D # 当更改文件和模板时,显示这些文件得差异,比--check效果好。
-f # 指定定要使用的并行进程数,默认为5个。
-i # 指定主机清单文件或逗号分隔的主机,默认为/etc/ansible/hosts。
-l # 进一步限制所选主机/组模式,只执行-l 后的主机和组。 也可以这样使用 -l @retry_hosts.txt
-m   # 要执行的模块,默认为command。
-M   # 要执行的模块的路径。
-o   # 压缩输出,摘要输出.尝试一切都在一行上输出。
-v, --verbose # 输出执行的详细信息,使用-vvv获得更多,-vvvv 启用连接调试
--version # 显示程序版本号
-e --extra-vars=EXTRA_VARS # 添加附加变量,比如key=value,yaml,json格式。
--list-hosts # 输出将要操作的主机列表,不会执行操作
--output=OUTPUT_FILE # 加密或解密输出文件名 用于标准输出。
--tree=TREE # 将日志内容保存在该目录中,文件名以执行主机名命名。
--syntax-check # 对playbook进行语法检查,且不执行playbook。
--ask-vault-pass # vault 密码。
--vault-password-file=VAULT_PASSWORD_FILE vault密码文件
--new-vault-password-file=NEW_VAULT_PASSWORD_FILE 新vault密钥文件。


连接选项:
-k --ask-pass # 要求用户输入请求连接密码
-u --user=REMOTE_USER # 连接远程用户
-c --connection=CONNECTION # 连接类型,默认smart,支持local ssh 和 paramiko
-T --timeout=TIMEOUT # 指定默认超时时间,默认是10S
--ssh-common-args=SSH_COMMON_ARGS # 指定要传递给sftp / scp / ssh的常见参数 (例如 ProxyCommand)
--sftp-extra-args=SFTP_EXTRA_ARGS # 指定要传递给sftp,例如-f -l
--scp-extra-args=SCP_EXTRA_ARGS # 指定要传递给scp,例如 -l
--ssh-extra-args=SSH_EXTRA_ARGS # 指定要传递给ssh,例如 -R
--private-key=PRIVATE_KEY_FILE, --key-file=PRIVATE_KEY_FILE 私钥路径,使用这个文件来验证连接


特权升级选项:
-s --sudo # 使用sudo (nopasswd)运行操作, 不推荐使用
-U --sudo-user=SUDO_USER # sudo 用户,默认为root, 不推荐使用
-S --su # 使用su运行操作 不推荐使用
-R --su-user=SU_USER # su 用户,默认为root,不推荐使用
-b --become # 运行操作
--become-method=BECOME_METHOD # 权限升级方法使用 ,默认为sudo,有效选择:sudo,su,pbrun,pfexec,runas,doas,dzdo
--become-user=BECOME_USER # 使用哪个用户运行,默认为root
--ask-sudo-pass # sudo密码,不推荐使用
--ask-su-pass # su密码,不推荐使用
-K --ask-become-pass # 权限提升密码

ansible-doc

用于查看模块信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Usage: ansible <host-pattern> [options]

选项
-h --help # 显示此帮助信息
-l --list # 列出可用的模块
-s --snippet # 显示playbook制定模块的用法
-v --verbose # 详细模式(-vvv表示更多,-vvvv表示启用连接调试)
--version # 显示程序版本号
-M --module-path=MODULE_PATH # 指定模块库的路径


示例:
ansible-doc -l
ansible-doc shell
ansible-doc -s shell

ansible-playbook

对于需反复执行的、较为复杂的任务,我们可以通过定义 Playbook 来搞定。它允许使用变量、条件、循环、以及模板,也能通过角色及包含指令来重用既有内容。

1
2
3
4
5
6
7
8
9
10
11
12
Usage: ansible-playbook playbook.yml

相对于ansible,增加了下列选项:
--flush-cache # 清除fact缓存
--syntax-check # 语法检查
--force-handlers # 如果任务失败,也要运行handlers
--list-tags # 列出所有可用的标签
--list-tasks # 列出将要执行的所有任务
--skip-tags=SKIP_TAGS # 跳过运行标记此标签的任务
--start-at-task=START_AT_TASK # 在此任务处开始运行
--step 一步一步:在运行之前确认每个任务
-t TAGS, --tags=TAGS 只运行标记此标签的任务

Ansible 系列:

  • (一):[快速上手 Ansible](./Ansible 快速上手.md)
  • (二):[Ansible 命令](./Ansible 命令.md)
  • (三):[Ansible 主机清单配置文件](./Ansible 主机配置清单.md)
  • (四):[Ansible Playbook 剧本语法](./Playbook 剧本语法.md)

Ansible 主机配置清单文件

参考至官方文档,官方文档包含了清单文件的 YAML 写法

在通过 Ansible 操作目标主机之前,你需要先在 Inventory(主机清单)中配置目标主机信息。

默认情况下主机清单保存在系统的 /etc/ansible/hosts 文件中,你也可以通过命令行选项指定其它的清单文件 -i <path>

主机清单配置默认格式为 INI,下面是一个主机清单配置例子:

1
2
3
[web] 
www.abc.com1
192.168.0.2

上述配置中方括号为组名,www.abc.com1 和 192.168.0.2 为被监控主机的域名或 IP。也可以是主机名,但此时需要指定主机 IP 环境变量 。

1
2
[web] 
node ansible_host=192.0.2.50

默认采用密钥认证,如果没有密钥认证,那么你需要配置主机密码环境变量。

1
2
[web] 
www.abc.com1 ansible_user=root ansible_password=123456

你也可以对指定组下的主机进行统一的环境变量配置:

1
2
3
4
5
6
7
8
[web] 
www.abc.com1 ansible_user=root ansible_password=123456 http_port=8080
192.168.0.2 ansible_user=root ansible_password=123451

[web:vars]
http_port=80
ssh_port=22
redis_port=6379

如果主机名或 IP 是有序有规则的,你还可以采用数字或字符范围格式,而不是列出每个主机名:

1
2
3
4
5
[webservers]
www[01:50].example.com

[databases]
db-[a:f].example.com

组的继承

组还支持继承(嵌套),其格式为 组名+”:children” 组成,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[shenzhen]
host1
host2
[guangzhou]
host3
host4
[guangdong:children]
shenzhen
guangzhou
[guangdong:vars]
tomcat=192.168.8.8
nginx=192.168.8.66
apache=192.168.8.77
zabbix=192.168.8.88
[china:children]
guangdong
beijing
shanghai

上面我指定了深圳组有 host1、host2,广州组有host3、host4。广东组包含深圳和广州,同时为该组内的所有主机指定了四个环境变量。后又设定了一个中国组,包含广东、北京、上海。

默认组

所有主机都属于两个隐式的组:

  • all:包含每个主机
  • ungrouped:除了 all 组之外没有其它组的主机

组织环境变量

环境变量除了写在主机清单文件中,还可以单独存储在单个文件中。

我们可以在主机清单文件的同级目录中创建两个目录 “group_vars” 和 “host_vars”,分别存储组变量与主机变量文件。

如我们在主机清单文件中声明了组 test,此时在 group_vars 目录下创建一个名为 test 的文件(该文件为 YAML 格式),其内容如下:

1
tomcat: 192.168.8.8

此时 test 组中的主机将会包含 tomcat 这个环境变量。

当 “group_vars” 或 “host_vars” 外部环境变量文件与主机清单文件中的环境变量冲突时,前者优先级更高。

特殊环境变量

Ansible 定义了一些固定的环境变量名,这些环境变量将会影响 Ansible 的行为。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ansible_connection #主机连接类型,这可以是任何 ansible 连接插件的名称,如 smart、ssh、paramiko、local
ansible_ssh_host # 将要连接的远程主机名.与你想要设定的主机的别名不同的话,可通过此变量设置.
ansible_ssh_port # 连接端口号(默认22)
ansible_ssh_user # 连接主机时的用户名
ansible_ssh_pass # 用于验证主机的密码
ansible_ssh_private_key_file # ssh 使用的私钥文件.适用于有多个密钥,而你不想使用 SSH 代理的情况.
ansible_ssh_common_args # 此设置附加到 sftp,scp 和 ssh 的缺省命令行
ansible_sftp_extra_args # 此设置附加到默认 sftp 命令行
ansible_scp_extra_args # 此设置附加到默认 scp 命令行
ansible_ssh_extra_args # 此设置附加到默认 ssh 命令行
ansible_ssh_pipelining # 确定是否使用 SSH 管道。 这可以覆盖 ansible.cfg 中得设置
ansible_shell_type # 目标系统的 shell 类型,默认情况下命令的执行使用 'sh' 语法,可设置为 'csh' 或 'fish'
ansible_python_interpreter # 目标主机的 python 路径,适用于的情况: 系统中有多个 Python, 或者命令路径不是"/usr/bin/python",比如 *BSD, 或者 /usr/bin/python
ansible_interpreter # 这里的""可以是 ruby、perl 或其他语言的解释器,作用和ansible_python_interpreter 类似
ansible_shell_executable # 这将设置 ansible 控制器将在目标机器上使用的 shell,覆盖 ansible.cfg 中的配置,默认为 /bin/sh

DeepEXI 脚手架工具

概念

  • 脚手架工具:指的是用于生成脚手架的工具
  • 脚手架:项目雏形,一般基于脚手架可以直接开始开发工作

介绍

DeepEXI脚手架工具是用于微服务架构环境下,为快速开发而准备的一件利器。

基于我们的脚手架工具,您可以快速、按需地集成业界主流的框架或中间件以生成定制的脚手架,减少大量的前期项目环境的搭建时间成本。

如果您购买了我们的其它产品(如业务中台、数据中台),也可以通过脚手架工具快速地进行集成。

我们提供了多种方式方便您使用我们的脚手架工具,例如CLI、UI或DevOps。无论是新手还是大牛,研发还是产品,相信总有一种使用方式适合您。

我们的所有脚手架工具都是开源的,如果有好的需求、建议,欢迎前往各项目主页提issue或pr。

相关链接

其它使用场景

除了用于实际开发外,您还可以将脚手架用于以下场景

学习

我们的脚手架几乎对大多数的集成项都提供了相关的使用demo及文档,通过这些内容,您可以快速的学习一些尚不熟悉的框架的使用方式,快速扩宽您的知识面。

实验性项目

许多时候,我们会突然想到一些好的点子,但这个点子的试验往往要创建一个新的项目来进行(担心弄乱现有项目),并且可能有许多麻烦的依赖要配置,而这个项目往往在验证完想法后就会被舍弃,为此花这么多时间来搭建实在不值得。这个时候,使用我们的脚手架工具就可以为您解决这个问题。

如何使用

以下说明均以spring cloud脚手架工具为例

CLI

需要node.js环境

1
2
3
4
5
$ npm install -g yo
$ npm install -g generator-deepexi-spring-cloud
$ mdir demo
$ cd demo
$ yo deepexi-spring-cloud

接下来按照交互模式的指引操作,即可生成属于你的脚手架

scaffold cli

UI

本地部署

1
2
3
$ npm install -g yo
$ npm install deepexi-scaffold-ui -g
$ scaffold-ui start -p 7001 -s

等待web应用运行,访问 localhost:7001/index.html

第一次访问页面时需要读取脚手架工具的相关信息,可能会比较缓慢,请耐心等候,加载过一次之后就很快了。

scaffold ui index

scaffold ui sc

在DeepEXI DevOps上使用

TODO:: 待补充

其它示例截图

项目结构

project structure

Swagger运行时文档

swagger

上传 jar 到 maven 中心仓库

一、前奏准备

了解几个 maven 相关地址:

  • 工单管理地址,就是申请上传资格和 groupId 的地方,没有账号的要先从这个地址里面注册账号
  • 构建仓库,把 jar 包上传到这里,Release 之后就会同步到 maven 中央仓库
  • 中心仓库查找地址,最终表现在这里可以搜索到

二、创建工单

没有账号的先去工单管理地址注册账号,账密要记住

创建issue

  • Group Id,唯一标识,采用 com.github.xxxxx 会比较方便,也可以使用自己的网站
  • ProjectURL,填写项目地址,如果不想公开源码,填写一个只含 README 的项目的地址就可以了。

其实管理员主要是审核 Group Id

创建issue

这里可以看到审核结果,第一次审核有可能会需要比较长的时间。

三、配置 Maven Settings.xml & pom.xml

1. 配置 Maven 中的 conf/setting.xml

1
2
3
4
5
6
<server>
<!-- 注意这里的 id 要和 pom.xml 中的 distributionManagement 中的 id 一致 -->
<id>ossrh</id>
<username>https://issues.sonatype.org的账号</username>
<password>https://issues.sonatype.org的密码</password>
</server>

2. 配置使用项目中的 pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<licenses>
<license>
<name>The Apache Software License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>

<!--写你自己的,这只是个例子-->
<scm>
<url>https://github.com/taccisum/shiro-starter</url>
<connection>https://github.com/taccisum/shiro-starter.git</connection>
<developerConnection>https://github.com/taccisum</developerConnection>
</scm>
<!--写你自己的,这只是个例子-->
<developers>
<developer>
<name>taccisum</name>
<email>514162920@qq.com</email>
<url>https://taccisum.github.io</url>
</developer>
</developers>

<!--写你自己的,这只是个例子-->
<distributionManagement>
<repository>
<id>ossrh</id>
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2</url>
</repository>
<snapshotRepository>
<id>ossrh</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
</snapshotRepository>
</distributionManagement>

四、GPG 环境

需要安装一个 GPG 环境,用来对上传的文件进行加密和签名,防止 jar 包被篡改。GPG下载地址

1
2
3
4
5
6
7
8
9
10
# 查看是否安装完成
$ gpg -version
# 生成 gen-key
$ gpg --gen-key
# 查看本地秘钥
$ gpg --list-keys
pub rsa2048 2019-04-20 [SC] [有效至:2021-04-19]
[xxxxxxxxx]
uid [ 绝对 ] itmuch.com <eacdy0000@126.com>
sub rsa2048 2019-04-20 [E] [有效至:2021-04-19]

过程中需要填写一个密码,打包上传的时候需要用到

五、发布

执行如下命令即可将依赖发布到中央仓库

1
mvn clean install deploy -P release

不出意外,构建会报xxx服务器无法找到GPG的异常。原因是前文生成的秘钥尚未发布到key server。keyserver的地址会在异常中打印出来。我的项目报的是 http://keys.gnupg.net:11371/ 。于是执行

1
2
gpg --keyserver  http://keys.gnupg.net:11371/ --send-keys [xxxxxxxxx]
其中的[xxxxxxxxx],可用gpg --list-keys显示出来。

然后在执行

1
mvn clean install deploy -P release

此时即可发布成功。

若要修改发布的版本

1
mvn versions:set -DnewVersion=1.0.2-SNAPHOST