🔥🔥🔥提高服务器使用效率:合并微服务优化实践

前言

现在各个公司都在进行降本增效,其中的一个方面就是提高服务器的使用效率,由于在前期,错误的预估了业务的规模,导致了微服务拆分过细,很多服务器的使用在10%以下,为了解决这个情况,笔者根据eureka的注册原理,合并了多个服务,提高了服务的使用率。

目标

合并微服务势必会对前端的调用产生影响,所以此次我们的目的主要是降低对前端的影响。在不影响依赖方的情况下,将符合精简条件的server服务迁移至新服务中

调用流程 目前各服务启动后各自注册自己的实例信息到注册中心,调用方通过feign客户端信息到注册中心拉取目标服务端的路由信息进行接口调用 此处就不合大家讲具体的业务服务合并细节,我们先看一下eureka是如何支持一个服务注册多个服务名的。

原理分析

服务注册触发路径

java 复制代码
SpringApplication.run() -> 
this.refreshContext(context) -> 
this.refresh(context) -> 
ServletWebServerApplicationContext.refresh() -> 
this.finishRefresh() -> 
AbstractApplicationContext.finishRefresh -> 
DefaultLifecycleProcessor.onRefresh() -> 
this.startBeans -> 
this.start() -> 
this.doStart()

eureka的服务注册初始化加载是通过spring的SPI机制实现的

spring-cloud-starter-netflix-eureka-client -> spring.factories

java 复制代码
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.netflix.eureka.config.EurekaClientConfigServerAutoConfiguration,\
org.springframework.cloud.netflix.eureka.config.EurekaDiscoveryClientConfigServiceAutoConfiguration,\
org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration,\
org.springframework.cloud.netflix.ribbon.eureka.RibbonEurekaAutoConfiguration,\
org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration

Eureka Client 的配置是通过 EurekaClientAutoConfiguration 和 EurekaDiscoveryClientConfiguration 进行处理的

java 复制代码
EurekaClientAutoConfiguration

public EurekaDiscoveryClient discoveryClient(EurekaClient client,
      EurekaClientConfig clientConfig) {
   return new EurekaDiscoveryClient(client, clientConfig);
}

eureka服务注册核心的类为 : EurekaServiceRegistry,通过这个类中提供的方法实现注册与下线,此外EurekaAutoServiceRegistration类实现了SmartLifecycle接口,会在spring容器加载和初始化完后调用start()方法

