开发者博客 – IT技术 尽在开发者博客

开发者博客 – 科技是第一生产力


  • 首页

  • 归档

  • 搜索

Eureka分区集群部署

发表于 2021-11-13

「这是我参与11月更文挑战的第13天,活动详情查看:2021最后一次更文挑战」

1、简介

Eureka集群提供了分区功能,这种功能设计理念来源于亚马逊云AWS创造的两个基础概念region(地域)和zone(可用区) :

  • region:地域就是物理意义上的不同地域,在服务部署时会选择在用户需求大的区域建设多个机房并部署服务,这样可以减少网络不稳定带来的问题
  • zone:一个region下可以划分为多个zone。也就是说一个区域的多个机房服务器,按照一定规则划分为不同的可用区(比如说一个区域有三个机房,这三个机房就可以划分为三个zone)。通过划分zone,可以达到容灾的效果,如果一个zone故障,其他zone仍然可以对外提供服务。

结合上面两个概念可知,当公司存在Eureka Server分地域部署的时候,我们可以采用Eureka集群提供的分区功能。这样可以保证某一个region中的zone注册的服务优先于另一个zone中注册的服务被调用,当前者不可用时,再去选择其他zone中注册的服务发起调用,这样可以保证服务调用的延迟降低。

Region和Zone的关系图:

2、集群部署

2.1 资源清单

本次一共搭建四台Eureka Server服务,其资源清单如下所示:

application-name zone instance-hostname prot
Eureka-Ynatian zone-yantian eureka18881.com 18881
Eureka-Luohu zone-luohu eureka18882.com 18882
Eureka-Nanshan zone-yantian eureka18883.com 18883
Eureka-Baoan zone-baoan eureka18884.com 18884

Eureka Server Cluster集群图(Region-深圳、zone-罗湖、zone-盐田、zone-南山、zone-宝安):

这里搭建的Eureka Server Cluster 是单个Region,包含四个zone,如果公司的需要的Eureka Server高可用要求特别高,可以在Region的每个zone中部署多个Eureka Server服务器。需要做分区域,参照Region Shenzhen配置多个Region即可。

2.2 配置文件

Eureka-Ynatian的Eureka Server配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
yaml复制代码server:
port: 18881

spring:
application:
name: Eureka-Ynatian

eureka:
instance:
hostname: eureka18881.com
client:
prefer-same-zone-eureka: true
register-with-eureka: true
region: shenzhen
availability-zones:
shenzhen: zone-yantian,zone-luohu,zone-nanshan,zone-baoan
fetch-registry: true
service-url:
zone-yantian: http://eureka18881.com:18881/eureka/
zone-luohu: http://eureka18882.com:18882/eureka/
zone-nanshan: http://eureka18883.com:18883/eureka/
zone-baoan: http://eureka18884.com:18884/eureka/

Eureka-Luohu的Eureka Server配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
yaml复制代码server:
port: 18882

spring:
application:
name: Eureka-Luohu

eureka:
instance:
hostname: eureka18882.com
client:
prefer-same-zone-eureka: true
register-with-eureka: true
region: shenzhen
availability-zones:
shenzhen: zone-luohu,zone-yantian,zone-nanshan,zone-baoan
fetch-registry: true
service-url:
zone-yantian: http://eureka18881.com:18881/eureka/
zone-luohu: http://eureka18882.com:18882/eureka/
zone-nanshan: http://eureka18883.com:18883/eureka/
zone-baoan: http://eureka18884.com:18884/eureka/

Eureka-Nanshan的Eureka Server配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
yaml复制代码server:
port: 18883

spring:
application:
name: Eureka-Nanshan

eureka:
instance:
hostname: eureka18883.com
client:
prefer-same-zone-eureka: true
register-with-eureka: true
region: shenzhen
availability-zones:
shenzhen: zone-nanshan,zone-luohu,zone-yantian,zone-baoan
fetch-registry: true
service-url:
zone-yantian: http://eureka18881.com:18881/eureka/
zone-luohu: http://eureka18882.com:18882/eureka/
zone-nanshan: http://eureka18883.com:18883/eureka/
zone-baoan: http://eureka18884.com:18884/eureka/

Eureka-Baoan的Eureka Server配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
yaml复制代码server:
port: 18884

spring:
application:
name: Eureka-Baoan

eureka:
instance:
hostname: eureka18884.com
client:
prefer-same-zone-eureka: true
register-with-eureka: true
region: shenzhen
availability-zones:
shenzhen: zone-baoan,zone-nanshan,zone-luohu,zone-yantian
fetch-registry: true
service-url:
zone-yantian: http://eureka18881.com:18881/eureka/
zone-luohu: http://eureka18882.com:18882/eureka/
zone-nanshan: http://eureka18883.com:18883/eureka/
zone-baoan: http://eureka18884.com:18884/eureka/

2.3 启动集群

依次启动集群中四个Eureka Server服务,等待集群之间稳定后,访问Eureka Server的Dashboard,此时在四台Eureka Server中的任意一台都可以看到相同的服务注册信息Instance currently registered with Eureka,以及DS Replicaes信息(这个不相同哦,不需要复制自己……),此时说明Eureka Server Cluster已经正常启动了。

2.4 服务注册

Eureka Server分区本质上除了达到高可用之外,最主要是为了给客户端提供最优的服务,以此来达到服务的最快响应。在Eureka Server中注册两个服务Server-01和Server-02,Server-01中metadata-map zone选择zone-yantian,Server-02中metadata-map zone选择zone-louhu。(注意两个服务的服务名均为Server)

Server-01配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
yaml复制代码server:
port: 19991

spring:
application:
name: Server

eureka:
instance:
prefer-ip-address: true
metadata-map:
zone: zone-yantian
client:
region: shenzhen
availability-zones:
shenzhen: zone-yantian,zone-luohu,zone-nanshan,zone-baoan
service-url:
zone-yantian: http://eureka18881.com:18881/eureka/
zone-luohu: http://eureka18882.com:18882/eureka/
zone-nanshan: http://eureka18883.com:18883/eureka/
zone-baoan: http://eureka18884.com:18884/eureka/
fetch-registry: true
register-with-eureka: true
prefer-same-zone-eureka: true

Server-01中提供一个Rest Api端点

1
2
3
4
5
6
7
8
9
10
less复制代码@RestController
@RequestMapping("/zone")
public class ZoneController {

@GetMapping
public String zone() {
return "Server zone-yantian";
}

}

Server-02配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
yaml复制代码server:
port: 19992

spring:
application:
name: Server

eureka:
instance:
prefer-ip-address: true
metadata-map:
zone: zone-luohu
client:
region: shenzhen
availability-zones:
shenzhen: zone-luohu,zone-yantian,zone-nanshan,zone-baoan
service-url:
zone-yantian: http://eureka18881.com:18881/eureka/
zone-luohu: http://eureka18882.com:18882/eureka/
zone-nanshan: http://eureka18883.com:18883/eureka/
zone-baoan: http://eureka18884.com:18884/eureka/
fetch-registry: true
register-with-eureka: true
prefer-same-zone-eureka: true

Server-02中提供一个Rest Api端点

1
2
3
4
5
6
7
8
9
10
less复制代码@RestController
@RequestMapping("/zone")
public class ZoneController {

@GetMapping
public String zone() {
return "Server zone-luohu";
}

}

此时访问Eureka dashboard可以看到Server 有两个实例注册到了Eureka Server Cluster中。

2.4 服务消费

创建一个服务消费者Consumer-01,用于测试服务调用分发的具体情况。

Consumer-01配置文件,其中指定metadata-map.zone为zone-luohu

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
yaml复制代码server:
port: 17771

spring:
application:
name: Consumer-01

eureka:
instance:
prefer-ip-address: true
metadata-map:
zone: zone-luohu
client:
region: shenzhen
availability-zones:
shenzhen: zone-luohu,zone-yantian,zone-nanshan,zone-baoan
service-url:
zone-yantian: http://eureka18881.com:18881/eureka/
zone-luohu: http://eureka18882.com:18882/eureka/
zone-nanshan: http://eureka18883.com:18883/eureka/
zone-baoan: http://eureka18884.com:18884/eureka/
fetch-registry: true
register-with-eureka: true
prefer-same-zone-eureka: true

提供一个Rest Api访问端点:

1
2
3
4
5
6
7
8
9
10
11
12
13
less复制代码@RestController
@RequestMapping("/consumer")
public class ZoneController {

@Autowired
private RestTemplate restTemplate;

@GetMapping
public String zone() {
return restTemplate.getForObject("http://server/zone", String.class);
}

}

在浏览器访问该端点,无论怎么刷新都会输出Server zone-louhu,说明请求一直打到了Server-02

修改Consumer-01的配置文件,修改metadata-map:zone为zone-yantian

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
yaml复制代码server:
port: 17771

spring:
application:
name: Consumer-01

