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

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


  • 首页

  • 归档

  • 搜索

Nacos注册中心使用实战!Spring Cloud集成Na

发表于 2021-11-24

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

基本概念

  • 通过Nacos Server和spring-cloud-starter-alibaba-nacos-config实现配置的动态变更
  • 通过Nacos Server和spring-cloud-starter-alibaba-nacos-discovery实现服务的注册与发现

配置

Nacos config

  • Nacos提供用于存储配置和其余元数据的key-value存储,为分布式系统中的外部化配置提供服务器端和客户端支持
  • 使用Spring Cloud Alibaba Nacos Config, 可以在Nacos Server集中管理你Spring Cloud应用的外部属性配置
  • 初始化Nacos:
    • 启动Nacos, 然后在Nacos添加配置:
1
2
3
4
5
nacos复制代码Data ID:nacos-config.properties
Group:DEFAULT_GROUP
配置格式:Properties
配置内容:user.name=nacos-config-properties
user.age=90
  • 其中dataid是以默认的文件扩展名方式properties为扩展名
  • 配置:
+ 引入**nacos config**依赖
1
2
3
4
xml复制代码<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
  • 使用bootstrap.properties配置文件来配置Nacos Server地址:
1
2
properties复制代码spring.application.name=nacos-config
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
  • 如果使用域名的方式来访问nacos时 ,spring.cloud.nacos.config.server-addr配置的方式为 [域名:port]
    • 比如nacos的域名为abc.com.nacos, 监听的端口为80. 则配置为spring.cloud.nacos.config.server-addr=abc.com.nacos:80. 端口号80不能省略
  • 启动:
1
2
3
4
5
6
7
8
9
10
java复制代码@SpringBootApplication
public class ProviderApplication {

public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(ProviderApplication.class, args);
String userName = applicationContext.getEnvironment().getProperty("user.name");
String userAge = applicationContext.getEnvironment().getProperty("user.age");
System.err.println("user name :"+userName+"; age: "+userAge);
}
}
  • 输出:
1
console复制代码user name :nacos-config-properties; age: 90

yaml文件配置

  • spring-cloud-starter-alibaba-nacos-config支持yaml格式
  • 在应用的bootstrap.properties配置文件中显示的声明dataid文件扩展名:
1
properties复制代码spring.cloud.nacos.config.file-extension=yaml
  • 初始化Nacos,添加一个dataid为yaml扩展名的配置:
1
2
3
4
5
nacos复制代码Data ID:nacos-config.yaml
Group:DEFAULT_GROUP
配置格式:YAML
配置内容:user.name:nacos-config-yaml
user.age:68
  • 启动:
1
2
3
4
5
6
7
8
9
10
java复制代码@SpringBootApplication
public class ProviderApplication {

public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(ProviderApplication.class, args);
String userName = applicationContext.getEnvironment().getProperty("user.name");
String userAge = applicationContext.getEnvironment().getProperty("user.age");
System.err.println("user name :"+userName+"; age: "+userAge);
}
}
  • 输出:
1
console复制代码user name :nacos-config-yaml; age: 68

配置动态更新

  • spring-cloud-starter-alibaba-nacos-config支持配置的动态更新
1
2
3
4
5
6
7
8
9
10
11
12
13
java复制代码@SpringBootApplication
public class ProviderApplication {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(ProviderApplication.class, args);
while(true) {
//当动态配置刷新时,会更新到 Enviroment中,因此这里每隔一秒中从Enviroment中获取配置
String userName = applicationContext.getEnvironment().getProperty("user.name");
String userAge = applicationContext.getEnvironment().getProperty("user.age");
System.err.println("user name :" + userName + "; age: " + userAge);
TimeUnit.SECONDS.sleep(1);
}
}
}
  • 可以在配置文件中关闭动态更新:
1
properties复制代码spring.cloud.nacos.config.refresh.enabled=false

配置profiles粒度

  • spring-cloud-starter-alibaba-nacos-config加载配置时:
    • 不仅仅加载以dataid为 [spring.application.name].[file-extension:properties] 为前缀的基础配置
    • 还加载dataid为 [spring.application.name]-[profile].[file-extension:properties] 的基础配置
  • 多环境下的不同配置,可以通过Spring提供的spring.profiles.active这个配置项来配置:
1
2
properties复制代码# spring.profiles.active当通过配置文件来指定时必须放在bootstrap.properties文件中
spring.profiles.active=develop
  • 初始化Nacos配置:
1
2
3
4
nacos复制代码Data ID:nacos-config-develop.yaml
Group:DEFAULT_GROUP
配置格式:YAML
配置内容:current.env:develop-env
  • 启动:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
java复制代码@SpringBootApplication
public class ProviderApplication {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(ProviderApplication.class, args);
while(true) {
String userName = applicationContext.getEnvironment().getProperty("user.name");
String userAge = applicationContext.getEnvironment().getProperty("user.age");
//获取当前部署的环境
String currentEnv = applicationContext.getEnvironment().getProperty("current.env");
System.err.println("in "+currentEnv+" enviroment; "+"user name :" + userName + "; age: " + userAge);
TimeUnit.SECONDS.sleep(1);
}
}
}
  • 输出:
1
console复制代码in develop-env enviroment; user name :nacos-config-yaml-update; age: 68
  • 如果需要切换到生产环境,只需要更改spring.profiles.active参数配置:
1
properties复制代码spring.profiles.active=product
  • 在项目实践中这个变量的值是需要不同环境而有不同的值,通常的做法是通过-Dspring.profiles.active=< profile >参数指定其配置来达到环境间灵活的切换

配置自定义namespace

  • Nacos中的namespace:
    • namespace用于进行租户粒度的配置隔离
    • 不同的命名空间下,可以存在相同的Group或Data ID的配置
    • namespace常常用于不同环境的配置的区分隔离
  • 在没有明确指定spring.cloud.nacos.config.namespace配置时,默认使用的是Nacos上Public这个namespae. 可以通过配置自定义的命名空间:
1
properties复制代码spring.cloud.nacos.config.namespace=b3404bc0-d7dc-4855-b519-570ed34b62d7
  • 该配置必须放在bootstrap.properties文件中,此外spring.cloud.nacos.config.namespace的值是namespace对应的id,id值可以在Nacos的控制台获取.并且在添加配置时注意不要选择其余的namespace, 否则将会导致读取不到正确的配置

配置自定义Group

  • 在没有明确指定spring.cloud.nacos.config.group配置的情况下,默认使用的是DEFAULT_GROUP. 可以通过配置自定义Group:
1
properties复制代码spring.cloud.nacos.config.group=DEVELOP_GROUP
  • 该配置必须放在bootstrap.properties文件中,并且在添加配置时Group的值一定要和 spring.cloud.nacos.config.group的配置值一致

配置自定义Data Id

  • Spring Cloud Alibaba Nacos Config支持自定义Data Id的配置,示例如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
properties复制代码spring.application.name=opensource-service-provider
spring.cloud.nacos.config.server-addr=127.0.0.1:8848

# config external configuration
# 1、Data Id 在默认的组 DEFAULT_GROUP,不支持配置的动态刷新
spring.cloud.nacos.config.extension-configs[0].data-id=ext-config-common01.properties

# 2、Data Id 不在默认的组,不支持动态刷新
spring.cloud.nacos.config.extension-configs[1].data-id=ext-config-common02.properties
spring.cloud.nacos.config.extension-configs[1].group=GLOBALE_GROUP

