RPC框架(5 - 实现基于 Nacos 的服务器注册与发现)

本文涉及的产品
传统型负载均衡 CLB,每月750个小时 15LCU
应用型负载均衡 ALB,每月750个小时 15LCU
网络型负载均衡 NLB,每月750个小时 15LCU
简介: RPC框架(5 - 实现基于 Nacos 的服务器注册与发现)

5.5实现基于 Nacos 的服务器注册与发现



我们目前实现的框架看起来工作的还不错,但是有一个问题:我们的服务端地址是固化在代码中的,也就是说,对于一个客户端,它只会去寻找那么一个服务提供者,如果这个提供者挂了或者换了地址,那就没有办法了。


在分布式架构中,有一个重要的组件,就是服务注册中心,它用于保存多个服务提供者的信息,每个服务提供者在启动时都需要向注册中心注册自己所拥有的服务。这样客户端在发起 RPC 时,就可以直接去向注册中心请求服务提供者的信息,如果拿来的这个挂了,还可以重新请求,并且在这种情况下可以很方便地实现负载均衡。


常见的注册中心有 Eureka、Zookeeper 和 Nacos。


5.5.1获取Nacos


Nacos 是阿里开发的一款服务注册中心,在 SpringCloud Alibaba 逐步替代原始的 SpringCloud 的过程中,Nacos 逐步走红,所以我们就是用 Nacos 作为我们的注册中心。


下载解压的过程略过。注意 Nacos 是依赖数据库的,所以我们需要在配置文件中配置 Mysql 的信息。


  • 为了简单,我们先以单机模式运行:

sh startup.sh -m standalone


  • 或者直接运行startup.cmd


启动后可以访问 Nacos 的web UI,地址 http://127.0.0.1:8848/nacos/index.html。默认的用户名和密码都是 nacos


5.5.2项目中使用Nacos


引入 nacos-client 依赖:

<dependency>
        <groupId>com.alibaba.nacos</groupId>
        <artifactId>nacos-client</artifactId>
        <version>1.3.0</version>
    </dependency>


这里我们修正之前的概念,第二节把本地保存服务的类称为 ServiceRegistry,现在更改为 ServiceProvider,而 ServiceRegistry 作为远程注册表(Nacos)使用,对应的类名也有修改。


这里我们实现一个接口 ServiceRegistry:

public interface ServiceRegistry {
    void register(String serviceName, InetSocketAddress inetSocketAddress);
    InetSocketAddress lookupService(String serviceName);
}


  • register 方法将服务的名称和地址注册进服务注册中心
  • lookupService 方法则是根据服务名称从注册中心获取到一个服务提供者的地址。


InetSocketAddress:服务的地址(ip+端口号)

ps:本机的域名是"localhost",IPv4地址是127.0.0.1.

该类中包含了一个InetAddress对象,代表了IP地址和端口号,专门用于socket网络通信,用于需要IP地址和端口号的场景

  • 构造方法

//根据域名(主机名)获得ip地址加端口对象
InetSocketAddress localhost = new InetSocketAddress("localhost", 8080); 
//通过IP地址获取ip地址加端口对象
InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1", 8080);


  • 主要方法

InetAddress getAddress():返回此端口的IP地址。
String getHostName():返回此端口的主机名。
int getPort():返回此端口的端口号。


接口有了,我们就可以写实现类了,我们实现一个 Nacos 作为注册中心的实现类:NacosServiceRegistry,我们也可以使用 ZooKeeper 作为注册中心,实现接口就可以

public class NacosServiceRegistry implements ServiceRegistry {
    private static final Logger logger = LoggerFactory.getLogger(NacosServiceRegistry.class);
    private static final String SERVER_ADDR = "127.0.0.1:8848";
    private static final NamingService namingService;
    static {
        try {
            namingService = NamingFactory.createNamingService(SERVER_ADDR);
        } catch (NacosException e) {
            logger.error("连接到Nacos时有错误发生: ", e);
            throw new RpcException(RpcError.FAILED_TO_CONNECT_TO_SERVICE_REGISTRY);
        }
    }
    @Override
    public void register(String serviceName, InetSocketAddress inetSocketAddress) {
        try {
            namingService.registerInstance(serviceName, inetSocketAddress.getHostName(), inetSocketAddress.getPort());
        } catch (NacosException e) {
            logger.error("注册服务时有错误发生:", e);
            throw new RpcException(RpcError.REGISTER_SERVICE_FAILED);
        }
    }
    @Override
    public InetSocketAddress lookupService(String serviceName) {
        try {
            List<Instance> instances = namingService.getAllInstances(serviceName);
            Instance instance = instances.get(0);
            return new InetSocketAddress(instance.getIp(), instance.getPort());
        } catch (NacosException e) {
            logger.error("获取服务时有错误发生:", e);
        }
        return null;
    }
}