eureka:
instance:
prefer-ip-address: true
metadata-map:
zone: zone-yantian
client:
region: shenzhen
availability-zones:
shenzhen: zone-luohu,zone-yantian,zone-nanshan,zone-baoan
service-url:
zone-yantian: http://eureka18881.com:18881/eureka/
zone-luohu: http://eureka18882.com:18882/eureka/
zone-nanshan: http://eureka18883.com:18883/eureka/
zone-baoan: http://eureka18884.com:18884/eureka/
fetch-registry: true
register-with-eureka: true
prefer-same-zone-eureka: true

重新访问浏览器,请求Consumer-01提供的端点,此时无论怎么刷新都会输出Server zone-yantian,说明请求一直打到了Server-01

2.5 配置详解

在服务注册和服务消费中有几个非常重要的配置项prefer-same-zone-eureka和eureka.instance.metadata-map.zone

prefer-same-zone-eureka

1
2
3
4
5
6
7
8
9
10
yaml复制代码eureka:
client:
region: shenzhen
availability-zones:
shenzhen: zone-luohu,zone-yantian,zone-nanshan,zone-baoan
service-url:
zone-yantian: http://eureka18881.com:18881/eureka/
zone-luohu: http://eureka18882.com:18882/eureka/
zone-nanshan: http://eureka18883.com:18883/eureka/
zone-baoan: http://eureka18884.com:18884/eureka/
  • true:获取eureka.client.availability-zones下的zone列表,选择第一个zone进行服务注册,如果第一个注册失败,依次选择其他zone进行服务注册
  • false:获取eureka.client.service-url列表,选择第一个service-url地址进行服务注册,如果第一个地址注册失败,依次选择其他service-url服务地址注册

eureka.instance.metadata-map.zone

1
2
3
4
yaml复制代码eureka:
instance:
metadata-map:
zone: zone-yantian

eureka.instance.metadata-map.zone配置项用于标识服务提供者和服务消费者属于那个zone,服务消费者通过ribbon在Eureka Server中拉去服务列表。如果一个zone中有多个服务,则轮询每个zone中的服务列表;如果zone中的服务均不能访问,则尝试访问其他zone下的服务。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

MySQL之where使用

发表于 2021-11-13

「这是我参与11月更文挑战的第13天,活动详情查看:2021最后一次更文挑战」

1、简介

当我们需要获取数据库表数据的特定子集时,可以使用where子句指定搜索条件进行过滤。where子句的使用场景非常丰富,它是MySQL语句中需要重点掌握的一个知识点。where实现的所有功能都可以在MySQL之外实现,但是直接在MySQL中过滤查询速度更快,也能节省网络传输开销。

2、正文

首先准备一张User表,DDL和表数据如下所示,可以直接复制使用。

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
sql复制代码SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '用户名',
`age` int(11) NOT NULL COMMENT '年龄',
`sex` smallint(6) NOT NULL COMMENT '性别',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, '李子捌', 18, 1);
INSERT INTO `user` VALUES (2, '张三', 22, 1);
INSERT INTO `user` VALUES (3, '李四', 38, 1);
INSERT INTO `user` VALUES (4, '王五', 25, 1);
INSERT INTO `user` VALUES (5, '六麻子', 13, 0);
INSERT INTO `user` VALUES (6, '田七', 37, 1);
INSERT INTO `user` VALUES (7, '谢礼', 18, 1);

SET FOREIGN_KEY_CHECKS = 1;

数据的初始顺序如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
sql复制代码mysql> select * from user;
+----+--------+-----+-----+
| id | name | age | sex |
+----+--------+-----+-----+
| 1 | 李子捌 | 18 | 1 |
| 2 | 张三 | 22 | 1 |
| 3 | 李四 | 38 | 1 |
| 4 | 王五 | 25 | 1 |
| 5 | 六麻子 | 13 | 0 |
| 6 | 田七 | 37 | 1 |
| 7 | 谢礼 | 18 | 1 |
+----+--------+-----+-----+
7 rows in set (0.00 sec)

2.1 where子句位置

  • where子句位于from之后,比如:
1
2
3
4
5
6
7
8
9
sql复制代码mysql> select * from user where age=18;
+----+--------+-----+-----+
| id | name | age | sex |
+----+--------+-----+-----+
| 1 | 李子捌 | 18 | 1 |
| 7 | 谢礼 | 18 | 1 |
| 8 | 李子柒 | 18 | 1 |
+----+--------+-----+-----+
3 rows in set (0.00 sec)
  • 如果使用order by,where子句位于order by之前,比如:
1
2
3
4
5
6
7
8
9
sql复制代码mysql> select * from user where age = 18 order by name;
+----+--------+-----+-----+
| id | name | age | sex |
+----+--------+-----+-----+
| 1 | 李子捌 | 18 | 1 |
| 8 | 李子柒 | 18 | 1 |
| 7 | 谢礼 | 18 | 1 |
+----+--------+-----+-----+
3 rows in set (0.00 sec)

2.2 操作符

where子句支持8个操作符,它们分别如下所示:

操作符 操作符说明
= 等于
<> 不等于
!= 不等于
< 小于
<= 小于等于
大于
>= 大于等于
BETWEEN AND 两个值之间的区间 比如 BETWEEN 1 AND 100

接下来使用表中操作符一个个的进行where子查询。

2.2.1 操作符 =

操作符=可以用于查询完全匹配的数据,注意MySQL默认不区分英文大小写.


需求:

查询name等于李子捌的数据

语句:

1
sql复制代码select * from user where name = '李子捌';

结果:

1
2
3
4
5
6
sql复制代码+----+--------+-----+-----+
| id | name | age | sex |
+----+--------+-----+-----+
| 1 | 李子捌 | 18 | 1 |
+----+--------+-----+-----+
1 row in set (0.00 sec)

操作符=如果存在多条匹配数据,那么符合where字句条件的数据均会返回,如果需要指定排序方式,可以结合order by对数据进行排序。

2.2.2 操作符 <> 和 !=

这两个操作符实现的效果相同,均是匹配不相等的数据。

需求:

查询name不等于李子捌的数据

语句:

1
2
sql复制代码select * from user where name <> '李子捌';
select * from user where name != '李子捌';

结果:

1
2
3
4
5
6
7
8
9
10
11
12
sql复制代码+----+--------+-----+-----+
| id | name | age | sex |
+----+--------+-----+-----+
| 2 | 张三 | 22 | 1 |
| 3 | 李四 | 38 | 1 |
| 4 | 王五 | 25 | 1 |
| 5 | 六麻子 | 13 | 0 |
| 6 | 田七 | 37 | 1 |
| 7 | 谢礼 | 18 | 1 |
| 8 | 李子柒 | 18 | 1 |
+----+--------+-----+-----+
7 rows in set (0.00 sec)

2.2.3 操作符<= 、 <、>=、>

这四个操作符用于数值类型的列数据比较,但是如果作用于文本字段,MySQL也能执行只是返回的结果可能并不是你预期的数据(理论上没人会这么玩,但确实不报错!)

需求:

查询年龄小于等于20的所有用户

语句:

1
sql复制代码 select * from user where age <= 20;

结果:

1
2
3
4
5
6
7
8
9
sql复制代码+----+--------+-----+-----+
| id | name | age | sex |
+----+--------+-----+-----+
| 1 | 李子捌 | 18 | 1 |
| 5 | 六麻子 | 13 | 0 |
| 7 | 谢礼 | 18 | 1 |
| 8 | 李子柒 | 18 | 1 |
+----+--------+-----+-----+
4 rows in set (0.00 sec)

2.2.4 BETWEEN AND

BETWEEN AND 用于查询两个数值范围之间的值,这个范围是两个闭区间,因此包含起始值和结束值。比如BETWEEN 1 AND 100,包含1和100的数据。

需求:

查询年龄大于等于20小于等于50的用户

语句:

1
sql复制代码select * from user where age between 20 and 50;
1
2
3
4
5
6
7
8
9
sql复制代码+----+------+-----+-----+
| id | name | age | sex |
+----+------+-----+-----+
| 2 | 张三 | 22 | 1 |
| 3 | 李四 | 38 | 1 |
| 4 | 王五 | 25 | 1 |
| 6 | 田七 | 37 | 1 |
+----+------+-----+-----+
4 rows in set (0.00 sec)

2.3 空值null

空值null指的是不包含数据,它可以在建表的时候指定其中的列是否可以包含空值。需要注意null和数据值类型的0,字符类型的空格不一样,空值null指的是没有值。

关于空值null的查询,MySQL提供了专门的where子句is null。

需求:

查询name为空值的数据

语句:

1
csharp复制代码select * from user where name is null;

结果:

1
sql复制代码Empty set (0.00 sec)

因为user表中不存在name值为空值的数据,所以没有数据返回。如果我们需要查询name列不为空的数据,该怎么查询呢?

这个时候我们可以使用is not null

1
2
3
4
5
6
7
8
9
10
11
12
13
14
sql复制代码mysql> select * from user where name is not null;
+----+--------+-----+-----+
| id | name | age | sex |
+----+--------+-----+-----+
| 1 | 李子捌 | 18 | 1 |
| 2 | 张三 | 22 | 1 |
| 3 | 李四 | 38 | 1 |
| 4 | 王五 | 25 | 1 |
| 5 | 六麻子 | 13 | 0 |
| 6 | 田七 | 37 | 1 |
| 7 | 谢礼 | 18 | 1 |
| 8 | 李子柒 | 18 | 1 |
+----+--------+-----+-----+
8 rows in set (0.00 sec)

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