# 3、Data Id 既不在默认的组,也支持动态刷新
spring.cloud.nacos.config.extension-configs[2].data-id=ext-config-common03.properties
spring.cloud.nacos.config.extension-configs[2].group=REFRESH_GROUP
spring.cloud.nacos.config.extension-configs[2].refresh=true
  • spring.cloud.nacos.config.extension-configs[n].data-id配置支持多个Data Id的配置
  • spring.cloud.nacos.config.extension-configs[n].group配置自定义Data Id所在的组,不明确配置的话,默认是DEFAULT_GROUP

本文转载自: 掘金

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

Ribbon 心跳检测的原理及常用配置项

发表于 2021-11-24

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

你好,我是悟空呀。

Ribbon 的心跳检测原理和 Eureka 还不一样,Ribbon 不是通过每个服务向 Ribbon 发送心跳或者 Ribbon 给每个服务发送心跳来检测服务是否存活的。

先来一张图看下 Ribbon 的心跳检测机制:

Ribbon 心跳检测的原理

Ribbon 心跳检测原理:对自己本地缓存的 Server List 进行遍历,看下每个服务的状态是不是 UP 的。具体的代码就是 isAlive 方法。

核心代码:

1
JAVA复制代码isAlive = status.equals(InstanceStatus.UP);

那么多久检测一次呢?

默认每隔 30s 执行以下 PingTask 调度任务,对每个服务执行 isAlive 方法,判断下状态。

Ribbon 常用配置项

禁用 Eureka

1
2
properties复制代码# 禁用 Eureka
ribbon.eureka.enabled=false

服务注册列表默认是从 Eureka 获取到的,如果不想使用 Eureka,可以禁用掉。然后我们需要手动配置服务列表。

配置服务列表

1
properties复制代码ribbon-config-passjava.ribbon.listOfServers=localhost:8081,localhost:8083

这个配置是针对具体服务的,前缀就是服务名称,配置完之后就可以和之前一样使用服务名称来调用接口了。

其他配置项

Spring Cloud 微服务中 负载均衡组件 Ribbon 架构原理,分为几大块,都在前面的文章进行了讲解。分为几大块:

  • Ribbon 的六大核心组件
  • Ribbon 如何拦截请求并进行转发的。
  • Ribbon 初始化的原理。
  • Ribbon 如何同步 Eureka 注册表的原理。
  • Eureka 和 Ribbon 两种 心跳检测的原理
  • Ribbon 的常用配置项。

作者简介:悟空,8年一线互联网开发和架构经验,用故事讲解分布式、架构设计、Java 核心技术。《JVM性能优化实战》专栏作者,开源了《Spring Cloud 实战 PassJava》项目,公众号:悟空聊架构。本文已收录至 www.passjava.cn

本文转载自: 掘金

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

网络协议——GVRP、VCMP、VTP、DTP(全网最完整的

发表于 2021-11-24

本文主要了解和学习华为私有协议GVRP、VCMP与思科私有协议VTP、DTP


一、GVRP

了解GVRP之前先了解一下GARP

1、GARP(Generic Attribute Registration Protocol)

通用属性注册协议

主要用于一种属性传递扩散的机制

为处于同一个交换网的交换机提供了一种分发,传播,注册的方式

2、GVRP (Generic VLAN Redistration Protocol)

GARP的一种高级应用,用于注册和注销VLAN属性

通过GVRP协议,一台设备上的VLAN信息会迅速传播到整个交换网

GVRP实现动态分发,注册和传播VLAN属性,从而达到网络管理员的手工配置量及保证VLAN配置正确的目的

3、GVRP的注册模式

Normal模式,默认是Normal模式,允许动态和静态VLAN注册,同时会发送静态vlan和动态vlan的申明信息

Fixed模式,比如某一个端口为Fixed模式,不允许动态vlan注册个注销,且只发送静态vlan的申明信息

Forbidden模式,比如某一个端口为Forbbid模式,不允许动态vlan在端口上进行注册,同时删除端口上除了vlan1以外的所有vlan,只发送vlan1的申明信息

4、配置案例

在这里插入图片描述

配置思路:
&全局开启GVRP
&配置链路模式并允许所有vlan通过
&端口开启GVRP

SW1

1
2
3
4
5
6
7
8
9
c复制代码<Huawei>system-view 
[Huawei]sysname sw1
[sw1]gvrp \全局开启GVRP
Info: GVRP has been enabled.
[sw1]interface GigabitEthernet 0/0/1
[sw1-GigabitEthernet0/0/1]port link-type trunk \配置链路为trunk
[sw1-GigabitEthernet0/0/1]port trunk allow-pass vlan all\允许所有vlan通过
[sw1-GigabitEthernet0/0/1]gvrp \端口开启GVRP
sw1-GigabitEthernet0/0/1]quit

SW2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
c复制代码<Huawei>system-view
[Huawei]sysname sw2
[sw2]gvrp
Info: GVRP has been enabled.
[sw2]interface GigabitEthernet 0/0/1
[sw2-GigabitEthernet0/0/1]port link-type trunk
[sw2-GigabitEthernet0/0/1]port trunk allow-pass vlan all
[sw2-GigabitEthernet0/0/1]gvrp
Info: GVRP has been enabled.
[sw2-GigabitEthernet0/0/1]quit
[sw2]interface GigabitEthernet 0/0/2
[sw2-GigabitEthernet0/0/2]port link-type trunk
[sw2-GigabitEthernet0/0/2]port trunk allow-pass vlan all
[sw2-GigabitEthernet0/0/2]gvrp
Info: GVRP has been enabled.
[sw2-GigabitEthernet0/0/2]quit

SW3

1
2
3
4
5
6
7
8
9
10
javascript复制代码<Huawei>system-view
[Huawei]sysname sw3
[sw3]gvrp
Info: GVRP has been enabled.
[sw3]interface GigabitEthernet 0/0/1
[sw3-GigabitEthernet0/0/1]port link-type trunk
[sw3-GigabitEthernet0/0/1]port trunk allow-pass vlan all
[sw3-GigabitEthernet0/0/1]gvrp
Info: GVRP has been enabled.
[sw3-GigabitEthernet0/0/1]quit

创建vlan

1
javascript复制代码[sw1]vlan batch 5 10 50           \创建vlan 5 10 50

验证,测试

SW1
在这里插入图片描述

SW2
在这里插入图片描述

SW3
在这里插入图片描述

5、实验总结

在配置过程中,一定要在全局开启GVRP,再在接口开启,否则学习不到。
转载于: 51CTO

二、VCMP

1、VCMP(VLAN Central Management Protocol)

VLAN集中管理协议VCMP

可以实现VLAN的集中维护和管理

VCMP是华为的私有协议,工作于链路层,提供了一种在二层网络中传播VLAN配置信息,从而保证整个二层网络中VLAN配置信息一致

相较于手工配置,VCMP具有维护工作量小、VLAN配置一致的优点

2、管理域

VCMP使用域来管理交换机,这个域就称为VCMP管理域

通过角色定义来确定设备的属性,称为VCMP的角色

VCMP共定义了Server、Client、Transparent和Silent四种角色。

在这里插入图片描述
VCMP管理域

VCMP管理域由一组域名相同的交换机通过Trunk或Hybrid链路类型的接口互连构成。同一域内的每台交换机都必须使用相同的域名,且一台交换机只能加入一个VCMP管理域,不同域的交换机间不能同步VLAN信息。