java 复制代码
public void start() {
		// only set the port if the nonSecurePort or securePort is 0 and this.port != 0
		if (this.port.get() != 0) {
			if (this.registration.getNonSecurePort() == 0) {
				this.registration.setNonSecurePort(this.port.get());
			}

			if (this.registration.getSecurePort() == 0 && this.registration.isSecure()) {
				this.registration.setSecurePort(this.port.get());
			}
		}

		// only initialize if nonSecurePort is greater than 0 and it isn't already running
		// because of containerPortInitializer below
		if (!this.running.get() && this.registration.getNonSecurePort() > 0) {

			this.serviceRegistry.register(this.registration);

			this.context.publishEvent(
					new InstanceRegisteredEvent<>(this, this.registration.getInstanceConfig()));
			this.runnin

eureka的注册信息全部保存在ApplicationInfoManager

创建ApplicationInfoManager类的时候,创建了一个InstanceInfo类 ApplicationInfoManager通过EurekaInstanceConfigBean、InstanceInfo进行创建

java 复制代码
InstanceInfoReplicator

    public void run() {
        try {
            discoveryClient.refreshInstanceInfo();

            Long dirtyTimestamp = instanceInfo.isDirtyWithTime();
            if (dirtyTimestamp != null) {
                discoveryClient.register();
                instanceInfo.unsetIsDirty(dirtyTimestamp);
            }
        } catch (Throwable t) {
            logger.warn("There was a problem with the instance info replicator", t);
        } finally {
            Future next = scheduler.schedule(this, replicationIntervalSeconds, TimeUnit.SECONDS);
            scheduledPeriodicRef.set(next);
        }
    }

以上就是Eureka服务注册的主要流程,在最后附上配置多服务的配置类

java 复制代码
@Configuration
public class RegistryConfig implements SmartLifecycle {

    private static String appName = "server-name";
    public Map<String, CloudEurekaClient> applicationNameClientMap = new ConcurrentHashMap();
    @Autowired(required = false)
    private ObjectProvider<HealthCheckHandler> healthCheckHandler;
    @Autowired
    private InetUtils inetUtils;
    @Autowired
    private EurekaInstanceConfigBean eurekaInstanceConfigBean;
    @Autowired
    private EurekaClientConfig eurekaClientConfig;
    @Autowired
    private AbstractDiscoveryClientOptionalArgs<?> optionalArgs;
    @Autowired
    private ApplicationContext context;
    @Autowired
    private EurekaServiceRegistry serviceRegistr;
    private AtomicBoolean running = new AtomicBoolean(false);

    @Override
    public void start() {
        EurekaInstanceConfigBean eurekaInstanceConfigBean = new EurekaInstanceConfigBean(inetUtils);
        BeanUtils.copyProperties(this.eurekaInstanceConfigBean, eurekaInstanceConfigBean);
        eurekaInstanceConfigBean.setAppname(appName);
        eurekaInstanceConfigBean.setVirtualHostName(appName);
        eurekaInstanceConfigBean.setSecureVirtualHostName(appName);
        InstanceInfo instanceInfo = (new InstanceInfoFactory()).create(eurekaInstanceConfigBean);
        ApplicationInfoManager manager = new ApplicationInfoManager(eurekaInstanceConfigBean, instanceInfo);
        CloudEurekaClient cloudEurekaClient =
            new CloudEurekaClient(manager, eurekaClientConfig, this.optionalArgs, this.context);
        EurekaRegistration registration =
            EurekaRegistration.builder(eurekaInstanceConfigBean).with(manager).with(cloudEurekaClient)
                .with(this.healthCheckHandler).build();
        EurekaAutoServiceRegistration eurekaAutoServiceRegistration =
            new EurekaAutoServiceRegistration(this.context, this.serviceRegistr, registration);
        CloudEurekaClient mapResult = this.applicationNameClientMap.putIfAbsent(appName, cloudEurekaClient);
        if (mapResult == null) {
            eurekaAutoServiceRegistration.start();
        }
        this.running.set(true);
    }

    @Override
    public void stop() {
        CloudEurekaClient cloudEurekaClient = this.applicationNameClientMap.get(appName);
        if (cloudEurekaClient != null) {
            cloudEurekaClient.shutdown();
        }
        this.applicationNameClientMap.remove(appName);
        this.running.set(false);
    }

    @Override
    public boolean isRunning() {
        return this.running.get();
    }
}

尾声

eureka的技术栈已经有很多年了,很多公司已经在逐渐的淘汰,但其中还有很多设计的思想值得我们学习,在一开始,笔者也只是抱着试试看的心态来尝试一下,还好eureka的扩展足够支持,否则,只能让前端同事大量的修改调用的路径,测试同事进行大规模的回归测试了。大家要是有更好的方案也可以讨论一下,我的目的只是提供一些思路和引发讨论,以期能够共同进步。

相关推荐
disgare15 分钟前
SpringBoot 请求调用时关于高可用机制选型和落地
java·spring boot·后端
余衫马37 分钟前
在 Windows 服务中托管 ASP.NET Core Web API (.net6)
运维·windows·后端·asp.net·.net
indexsunny1 小时前
互联网大厂Java面试实战:Spring Boot微服务与Kafka消息队列深度解析
java·spring boot·微服务·面试·kafka·消息队列·电商
预知同行1 小时前
RAG 架构设计深度解析:从向量数据库选型到生产级检索系统
后端·架构
二哈赛车手1 小时前
github拉取自己项目出现的模块依赖问题解决方案
后端
用户6757049885021 小时前
AI开发实战4、AI总是忘记项目规范?因为你缺了这份终极上下文文件
后端·aigc·ai编程
海月水母1 小时前
webrtc网页端拉流流程
后端
阿虎儿1 小时前
Docker 安装 Nacos v1.4.4 踩坑实录
后端
用户6757049885021 小时前
AI开发实战3、90%人用AI写前端都踩的坑:API层混乱!3步教你标准化
后端·aigc·ai编程
程序员清风1 小时前
2026年AI编程工具对比:谁最值得用?
java·后端·面试