Linux 小知识 文件属性 Linux 小知识 文

发表于 2021-11-13

这是我参与11月更文挑战的第13天,活动详情查看:2021最后一次更文挑战

Linux 小知识 | 文件属性

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
bash复制代码[root@VM-8-10-centos /]# ls -al # 文件属性
total 80
# 权限
dr-xr-xr-x. 19 766777777777root root 4096 11月 5 16:14 .
dr-xr-xr-x. 19 root root 4096 11月 5 16:14 ..
-rw-r--r-- 1 root root 0 11月 26 2019 .autorelabel
lrwxrwxrwx 1 root root 7 11月 3 2020 bin -> usr/bin
dr-xr-xr-x. 5 root root 4096 9月 14 20:49 boot
drwxr-xr-x 2 root root 4096 12月 10 2019 data
drwxr-xr-x 19 root root 2900 9月 15 00:51 dev
drwxr-xr-x. 104 root root 12288 11月 3 14:18 etc
drwxr-xr-x. 8 root root 4096 11月 3 14:16 home
lrwxrwxrwx 1 root root 7 11月 3 2020 lib -> usr/lib
lrwxrwxrwx 1 root root 9 11月 3 2020 lib64 -> usr/lib64
drwx------. 2 root root 16384 11月 26 2019 lost+found
drwxr-xr-x. 2 root root 4096 11月 3 2020 media
drwxr-xr-x. 2 root root 4096 11月 3 2020 mnt
drwxr-xr-x. 3 root root 4096 9月 16 10:59 opt
dr-xr-xr-x 118 root root 0 9月 15 00:51 proc
dr-xr-x---. 10 root root 4096 11月 4 10:48 root
drwxr-xr-x 31 root root 1000 11月 4 14:34 run
lrwxrwxrwx 1 root root 8 11月 3 2020 sbin -> usr/sbin
drwxr-xr-x. 2 root root 4096 11月 3 2020 srv
dr-xr-xr-x 13 root root 0 9月 15 00:51 sys
drwxrwxrwt. 5 root root 4096 11月 5 15:13 tmp
drwxr-xr-x. 15 root root 4096 9月 16 09:13 usr
drwxr-xr-x. 20 root root 4096 6月 18 11:34 var

权限

权限一共可以有10位
第一位

  • d 表示目录/文件夹
  • - 文件
  • l 链接文档

r 可读

w可写

x可执行

-没有当前权限

第二位 - 第四位

属主权限: 文件所在用户可以做什么

谁创建的, 默认属主就是谁

第五位 - 第七位

属组的权限: 文件所在的用户组可以做什么

谁创建的就是谁的用户组

第八为 - 第十位

其它用户的权限

除了属组以外的其他用户

1
2
3
4
5
bash复制代码-rwxr-xr-x
# [-] 这是一个文件
# [rwx] 属主 可读可写可以执行
# [r-x] 用户组 可读不能写可以执行
# [r-x] 其它用户 可读 不可写 可执行
文件类型 属主权限 属组权限 其它用户权限
0 1 2 3 4 5 6 7 8 9
d目录 / -文件 r 读 w 写 x 执行 r 读 - 不可写 x执行 r读 -不可写 x执行

更改文件属性

chgrp 命令 changegroup 修改所在的用户组

1
2
3
bash复制代码chgrp [选项参数] [所属群组] [文件或目录]
[root@VM-8-10-centos app]# chgrp root test
[root@VM-8-10-centos app]# chgrp -v root test # 显示提示语句

chown 更改属主 | 更改数组和属组

1
2
3
4
bash复制代码chown 属主名 文件名 # 更改属主
chown [参数选项] 属主名 : 属组名 文件名 # 更改数组和属组
# 选项参数
-R 处理指定目录以及子目录下的所有文件

示例

1
2
3
4
5
6
7
8
9
10
11
12
bash复制代码# 修改属主
[root@VM-8-10-centos app]# ls -al
drwxr-xr-x 2 root root 4096 11月 4 18:00 test
[root@VM-8-10-centos app]# chown user1 test
[root@VM-8-10-centos app]# ls -al
drwxr-xr-x 2 user1 root 4096 11月 4 18:00 test
# 修改 属主 + 属组
[root@VM-8-10-centos app]# chown root:root test
[root@VM-8-10-centos app]# ls -al
drwxr-xr-x 2 root root 4096 11月 4 18:00 test
# test 以及 test 内所有文件都改为 root
[root@VM-8-10-centos app]# chown -R root:root test

chmod 权限命令

作用: 修改属主,属组,其他用户的权限

参数选项

参数 作用
-c 若该文件权限确实已经更改,才显示其更改动作
-f 若该文件权限无法被更改也不要显示错误信息
-v 显示权限变更的详细资料
-R 对目前目录下的所有文档与子目录进行相同的权限变更(即以递归的方式逐个变更)
–help 显示帮助说明
–version 显示版本
数字权限 rwx对应数值相加的和

修改方式1: 数字方式

权限 英文 缩写 数字序号
读 read r 4
写 write w 2
执行 execute x 1
无权限 - 0
1
2
3
4
5
6
7
8
9
bash复制代码# rwx 4+2+1 = 7
# 5: 4+1 => r-x
[root@VM-8-10-centos test]# ls -l
total 4
drwxr-xr-x 2 root root 4096 11月 5 17:57 aaa
[root@VM-8-10-centos test]# chmod -R 777 aaa
[root@VM-8-10-centos test]# ls -l
total 4
drwxrwxrwx 2 root root 4096 11月 5 17:57 aaa

修改方式2: 符号方式

符号 英文 说明
u user 属主权限
g group 属组权限
o others 其他权限
a all 全部身份
+ 加入权限
- 除去权限
= 设定权限
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
bash复制代码# chmod -R u=rwx,g=rx,o=r a.txt

[root@VM-8-10-centos test]# ls -l
total 4
drwxrwxrwx 2 root root 4096 11月 5 17:57 aaa
-rw-r--r-- 1 root root 0 11月 5 18:01 a.txt
[root@VM-8-10-centos test]# chmod -R u=rwx,g=rx,o=r a.txt
[root@VM-8-10-centos test]# ls -l
total 4
drwxrwxrwx 2 root root 4096 11月 5 17:57 aaa
-rwxr-xr-- 1 root root 0 11月 5 18:01 a.txt

# 去掉其他用户 a.txt的所有权限
chmod -R o-rwx a.txt
[root@VM-8-10-centos test]# chmod -R o-rwx a.txt
[root@VM-8-10-centos test]# ls -l
total 4
drwxrwxrwx 2 root root 4096 11月 5 17:57 aaa
-rwxr-x--- 1 root root 0 11月 5 18:01 a.txt

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

Spring Cloud / Alibaba 微服务架构

发表于 2021-11-12

这是我参与11月更文挑战的第11天,活动详情查看:2021最后一次更文挑战。

上篇文章介绍了JWT的基本概念和JWT的适用场景,本篇文章将介绍如何搭建授权、鉴权中心微服务以及验证服务的可用性。

搭建授权、鉴权中心微服务

一、授权中心微服务框架搭建

1、创建子模块并修改pom文件

同样的方式创建子模块,e-commerce-authority-center。

2、在resources文件夹下创建配置文件

创建并配置bootstrap.yml文件。

3、创建授权中心启动入口

创建AuthorityCenterApplication类,加入注解@SpringBoot Application和@EnableDiscoveryClient(服务发现和服务注册)。

4、验证

启动授权中心服务并将Nacos启动,打开Nacos console页面查看是否成功将授权中心注册上去。

二、建库建表及ORM过程

1、创建数据库及数据表

直接使用如下语句创建并使用数据库e_commerce。

1
2
ini复制代码create database e_commerce;
use e_commerce;

接下来先创建一张用户表,数据表设计如下:

image.png

2、创建entity包存放表的实体类

由于我们的项目使用的是Jpa这样一个ORM框架,所以我们通常会创建一个entity包来存放实体类映射数据表。

新建EcommerceUser用户表实体类,加上 @Entity注解 表明它是一个数据表, @EntityListeners(AuditingEntityListener.class) 这个监听器的意思是说,我们在操作这张数据表的时候,让Jpa去帮我们实现监听,给AuthorityCenterApplication类再加上一个注解 @EnableJpaAuditing,这样就可以配合刚刚加在实体类上的监听器表示允许Jpa的自动审计,当我们增删改表记录的时候,它会自动帮我们更新创建时间和修改时间,或者其他的字段进行一些操作,当然这需要对应的注解去标识。如给字段加上@CreatedDate注解,就是Jpa的一个自动审计功能,当你插入一条数据时,@CreatedDate注解会生效,由它为你生成创建时间。

3、创建dao包存放接口定义