VCMP管理域确定了VCMP管理设备的范围,凡是加入域的交换机,均会受到域内管理设备的管理。域中只能有一台管理设备,但可以有多台被管理设备。

3、角色

在这里插入图片描述
Transparent和Silent不属于任何VCMP管理域

VCMP管理域的边缘设备如果希望受VCMP的管理,也可设置为Client角色,但为防止本域的VCMP报文传输到其他域中,需要将连接其他域的接口去使能VCMP功能

4、应用场景

在这里插入图片描述
某企业有部门A和部门B两个部门,分别属于不同二层网络,各部门规模较大,需要配置和维护的VLAN信息很多。为了方便VLAN的配置和维护,可在部门A和部门B内分别部署VCMP,管理域分别为VCMP1和VCMP2,并选择汇聚交换机AGG1作为VCMP1的Sever,接入交换机ACC1~ACC2作为VCMP1的Client,汇聚交换机AGG2作为VCMP2的Server,接入交换机ACC3~ACC4作为VCMP2的Client。这样,网络管理员只需分别在AGG1和AGG2上创建、删除VLAN或修改VLAN的名称、描述,ACC1~ACC2和ACC3~ACC4会分别同步AGG1和AGG2上的VLAN信息,实现了VLAN的统一配置和管理。
同时,为免去手工设置链路类型的麻烦,配置通过LNP自动协商链路类型。

采用如下的思路配置VCMP:

配置LNP,实现链路类型自动协商,简化用户配置。

指定各设备的角色,以确定VCMP管理范围、管理与被管理对象。

在角色为Server和Client的设备上分别配置VCMP相关参数,包括认证密码、设备ID等,以保证Server和Client间能安全通信和身份识别。

使能VCMP,使VCMP功能生效。

5、VCMP协议报文

VCMP通过在各角色设备间交互VCMP报文实现VLAN的集中管理,VCMP报文只能在Trunk或Hybrid类型接口的VLAN 1上传输。为确保在各种场景下Server与Client的VLAN信息保持一致,VCMP协议定义了Summary-Advert、Subset-Advert和Advert-Request三种组播方式的报文。三种报文的作用及触发场景
在这里插入图片描述

其中,由Server发送的Summary-Advert和Subset-Advert报文会携带配置修订号。配置修订号用来确定Server发送的VLAN信息是否比当前的更新,Client使用它来判断是否需要同步Server的VLAN信息。它以8位十六进制数体现,高四位用来标识VCMP管理域或设备ID的变更,低四位用来标识VLAN的变更。只要Server有VLAN变更,配置修订号就会自动递增。而当VCMP管理域名或设备ID变更时,配置修订号的高四位会重新计算,低四位会清零。

6、实现机制

Server上配置变更的VLAN同步机制

当Server上的配置变更(包括创建、删除VLAN,修改VLAN的名称、描述,VCMP管理域名、设备ID修改,以及Server重启等情况)时,Server会发送携带变更信息的Summary-Advert和Subset-Advert报文,以通告VCMP管理域内的Client进行同步。

新增Client的VLAN同步机制

为确保Server与Client上的VLAN信息的同步,Server每5分钟发送一次Summary-Advert报文,向全域通告VCMP管理域名、设备ID和配置修订号,Server还会发送Subset-Advert报文来通告发生变更的VLAN名称和VLAN描述。当新加入一台Client或Client重启时,为了及时获取Server上的VLAN配置信息,新Client和重启的Client会发送Advert-Request组播报文,请求Server的VLAN配置信息。

多Server告警机制

VCMP管理域内只能有一台Server。为防止用户假冒Server攻击网络,Server在收到Summary-Advert报文后,会将报文中的VCMP管理域名、设备ID、源MAC地址与本地的进行匹配。如果VCMP管理域名和设备ID匹配,但报文中的源MAC地址与本地的系统MAC地址不同,则会向网管发送“多Server”事件告警。

为了防止告警太多影响Server性能,VCMP抑制告警的发送次数,每30分钟向网管发送一次告警。

VCMP认证机制

未知交换机加入VCMP管理域,可能会将其设备上的VLAN信息同步到域内,进而影响域内网络的稳定。为防止未知交换机加入,使VCMP管理域更安全,可以为域中的Server和Client配置域认证密码。

如果Server或Client配置了域认证密码,则用该密码字符串(默认使用空字符串)作为Key值,对报文中的VCMP管理域名、设备ID等字段进行SHA-256摘要计算,并把得到的摘要信息随Summary-Advert报文、Subset-Advert报文或Advert-Request报文发送。域内每台Client在收到的Server的Summary-Advert或Subset-Advert报文时,则用本地配置的认证密码对报文中的VCMP管理域名、设备ID和配置修订号等字段进行SHA-256摘要计算,并把得到的摘要信息与报文中携带的摘要信息进行比较。如果匹配,则认证通过,进行后续VCMP处理;否则,丢弃该Summary-Advert或Subset-Advert报文。Server收到Client的Advert-Request报文时进行同样的认证处理。

如果未配置域密码,则直接认证通过。
转载于: 华为

三、VTP

1、VTP(Vlan Trunk Protocol)

VLAN 中继协议 (VTP) 允许网络管理员在配置为 VTP 服务器的交换机上管理 VLAN

VTP 服务器会分配中继链路中的 VLAN 信息,并将其与整个交换网络中启用 VTP 的交换机同步

这可以最大程度地减少因配置错误和配置不一致而导致的问题

2、VTP组件

在这里插入图片描述

3、VTP模式

在这里插入图片描述

4、配置案例

在这里插入图片描述
交换机 S1 为 VTP 服务器,而 S2 和 S3 为客户端

VTP 的配置步骤:

第 1 步:配置 VTP 服务器
在这里插入图片描述

第 2 步:配置 VTP 域名和密码
在这里插入图片描述

第 3 步:配置 VTP 客户端
在这里插入图片描述

第 4 步:在 VTP 服务器上配置 VLAN
在这里插入图片描述

第 5 步:验证 VTP 客户端是否获得了新的 VLAN 信息
在这里插入图片描述

创建 VLAN

在这里插入图片描述

为 VLAN 分配端口

在这里插入图片描述

验证 VLAN 信息

在这里插入图片描述

四、DTP

1、DTP(Dynamic Trunk Protocol)

动态中继协议 ,Cisco 私有协议

以太网 TRUNK 接口支持不同的中继模式

接口可以设置为中继或非中继,或者与相邻接口协商中继

TRUNK 协商由动态中继协议 (DTP) 管理,它仅在网络设备之间点对点地进行操作
在这里插入图片描述
在这里插入图片描述

2、协商接口模式

在这里插入图片描述
switchport mode access - 将接口(接入端口)置于永久非中继模式,并协商将链路转换成非中继链路。不管相邻接口是否为 TRUNK 接口,该接口都会变为非 TRUNK 接口。

switchport mode dynamic auto - 使接口能够将链路转换为中继链路。当相邻接口设置为 trunk 或 desirable 模式时,该接口将变为 TRUNK 接口。所有以太网接口的默认 switchport 模式为 dynamic auto。

switchport mode dynamic desirable - 使接口主动尝试将链路转换为中继链路。当相邻接口设置为中继、期望或动态自动模式时,该接口将变为中继接口。这是老式交换机的默认 switchport 模式,例如 Catalyst 2950 和 3550 系列交换机。

switchport mode trunk - 中继模式会将接口置为永久中继模式,并协商将相邻链路转换为中继链路。即使相邻端口不是 TRUNK 接口,该接口也会变为 TRUNK 接口。

