spring cloud eureka 运用与源码分析

spring cloud eureka 运用与源码分析

简介

eureka 作为注册中心而言如今也没有太多企业采用,因为eureka2.0之后闭源,且不再对现有版本维护,而且spring cloud也支持了zoomkeeper了作为注册中心。所以大多企业更愿倾向于zoomkeeper或者consul注册中心。对于初学者而言,eureka还是有一定的必要,因为它最先呗spring cloud集成,从职责单一性而言,比zoomkeeper要简单易学。而且与ribbon,Hystrix更容易结合,都是同一公司Netflix产品。

eureka的运用

eureka作为注册中心,需要三个部分组成,一个是服务端,一个是client端,一个是消费端。
eureka架构
从图上而言,eureka server端起来后,client端会向server端进行注册,消费端从server端拉取所有已注册的服务信息,获取所需服务的ip以及其他信息,进行消费。

server端

笔者目前使用的是spring boot为2.1.4.RELEASE 版本,spring cloud 为Greenwich.SR1版本。
引入对应的pom依赖

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>

application.properties的修改

`# 服务名称
spring.application.name=eureka-server
# 启动端口
server.port=1101
# 实例地址
eureka.instance.hostname=localhost
# 是否自i注册
eureka.client.register-with-eureka=false
# 是否拉取注册服务信息
eureka.client.fetch-registry=false
# 设置与Eureka Server交互的地址,查询服务和注册服务都需要依赖这个地址。多个地址可使用 , 分隔。
eureka.client.serviceUrl.defaultZone=http://localhost:7001/eureka/

启动类的修改

@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class, args);
    }

}

启动类上添加一个@EnableEurekaServer注解,就可以启动了。启动后访问http://localhost:1101/就可以看到注册中心界面。
eureka注册中心
application下的值为空,说明目前没有服务注册上去。ds replicas值为空,说明目前也没有集群。

client端

pom文件依赖

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

引入client的启动依赖。
application.properties的修改

spring.application.name=eureka-client
server.port=2101
# 指向eureka server端的IP地址
eureka.client.serviceUrl.defaultZone=http://localhost:1101/eureka/

相比于spring boot1.x版本,eureka client 端的启动不需要添加@EnableDiscoveryClient 这个注解,已经集成到spring boot auto configuration 里。
Controller层代码的编写

 @GetMapping("/")
    public String hello(@RequestParam String name) {
        return "Hello, " + name + " " + new Date();
    }

然后启动client端。
在这里插入图片描述
注册中心可以明显观察到一个子服务注册上去了。
访问http://localhost:2101/hello/?name=%22aaa%22
在这里插入图片描述

源码分析

关于eureka的启动源码分析,我推荐SpringCloud(第 049 篇)Netflix Eureka 源码深入剖析(上)这篇文章讲的很详细,需要耐心去读。在他的基础上,个人总结和简化一些。

eureka server端的启动类是EurekaServerBootstrap,启动的时候初始化了一些环境配置和内容。

public void contextInitialized(ServletContext context) {
		try {
			// 初始化环境配置
			initEurekaEnvironment();
			// 初始化内容
			initEurekaServerContext();

			context.setAttribute(EurekaServerContext.class.getName(), this.serverContext);
		}
		catch (Throwable e) {
			log.error("Cannot bootstrap eureka server :", e);
			throw new RuntimeException("Cannot bootstrap eureka server :", e);
		}
	}

进入到initEurekaEnvironment()方法里,清楚的发现eureka启动时会去加载数据中心的实例,如果没有就使用默认。

protected void initEurekaEnvironment() throws Exception {
		log.info("Setting the eureka configuration..");

		String dataCenter = ConfigurationManager.getConfigInstance()
				.getString(EUREKA_DATACENTER);
		if (dataCenter == null) {
			log.info(
					"Eureka data center value eureka.datacenter is not set, defaulting to default");
			ConfigurationManager.getConfigInstance()
					.setProperty(ARCHAIUS_DEPLOYMENT_DATACENTER, DEFAULT);
		}
		else {
			ConfigurationManager.getConfigInstance()
					.setProperty(ARCHAIUS_DEPLOYMENT_DATACENTER, dataCenter);
		}
		String environment = ConfigurationManager.getConfigInstance()
				.getString(EUREKA_ENVIRONMENT);
		if (environment == null) {
			ConfigurationManager.getConfigInstance()
					.setProperty(ARCHAIUS_DEPLOYMENT_ENVIRONMENT, TEST);
			log.info(
					"Eureka environment value eureka.environment is not set, defaulting to test");
		}
		else {
			ConfigurationManager.getConfigInstance()
					.setProperty(ARCHAIUS_DEPLOYMENT_ENVIRONMENT, environment);
		}
	}

接下来我们查看另一个内容初始化的方法。

protected void initEurekaServerContext() throws Exception {
		// For backward compatibility
		JsonXStream.getInstance().registerConverter(new V1AwareInstanceInfoConverter(),
				XStream.PRIORITY_VERY_HIGH);
		XmlXStream.getInstance().registerConverter(new V1AwareInstanceInfoConverter(),
				XStream.PRIORITY_VERY_HIGH);

		if (isAws(this.applicationInfoManager.getInfo())) {
			this.awsBinder = new AwsBinderDelegate(this.eurekaServerConfig,
					this.eurekaClientConfig, this.registry, this.applicationInfoManager);
			this.awsBinder.start();
		}

		EurekaServerContextHolder.initialize(this.serverContext);

		log.info("Initialized server context");

		// Copy registry from neighboring eureka node
		int registryCount = this.registry.syncUp();
		this.registry.openForTraffic(this.applicationInfoManager, registryCount);

		// Register all monitoring statistics.
		EurekaMonitors.registerAllStats();
	}

从这里明白,内容初始化后的服务注册是在这里实现的。然后追踪这个register,定位到EurekaServerAutoConfiguration这个类

@Bean
	public PeerAwareInstanceRegistry peerAwareInstanceRegistry(
			ServerCodecs serverCodecs) {
		this.eurekaClient.getApplications(); // force initialization
		return new InstanceRegistry(this.eurekaServerConfig, this.eurekaClientConfig,
				serverCodecs, this.eurekaClient,
				this.instanceRegistryProperties.getExpectedNumberOfClientsSendingRenews(),
				this.instanceRegistryProperties.getDefaultOpenForTrafficCount());
	}

了解到从client端获取服务注册的信息。

接下来分析一下关于eureka client端的代码,首先进行@EnableDiscoveryClient注解类。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({EnableDiscoveryClientImportSelector.class})
public @interface EnableDiscoveryClient {
    boolean autoRegister() default true;
}

这个类导入了EnableDiscoveryClientImportSelector类,然后进去看看这个类干了啥。

 public String[] selectImports(AnnotationMetadata metadata) {
        String[] imports = super.selectImports(metadata);
        AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(this.getAnnotationClass().getName(), true));
        boolean autoRegister = attributes.getBoolean("autoRegister");
        if (autoRegister) {
            List<String> importsList = new ArrayList(Arrays.asList(imports));
            importsList.add("org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration");
            imports = (String[])importsList.toArray(new String[0]);
        } else {
            Environment env = this.getEnvironment();
            if (ConfigurableEnvironment.class.isInstance(env)) {
                ConfigurableEnvironment configEnv = (ConfigurableEnvironment)env;
                LinkedHashMap<String, Object> map = new LinkedHashMap();
                map.put("spring.cloud.service-registry.auto-registration.enabled", false);
                MapPropertySource propertySource = new MapPropertySource("springCloudDiscoveryClient", map);
                configEnv.getPropertySources().addLast(propertySource);
            }
        }

        return imports;
    }

    protected boolean isEnabled() {
        return (Boolean)this.getEnvironment().getProperty("spring.cloud.discovery.enabled", Boolean.class, Boolean.TRUE);
    }

代码中主要两个方法,selectImports()方法调用父类中的selectImports()方法去读取jar包里的配置文件。isEnabled()方法就很易懂,判断是否可发现。也就是说client端起来的时候读取本地的配置文件,并发送请求到server端进行注册。
服务健康判断以及拉取所有服务的代码也简单明了。EurekaDiscoveryClient类里的getservice()方法拉取服务端所有注册的服务。

public List<String> getServices() {
		Applications applications = this.eurekaClient.getApplications();
		if (applications == null) {
			return Collections.emptyList();
		}
		List<Application> registered = applications.getRegisteredApplications();
		List<String> names = new ArrayList<>();
		for (Application app : registered) {
			if (app.getInstances().isEmpty()) {
				continue;
			}
			names.add(app.getName().toLowerCase());

		}
		return names;
	}

同理服务的健康监听也有专门的EurekaHealthIndicator去实现。
再讲一个重要的类EurekaClientConfigBean,这个类配置了很多注册以及缓存的相关信息。

// 标注了服务注册的延时时间
public int getInitialInstanceInfoReplicationIntervalSeconds() {
		return initialInstanceInfoReplicationIntervalSeconds;
	}

public int getRegistryFetchIntervalSeconds() {
		return registryFetchIntervalSeconds;
	}
	// 标注了获的服务实例的延时时间
public int getInstanceInfoReplicationIntervalSeconds() {
		return instanceInfoReplicationIntervalSeconds;
	}

所以这就是eureka注册服务比较慢的原因,也是它不能保持cp的原因,因为注册服务以及更新缓存都有一定的延时性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值