三、验证授权中心环境可用性

在test包下创建AuthorityCenterApplicationTests类作为授权中心测试入口。至此我们就完成了授权中心的搭建,可以尝试启动它,需要将本地或远程的Nacos启动起来,然后看看是否能够完成注册,如果能将我们这个服务成功注册上去,那就说明当前授权中心模块搭建成功。

至此我们就初步完成了授权中心的框架基本搭建,接下来我们会一步步去填充并完善它的功能。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

捋一捋Python的文件处理(上) 先学会文件的读写! 我们

发表于 2021-11-12

「这是我参与11月更文挑战的第10天,活动详情查看:2021最后一次更文挑战」

正式的Python专栏第34篇,同学站住,别错过这个从0开始的文章!

前面写了很多内容了,都是在进行一些数据处理,但是我们需要储存读取数据的时候,就需要文件了。 这篇学委带大家过一过文件处理。

先学会文件的读写!

比如像以前在学校读书的时候,第一门编程课设计要求是制作学生管理系统。

这就需要使用文件来处理(也可以用数据库,但是一般C语言都是很多计算机系新生的首选语言,这时候大概率也不知道数据库)。

python 最常用的是open和write函数,如下:

1
2
3
4
5
6
7
8
9
python复制代码#open函数:接收一个文件名,还有其他参数可省略不写。
one_file = open('myfile.txt')
#读取数据赋值给data变量
data = one_file.read()

#一个文件对象的write函数
one_file = open('myfile.txt','w')
#write函数:传入数据,write函数把数据写入到one_file对应的文件中。
one_file.write('写到文件的数据')

我们看看一些文件操作示例吧

读取文件数据

保留下面数据到为文件:sample.txt

1
2
3
复制代码持续学习
持续开发
我雷学委
1
2
python复制代码afile = open("sample.txt")
print(afile.read())

屏幕快照 2021-11-13 上午12.07.35.png

写数据简单展示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
kotlin复制代码#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2021/11/12 11:58 下午
# @Author : LeiXueWei
# @CSDN/Juejin/Wechat: 雷学委
# @XueWeiTag: CodingDemo
# @File : filedemo.py
# @Project : hello

afile = open("sample.txt")
data = afile.read()
print("sample file content=", data)
afile.close()

#把sample读取的数据写到test文件
afile = open("./test.txt", "w")
afile.write(data)
afile.close()

print("write data to test file!")
afile = open("./test.txt")
data = afile.read()
afile.close()
print("test file content=", data)

我们看,读取到的内容确实是写入的。

这个文件操作非常简单。

屏幕快照 2021-11-13 上午12.19.19.png

读写文件就这么简单,但是我们操作文件之后,记得调用close函数(关闭文件,不然后续再读写操作会出现异常/错误!)

close函数的调用如下:

1
python复制代码one_file.close() #文件对象.close()

但是我们通常都是编写这种风格的文件读写:

1
2
3
python复制代码with open('sample.txt', 'r') as one_file:
data = one_file.read()
#无须调用close了,这个with代码块内,python会帮我们自动关闭文件。

以上都是一开文件就一次性读取的,Python中还可以一行一行读取。

按行读取/按行写数据

按行读取

我们基于前面读写文件代码改造,直接看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
python复制代码#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2021/11/12 11:58 下午
# @Author : LeiXueWei
# @CSDN/Juejin/Wechat: 雷学委
# @XueWeiTag: CodingDemo
# @File : filedemo2.py
# @Project : hello

with open("sample.txt") as afile:
data = afile.readline()
print("sample file content=", data)

with open("./test.txt", "w") as afile:
afile.write(data)
print("write data to test file!")

with open("test.txt") as afile:
data = afile.readline()
print("test file content=", data)

我们看到这里读取了sample文件的一行,然后写入到test文件,也只有一行!

屏幕快照 2021-11-13 上午12.40.52.png

总结

Python 文件的读取非常方便,内置的open函数和文件对象自带的write函数,设计非常简单。

开箱即用,所以简单敲敲,花几分钟学一些文件操作吧!

对了,喜欢Python的朋友,请关注学委的 Python基础专栏 or Python入门到精通大专栏

持续学习持续开发,我是雷学委!

编程很有趣,关键是把技术搞透彻讲明白。

欢迎关注微信,点赞支持收藏!

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

Windows转M1 MacOs全攻略 Java程序员环境配

发表于 2021-11-12

这是我参与11月更文挑战的第4天,活动详情查看:2021最后一次更文挑战

前言

​ 从苹果2021第二次秋季发布会就开始纠结是选择14寸还是13寸。最后考虑到真实使用情况以及经济能力,最后还是选择13寸的macbook。毕竟M1芯片已足够我日常使用、学习以及剪视频。

​ 可是在我拿到电脑的第一个晚上,却碰到了和windows电脑十分不同的变化以及各种不适应,找遍了全网也没找到好一点的文章。于是,决定带大家一起躲过自己踩过的坑,记录一下从打开盒子,到真正上手。

​ 我本人是Java程序员,本文会增加一个程序员的分界点。分界点之后的内容是程序员的一些环境变量配置的坑,分界点之前是日常普通用户的坑。

本文从2021年10月29日开始记录,持续更新。

MacOs版本:macOS Big Sur 11.6

所有人适用篇

第一步 开机

其实开机的步骤非常简单明了,打开屏幕翻盖就会自动开机。那我遇到的第一个坑呢是触摸板。**Macbook的触摸板非常特别,它是可以按下去的,有类似鼠标的按键段落感。**当第一次开机的时候,进入初始设置的时候,轻触是无效的,当然到后面系统中可以设置。相信这就是触摸板比鼠标好用的感觉吧。

触摸板单击 = 鼠标左键

触摸板是两只手指按下 = 鼠标右键

第二步 了解界面以及差异

进入到开机界面,最下面的一排就是Dock栏(和windows中的一致)

image-20211102211424632

那么首先呢,就是我们一个一个的图标,常用的软件你可以拖进Dock栏中常驻。

最左边是我们的访达Finder(即windows中的我的电脑)

点开之后可以发现和我的电脑还是比较类似的。内容大差不差,多点点他的下拉菜单,大致的功能就都在这里啦。

接下来,可能就有小伙伴要问了,我的那么大一个C盘D盘呢?那是因为在macOS中没有分盘的概念。(相当于只有C盘)

应用的安装通常不需要配置路径,若是有应用缓存存储位置,如FinalCutPro的项目缓存,可以在应用中单独设置。

接下来我们的目光转移到左上角。

image-20211106001045177

最左边的的苹果标志类似于windows中的开始菜单,里面是系统属性、设置以及开机关机的选项。

那在苹果标志的右边就是当前应用的菜单。

!注意!是当前应用的菜单,我打开访达就是访达的菜单,我打开Chrome就是Chrome的菜单。它会随着应用改变,这也是和windows非常大的不同,不在应用窗口的顶部,而是在屏幕桌面的顶部。

接着从顶部的左侧转移到右侧,那就是一些日期,电源以及一些程序的后台的显示。(类似于windows的右下角)

image-20211106001337581

如果你的某些后台和我不一样(比如说电源或者控制中心),不用担心那只是我的其他插件。

顶部菜单浏览完了,我们回到最下面的dock栏。

鼠标移到第二个图标,即启动台。它包含了我们全部的应用。我们下载安装的应用最终也是在这里显示。

第三个图标就是AppStore,这里就是官方的应用下载途径。当然官方的下载途径,审核会非常严苛,其中还有付费应用,我们也可以选择在网页中下载安装包来安装。我们最后来讲软件的下载方式,以及推荐的网站。

第四个Safari浏览器,是mac OS中自带的浏览器,比Chrome浏览器好用程度差不多!

在之后呢就是系统的自带应用。我们跟随的目光移动到一个齿轮的图标上,那是我们系统的设置,和左上角点击苹果图标中,系统偏好设置一样。

在它的右边两个白色的分隔线。

image-20211102215943089

这两个白色分割之间的应用,他们是你最近打开的应用,会随着你打开其他应用改变。

第二个分割线的右侧是当前打开的应用的页面(最小化)

在这个区域内最左边是下载文件夹,我们下载的东西都在这里。

最右边是垃圾篓即回收站

第三步 快捷按键

Windows的系统快捷按键和macOS的快捷按键还是有非常大的不同,但是大部分都比较相识,几个比较常用的是复制:command + c 粘贴:command + v command键比较类似windows的control键。

  • command + q 关闭应用
  • command + w 关闭当前窗口
  • command + t 新建一个窗口
  • command + m 最小化窗口
  • command + control + F 最大化窗口
  • command + option + J 打开chrome 的 console
  • command + shift + T 恢复刚关闭的窗口
  • command + d 刷新浏览器
  • command + space 打开聚焦搜索
  • command + control + Q锁屏
  • command + A 全选
  • command + F 搜索
  • command + S 保存
  • command + X 剪切
  • command + C 复制
  • command + V 复制
  • command + 左右 跳到 行首 / 行末
  • command + 点击 选中多行的某个位置