switchport nonegotiate - 可防止接口生成 DTP 帧。只有当接口的 switchport 模式是 access 或 trunk 时,可以使用此命令。您必须手动将相邻接口配置为中继接口,才能建立中继链路。

3、预测DTP行为

在这里插入图片描述

总结

管理VLAN:GVRP、VCMP、VTP
管理链路模式:DTP

本文转载自: 掘金

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

网络协议——SNMP网络管理协议 一、SNMP 二、MIB

发表于 2021-11-24

本文正在参与 “网络协议必知必会”征文活动

甲:“你学什么专业的?”乙:“路由器交换机相关。”甲:“那好啊!从事调试还是维修?”甲:“搬运!”乙:“……”

在这里插入图片描述

@TOC

一、SNMP

SNMP:Simple Network Management Protocol,简单网络管理协议。

  • 广泛应用于TCP/IP网络的一种网络管理协议。
  • 支持管理Internet上众多厂家生产的软硬件平台。
  • 用来在网络管理工作站(NMS)和被管理设备之间传输管理信息

1、网络管理工作站(NMS)

在这里插入图片描述
在这里插入图片描述

2、SNMP架构

在这里插入图片描述
在这里插入图片描述

3、版本

在这里插入图片描述

在这里插入图片描述

4、SNMP数据包

在这里插入图片描述

二、MIB

MIB:Management Information Base,管理信息库

  • 是一个被管理对象的集合,是NMS同Agent进行沟通的桥梁。
  • 使网管软件和设备进行标准对接。
  • 每一个Agent都维护一个MIB库,NMS可以对MIB库中对象的值进行读取或设置。
  • MIB以树状结构进行存储,树的叶子节点表示管理对象,它可以通过从根节点开始的一条惟一路径来识别,这也就是OID(Object Identifier)。

MIB对象及节点
在这里插入图片描述)在这里插入图片描述
OID值
在这里插入图片描述

三、拓扑及配置

MIB Broeser收集交换机S5700上的所有信息

1、实验拓扑

在这里插入图片描述

2、配置

Cloud
在这里插入图片描述
交换机基础配置

1
2
3
4
javascript复制代码sys
sys S1
int vlan1
ip add 192.168.0.11 24

测试交换与电脑的连通性
在这里插入图片描述
在这里插入图片描述
连通性正常

SNMP配置

1
2
3
4
5
6
javascript复制代码snmp-agent                                       #开启SNMP Agent
snmp-agent sys-info version v2c #配置SNMP版本
snmp-agent sys-info contact libai QQ:724284659 #配置管理员联系方式
snmp-agent sys-info location QHNU #配置管理员位置
snmp-agent community read libai #配置读团体名
snmp-agent community write huawei #配置写团体名

四、MIB Broeser

在这里插入图片描述

MIB Browser是来自ManageEngine的一款免费的MIB浏览器工具,用于监控采用SNMP协议的网络设备和服务器。利用MIB浏览器可以加载查看设备的MIB,执行GET、GETNEXT和SET SNMP操作。

1、执行工具

在这里插入图片描述
填入交换机管地址、SNMP协议号、读写团体名、SNMP版本

2、wireshark抓包

在这里插入图片描述
在这里插入图片描述

SNMP协议版本、Get-Next-Request数据包(NMS从Agent的MIB中按照字典式排序提取下一个参数值)

3、MIB Broeser收集信息

抓取到了交换机上配置的SNMP基本信息
在这里插入图片描述
在这里插入图片描述
路由表

在这里插入图片描述

在这里插入图片描述
接口信息、MAC地址均可查看

==由于这个工具是免费白嫖的,所有功能有限。如果条件满足买企业级管理软件可实现信息可视化。==

著名的网络管理软件:CACTI、WhatsUp Gold 、Zabbix、PRTG

本文转载自: 掘金

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

一文搞懂RPC组成结构,与Restful区别 1、什么是RP

发表于 2021-11-24

1、什么是RPC?

RPC(Remote Procedure Call),远程过程调用,是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。通俗地讲,就是开发者能够像调用本地方法一样调用远程的服务。RPC的作用主要体现在两个方面:

  1. 屏蔽远程调用跟本地调用的区别,让我们感觉就是调用项目内的方法;
  2. 隐藏底层网络通信的复杂性,让我们更专注于业务逻辑。

两个或多个应用程序都分布在不同的服务器上,它们之间的调用都像是本地方法调用一样。比较知名开源的RPC框架有阿里的Dubbo、google的gRPC、Go语言的rpcx、Apache的thrift等。
要实现向调用本地服务一样调用远程方法可能需要涉及的知识有:

Image.jpg

  • 动态代理
  • 反射
  • 序列化、反序列化
  • 网络通信
  • 编解码
  • NIO
  • 服务发现和注册
  • 心跳与链路检测
  • 负载均衡
  • ……

2、RPC 框架基本架构

Image.png
从图上可以看出,RPC 框架主要由三部分构成:

  1. 注册中心,服务端在启动后,会将它提供的服务列表发布到注册中心;客户端向注册中心订阅服务地址。
  2. 客户端:通过本地代理模块 Proxy 调用服务端,Proxy 模块收到负责将方法、参数等数据编码转化成网络字节流;从服务列表中选取其中一个的服务地址,并将数据通过网络发送给服务端
  3. 服务端:接收到数据进行解码,得到请求信息;根据解码后的请求信息调用对应的服务,然后将调用结果返回给客户端;服务端和客户端的编解码是逆向的。

3、各组件的职责

3.1 一次 RPC 调用流程:

一次 RPC 调用流程.png

  1. 服务消费者(Client 客户端)通过本地调用的方式调用服务。
  2. 客户端存根(Client Stub)接收到调用请求后负责将方法、入参等信息序列化(组装)成能够进行网络传输的消息体。
  3. 客户端存根(Client Stub)找到远程的服务地址,并且将消息通过网络发送给服务端。
  4. 服务端存根(Server Stub)收到消息后进行解码(反序列化操作)。
  5. 服务端存根(Server Stub)根据解码结果调用本地的服务进行相关处理
  6. 服务端(Server)本地服务业务处理。
  7. 处理结果返回给服务端存根(Server Stub)。
  8. 服务端存根(Server Stub)序列化结果。
  9. 服务端存根(Server Stub)将结果通过网络发送至消费方。
  10. 客户端存根(Client Stub)接收到消息,并进行解码(反序列化)。
  11. 服务消费方得到最终结果。

3.2 注册中心

注册中心的存在是为了更好更方便的管理应用中的每一个服务,是各个分布式节点之间的纽带。注册中心主要提供以下核心功能:

Image.gif

  • 服务注册与发现:动态的增减服务节点,服务节点增减后动态的通知服务消费者,而不需要由消费者来更新配置。服务的实例列表发生变化(新增或者移除)时,通知订阅该服务的 Consumer,从而让 Consumer 能够刷新本地缓存。
  • 服务配置:动态修改服务配置,并将其推送到服务提供者和服务消费者而不需要重启服务。
  • 健康检查和服务摘除:主动的检查服务健康情况,对于宕机的服务将其摘除服务列表,Server超过一定时间未心跳时,从服务的实例列表移除.客户端长时间没有心跳也可以关闭连接。

3.2.1 CAP理论

CAP理论是分布式架构中重要理论

  • 一致性(Consistency) (所有节点在同一时间具有相同的数据)
  • 可用性(Availability) (保证每个请求不管成功或者失败都有响应)
  • 分隔容忍(Partition tolerance) (系统中任意信息的丢失或失败不会影响系统的继续运作)

