Eureka,Config到Nacos的无痛迁移

一.  概述

最近公司在做组件的技术升级,需要将原有注册中心 Eureka,配置中心 Spring Cloud Config 替换为 Nacos,主要原因如下:

1. Spring Cloud Netflix Eureka项目宣布闭源

Spring Cloud的这一重要宣布,意味着,随着技术的发展,最终使用Eureka的用户都会面临注册中心迁移的问题

2. Nacos更强

<1> 在服务注册上,Nacos 几乎实现了 Eureka 所支持的所有特性;

<2> 在配置管理上,Nacos 具备 Config 所支持的所有功能;

<3> Nacos 提供了控制台UI,便于更方便地进行服务管理,配置管理;

可以理解为,Nacos = Spring Cloud Eureka + Spring Cloud Config,但是其功能更强大。

因此,本文将探讨:如何以 "零代码" 的方式,将使用 Eureka,Config 的应用迁移到 Nacos 上。

二. Nacos简介及主要特性展示

       Nacos 通过提供简单易用的动态服务发现、服务配置、服务共享与管理等服务基础设施,帮助用户在云原生时代,在私有云、混合云或者公有云等所有云环境中,更好的构建、交付、管理自己的微服务平台,更快的复用和组合业务服务,更快的交付商业创新的价值,从而为用户赢得市场。下面是Nacos的总览图:

一句话总结就是,Nacos提供了服务管理,配置管理,多环境管理,后续还会支持权限管理等模块。关于Nacos的详细功能请参考官网。

三. 实施过程

1. 前期准备

<1> 梳理待迁移的服务列表

目前所有的微服务都需要进行迁移,整理服务清单(便于后期追溯,避免改漏);

<2> 确定组件所使用的版本

版本依赖选型,需要参考官方Wiki:

https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E

另外,Hibernate-Validator版本低于5.X.X时,Nacos-Client报错;

最终,确定的版本情况如下:

2. Nacos 部署

完成了前期准备,下一步便是Nacos的部署。这里采用集群部署的方式,保证Nacos集群高可用。

部署方式采用官方推荐的方式。物理架构如下图所示:

其中,Nacos节点为无状态节点。因此,只通过域名指定轮询访问其中一台即可保证高可用。

<1> Nacos安装

本次使用的Nacos版本为1.1.4,需要三台机器,每台机器都执行同样的安装操作;

1~ 初始化数据库

初始化SQL脚本为 nacos-mysql.sql,文件位于nacos/conf目录下。初始化的表共有11个,如下图所示:

2~ Nacos下载

下载地址:https://github.com/alibaba/nacos/releases/tag/1.1.4

3~ Nacos安装与配置

解压 nacos-server-1.1.4.tar.gz。

修改application.properties,文件位于nacos/conf目录下,修改文件中的数据库配置。

spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://10.0.17.21:3306/registry?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user=root
db.password=123456

修改cluster.conf,文件位于nacos/conf目录下,配置集群节点的信息(IP端口形式)。

#it is ip
#example
10.0.112.64:8848
10.0.112.65:8848
10.0.112.66:8848

4~ 启动

进入nacos/bin目录下,通过 sh startup.sh 启动 nacos 即可。

<2> Nginx配置

       因为状态都保存在数据库,因此Nacos是无状态节点,配置Nginx时,只通过域名指定轮询访问其中一台即可保证高可用,配置如下所示:

upstream nacos{
        server 10.0.112.64:8848;
        server 10.0.112.65:8848;
        server 10.0.112.66:8848;
}
server {
        listen       8848;
        server_name  10.0.17.115;
        location / {
            proxy_pass http://nacos;
            proxy_set_header Host $http_host;
        }
}

<3> 验证

通过域名(或者IP端口)的方式访问,出现如下界面,证明部署成功。

3. 配置多环境隔离

Nacos通过命名空间的形式,实现了环境的逻辑隔离。通过不同的命名空间,隔离了不同环境。

登录Nacos控制台,新增命名空间即可。例如需要开发环境和生产环境,则新增 dev,pro 两个命名空间即可。

4. 服务注册与发现

<1> 引入依赖