第四步 推荐的系统设置

首先是触摸板

打开系统偏好设置

image-20211108214629460

我们可以把触摸板里面的几个选项全部选上,大部分都是我们需要的

image-20211108214916466

在这里就可以设置我们的轻触左键,快速双击右键。

打开触控ID

image-20211108215413240

我们可以设置更多的指纹以及其他指纹id选项,建议可以全部勾选上。

打开程序坞与菜单栏

image-20211108215702757

在这里我们可以设置程序坞的位置、大小以及缩放效果等等

image-20211108215931984

缩放效果 image-20211108220142413

第五步 软件下载

那macOS的安装有几种方式

第一 选择在appStore下载软件

在这里的软件都是通过苹果商店审核的,登陆appId,获取软件,安装即可。(当然也有收费的应用)

image-20211108220714679

image-20211108220728944

收费应用

image-20211108220755900

第二 PKG 文件 或 DMG 文件

一般去官网下载的应用都会是这两种格式

这里推荐一个网站www.macwk.com ,这里有大部分你需要的软件,并且免费。

下载下来后,在访达中打开下载目录,双击打开你下载的dmg文件或者pkg文件

image-20211108221512371

双击打开是类似这样的安装界面

image-20211108221549072

有类似这种双击打开按步骤的安装,有类似一个箭头指向Application文件夹的界面,这时候需要你拖拽箭头后面的文件,拖拽到箭头指向的Application。

也有打开是类似windows应用的安装界面,跟着一步步下一步即可安装成功。

Java程序员篇

那么接下来,就作为程序员带大家配置一下常用的开发环境。

推荐大家是跟着我一步一步来。

安装xcode-select(自动安装git)

因为很多环境和软件都需要命令行工具,所以我们只需要安装xcode-select。

打开终端

在这里停顿一下,目前版本的macOS应该是默认终端为zsh

我们在终端中输入cat /etc/shells查看有哪几种shell

输入echo $SHELL查看当前的shell是什么

如果你的shell使用的是bash,那么我们输入chsh -s /bin/zsh切换到zsh,重启终端。

当然如果你不想试用zsh的话,也没有任何问题,跳过这里即可。

输入

1
bash复制代码xcode-select --install

在跳出的窗口中,点击安装。

等待安装的过程可能会比较久,需求一个网络状况较好的环境。

随着安装结束之后呢,他也会自动安装git

1
ba复制代码git --version #查看当前git版本

安装Homebrew

我们试用Homebrew国内的安装脚本 gitee.com/cunkai/Home…

苹果电脑标准安装脚本:(推荐 优点全面 缺点慢一点)

1
bash复制代码/bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)"

苹果电脑极速安装脚本:(优点安装速度快 缺点update功能需要命令修复 )

1
bash复制代码/bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)" speed

苹果电脑卸载脚本:

1
bash复制代码/bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/HomebrewUninstall.sh)"

打开终端选择,之后按提示内容选择就行

输入brew -v查看Homebrew的版本号,说明安装成功

Homebrew是什么?

Homebrew是Mac OS 不可或缺的套件管理器,是一款Mac OS平台下的软件包管理工具,拥有安装、卸载、更新、查看、搜索等很多实用的功能。简单的一条指令,就可以实现包管理,而不用你关心各种依赖和文件路径的情况,十分方便快捷。

Homebrew命令

在终端上输入brew,获取下列提示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
bash复制代码Example usage:
brew search TEXT|/REGEX/ #搜索软件 比如 brew search docker
brew info [FORMULA|CASK...] #获得一些软件信息,比如说安装位置,依赖,软件简介
brew install FORMULA|CASK... #安装软件
brew update # 升级brew
brew upgrade [FORMULA|CASK...] # 升级软件
brew uninstall FORMULA|CASK... # 卸载软件
brew list [FORMULA|CASK...] # 列出安装过的软件

Troubleshooting:
brew config # brew的一些配置信息
brew doctor # brew诊断程序
brew install --verbose --debug FORMULA|CASK

Contributing: # 创建brew 的一些配置
brew create URL [--no-fetch]
brew edit [FORMULA|CASK...]

Further help: #更多帮助
brew commands
brew help [COMMAND]
man brew
https://docs.brew.sh

安装services

在终端中输入brew services,如果你没有安装services,会自动安装。

services可以帮助我们来控制一些后台程序,比如说mysql、redis等

1
2
3
4
5
6
7
bash复制代码#常见命令
brew services list # 查看使用brew安装的服务列表
brew services run formula|--all # 启动服务(仅启动不注册)
brew services start formula|--all # 启动服务,并注册
brew services stop formula|--all # 停止服务,并取消注册
brew services restart formula|--all # 重启服务,并注册
brew services cleanup # 清除已卸载应用的无用的配置

安装JDK

打开ORACLE官网www.oracle.com/java/techno…

目前ORACLE官网只有JDK17原生支持M1芯片

选择Arm版本下载即可

也打算写一篇新特性的文章从JDK8~JDK17

JDK8和JDK11可以使用zuluJDK

更好的终端体验 安装iterm2

进入iterm2的官网选择最新的稳定版下载iterm2.com/downloads.h…

在启动台中找到iterm2,启动

iterm2中可以做很多个性化的操作,网上有很多文章可以借鉴。有兴趣可以了解一下!

环境配置

1
2
3
4
5
6
bash复制代码cat ~/.zshrc #查看当前的环境配置

# 如果终端提示你没有这个文件 你可以选择手动创建
touch ~/.zshrc
# 之后的所有环境配置都会在这里进行,如果你使用bash,可以选择配置bash的配置
# 我们可以使用vim或者其他编辑器编辑

Tips:
~ 代表当前用户的根目录
.zshrc 是隐藏文件 在访达中按下 command + shift + . 可以显示隐藏文件

配置多环境JDK

如果你安装了比较多的版本JDK,想我一样(安装了zulujdk8,以及jdk17)

我们可以在~/.zshrc文件中这样配置

1
2
3
4
5
6
7
8
9
10
11
12
13
bash复制代码# Java
export JAVA_8_HOME=/Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home #这里配置你的jdk目录
export JAVA_17_HOME=/Library/Java/JavaVirtualMachines/jdk-17.0.1.jdk/Contents/Home

# 默认JDK1.8
export JAVA_HOME=$JAVA_8_HOME

# alias命令动态切换JDK版本
alias jdk8="export JAVA_HOME=$JAVA_8_HOME"
alias jdk17="export JAVA_HOME=$JAVA_17_HOME"

export CLASS_PATH=$JAVA_HOME/lib
export PATH=$PATH:$JAVA_HOME/bin

刷新一下配置

1
2
3
4
5
6
7
8
9
10
11
12
bash复制代码source ~/.zshrc
# 之后我们就可以通过命令切换环境
jdk8
java -version
openjdk version "1.8.0_312"
OpenJDK Runtime Environment (Zulu 8.58.0.13-CA-macos-aarch64) (build 1.8.0_312-b07)
OpenJDK 64-Bit Server VM (Zulu 8.58.0.13-CA-macos-aarch64) (build 25.312-b07, mixed mode)
jdk17
java -version
java version "17.0.1" 2021-10-19 LTS
Java(TM) SE Runtime Environment (build 17.0.1+12-LTS-39)
Java HotSpot(TM) 64-Bit Server VM (build 17.0.1+12-LTS-39, mixed mode, sharing)

安装IDEA

安装IDEA有个推荐的工具,JetBrains自家的Toolbox,有点像AdobeCloud也是帮助你下载JetBrains的工具的一个工具集管理应用。在上面我们可以很方便下载最新的IDEA以及更新。

image-20211109200158515

注意新版(2021.2.3以上)的试用需要你登陆JetBrains账号,无限使用的方法详情先看下面的地址

无限试用zhile.io/2020/11/18/…

安装Maven

打开终端,输入brew search maven

image-20211109210805871
输入brew install mavenbrew 就会帮你自动安装。

如果你不想使用homebrew安装,可以在maven官网,下载压缩包,解压之后。

在~/.zshrc文件中配置环境

1
2
3
bash复制代码# Maven
export MAVEN_HOME=/opt/maven/apache-maven-3.8.3 #这里为你的maven的解压目录
export PATH=$PATH:$MAVEN_HOME/bin

配置国内镜像

淘宝Maven镜像地址developer.aliyun.com/mvn/guide

找到maven根目录下的config/settings.xml文件夹,在<mirrors></mirrors>标签中添加 mirror 子节点

1
2
3
4
5
6
xml复制代码<mirror>
<id>aliyunmaven</id>
<mirrorOf>*</mirrorOf>
<name>阿里云公共仓库</name>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>

安装MySQL

使用Homebrew安装MySQL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
bash复制代码brew search mysql # 1.在终端中搜索mysql
brew install mysql # 2.下载mysql
brew info mysql # 3.查看下载的mysql信息(可选)

#info 返回消息 其中有一段
==> Caveats
We've installed your MySQL database without a root password. To secure it run:
mysql_secure_installation # 初始化密码