由于C与A的特性无法共存.CAP 不可能都取,只能取其中2个,要么AP要么CP

3.2.2 微服务架构中常见的注册中心

Zookeeper
Zoopkeeper 在国内很长一段时间都是注册中心一哥.大部分是因为Dubbo 在国能的盛行.

Eureka
Eureka是一家在线影片租赁提供商Netflix开源的, 这家公司的理念还是满超前的.

Nacos
Nacos 是阿里开源的, 功能其实也很多, 服务注册, 配置管理, 动态 DNS 服务, 元数据管理

3.3 客户端Client

服务端其实包括了两个部分:一部分是接口定义或者叫契约,对客户端提供的其实只是提供的服务契约,让客户端知道请求和返回信息;另一部分是服务真正的实现。就跟我们在本地开发Service一样,会先定义Interface然后再写impl实现。服务端的做法其实和接口和实现分离十分类似。

客户端并不关心也不需要知道服务端的具体实现,只需要服务端对外暴露需要的接口规范。客户端要做的事情有:

  • 实现RPC协议,把请求和响应正确的编解码并传输到服务端或者从服务端接收响应,实现序列化或者反序列化
  • 实现服务发现,服务端必须知道真正要调用的服务的实现,当然这个逻辑需要通过动态代理来进行统一封装,这样才能在实际调用的时候实现无感知,一般契约和具体的实现会通过一个类似URL的唯一标识进行绑定
  • 以及负载均衡,必须能够拉取到服务端信息,并通过一定的负载均衡策略选择服务端实例来提供服务,具体负载均衡策略可以参考【5】

3.4 服务端Server

Server端要实现的工作有:

  • 加载服务,并缓存启动服务注册
  • 对外暴露接口
  • 本地调用具体实现
  • 和客户端一样实现RPC协议,完成编解码序列化反序列化等工作
    因此实现RPC一些这一部分一般都会单独做成一个模块,然后客户端和服务端就不用分别再去做这部分工作了。

4、简单对比 RPC 和 Restful API

面对对象不同:RPC 更侧重于动作。Restful 的主体是资源

RESTful 是面向资源的设计架构,但在系统中有很多对象不能抽象成资源,比如登录,修改密码等而 RPC 可以通过动作去操作资源。所以在操作的全面性上 RPC 大于 RESTful。

传输效率:RPC 效率更高。RPC,使用自定义的 TCP 协议,可以让请求报文体积更小,或者使用 HTTP2 协议,也可以很好的减少报文的体积,提高传输效率。复杂度:RPC 实现复杂,流程繁琐。REST 调用及测试都很方便。

RPC 实现需要实现编码,序列化,网络传输等。而 RESTful 不要关注这些,RESTful 实现更简单。灵活性:HTTP 相对更规范,更标准,更通用,无论哪种语言都支持 HTTP 协议。RPC 可以实现跨语言调用,但整体灵活性不如 RESTful。

总结:RPC 主要用于公司内部的服务调用,性能消耗低,传输效率高,实现复杂。、

HTTP 主要用于对外的异构环境,浏览器接口调用,App 接口调用,第三方接口调用等。

RPC 使用场景(大型的网站,内部子系统较多、接口非常多的情况下适合使用 RPC):

  1. 长链接。不必每次通信都要像 HTTP 一样去 3 次握手,减少了网络开销。
  2. 注册发布机制。RPC 框架一般都有注册中心,有丰富的监控管理;发布、下线接口、动态扩展等,对调用方来说是无感知、统一化的操作。
  3. 安全性,没有暴露资源操作。
  4. 微服务支持。就是最近流行的服务化架构、服务化治理,RPC 框架是一个强力的支撑。

【参考】

【1】如何手撸一个较为完整的RPC框架

【2】RPC基本原理以及如何用Netty来实现RPC

【3】花了一个星期,我终于把RPC框架整明白了!

【4】谈一谈我所理解的微服务中的注册中心

【5】rpc之负载均衡

本文已参与「新人创作礼」活动, 一起开启掘金创作之路。

本文转载自: 掘金

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

Python matplotlib 绘制量场图 复习回顾 1

发表于 2021-11-24

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

复习回顾

matplotlib 是基于Python语言的开源项目,pyplot提供一系列绘制2D图形的方法。随着版本的迭代,matplotlib 模块也支持绘制3D图形mplot3d工具包,制作动态图Animation类,对于动态图的制作也可以使用pyplot交互模式进行绘制,提供image类对图像进行加载、缩放及显示操作。

image.png

  • pyplot.plot()绘制折线图方法:matplotlib 绘制折线图
  • pyplot.ion()交互模式绘制动态图:matplotlib 绘制动态图
  • animation类绘制动态图:matplotlib Animation类:
  • 对图像处理的image类:matplotlib 图像处理

我们前面已经学习matplot pyplot 提供绘制折线、柱状、散点、饼、直方、图形等方法,pyplot 也提供绘制特殊的图形,常见于物理磁场图、箱型形图、提琴图等

本期,我们将学习matplotlib.pyplot.quiver()相关方法属性学习,let’s go~

  1. 量场图概述

  • 什么是量场图?

+ 量场图又名振动图、量场图。使用一组矢量箭头进行表示
+ 量场图表示一个向量对应另一个向量
+ 形成场的量为向量,称为向量场
  • 量场图使用场景

+ 量场图通常使用在物理学中如电磁场表示
+ 量场图也用于地磁图绘制根据各地磁台的观测的数据进行绘制表示
+ 在物理中常用的向量场有风场、引力场、电磁场、水流场等
  • 绘制量场图方法

1
2
3
python复制代码import matplotlib.pyplot as plt

plt.quiver()
  1. 量场图属性

  • 设置颜色

+ 向量颜色关键字:color or facecolor
+ 当facecolor与color同时设置时,会优先facecolor
+ 取值范围
    - 表示颜色的英文单词:如红色"red"
    - 表示颜色单词的简称如:红色"r",黄色"y"
    - RGB格式:十六进制格式如"#88c999";(r,g,b)元组形式
    - 也可以传入颜色列表
  • 设置透明度

+ 关键字:alpha
+ 取值为0~1
  • 设置向量箭头尺寸

+ 关键字:units
+ 默认值为:width
+ 可取值有:{'width', 'height', 'dots', 'inches', 'x', 'y', 'xy'}
    - width,height:代表轴的宽度、轴的高度
    - dots,inches: 基于图形dpi的像素或者英寸
    - x,y,xy:x,y或者(x^2+y^2)的平方根的数据
  • 设置坐标中向量箭头位置

+ 关键字:pivot
+ 默认值为:tail
+ 可以取值:{'tail', 'mid', 'middle', 'tip'}
  • 设置向量箭头宽度

+ 关键字:width
+ 默认值为:0.005
+ 取值为类型为:浮点型
  1. 绘制量场图步骤

我们在绘制量场图时,同样需要使用matplotlib.pyplot,因此我们来看一下绘制量场步骤

  • 导入matplotlib.pyplot类
1
python复制代码import matplotlib.pyplot as plt
  • 使用numpy库里的arange(),random(),sin(),cos()等方法准备x,y轴数据
1
2
python复制代码x = np.arange(-10,10,1)
y = np.arange(-10,10,1)
  • 调用numpy.meshgrid()方法生成二维网格坐标