Nacos 的使用很简单,通过 NamingFactory 创建 NamingService 连接 Nacos(连接的时候没有找到修改用户名密码的方式……是不需要吗),连接的过程写在了静态代码块中,在类加载时自动连接。namingService 提供了两个很方便的接口,registerInstance 和 getAllInstances 方法:


  • registerInstance :直接向 Nacos 注册服务
  • getAllInstances:可以获得提供某个服务的所有提供者的列表。


在 lookupService 方法中,通过 getAllInstance 获取到某个服务的所有提供者列表后,需要选择一个,这里就涉及了负载均衡策略,这里我们先选择第 0 个,后面某节会详细讲解负载均衡。


5.5.3注册服务


我们修改 RpcServer 接口,新增一个方法 publishService,用于向 Nacos 注册服务:

<T> void publishService(Object service, Class<T> serviceClass);


接着只需要实现这个方法即可,以 NettyServer 的实现为例,NettyServer 在创建时需要创建一个 ServiceRegistry 了:

public NettyServer(String host, int port) {
    this.host = host;
    this.port = port;
    serviceRegistry = new NacosServiceRegistry();
    serviceProvider = new ServiceProviderImpl();
}


接着实现 publishService 方法即可:

public <T> void publishService(Object service, Class<T> serviceClass) {
    if(serializer == null) {
        logger.error("未设置序列化器");
        throw new RpcException(RpcError.SERIALIZER_NOT_FOUND);
    }
    serviceProvider.addServiceProvider(service);
    serviceRegistry.register(serviceClass.getCanonicalName(), new InetSocketAddress(host, port));
    start();
}


publishService 需要将服务保存在本地的注册表,同时注册到 Nacos 上。我这里的实现是注册完一个服务后直接调用 start() 方法,这是个不太好的实现……导致一个服务端只能注册一个服务,之后可以多注册几个然后再手动调用 start() 方法。


5.5.4服务发现


客户端的修改就更简单了,以 NettyClient 为例,在过去创建 NettyClient 时,需要传入 host 和 port,现在这个 host 和 port 是通过 Nacos 获取的,sendRequest 修改如下:

