bool
1 | fn main() { |
char
1 | unicode |
整数类型
i8 u8, i16 u16 … i128, u128, isize, usize
1 | let var1 : i32 = 32; // 十进制表示 |
(pointer size)isize和usize占用字节数和32位或者64位系统有关系
浮点类型
1 | let f1 = 123.0f64; // type f64 |
1 | fn main() { |
1 | unicode |
i8 u8, i16 u16 … i128, u128, isize, usize
1 | let var1 : i32 = 32; // 十进制表示 |
(pointer size)isize和usize占用字节数和32位或者64位系统有关系
1 | let f1 = 123.0f64; // type f64 |
1 | let variable : i32 = 100; |
这段代码会报错,变量是只读的。mut关键字申明的变量才是可写的。
1 | let mut x = 5; // mut x: i32 |
mut x是一个“模式”,我们还可以用这种方式同时声明多个变量
1 | let (mut a, mut b) = (1, 2); |
Rust中,每个变量必须被合理初始化之后才能被使用。
编译器会帮我们做一个执行路径的静态分析,确保变量在使用前一定被初始化:
1 | fn test(condition: bool) { |
NamingGrpcClientProxy是Nacos SDK中,Grpc调用的主要实现类。话不多说,看一下类图:

NamingGrpcClientProxy#NamingGrpcClientProxy
->start()
->RpcClient#start()
构造函数逻辑
1 | public NamingGrpcClientProxy(String namespaceId, SecurityProxy securityProxy, ServerListFactory serverListFactory, |
向Nacos注册服务,存在两种类型。临时(Ephemeral)服务和持久(Persistent)服务。服务信息的存储和节点间同步,分别基于Distro协议和Raft协议。
可以从DistroClientComponentRegistry入手去阅读源代码,下面这些成员变量,都是用来实现协议的。
看下来主要有两个功能点
1 | private void startDistroTask() { |
startVerifyTask()
-> DistroVerifyTimedTask#run()
-> DistroVerifyExecuteTask#run()
发送方,发送:
获取当前节点所有客户端信息,获取所有Nacos Server节点。将客户端信息发送到所有节点,进行校验。我们看下具体校验逻辑是啥?
接收方,校验逻辑:
DistroDataRequestHandler#handle()
->handleVerify()
->DistroProtocol#onVerify()
->DistroClientDataProcessor#processVerifyData()
->EphemeralIpPortClientManager#verifyClient(*)
比较一下内存中版本号,如果版本号不一致,就更新内存中服务实例信息。
发送方,处理校验结果:
DistroVerifyCallbackWrapper#onResponse(*)
1 | public void onResponse(Response response) { |
除了记录log和metric,就是发布了一个ClientEvent.ClientVerifyFailedEvent事件。该事件最终触发同步数据请求,发送到校验失败的服务器。
收到请求后的处理逻辑在这里:
DistroDataRequestHandler#handle()
->handleSyncData()
-> DistroProtocol#onReceive()
-> DistroClientDataProcessor#handlerClientSyncData() ->upgradeClient(*)
1 | private void upgradeClient(Client client, ClientSyncData clientSyncData) { |
这里显示将收到的数据进行封装,然后维护更新内存种数据。同时发出了3个事件:
1 | private void load() throws Exception { |
服务订阅的目的是为了,能够第一时间感知到被调用服务的实例变化,从而能够及时更新本地缓存,避免调用失败。
subscribe和unsubscribe的逻辑,只有临时实例才有,永久实例是不存在订阅逻辑的。所以还是比较推荐用临时类型的服务注册,这也是官方客户端默认的类型
看一下服务调用的逻辑如何实现的:
SubscribeServiceRequestHandler#handle(*)
1 | public SubscribeServiceResponse handle(SubscribeServiceRequest request, RequestMeta meta) throws NacosException { |
-> EphemeralClientOperationServiceImpl#subscribe(*)
1 | public void subscribeService(Service service, Subscriber subscriber, String clientId) { |
和上一篇一样,大部头的逻辑都是通过事件机制驱动的:
1 | //ClientServiceIndexesManager.class |
1 | //NamingSubscriberServiceV2Impl.class |
服务订阅和注册的唯一项如何确定,由如下三个属性决定:
1 | public class Service implements Serializable { |
总结:
之前分析客户端的代码,发现Naming相关的后端调用主要有这几个,代码为NamingClientProxyDelegate这个class:
我们先去看Naming Server的grpc实现
代码调用链路,grpc方式注册的服务,默认为临时服务。
1 | public InstanceResponse handle(InstanceRequest request, RequestMeta meta) throws NacosException { |
1 | private InstanceResponse registerInstance(Service service, InstanceRequest request, RequestMeta meta) |
1 | public void registerInstance(Service service, Instance instance, String clientId) throws NacosException { |
1 | public boolean addServiceInstance(Service service, InstancePublishInfo instancePublishInfo) { |
这里最终将实例信息,保存到一个map,做了metric统计和发布一个事件,上述列出的代码总共发布了4个事件:
Nacos是阿里巴巴开源的,用于服务发现和配置管理的中间件。配置中心已经使用Apollo,所以我们只需要使用服务发现能力即可。
学习研究Nacos过程中,发现如下几点:
第2点是为了用户方便使用公司内部账号访问Nacos。但这结合第3点使用就出现了冲突。假设一个部门多个人同时进行开发,这样某个人的用户名和密码就会暴露给了其他开发人员。
1 | spring.cloud.nacos.discovery.namespace=provider |
翻了一下源码,这两个方式,主要区别在于,ldap方式会先用Nacos本地身份验证,如果验证失败,才会使用ldap方式验证。
manager类涉及到身份认证,区别在上述已经说明。authPluginService涉及权限验证,两种方式代码完全是一样的,不存在差别。