1
python复制代码u,v = np.meshgrid(x,y)
  • 调用pyplot.quiver(x,y,u,v,c)绘制量场图
1
python复制代码plt.quiver(x,y,u,v,alpha=0.4)
参数 说明
x 一维、二维数组或者序列,表示箭头位置的x坐标
y 一维、二维数组或者序列,表示箭头位置的y坐标
u 一维、二维数组或者序列,表示箭头向量的x分量
v 一维、二维数组或者序列,表示箭头向量的y分量
c 一维、二维数组或者序列,表示箭头颜色
  • 调用pyplot.show()渲染显示图标
1
scss复制代码plt.show()

image.png

  1. 小试牛刀

我们学习以上绘制量场图的方法,我们来绘制一个高中物理中场景的电磁场图

image.png

  • 调用numpy.meshgrid()方法对x,y轴数据生成二维列表
  • 调用numpy.gradient()对u,v生成梯度数据
1
2
3
4
5
6
7
8
9
10
11
python复制代码a = np.arange(-2,2,0.2)
b = np.arange(-2,2,0.25)
x,y = np.meshgrid(a,b)
z = x*np.exp(-x**2-y**2)
v,u = np.gradient(z,0.2,0.2)

plt.quiver(x,y,u,v,color="pink",pivot="tip",units="inches")

plt.scatter(x,y,color="b",s=0.05)

plt.show()

总结

本期,我们对matplotlib.pyplot 绘制量场图方法quiver()相关属性进行学习。量场图常用在地质研究、电磁场等向量场景中。

以上是本期内容,欢迎大佬们点赞评论,下期见~

本文转载自: 掘金

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

【k8s 系列】k8s 学习八,在 K8S 中部署一个应用

发表于 2021-11-24

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

接着上一篇继续部署应用到 K8S中

之前简单部署的简单集群,三个工作节点是运行在 docker 和 kubelet 的,还有一个是控制节点

ReplicationController , pod 和 service 本次关系

之前有提到 ReplicationController , pod 和 服务是如何组合在一起的呢?

可以通过这张如图来解释一下

我们之前创建 pod 的时候不是直接创建的,是通过 docker run 来创建的一个 replicationController ,然后基于 rc 来创建的一个 pod 实例

为了让 pod 能够被外部访问到,所以我们需要让 K8S 将 replicationController 管理的所有 pod 由一个服务对外暴露,因此有了 kubia-http

  • 服务是有对外暴露 IP 的,请求打到 service 上
  • service 将请求转到 pod 上面的 9999 端口上,然后 pod 提供服务

ReplicationController 角色是啥样的

通过上面的案例,我们应该知道 ReplicationController实际上是用于复制 pod 的,通过 ReplicationController 来创建多个 pod 副本

ReplicationController 始终确保存在一个运行中的 pod 实例

如果我们上面创建的 pod 消失了,那么 ReplicationController 将会创建一个新的 pod 来替换消失的 pod

为什么需要 service

再来看看 service ,也就是上面 kubia-http 服务

为什么需要服务,有了 pod ,还拿 service 干啥?

通过上面的 ReplicationController 我们知道,pod 消失之后, ReplicationController 会再创建一个新的将其替换

那么我们也知道,每一个 pod 都有自己的独立的主机名和 IP

pod 在环境中会因为任何原因直接挂掉和消失,然后又被新的 pod 替换,这个时候 pod 的 IP 变化了,外部如何正确访问到我们的服务呢?

这个时候就需要 service 了

  • service 可以解决不断变化的 pod IP 问题
  • service 可以在一个固定 IP 和端口上对外暴露多个pod

当一个 service 被创建的时候,会得到一个静态的 IP,在 service 整个生命周期中,它的 IP 是不会变的

客户端只需要通过这个固定 IP 连接服务即可,服务会将请求转到 内部其中一个 pod, 客户端不需要关心 pod 在哪里,只需要关系 service 在哪里即可

增加副本数量

当前的系统里面只有的一个副本,现在我们可以增加到 3 个副本

我们可以通过指令 kubectl get replicationcontrollers 来查看当前的副本数

可以通过 kubectl scale rc mykubia --replicas=3,来将副本数调整至 3 个

  • 我们执行上面这个指令,只是告诉 K8S 系统中期望的副本数量,并没有告诉 K8S 需要如何去操作,如何去实现
  • K8S 自身会去检查当前的状态是否和期望的状态一致,如果不一致就会进行调整, 这个是 K8S 中基本的原则之一

我们用到的指令中,好多都是 kubectl get xxx 的,就是获取对应的 pod,service ,rc 等等信息的

其实我们也可以直接执行 kubectl get,这样可以列出所有可能类型的对象,还能够显示缩写

最新的系统状态

通过执行上述的指令,系统中将 1 个副本,调整成了 3 个副本

这就是 K8S 可以轻易的做到水平伸缩,我们要扩充副本的时候,再也不需要手动的去安装和运行其他副本了,只用执行指令,修改期望数量即可

当然,我们放进 pod 的服务,也需要做成无状态,可横向扩展的,这样才能更好的使用 K8S 的能力

这样子的话,最新的系统应该是这个样子的

外部请求打到 service 上面,service 会将请求给到 任意一个 pod ,对应的 pod 即提供服务即可

今天就到这里,学习所得,若有偏差,还请斧正

欢迎点赞,关注,收藏

朋友们,你的支持和鼓励,是我坚持分享,提高质量的动力

好了,本次就到这里

技术是开放的,我们的心态,更应是开放的。拥抱变化,向阳而生,努力向前行。

我是小魔童哪吒,欢迎点赞关注收藏,下次见~

本文转载自: 掘金

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

网络协议——DHCP工作原理及DHCP Relay

发表于 2021-11-24

本文正在参与 “网络协议必知必会”征文活动

📄技术背景

网络复杂度的不断提高伴随着网络规模的不断扩大,计算机的数量经常超过可供分配的IP地址数量。便携设备及无线网络的广泛使用也导致计算机的位置也经常变化,相应的IP地址也必须经常更新。所以需要一种技术,能动态合理地为主机分配IP地址。
在这里插入图片描述

🌐DHCP

Dynamic Host Configure Protocol,动态主机配置协议

Windows PC中的DHCP
在这里插入图片描述

  • 为大量主机动态分配TCP/IP信息(IP地址、掩码、默认网关、DNS服务器等)。
  • 实现集中管理。
  • 分配出去的信息是有租期的。
    在这里插入图片描述
  • DHCP典型组网*
    在这里插入图片描述
角色 Value
DHCP Client (客户端) 通过DHCP协议请求获取IP地址等网络参数的设备
DHCP Server(服务器) 负责为DHCP客户端分配网络参数的设备
DHCP Relay (中继) 负责转发服务器和客户端之间的DHCP报文,可选

DHCP工作流程:正常获取地址
在这里插入图片描述

DHCP报文
在这里插入图片描述

DHCP租期更新
在这里插入图片描述

T1:当租期达到50%时,DHCP客户端会自动以单播的方式向DHCP服务器发送DHCP REQUEST报文,请求更新IP地址租期。
在这里插入图片描述
T2:当租期达到87.5%时,DHCP客户端会自动以广播的方式向DHCP服务器发送DHCP REQUEST报文,请求更新IP地址租期。
在这里插入图片描述
T3:当租期达到100%时,还没有收到服务器的回应,客户端停止使用此IP地址,重新发送DHCP DISCOVER报文请求新的IP地址。
在这里插入图片描述
释放:如果DHCP客户端不再使用分配的IP地址,也可以主动向DHCP服务器发送DHCP RELEASE报文,释放该IP地址。
在这里插入图片描述
失败
在这里插入图片描述
DHCP地址池(Pool):所分配地址的集合
在这里插入图片描述
在这里插入图片描述

