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

前言

现在各个公司都在进行降本增效,其中的一个方面就是提高服务器的使用效率,由于在前期,错误的预估了业务的规模,导致了微服务拆分过细,很多服务器的使用在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的扩展足够支持,否则,只能让前端同事大量的修改调用的路径,测试同事进行大规模的回归测试了。大家要是有更好的方案也可以讨论一下,我的目的只是提供一些思路和引发讨论,以期能够共同进步。

相关推荐
曼岛_1 分钟前
[架构之美]Spring Boot多环境5种方案实现Dev/Test/Prod环境隔离
spring boot·后端·架构
Top`5 分钟前
服务预热原理
java·后端·spring
幽络源小助理30 分钟前
SpringBoot框架开发网络安全科普系统开发实现
java·spring boot·后端·spring·web安全
boring_1111 小时前
异地多活单元化架构下的微服务体系
数据库·微服务·架构
酷小洋2 小时前
JavaWeb基础
后端·web
Absinthe_苦艾酒2 小时前
SpringCloud之Gateway基础认识-服务网关
spring cloud·微服务·gateway
一切皆有迹可循2 小时前
Spring Boot 基于 Cookie 实现单点登录:原理、实践与优化详解
java·spring boot·后端
bing_1582 小时前
Spring Boot 中 MongoDB @DBRef注解适用什么场景?
spring boot·后端·mongodb
sunywz4 小时前
微服务不注册到nacos的方法
微服务·云原生·架构
RedJACK~4 小时前
Go语言Stdio传输MCP Server示例【Cline、Roo Code】
开发语言·后端·golang