public Object sendRequest(RpcRequest rpcRequest) {
        if(serializer == null) {
            logger.error("未设置序列化器");
            throw new RpcException(RpcError.SERIALIZER_NOT_FOUND);
        }
        AtomicReference<Object> result = new AtomicReference<>(null);
        try {
            InetSocketAddress inetSocketAddress = serviceRegistry.lookupService(rpcRequest.getInterfaceName());
            Channel channel = ChannelProvider.get(inetSocketAddress, serializer);
...


重点是最后两句,过去是直接使用传入的 host 和 port 直接构造 channel,现在是首先从 ServiceRegistry 中获取到服务的地址和端口,再构造。


5.5.5测试


NettyTestClient 如下:

public class NettyTestClient {
    public static void main(String[] args) {
        RpcClient client = new NettyClient();
        client.setSerializer(new ProtobufSerializer());
        RpcClientProxy rpcClientProxy = new RpcClientProxy(client);
        HelloService helloService = rpcClientProxy.getProxy(HelloService.class);
        HelloObject object = new HelloObject(12, "This is a message");
        String res = helloService.hello(object);
        System.out.println(res);
    }
}


构造 RpcClient 时不再需要传入地址和端口(服务地址),直接去向注册中心请求服务提供者的信息。


NettyTestServer 如下:

public class NettyTestServer {
    public static void main(String[] args) {
        HelloService helloService = new HelloServiceImpl();
        NettyServer server = new NettyServer("127.0.0.1", 9999);
        server.setSerializer(new ProtobufSerializer());
        server.publishService(helloService, HelloService.class);
    }
}


我这里是把 start 写在了 publishService 中,实际应当分离,否则只能注册一个服务。


分别启动,可以看到和之前相同的结果。


这里如果通过修改不同的端口,启动两个服务的话,会看到即使客户端多次调用,也只是由同一个服务端提供服务,这是因为在 NacosServiceRegistry 中,我们直接选择了服务列表的第 0 个,这个会在之后讲解负载均衡时作出修改。

相关实践学习
SLB负载均衡实践
本场景通过使用阿里云负载均衡 SLB 以及对负载均衡 SLB 后端服务器 ECS 的权重进行修改,快速解决服务器响应速度慢的问题
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
相关文章
|
24天前
|
Java Nacos 数据库
使用 nacos 搭建注册中心及配置中心
使用 nacos 搭建注册中心及配置中心
47 5
|
24天前
|
NoSQL Java Nacos
SpringCloud集成Seata并使用Nacos做注册中心与配置中心
SpringCloud集成Seata并使用Nacos做注册中心与配置中心
57 3
|
2天前
|
Dubbo 网络协议 Java
RPC框架:一文带你搞懂RPC
这篇文章全面介绍了RPC(远程过程调用)的概念、原理和应用场景,解释了RPC如何工作以及为什么在分布式系统中广泛使用,并探讨了几种常用的RPC框架如Thrift、gRPC、Dubbo和Spring Cloud,同时详细阐述了RPC调用流程和实现透明化远程服务调用的关键技术,包括动态代理和消息的编码解码过程。
RPC框架:一文带你搞懂RPC
|
19天前
|
XML 存储 JSON
(十二)探索高性能通信与RPC框架基石:Json、ProtoBuf、Hessian序列化详解
如今这个分布式风靡的时代,网络通信技术,是每位技术人员必须掌握的技能,因为无论是哪种分布式技术,都离不开心跳、选举、节点感知、数据同步……等机制,而究其根本,这些技术的本质都是网络间的数据交互。正因如此,想要构建一个高性能的分布式组件/系统,不得不思考一个问题:怎么才能让数据传输的速度更快?
|
24天前
|
Nacos 微服务
Zookeeper 的 ZAB 协议 以及 zookeeper 与 nacos 注册中心比对
Zookeeper 的 ZAB 协议 以及 zookeeper 与 nacos 注册中心比对
23 4
|
5天前
|
Java Nacos Docker
"揭秘!Docker部署Seata遇上Nacos,注册成功却报错?这些坑你不得不防!一网打尽解决秘籍,让你的分布式事务稳如老狗!"
【8月更文挑战第15天】在微服务架构中,Nacos搭配Seata确保数据一致性时,Docker部署Seata后可能出现客户端连接错误,如“can not connect to services-server”。此问题多由网络配置不当、配置文件错误或版本不兼容引起。解决策略包括:调整Docker网络设置确保可达性;检查并修正`file.conf`和`registry.conf`中的Nacos地址和端口;验证Seata与Nacos版本兼容性;修改配置后重启服务;参考官方文档和最佳实践进行配置。通过这些步骤,能有效排除故障,保障服务稳定运行。
16 0
|
5天前
|
Kubernetes Nacos 微服务
【技术难题破解】Nacos v2.2.3 + K8s 微服务注册:强制删除 Pod 却不消失?!7步排查法+实战代码,手把手教你解决Nacos Pod僵死问题,让服务瞬间满血复活!
【8月更文挑战第15天】Nacos作为微服务注册与配置中心受到欢迎,但有时会遇到“v2.2.3 k8s 微服务注册nacos强制删除 pod不消失”的问题。本文介绍此现象及其解决方法,帮助开发者确保服务稳定运行。首先需检查Pod状态与事件、配置文件及Nacos配置,确认无误后可调整Pod生命周期管理,并检查Kubernetes版本兼容性。若问题持续,考虑使用Finalizers、审查Nacos日志或借助Kubernetes诊断工具。必要时,可尝试手动强制删除Pod。通过系统排查,通常能有效解决此问题。
11 0
|
5天前
|
安全 Nacos 数据库
【技术安全大揭秘】Nacos暴露公网后被非法访问?!6大安全加固秘籍,手把手教你如何保护数据库免遭恶意篡改,打造坚不可摧的微服务注册与配置中心!从限制公网访问到启用访问控制,全方位解析如何构建安全防护体系,让您从此告别数据安全风险!
【8月更文挑战第15天】Nacos是一款广受好评的微服务注册与配置中心,但其公网暴露可能引发数据库被非法访问甚至篡改的安全隐患。本文剖析此问题并提供解决方案,包括限制公网访问、启用HTTPS、加强数据库安全、配置访问控制及监控等,帮助开发者确保服务安全稳定运行。
13 0
|
5天前
|
安全 Nacos 数据安全/隐私保护
【技术干货】破解Nacos安全隐患:连接用户名与密码明文传输!掌握HTTPS、JWT与OAuth2.0加密秘籍,打造坚不可摧的微服务注册与配置中心!从原理到实践,全方位解析如何构建安全防护体系,让您从此告别数据泄露风险!
【8月更文挑战第15天】Nacos是一款广受好评的微服务注册与配置中心,但其连接用户名和密码的明文传输成为安全隐患。本文探讨加密策略提升安全性。首先介绍明文传输风险,随后对比三种加密方案:HTTPS简化数据保护;JWT令牌减少凭证传输,适配分布式环境;OAuth2.0增强安全,支持多授权模式。每种方案各有千秋,开发者需根据具体需求选择最佳实践,确保服务安全稳定运行。
22 0
|
1月前
|
网络协议 Dubbo Java
什么是RPC?RPC和HTTP对比?RPC有什么缺点?市面上常用的RPC框架?
选择合适的RPC框架和通信协议,对于构建高效、稳定的分布式系统至关重要。开发者需要根据自己的业务需求和系统架构,综合考虑各种因素,做出适宜的技术选型。
44 1