📠DHCP应用场景

在这里插入图片描述
在这里插入图片描述

📣DHCP配置

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

🔧DHCP Relay

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

🚀免费福利

点击下方关注回复“华为认证”免费领取HCIA视频&资料👇 👇 👇

本文转载自: 掘金

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

Java中的Lock与Condition 为什么需要Lock

发表于 2021-11-24

Java SDK 并发包通过 Lock 和 Condition 两个接口来实现管程,其中 Lock 用于解决互斥问题,Condition 用于解决同步问题。

为什么需要Lock与Condtion

synchronized提供了便捷性的隐式获取锁释放锁机制(基于JVM机制),但同时降低了开发人员对于锁操控的灵活,Lock与Condition在Java代码层次再实现一个管程模型,提供了synchronized缺少的

  • 灵活的条件队列
  • 响应中断
  • 支持超时
  • 非阻塞地获取锁
    等灵活特性。

互斥与 等待队列:Lock

Lock总体结构

image.png
可以看到Lock接口对外提供API,ReentrantLock是Lock接口基础的实现类。

在ReentrantLock中以内部类的形式实现Sync,对外暴露的接口实际上是对Sync内部类方法的包装。因此Sync是ReentrantLock的最底层结构。

image.png

image.png

实现细节

AQS队列同步器

Sync的父类为AbstractQueuedSynchronizer队列同步器,具有阻塞功能

实现一把具有阻塞和唤醒功能的锁的核心要素为:

  • 需要一个state变量,标记锁的状态,对锁变量的操作需要保证线程安全,用CAS实现
  • 需要记录当前哪个线程持有锁
  • 需要底层支持对一个线程进行阻塞和唤醒
  • 需要一个队列记录所有阻塞的线程,这个队列必须是线程安全的无锁队列,需要用CAS实现

state锁变量与exclusiveOwnerThread

可以看到
AbstractQueuedSynchronizer源码中,使用Integer类型,且带有volatile的变量作为state变量。

state可以大于1,此时表示充入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
arduino复制代码/**
* Head of the wait queue, lazily initialized. Except for
* initialization, it is modified only via method setHead. Note:
* If head exists, its waitStatus is guaranteed not to be
* CANCELLED.
*/
private transient volatile Node head;

/**
* Tail of the wait queue, lazily initialized. Modified only via
* method enq to add new wait node.
*/
private transient volatile Node tail;

/**
* The synchronization state.
*/
private volatile long state;

在顶层抽象类
abstract class AbstractOwnableSynchronizer中定义
exclusiveOwnerThread变量记录了当前持有锁的线程。

1
2
3
4
5
arduino复制代码
/**
* The current owner of exclusive mode synchronization.
*/
private transient Thread exclusiveOwnerThread;

LockSupport

JVM底层提供Native函数Unsafe类中park/unpark实现阻塞和唤醒线程。

LockSupport类对park/unpark做了简单封装和拓展。特别地,unpark可以对某个线程进行精确唤醒,而notify无法指定具体线程。

每个使用LockSupport的线程都会与一个许可关联,如果该许可可用,并且可在进程中使用,则调用park()将会立即返回,否则可能阻塞。如果许可尚不可用,则可以调用 unpark 使其可用。但是注意许可不可重入,也就是说只能调用一次park()方法,否则会一直阻塞。 LockSupport定义了一系列以park开头的方法来阻塞当前线程,unpark(Thread thread)方法来唤醒一个被阻塞的线程。如下:

202105091543488191.png

CLH同步队列

(Craig, Landin, and Hagersten) lock queue

源码如下,可以看出CLH为储存线程对象的双向链表。线程如果获取锁失败时,AQS则会将当前线程已经等待状态等信息构造成一个节点(Node)并将其加入到CLH同步队列,同时会阻塞当前线程,当锁释放时,会把首节点唤醒(公平锁),使其再次尝试获取同步状态。

在CLH同步队列中,一个节点表示一个线程,它保存着线程的引用(thread)、状态(waitStatus)、前驱节点(prev)、后继节点(next)

waitStatus的不同值表示:

  • CANCELLED = 1:因为超时或者中断,节点会被设置为取消状态,被取消的节点时不会参与到竞争中的,他会一直保持取消状态不会转变为其他状态;
  • SIGNAL = -1:后继节点的线程处于等待状态,而当前节点的线程如果释放了同步状态或者被取消,将会通知后继节点,使后继节点的线程得以运行
  • CONDITION = -2:节点在等待队列中,节点线程等待在Condition上,当其他线程对Condition调用了signal()后,改节点将会从等待队列中转移到同步队列中,加入到同步状态的获取中
  • PROPAGATE = -3:表示下一次共享式同步状态获取将会无条件地传播下去
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
arduino复制代码static final class Node {
/** Marker to indicate a node is waiting in shared mode */
static final Node SHARED = new Node();
/** Marker to indicate a node is waiting in exclusive mode */
static final Node EXCLUSIVE = null;

/** waitStatus value to indicate thread has cancelled */
static final int CANCELLED = 1;
/** waitStatus value to indicate successor's thread needs unparking */
static final int SIGNAL = -1;
/** waitStatus value to indicate thread is waiting on condition */
static final int CONDITION = -2;
/**
* waitStatus value to indicate the next acquireShared should
* unconditionally propagate
*/
static final int PROPAGATE = -3;


volatile int waitStatus;


volatile Node prev;


volatile Node next;


volatile Thread thread;


Node nextWaiter;


final boolean isShared() {
return nextWaiter == SHARED;
}


final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
}

Node() { // Used to establish initial head or SHARED marker
}

Node(Thread thread, Node mode) { // Used by addWaiter
this.nextWaiter = mode;
this.thread = thread;
}

Node(Thread thread, int waitStatus) { // Used by Condition
this.waitStatus = waitStatus;
this.thread = thread;
}
}

同时对队列的操作接口都是用Unsafe类提供的native函数CAS实现:
addWaiter尝试快速添加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ini复制代码private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}

如果失败则调用enq,enq(Node node)方法中,AQS通过“死循环”的方式来保证节点可以正确添加,只有成功添加后,当前线程才会从该方法返回,否则会一直执行下去。

compareAndSetTail方法的底层是unsafe类:

1
2
3
4
5
6
kotlin复制代码/**
* CAS tail field. Used only by enq.
*/
private final boolean compareAndSetTail(Node expect, Node update) {
return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
}

公平与非公平

条件队列:Condition

Condition是一种广义上的条件队列。他为线程提供了一种更为灵活的等待/通知模式,线程在调用await方法后执行挂起操作,直到线程等待的某个条件为真时才会被唤醒。Condition必须要配合锁一起使用,因为对共享状态变量的访问发生在多线程环境下。一个Condition的实例必须与一个Lock绑定,因此Condition一般都是作为Lock的内部实现。
image.png

实现细节

每个Condition对象都包含着一个FIFO队列,该队列是Condition对象通知/等待功能的关键。在队列中每一个节点都包含着一个线程引用,该线程就是在该Condition对象上等待的线程。
image.png

Node里面包含了当前线程的引用。Node定义与AQS的CLH同步队列的节点使用的都是同一个类(AbstractQueuedSynchronized.Node静态内部类)。