建议通过父pom的方式引入,避免parent的方式。依赖如下:

    <!-- mvn依赖管理 -->
    <dependencyManagement>
        <dependencies>
            <!-- spring boot -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-parent</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!-- spring cloud -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!-- spring cloud alibaba dependencies -->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${alibaba-spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

在子项目中直接引入即可:

        <!-- nacos discovery -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

同时去掉Eureka的依赖:spring-cloud-starter-netflix-eureka-client

<2> 配置替换

只需要在配置中去掉Eureka的配置,改为Nacos的配置即可。

spring.cloud.nacos.discovery.server-addr=10.0.17.115:8848

<3> 加上注解

在启动类上加上 @EnableDiscoveryClient 即可。

这样子,就完成了服务注册与发现的改造。

5. 服务配置管理

<1> 引入依赖

在前面的基础上,直接引入下面依赖即可:

        <!-- nacos config -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>

同时去掉 Config 的依赖。

<2> 配置替换

同服务注册与发现,只需要在配置中去掉Config的配置,改为Nacos的配置即可。

spring.cloud.nacos.config.server-addr=10.0.17.115:8848

这样子,服务就加入了Nacos配置中心。

四. 存在的问题

在实施的过程中,我们也遇到了一些问题:

1. 多环境管理的做法

我们既然在Nacos配置了多环境隔离,那么服务自然也支持多环境隔离,因此我们的做法是:

首先,我们配置了多个配置文件,如下所示:

boostrap.yml 为公用配置,boostrap-xx.yml 作为不同环境的区分,boostrap-xx.yml 内容如下:

spring:
  cloud:
    nacos:
      discovery:
        namespace: 0ec603e3-261f-4705-8530-60093f0c8309
      config:
        namespace: 0ec603e3-261f-4705-8530-60093f0c8309

可以看到,通过namespace区分不同的环境。最终应用启动时,通过 spring.profiles.active 指定环境即可。

2. 配置的实时生效

Nacos支持客户端配置的自动更新,因此,这里提供三种方法实现配置的自动更新(任一种均可):

<1> 通过@Configuration注解指定的配置类, 且在类上加上@RefreshScope

<2> 通过@Value注解注入的属性,且在类上加上@RefreshScope

<3> 通过ConfigurableApplicationContext

@Autowired
private ConfigurableApplicationContext applicationContext;

applicationContext.getEnvironment().getProperty("user.name")

这样,我们在修改配置的同时,配置也会实时在各个客户端生效。

3. Nacos权重配置不生效,且服务无法秒级下线

       经过测试发现,Nacos的权限配置不生效。经过查阅资料,最终通过自定义负载均衡器解决,它是一个基于权重的自定义Nacos负载均衡器,并将Ribbon使用的负载均衡策略由轮询改为它。实现过程如下:

import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.ribbon.NacosServer;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.DynamicServerListLoadBalancer;
import com.netflix.loadbalancer.Server;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * 基于权重的自定义负载均衡器
 * @author zz
 * @date 2019/12/5 13:55
 **/
@Slf4j
public class NacosWeightLoadBalancerRule extends AbstractLoadBalancerRule {

    @Override
    public Server choose(Object o) {
        DynamicServerListLoadBalancer loadBalancer = (DynamicServerListLoadBalancer) getLoadBalancer();
        String name = loadBalancer.getName();
        try {
            Instance instance = discoveryProperties.namingServiceInstance().selectOneHealthyInstance(name);
            log.info("select instance:{}", instance);
            return new NacosServer(instance);
        } catch (NacosException ee) {
            log.error("select instance error! message:", ee);
        } catch (Exception e) {
            log.error("select instance error! message:", e);
        }
        return null;
    }

    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {
    }

    @Autowired
    private NacosDiscoveryProperties discoveryProperties;
import com.netflix.loadbalancer.IRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Ribbon配置
 * @author zz
 * @date 2019/12/5 14:14
 **/
@Configuration
public class RibbonConfig {

    @Bean
    public IRule getRibbonRule() {
        return new NacosWeightLoadBalancerRule();
    }

}

这样,在Nacos进行权重变更时,客户端也能及时根据变更,进行正确的负载。

4. Feign的 @RequestBody 无法继承

看下面的例子,第一个为Feign客户端,第二个为服务端的Controller

@FeignClient("user")
public interface UserApi {
    
    @RequestMapping("/saveUser")
    public String saveUser(@RequestBody User user);
}
@RestController
public class UserController implements UserApi{
    @Override
    public String saveUser(User user) {
        return userService.save(user);
    }
}

通1过测试发现,使用该 Feign 客户端去调用的时候,报错:FeignException:status 404。而且发现服务端收到的参数均为null。

原因及解决:UserController无法继承UserApi的@RequestBody属性。因此,最笨的方法就是在UserController上加上@RequestBody,除此之外,另一种优雅的做法是,实现一个定制化的Processor,修改supportParameter的判断方法。判断到为Spring Cloud定义的接口,并且是自定义对象时,使用@RequestBody相同的内容转换器。

    @Override
    public boolean supportsParameter(MethodParameter parameter)
    {
        if (AnnotatedElementUtils.hasAnnotation(parameter.getContainingClass(), FeignClient.class) && isCustomizedType(parameter.getParameterType())) {
            return true;
        }
        return super.supportsParameter(parameter);
    }

五. 改造带来的收益

改造完成后,经过多轮测试,最终成功投产到生产环境。经过一段时间的验证,我们发现Nacos还是很香的。

1. 经济价值
我们替换了旧的配置和注册中心,目前开发测试共有5套环境,每套环境的配置和注册中心都部署集群的话,需要部署5*2*2=20个节点,现在只需要部署3个节点,即可实现高可用。这为我们省下了一笔机器的花销。

2. 实用价值

<1> 服务管理: 随时随地上下线应用,随时随地修改权重。
<2> 配置管理: 高效管理配置,支持修改对比,回滚配置,并且记录修改记录。

要知道,这两个功能,在Eureka均需要二次开发,这对中小企业来说,不够友好。而Nacos天然地支持服务,配置管理。

3. 其他价值

使用Nacos后,我们的应用完美融入了微服务生态。我们知道,Eureka和Config在融入云原生方面存在很大欠缺(虽然有解决方案,但较为繁琐)。而Nacos则不同,它天然地融入云原生中,据官方消息,Nacos将为融入Service Mesh做出更大改进。

六. 总结

经过上述改造,服务得以顺利地从 Eureka,Config 迁移到 Nacos 上,后续在微服务生态上能发挥更好的价值。

下面做个简单的总结:

1. Spring Cloud Alibaba 实现了Spring Cloud服务注册规范,这使得开发者能够无痛地迁移 Eureka 到 Nacos;

2. 在功能上,Nacos = Eureka + Nacos;

3. 在生态上,Nacos亲近开发者,无论是对dubbo,SpringCloud还是k8s的兼容,深得人心;

因此,可以相信,随着技术的演进, Nacos 一定会在微服务体系上走得更远,走得更好。

七. 参考资料

Nacos官网:https://nacos.io/

扩展Ribbon支持Nacos权重的三种方式:https://www.jianshu.com/p/6b6cf891ac6a

发布了21 篇原创文章 · 获赞 23 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_15898739/article/details/104004188