MySQL is configured to only allow connections from localhost by default

To connect run:
mysql -uroot # 连接mysql

To restart mysql after an upgrade:
brew services restart mysql #重启mysql
Or, if you don't want/need a background service you can just run:
/opt/homebrew/opt/mysql/bin/mysqld_safe --datadir=/opt/homebrew/var/mysql # 如果你不需要后台服务


brew start mysql # 4.启动并注册mysql
mysql -u root # 5.进入mysql初始没有密码 如果你初始化了密码再输入密码即可

exit # 6.退出mysql

安装Redis

安装Redis以及下面的Docker、Python,如果使用brew的话也很方便

1
2
3
4
5
bash复制代码brew search redis # 1.搜索redis 查看想要的版本
brew install redis # 2.安装redis
brew start redis # 3.启动并且注册redis
redis-cli -h localhost -p 6379 -a password # 4.连接redis
exit # 5.退出redis

安装Docker

安装docker我们可以使用Homebrew也可以在docker官网下载docker desktop应用安装

docker desktop 链接 www.docker.com/products/do…

使用brew安装docker

1
2
3
bash复制代码brew search docker # 1.搜索docker
brew install docker # 2.安装docker
docker -v # 3.检验docker

安装Python

macOS是自带python2的

1
2
3
4
5
6
7
8
9
10
11
bash复制代码sou7h@sou7hdeMacBook-Pro ~ % python

WARNING: Python 2.7 is not recommended.
This version is included in macOS for compatibility with legacy software.
Future versions of macOS will not include Python 2.7.
Instead, it is recommended that you transition to using 'python3' from within Terminal.