Condition的队列结构比CLH同步队列的结构简单些,新增过程较为简单只需要将原尾节点的nextWaiter指向新增节点,然后更新lastWaiter即可。

主要接口如下:

image.png

await()

首先将当前线程新建一个节点同时加入到条件队列中,然后释放当前线程持有的同步状态。然后则是不断检测该节点代表的线程释放出现在CLH同步队列中(收到signal信号之后就会在AQS队列中检测到),如果不存在则一直挂起,否则参与竞争同步状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
scss复制代码public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}

signal()

1
2
3
4
5
6
7
java复制代码public final void signal() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first);
}

整个通知的流程如下:

  1. 判断当前线程是否已经获取了锁,如果没有获取则直接抛出异常,因为获取锁为通知的前置条件。
  2. 如果线程已经获取了锁,则将唤醒条件队列的首节点
  3. 唤醒首节点是先将条件队列中的头节点移出,然后调用AQS的enq(Node node)方法将其安全地移到CLH同步队列中
  4. 最后判断如果该节点的同步状态是否为Cancel,或者修改状态为Signal失败时,则直接调用LockSupport唤醒该节点的线程。

ReentrantLock调用链

可以看出 ReentrantLock中主要接口都是对sync即AbstractQueuedSynchronizer
ReentrantLock中 Sync引用可以通过构造参数选择FairSync或NonfairSync,即公平与否。

image.png

1
2
3
4
5
6
7
8
9
10
11
12
13
csharp复制代码    public void lock()                { sync.acquire(1); }
public boolean tryLock() { return sync.tryAcquire(1); }
public void unlock() { sync.release(1); }
public Condition newCondition() { return sync.newCondition(); }
public boolean isLocked() { return sync.isHeldExclusively(); }
public boolean hasQueuedThreads() { return sync.hasQueuedThreads(); }
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}

acquire

加锁主要通过sync.acquire(1)。

1
2
3
4
5
scss复制代码public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}

tryAcquire

其中
tryAcquire函数是虚方法,实际实现在FairSync和NonfairSync中,且各有不同。
主要功能是检查锁变量state是否无锁并CAS尝试加锁,或持有锁的线程是否为当前线程(可重入)

公平和非公平的主要区别在于是否直接尝试加锁,公平加锁先判断自己是否是队列中的第一个节点。

Lock和tryAcquire中的加锁策略也是FairSync和NonfairSync唯二的不同
image.png

非公平Lock会尝试直接CAS抢锁(甚至不想进入等待队列),失败再进入acquire

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
java复制代码protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}


final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) { #非公平锁直接尝试更改锁变量
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}


protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}

acquireQueued和shouldParkAfterFailedAcquire

tryAcquire失败后会调用 acquireQueued函数则是对AQS队列进行维护,死循环判断是否为队列中的头节点,再进行加锁。
如果不符合上述条件,线程并不会立马进行阻塞,而是检查该线程的状态,检查状态的方法为 shouldParkAfterFailedAcquire(Node pred, Node node) 方法,该方法主要靠前驱节点判断当前线程是否应该被阻塞:

  • 如果当前线程的前驱节点状态为SINNAL,则表明当前线程需要被阻塞,调用unpark()方法唤醒,直接返回true,当前线程阻塞
  • 如果当前线程的前驱节点状态为CANCELLED(ws > 0),则表明该线程的前驱节点已经等待超时或者被中断了,则需要从CLH队列中将该前驱节点删除掉,直到回溯到前驱节点状态 <= 0 ,返回false
  • 如果前驱节点非SINNAL,非CANCELLED,则通过CAS的方式将其前驱节点设置为SINNAL,返回false

如果返回true会调用parkAndCheckInterrupt()阻塞并检查是否应该中断。

Lock包中提供lockInterruptibly->acquireInterruptibly->doAcquireInterruptibly->throw new InterruptedException() 该调用链响应中断,实现与原调用链类似,只有doAcquireInterruptibly与acquireQueue不同。

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
java复制代码final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}



private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
return true;
if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
/*
* waitStatus must be 0 or PROPAGATE. Indicate that we
* need a signal, but don't park yet. Caller will need to
* retry to make sure it cannot acquire before parking.
*/
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}

private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}

release

1
2
3
4
5
6
7
8
9
java复制代码public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}

tryRelease

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
java复制代码protected final boolean tryRelease(int var1) {
int var2 = this.getState() - var1;
if (Thread.currentThread() != this.getExclusiveOwnerThread()) {
throw new IllegalMonitorStateException();
} else {
boolean var3 = false;
if (var2 == 0) {
var3 = true;
this.setExclusiveOwnerThread((Thread)null);
}

this.setState(var2);
return var3;
}
}

ReentrantReadWriteLock

本文转载自: 掘金

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

网络协议——NAT网络地址转换

发表于 2021-11-24

本文正在参与 “网络协议必知必会”征文活动

个人简介:李白,华为云享专家、CSDN网络领域优质创作者、掘金创作者、BOSS有了创作者、公众号【新网工李白】、华为HCIE,全网粉丝1万+。我是一个爱好计算机科学,求索于逻辑思维的同时不忘哲学和浪漫主义科学,乐于分享技术与快乐的博主。博文内容涉及网络、华为、H3C路由交换、网络安全、渗透测试等。

🍎前言

IPV4地址枯竭限制网络发现的瓶颈,网络地址转换(NAT)用于实验公网地址不足的问题。将私网地址转换为公网地址。

🍏技术背景

  • 企业或庭所使用的网络为私有网络,使用私有地址。(内网)
  • 运营商维护的网络为公共网络,使用公有地址。(外网)
  • 私有地址不能在公网中路由。
  • NAT一般部署在连接内网和外网的网关设备上。

在这里插入图片描述

\

在这里插入图片描述

\

在这里插入图片描述

\

🍊NAT

Network Address Translation,网络地址转换

  • 将IP数据报文头中的IP地址转换为另一个IP地址的过程。
  • 有效避免来自外网的攻击,可以很大程度上提高网络安全性。
  • 控制内网访问外网、外网访问内网,解决了内网和外网不能互通的问题。

在这里插入图片描述

  • \

🍋NAT类型

静态NAT:一对一的地址转换,一个公网地址只保留给一个私网地址使用。

在这里插入图片描述

\

动态NAT:基于地址池动态转换。但当地址用尽以后,只能等待被占用的公网地址

被释放后,其他主机才能使用。

在这里插入图片描述

\

NAPT:Network Address Port Translation,多对一转换,多个私网地址映射到

同一个公网地址上,实现地址复用。

在这里插入图片描述

\

Easy IP:类似NAPT,直接使用公网接口的地址进行转换,适用于拨号的场景。

在这里插入图片描述

\

NAT Server:当需要外网访问内网服务的时候,可以配置“公网IP地址+端口号”

与“私网IP地址+端口号”间的映射关系来实现。

在这里插入图片描述

\

🍒配置案例

静态NAT配置:

在这里插入图片描述

\

在这里插入图片描述

\

动态NAT和NAPT配置:

在这里插入图片描述

\

在这里插入图片描述

\

Easy IP配置:

在这里插入图片描述

\

在这里插入图片描述

\

NAT Server配置:

在这里插入图片描述

\

🍆免费福利

微信搜索“新网工李白”加关注回复“华为认证”免费领取华为HCIA教程👇 👇 👇

\

本文转载自: 掘金

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

1…208209210…956

开发者博客

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