Python 2.7.16 (default, Aug 30 2021, 14:43:11)
[GCC Apple LLVM 12.0.5 (clang-1205.0.19.59.6) [+internal-os, ptrauth-isa=deploy on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>

安装python3我们也可以直接去python官网下载安装包安装,也可以直接用brew安装

python3官网 www.python.org/downloads/m…

使用brew安装

1
2
3
bash复制代码brew search python # 1.搜索python
brew install python # 2.安装python 直接下载python 会下载python3最新版本
python3 --version # 3.查看当前的python版本

安装Node/Npm

我实在记不清我电脑上的node是什么时候安装的了

但是我们依旧可以用brew安装

1
2
3
4
bash复制代码brew search node # 1.老规矩先搜索一下node
brew install node # 2.这样下载的是最新的node版本 指定版本根据名称 比如brew install node@14
node --version
npm -v # 3. 查看版本号

设置国内镜像

1
2
bash复制代码npm config set registry https://registry.npm.taobao.org
npm config get registry # 返回 https://registry.npm.taobao.org/

安装PicGo + Gitee + Typora 搭建个人图床(可选)

如果你有写作需求,搭建一个个人图床无疑非常方便。只需要三分钟,图片无烦恼!

  • 首先是下载PicGo

可以直接在上面推荐的网站中下载

也可以使用brew cask下载

  • 安装插件

安装picgo-plugin-github-plus插件

安装完成之后重启应用,就能在图床菜单中看到githubPlus

该插件适配于Github和Gitee

Tips:如果你没有下载成功,一直下载不了。你可以检查一下你的npm镜像,是否设置了淘宝镜像。

  • 创建gitee仓库

选择一个开源的仓库创建

如果你想,你也可以创建一个文件夹,例如img文件夹单独存放。

  • 配置私人令牌

打开右上角的设置

找到私人令牌的选项

点进去,选择生产新令牌

令牌生成后需要你记下来,它只会展示一次

  • 配置PicGo

找到GitHubPlus对应设置

image-20211102002136140

repo指的是你的仓库名称

branch指分支

token为刚刚生成的个人令牌

path为路径如果你的为根目录,那么不填即可

最后选择origin为gitee

  • 下载Typora

百度搜索Typora进入官网下载安装包

  • 设置Typora

打开Typora的偏好设置

image-20211102002945164

按照图片一步一步选择

最后第四步,点击验证图片上传选项,判断是否配置成功。

  • 结果

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

Netty 的服务启动流程1 Selector的创建 Ser

发表于 2021-11-12

这是我参与11月更文挑战的第10天,活动详情查看:2021最后一次更文挑战

今天我们来了解一下Netty里面服务启动的过程,使用Netty你只需要半个小时,但是了解它你却需要很长时间,没关系,我相信这是值得的。

Selector的创建

首先是对服务器的一个配置,先是初始化了两个EventLoopGroup,这里就使用的是主从Reactor模型了,Netty起了两个很好的名字bossGroup和workerGroup。

image.png

这里就是这个selector的创建了。
image.png

那selector是什么时候创建的呢?我们可以查看一下堆栈信息。

image.png

首先,服务是在52的位置启动,启动之后呢,初始化了这个NioEventLoopGroup,,然后到第142的位置的时候,初始化了一个NioEventLoop,之后Selector就被创建了。

ServerSocketChannel的创建

然后,绑定一个端口,开启服务,这步是一个同步过程,只有执行完,才能执行下一步。
image.png

这里进行校验,然后执行doBind方法,一般我们的doBind都是干活的方法,我们来看下。
image.png

这里有个初始化注册方法,我们点进去看下。
image.png

点进去之后呢是创建一个channel,我们看看创建之后有什么。
image.png

发现呢里面已经有一个ServerSocketChannel,就是说这个ServerSocketChannel已经被创建了,接着初始化这个Channel。
image.png

紧接着是注册,那么注册是干什么呢》注册就是把我们的这个ServerSocketChannel注册到我们的这个NioEventLoopGroup里面的这个Selector上。

image.png

注册这里面用到了一个选择,next是表示选择,里面有两种实现,先不讲了。
image.png

然后是register方法
image.png
然后,我们回到原来的方法,返回值是个Future,说明是个异步过程,写不完了有点。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

SQL优化及多数据库支持分享(二)

发表于 2021-11-12

「这是我参与11月更文挑战的第 10 天,活动详情查看:2021最后一次更文挑战」

1、前言

​ 上一次,我们说到了DB2环境的搭建,本次我们来分享下,如何把oracle中的表结构及数据同步到db2与mariadb中。
​ 刚开始,我拿到这个需求后也是很懵,有点无从下手。项目小组长给了我一个介绍db2与oracle有哪些不同的博客,然后就让我自己搞了了。整个项目有300多张表,挨个手工替换建表语句肯定是不现实的。后面通过百度也了解到了,阿里开源的datax等框架,但是datax并不支持oracle到db2的迁移。后来还是通过google,找到了一款SQL语句转换工具,SQLines (这个是线上版的,同时也有安装包) 现在我们就可以通过java程序批量将表结构转换成db2下的SQL,然后再写一个小工具先查出来,再插入到db2中即可。(这里也去找了些开源项目,但大多都好久没有维护了,调试运行起来太麻烦,干脆就自己简单写了下)

2、同步表结构及数据

​ 在这里我是先,把所有的表结构SQL语句导出到本地,然后批量转换成对应的DB2的建表语句。同步数据这里,因为就是要求简单快速,就也没有使用mybatis这些,我是简单写了一个小工具,这里推荐大家使用下hutool的数据库操作工具类,使用起来很简单。

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
java复制代码@Slf4j
public class ConvertSQLXML {

public static void main(String[] args) {
String pathPub = "D:\\data";
log.debug("加载SQL-INFO配置:" + pathPub);
List<String> paths = new ArrayList<String>();
getDependencyPath(paths, pathPub, (f, n) -> {
return n.endsWith("info.xml");
});
log.info("SQL:{}", paths);
paths.forEach(v -> {
try {
write(v);
} catch (IOException e) {
e.printStackTrace();
}
});

}

private static void write(String filePath) throws IOException {
//1、一行一行的读
File file = new File(filePath);
String outSrc = "C:\\data";
String outFileOraclePath = outSrc + File.separator + "oracle-" + file.getName();
String outFileMySQLPath = outSrc + File.separator + "mysql-" + file.getName();
String outFileDB2Path = outSrc + File.separator + "db2-" + file.getName();
File outFile = new File(outFileOraclePath);
File outFileMySQL = new File(outFileMySQLPath);
File outFileDB2 = new File(outFileDB2Path);
if (!outFile.exists()) {
outFile.createNewFile();//有路径才能创建文件
}
outFileMySQL.createNewFile();//有路径才能创建文件
outFileDB2.createNewFile();//有路径才能创建文件
BufferedReader reader = null;
BufferedWriter writer = null;
try {
writer = new BufferedWriter(new FileWriter(outFile, true));
reader = new BufferedReader(new FileReader(file));
String tempStr;
while ((tempStr = reader.readLine()) != null) {
if (StringUtils.isBlank(tempStr) || Pattern.matches(xml, tempStr) || Pattern.matches(DOCTYPE, tempStr)) {
continue;
}
if (Pattern.matches(sqlinfo, tempStr) || Pattern.matches(check, tempStr)
|| Pattern.matches(CDATA, tempStr) || Pattern.matches(sql, tempStr)
|| Pattern.matches(sp, tempStr)) {
writer.newLine();
continue;
}
writer.write(tempStr);
writer.newLine();
}
reader.close();
writer.flush();
writer.close();
convertOracleToOther(outFileOraclePath, outFileMySQLPath, "mysql");
convertOracleToOther(outFileOraclePath, outFileDB2Path, "db2");
} catch (IOException e) {
e.printStackTrace();
} finally {
reader.close();
writer.close();
}
}

private static void convertOracleToOther(String filePathSrc, String filePathOut, String targetType) {
//sqlines.exe -s=oracle -t=sql -in=script.sql
String cmd = "C:\\sqlines.exe";
try {
//执行exe cmd可以为字符串(exe存放路径)也可为数组,调用exe时需要传入参数时,可以传数组调用(参数有顺序要求)
new ProcessBuilder(cmd," -s=oracle"," -t=" + targetType, " -in=" + filePathSrc, " -out=" + filePathOut).start();
} catch (Exception e) {
e.printStackTrace();
}
}

private static void getDependencyPath(List<String> paths, String path, FilenameFilter filter){
File file =new File(path);
File[] files = file.listFiles();
String[] names = file.list();
if(names != null){
for(int i=0;i < names.length;i++){
if (filter.accept(null , names[i])) {
paths.add(path + File.separator + names[i]);
}
}
}
for (File a : files) {
if(a.isDirectory()){ //判断有子文件
getDependencyPath(paths, a.getAbsolutePath()+"\\", (f, n) -> {
return n.endsWith("info.xml");
});
}
}
}

}

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

🏆【Alibaba中间件技术系列】「RocketMQ技术专题

发表于 2021-11-12

这是我参与11月更文挑战的第12天,活动详情查看:2021最后一次更文挑战

前提背景

大家都知道,市面上有许多开源的MQ,例如,RocketMQ、Kafka、RabbitMQ等等,现在Pulsar也开始发光,今天我们谈谈笔者最常用的RocketMQ和Kafka,想必大家早就知道二者之间的特点以及区别,但是在实际场景中,二者的选取有可能会范迷惑,那么今天笔者就带领大家分析一下二者之间的区别,以及选取标准吧!

架构对比

RocketMQ的架构

RocketMQ由NameServer、Broker、Consumer、Producer组成,NameServer之间互不通信,Broker会向所有的nameServer注册,通过心跳判断broker是否存活,producer和consumer 通过nameserver就知道broker上有哪些topic。

Kafka的架构

Kafka的元数据信息都是保存在Zookeeper,新版本部分已经存放到了Kafka内部了,由Broker、Zookeeper、Producer、Consumer组成。

Broker对比

主从架构模型差异:

维度不同
  • Kafka的master/slave是基于partition(分区)维度的,而RocketMQ是基于Broker维度的;
+ Kafka的master/slave是可以切换的(主要依靠于Zookeeper的主备切换机制)
+ RocketMQ无法实现自动切换,当RocketMQ的Master宕机时,读能被路由到slave上,但写会被路由到此topic的其他Broker上。
刷盘机制

RocketMQ支持同步刷盘,也就是每次消息都等刷入磁盘后再返回,保证消息不丢失,但对吞吐量稍有影响。一般在主从结构下,选择异步双写策略是比较可靠的选择。

消息查询

RocketMQ支持消息查询,除了queue的offset外,还支持自定义key。RocketMQ对offset和key都做了索引,均是独立的索引文件。

消费失败重试与延迟消费

RocketMQ针对每个topic都定义了延迟队列,当消息消费失败时,会发回给Broker存入延迟队列中,每个消费者在启动时默认订阅延迟队列,这样消费失败的消息在一段时候后又能够重新消费。

  • 延迟时间与延迟级别一一对应,延迟时间是随失败次数逐渐增加的,最后一次间隔2小时。
  • 当然发送消息是也可以指定延迟级别,这样就能主动设置延迟消费,在一些特定场景下还是有作用的。
数据读写速度
  • Kafka每个partition独占一个目录,每个partition均有各自的数据文件.log,相当于一个topic有多个log文件。
  • RocketMQ是每个topic共享一个数据文件commitlog,

Kafka的topic一般有多个partition,所以Kafka的数据写入速度比RocketMQ高出一个量级。

但Kafka的分区数超过一定数量的文件同时写入,会导致原先的顺序写转为随机写,性能急剧下降,所以kafka的分区数量是有限制的。

随机和顺序读写的对比

  • 连续 / 随机 I/O(在底层硬盘维度)
+ 连续 I/O :指的是本次 I/O 给出的初始扇区地址和上一次 I/O 的结束扇区地址是完全连续或者相隔不多的。反之,如果相差很大,则算作一次随机 I/O。
  • 发生随机I/O可能是因为磁盘碎片导致磁盘空间不连续,或者当前block空间小于文件大小导致的。
连续 I/O 比随机 I/O 效率高的原因是
  • 连续 I/O,磁头几乎不用换道,或者换道的时间很短;
  • 随机 I/O,如果这个 I/O 很多的话,会导致磁头不停地换道,造成效率的极大降低。
随机和顺序速度比较

IOPS和吞吐量:为何随机是关注IOPS,顺序关注吞吐量?

  • 随机在每次IO操作的寻址时间和旋转延时都不能忽略不计,而这两个时间的存在也就限制了IOPS的大小;
  • 顺序读写可以忽略不计寻址时间和旋转延时,主要花费在数据传输的时间上。

IOPS来衡量一个IO系统性能的时候,要说明读写的方式以及单次IO的大小,因为读写方式会受到旋转时间和寻道时间影响,而单次IO会受到数据传输时间影响。

服务治理
  • Kafka用Zookeeper来做服务发现和治理,broker和consumer都会向其注册自身信息,同时订阅相应的znode,这样当有broker或者consumer宕机时能立刻感知,做相应的调整;
  • RocketMQ用自定义的nameServer做服务发现和治理,其实时性差点,比如如果broker宕机,producer和consumer不会实时感知到,需要等到下次更新broker集群时(最长30S)才能做相应调整,服务有个不可用的窗口期,但数据不会丢失,且能保证一致性。
+ 但是某个consumer宕机,broker会实时反馈给其他consumer,立即触发负载均衡,这样能一定程度上保证消息消费的实时性。

Producer差异

发送方式
  • kafka默认使用异步发送的形式,有一个memory buffer暂存消息,同时会将多个消息整合成一个数据包发送,这样能提高吞吐量,但对消息的实效有些影响;
  • RocketMQ可选择使用同步或者异步发送。
发送响应

Kafka的发送ack支持三种设置:

  • 消息存进memory buffer就返回(0);
  • 等到leader收到消息返回(1)
  • 等到leader和isr的follower都收到消息返回(-1)

上面也介绍了,Kafka都是异步刷盘

RocketMQ都需要等broker的响应确认,有同步刷盘,异步刷盘,同步双写,异步双写等策略,相比于Kafka多了一个同步刷盘。

Consumer差异

消息过滤
  • RocketMQ的queue和kafka的partition对应,但RocketMQ的topic还能更加细分,可对消息加tag,同时订阅时也可指定特定的tag来对消息做更进一步的过滤。
有序消息
  • RocketMQ支持全局有序和局部有序
  • Kafka也支持有序消息,但是如果某个broker宕机了,就不能在保证有序了。
消费确认

RocketMQ仅支持手动确认,也就是消费完一条消息ack+1,会定期向broker同步消费进度,或者在下一次pull时附带上offset。

Kafka支持定时确认,拉取到消息自动确认和手动确认,offset存在zookeeper上。

消费并行度

Kafka的消费者默认是单线程的,一个Consumer可以订阅一个或者多个Partition,一个Partition同一时间只能被一个消费者消费,也就是有多少个Partition就最多有多少个线程同时消费。

如分区数为10,那么最多10台机器来并行消费(每台机器只能开启一个线程),或者一台机器消费(10个线程并行消费)。即消费并行度和分区数一致。

RocketMQ消费并行度分两种情况:有序消费模式和并发消费模式,

  • 有序模式下,一个消费者也只存在一个线程消费,并行度同Kafka完全一致。
  • 并发模式下,每次拉取的消息按consumeMessageBatchMaxSize(默认1)拆分后分配给消费者线程池,消费者线程池min=20,max=64。也就是每个queue的并发度在20-64之间,一个topic有多个queue就相乘。所以rocketmq的并发度比Kafka高出一个量级。

并发消费方式并行度取决于Consumer的线程数,如Topic配置10个队列,10台机器消费,每台机器100个线程,那么并行度为1000。

事务消息

RocketMQ指定一定程度上的事务消息,当前开源版本删除了事务消息回查功能,事务机制稍微变得没有这么可靠了,不过阿里云的rocketmq支持可靠的事务消息;kafka不支持分布式事务消息。

Topic和Tag的区别?

业务是否相关联

  • 无直接关联的消息:淘宝交易消息,京东物流消息使用不同的 Topic 进行区分。
  • 交易消息,电器类订单、女装类订单、化妆品类订单的消息可以用Tag进行区分。

消息优先级是否一致:如同样是物流消息,盒马必须小时内送达,天猫超市 24 小时内送达,淘宝物流则相对会慢一些,不同优先级的消息用不同的 Topic 进行区分。

消息量级是否相当:有些业务消息虽然量小但是实时性要求高,如果跟某些万亿量级的消息使用同一个Topic,则有可能会因为过长的等待时间而“饿死”,此时需要将不同量级的消息进行拆分,使用不同的Topic。

Tag和Topic的选用

针对消息分类,您可以选择创建多个Topic,或者在同一个Topic下创建多个Tag。

不同的Topic之间的消息没有必然的联系。

Tag则用来区分同一个Topic下相互关联的消息,例如全集和子集的关系、流程先后的关系。

通过合理的使用 Topic 和 Tag,可以让业务结构清晰,更可以提高效率。

Tag怎么实现消息过滤

RocketMQ分布式消息队列的消息过滤方式有别于其它MQ中间件,是在Consumer端订阅消息时再做消息过滤的。

RocketMQ这么做是在于其Producer端写入消息和Consumer端订阅消息采用分离存储的机制来实现的,Consumer端订阅消息是需要通过ConsumeQueue这个消息消费的逻辑队列拿到一个索引,然后再从CommitLog里面读取真正的消息实体内容,所以说到底也是还绕不开其存储结构。

ConsumeQueue的存储结构:可以看到其中有8个字节存储的Message Tag的哈希值,基于Tag的消息过滤是基于这个字段值的。

Tag过滤方式
  • Consumer端在订阅消息时除了指定Topic还可以指定Tag,如果一个消息有多个Tag,可以用||分隔。
  • Consumer端会将这个订阅请求构建成一个SubscriptionData,发送一个Pull消息的请求给Broker端。
  • Broker端从RocketMQ的文件存储层—Store读取数据之前,会用这些数据先构建一个MessageFilter,然后传给Store。
  • Store从ConsumeQueue读取到一条记录后,会用它记录的消息tag hash值去做过滤,由于在服务端只是根据hashcode进行判断。

无法精确对tag原始字符串进行过滤,故在消息消费端拉取到消息后,还需要对消息的原始tag字符串进行比对,如果不同,则丢弃该消息,不进行消息消费。

Message Body过滤方式

向服务器上传一段Java代码,可以对消息做任意形式的过滤,甚至可以做Message Body的过滤拆分

数据消息的堆积能力

理论上Kafka要比RocketMQ的堆积能力更强,不过RocketMQ单机也可以支持亿级的消息堆积能力,我们认为这个堆积能力已经完全可以满足业务需求。

消息数据回溯

  • Kafka理论上可以按照Offset来回溯消息
  • RocketMQ支持按照时间来回溯消息,精度毫秒,例如从一天之前的某时某分某秒开始重新消费消息,典型业务场景如consumer做订单分析,但是由于程序逻辑或者依赖的系统发生故障等原因,导致今天消费的消息全部无效,需要重新从昨天零点开始消费,那么以时间为起点的消息重放功能对于业务非常有帮助。

性能对比

  • Kafka单机写入TPS约在百万条/秒,消息大小10个字节
  • RocketMQ单机写入TPS单实例约7万条/秒,单机部署3个Broker,可以跑到最高12万条/秒,消息大小10个字节。

数据一致性和实时性

消息投递实时性

  • Kafka使用短轮询方式,实时性取决于轮询间隔时间
  • RocketMQ使用长轮询,同Push方式实时性一致,消息的投递延时通常在几个毫秒。

消费失败重试

  • Kafka消费失败不支持重试
  • RocketMQ消费失败支持定时重试,每次重试间隔时间顺延

消息顺序

  • Kafka支持消息顺序,但是一台Broker宕机后,就会产生消息乱序
  • RocketMQ支持严格的消息顺序,在顺序消息场景下,一台Broker宕机后,发送消息会失败,但是不会乱序

Mysql Binlog分发需要严格的消息顺序

(题外话)Kafka没有的,RocketMQ独有的tag机制

普通消息、事务消息、定时(延时)消息、顺序消息,不同的消息类型使用不同的 Topic,无法通过Tag进行区分。

总结

  • RocketMQ定位于非日志的可靠消息传输(日志场景也OK),目前RocketMQ在阿里集团被广泛应用在订单,交易,充值,流计算,消息推送,日志流式处理,binglog分发等场景。
  • RocketMQ的同步刷盘在单机可靠性上比Kafka更高,不会因为操作系统Crash,导致数据丢失。
  • 同时同步Replication也比Kafka异步Replication更可靠,数据完全无单点。
  • 另外Kafka的Replication以topic为单位,支持主机宕机,备机自动切换,但是这里有个问题,由于是异步Replication,那么切换后会有数据丢失,同时Leader如果重启后,会与已经存在的Leader产生数据冲突。
  • 例如充值类应用,当前时刻调用运营商网关,充值失败,可能是对方压力过多,稍后在调用就会成功,如支付宝到银行扣款也是类似需求。这里的重试需要可靠的重试,即失败重试的消息不因为Consumer宕机导致丢失。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

【Spring 源码学习】Spring 源码环境搭建-43

发表于 2021-11-12

「这是我参与11月更文挑战的第12天,活动详情查看:2021最后一次更文挑战」。


一,前言

1
2
3
4
5
6
复制代码上一篇,使用 Spring 3.2.x 版本搭建了 Spring 源码环境
然而 3.2.x 版本 Spring 源码已经比较久远了,导入代码后很多错误都需要手工处理
甚至于连版本依赖的 jar 版本都需要重新修改才能 build 成功
搭建源码环境的初衷是为了学习源码,Spring 框架源码的核心主要是 AOP 和 IOC
而这两部分在版本迭代中基本稳定,版本升级对于学习框架本身并没有太大影响
所以,这次选用 Spring 4.x 源码重新搭建 Spring 源码环境

二,下载源码

这部分就不再赘述了,到 GitHub 直接下载对应版本的 Spring 源代码即可:

github.com/spring-proj…


三,构建工程

1,下载源码后,查看根目录下 README.md 文件:

README.md

2。执行 ./import-into-eclipse.sh (要求 JDK8)

import-into-eclipse

其中,提到了 STS - 一个基于 eclipse 的 Spring IDE 工具:

  • Eclipse downloads: download.eclipse.org/eclipse/dow…
  • STS downloads: spring.io/tools/sts/a…
  • STS nightly builds: dist.springsource.com/snapshot/ST…

耐心等待一段时间,执行步骤1,大约 15 分钟:

step1

步骤1成功后,继续执行步骤2:

step1-success

步骤 2、3、4、5 一样,直接下一步就好:

step3

step4

执行./gradlew install(约13分钟)

gradlew install

执行./gradlew build(约10分钟)

gradlew build


四,下载并安装STS(spring-tool-suite)

1
2
复制代码Spring 提供了一个基于 eclipse 的 Spring IDE,简称STS;
使用这个 IDE 在学习 Spring 源码时将会带来很多便利;

下载并安装STS:spring.io/tools/sts/a…

STS下载

下载完成后安装并启动STS:

启动STS


五,导入工程

导入spring源码工程:

导入spring源码工程

此时项目会有报错,因为Spring构建需要groovy

六,安装 groovy

安装groovy:
http://groovy-lang.org/download.html

brew安装:

Brave:~ Brave$ brew install groovy

查看版本号:

1
2
ruby复制代码Brave:~ Brave$ groovy -v
Groovy Version: 2.4.12 JVM: 1.8.0_51 Vendor: Oracle Corporation OS: Mac OS X

此时,eclipse 下的 Spring 项目依然报错,原因是 eclipse 需要安装对应版本的 groovy 插件;

注意:groovy 插件需要与 eclipse 版本相匹配,否则无效;

groovy报错


七,安装 eclipse-groovy 插件

安装插件:eclipse 的 Help -> Install New Software 中,下载 groovy 插件

STS 版本 3.9.2 基于 eclipse4.7.2 版本,需下载对应版本的 groovy 插件(4.7版本)

eclipse版本

  • Spring Tool Suite:spring.io/tools/sts/a…
  • Groovy/Grails Tool Suite:spring.io/tools/ggts/…

4.7 版本 Groovy 插件地址:
dist.springsource.org/release/GRE…

一般来讲,安装 groovy 插件后重启 eclipse -> clean 项目后问题解决;
但有时候还会继续报错,这可能是由于 groovy 版本导致的编译问题;


八,Groovy 编译版本问题

Groovy 的编译版本问题,会有下面的报错:

groovy编译问题

修改 eclipse 配置中 groovy compiler 版本:

修改Eclipse-groovy编译版本

转换后要求重启 eclipse,重启后问题解决;


九,Spring 4.3.6 源码编译完成

编译完成截图如下:

Spring源码编译完成

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

1…359360361…956

开发者博客

9558 日志
1953 标签
RSS
© 2025 开发者博客
本站总访问量次
由 Hexo 强力驱动
|
主题 — NexT.Muse v5.1.4
0%