为了开发效率高效和业务逻辑清晰,越来越多的项目采用分布式系统。分布式最重要的就是注册中心了。Eureka是SpringCloud原生提供的注册中心,来look一波吧。

超光速入门

服务端

引入依赖:

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

给启动类加上注解@EnableEurekaServer

@EnableEurekaServer
@SpringBootApplication
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}

配置一下yml文件:

#端口号
server:
port: 8331
#Eureka实例名,集群中根据这里相互识别
eureka:
instance:
hostname: eureka
#客户端
client:
#是否开启注册服务,作为注册中心,就不注册了
register-with-eureka: false
#是否拉取服务列表,这里我只提供服务给别的服务。
fetch-registry: false
#注册中心地址
service-url:
defaultZone: http://localhost:8331/eureka/

启动项目EurekaApplication ,浏览器访问http://localhost:8331/,Euerka 服务器搭建成功了。

现在还没有东西注册进来。

客户端

引入依赖:

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

这回启动类注解变了,@EnableDiscoveryClient

@EnableDiscoveryClient
@SpringBootApplication
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class, args);
}
}

接下来是配置文件:

#端口号
server:
port: 8332
#Eureka实例名,集群中根据这里相互识别
spring:
application:
name: first-service
eureka:
#客户端
client:
#注册中心地址
service-url:
defaultZone: http://localhost:8331/eureka/

然后启动项目,过一会儿,刷新页面:

注册中心有了刚才那个服务了。这个叫做first-service注册到注册中心,它既可以叫做生产者,也可以被叫做消费者,因为它可以为别的服务提供接口,也可以调用其他服务提供的接口。总之,无论是生产者还是消费者,它都被叫做Client,要用@EnableDiscoveryClient注解。我不小心点进去这个注解里面,发现还有个参数,boolean autoRegister() default true。这是是否项目已启动,该服务自动注册到注册中心。默认为自动。

除了@EnableDiscoveryClient这个注解以外,还可以使用另外一个注解@EnableEurekaClient。效果相同,如果是Eureka做注册中心的话,建议使用@EnableEurekaClient,如果是其他注册中心的话(例如阿里的nacos),建议使用@EnableDiscoveryClient。

原理

要想运行起来一个个微服务,形成分布式系统,作为注册中心和其中服务,应该实现一下需求:

  1. 服务们可以顺利注册到注册中心。
  2. 某服务可以通过注册中心知道注册中心有哪些服务可以使用,并且这一过程需要保证实时性。
  3. 注册中心需要实时知道服务们是否还存活。

一个服务client注册到注册中心eureka,该client的信息会被存在一个Map中,实现了第一步。同时,client会拉取一份名单,名单里面有其他注册服务的信息,并且为了保证实时性,每30s会再从注册中心那边拉取一份名单信息,实现了第二步。为了确保注册中心实时知道哪些服务还存活着,需要每个client,每隔一段时间(默认30s)向注册中心发送一个心跳,告诉注册中心,我还在,注册中心那份名单拿上还会记录着这个client还可以用,实现了第三步。

先看一眼注册中心,也就是服务端Service,有个启动引导类EurekaBootStrap,其中有个方法:

@Override
public void contextInitialized(ServletContextEvent event) {
try {
initEurekaEnvironment();
initEurekaServerContext();
ServletContext sc = event.getServletContext();
sc.setAttribute(EurekaServerContext.class.getName(), serverContext);
} catch (Throwable e) {
logger.error("Cannot bootstrap eureka server :", e);
throw new RuntimeException("Cannot bootstrap eureka server :", e);
}
}

这方法是初始化Eureka方法的,现在我特别想知道注册中心是用什么数据结构存下客户端client信息的,所以我得去找注册中心为客户端client提供的注册接口,于是乎,点进initEurekaServerContext()这个方法看看,有个PeerAwareInstanceRegistry这个接口,再点进去看看,发现了

void register(InstanceInfo info, boolean isReplication);

看下它的实现类

@Override
public void register(final InstanceInfo info, final boolean isReplication) {
int leaseDuration = Lease.DEFAULT_DURATION_IN_SECS;
if (info.getLeaseInfo() != null && info.getLeaseInfo().getDurationInSecs() > 0) {
leaseDuration = info.getLeaseInfo().getDurationInSecs();
}
super.register(info, leaseDuration, isReplication);
replicateToPeers(Action.Register, info.getAppName(), info.getId(), info, null, isReplication);
}

replicateToPeers() 这个方法用于注册中心是集群的情况,主要是注册完之后,同步该服务给其他eureka节点。

public void register(InstanceInfo registrant, int leaseDuration, boolean isReplication) {
try {
read.lock();
Map<String, Lease<InstanceInfo>> gMap = registry.get(registrant.getAppName());
REGISTER.increment(isReplication);
if (gMap == null) {
final ConcurrentHashMap<String, Lease<InstanceInfo>> gNewMap = new ConcurrentHashMap<String, Lease<InstanceInfo>>();
gMap = registry.putIfAbsent(registrant.getAppName(), gNewMap);
if (gMap == null) {
gMap = gNewMap;
}
}
Lease<InstanceInfo> existingLease = gMap.get(registrant.getId());
... 仿佛有好多代码...
} finally {
read.unlock();
}
}

目测 registry 应该就是储存着所有的服务,点一下看其结构。

private final ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>> registry
= new ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>>();

最外层是线程安全的ConcurrentHashMap,key值是registrant.getAppName(),也就是实例中的应用名称 first-service。 里面又是一个ConcurrentHashMap(代码里面是Map接口,但其实肯定是ConcurrentHashMap,你可以看gNewMap 对象怎么new的)。里面这个key是registrant.getId()实例id,value 是Lease,这里面存着服务实例和过期时间什么的。ok,具体注册,今天找到地方,先不看了。

关于client端,需要定时拉取服务名单,定时发送注册中心一个心跳。所以用了两个定时器。

在DiscoveryClient 类中,有个initScheduledTasks() 这个方法,是初始化那两个定时器的,简略代码如下:

private void initScheduledTasks() {
if (clientConfig.shouldFetchRegistry()) {
// registry cache refresh timer
int registryFetchIntervalSeconds = clientConfig.getRegistryFetchIntervalSeconds();
int expBackOffBound = clientConfig.getCacheRefreshExecutorExponentialBackOffBound();
scheduler.schedule(
new TimedSupervisorTask(
"cacheRefresh",
scheduler,
cacheRefreshExecutor,
registryFetchIntervalSeconds,
TimeUnit.SECONDS,
expBackOffBound,
new CacheRefreshThread()
),
registryFetchIntervalSeconds, TimeUnit.SECONDS);
} if (clientConfig.shouldRegisterWithEureka()) {
int renewalIntervalInSecs = instanceInfo.getLeaseInfo().getRenewalIntervalInSecs();
int expBackOffBound = clientConfig.getHeartbeatExecutorExponentialBackOffBound();
logger.info("Starting heartbeat executor: " + "renew interval is: {}", renewalIntervalInSecs); // Heartbeat timer
scheduler.schedule(
new TimedSupervisorTask(
"heartbeat",
scheduler,
heartbeatExecutor,
renewalIntervalInSecs,
TimeUnit.SECONDS,
expBackOffBound,
new HeartbeatThread()
),
renewalIntervalInSecs, TimeUnit.SECONDS); } else {
logger.info("Not registering with Eureka server per configuration");
}

小结

SpringBoot让集成Eureka非常的简单,本篇提供了快速入门的示例。今后还要考虑到注册中心集群的问题。当然,现在还有更好用的注册中心,阿里的nacos,不仅有注册中心的功能,同时还继承了配置中心的功能。了解Eureka工作原理,有助于帮助我们更好的理解分布式系统中的注册中心,为了将来学习了解其他注册中心提供理论基础。

【SpringCloud】Eureka入门与原理的更多相关文章

  1. springcloud+eureka简单入门案例

    springcloud+eureka简单入门案例 一.服务提供者 直接提供服务,入门案例没有特别要设置的地方,注意下端口,由于要启动多个服务,可能会冲突 配置文件(src/main/resources ...

  2. SpringCloud的入门学习之深入理解Eureka注册中心

    1.Eureka 注册中心三种角色. 答:a.Eureka Server,注册中心,通过 Register.Get.Renew 等接口提供服务的注册和发现. b.Application Service ...

  3. SpringCloud之Eureka注册中心原理及其搭建

    一.Eureka简介 Eureka是Netflix开发的服务发现框架,本身是一个基于REST的服务,主要用于定位运行在AWS域中的中间层服务,以达到负载均衡和中间层服务故障转移的目的.SpringCl ...

  4. SpringCloud的入门学习之Eureka(高可用注册中心HA)构建Provider服务、Consumer服务

    1.在高可用的Eureka注册中心中构建provider服务. 使用springboot的多环境配置,来搭建Eureka的高可用集群式部署.由于使用的是maven构建的springboot项目,所以首 ...

  5. Eureka 入门,带视频

    疯狂创客圈 Java 高并发[ 亿级流量聊天室实战]实战系列 [博客园总入口 ] 架构师成长+面试必备之 高并发基础书籍 [Netty Zookeeper Redis 高并发实战 ] 前言 Crazy ...

  6. springcloud Config 入门,带视频

    疯狂创客圈 Java 高并发[ 亿级流量聊天室实战]实战系列 [博客园总入口 ] 架构师成长+面试必备之 高并发基础书籍 [Netty Zookeeper Redis 高并发实战 ] 前言 Crazy ...

  7. 微服务之SpringCloud实战(二):SpringCloud Eureka服务治理

    服务治理 SpringCloud Eureka是SpringCloud Netflix微服务套件的一部分,它基于Netflix Eureka做了二次封装,主要完成微服务的服务治理功能,SpringCl ...

  8. 【原创】SpringBoot & SpringCloud 快速入门学习笔记(完整示例)

    [原创]SpringBoot & SpringCloud 快速入门学习笔记(完整示例) 1月前在系统的学习SpringBoot和SpringCloud,同时整理了快速入门示例,方便能针对每个知 ...

  9. 微服务SpringCloud+Docker入门到高级实战(教程详情)

    第一章 课程介绍和学习路线 1.微服务架构SpringCloud课程介绍 简介:课程介绍和课程大纲讲解,讲课风格和重点内容理解技巧 2.技术选型和学后水平 简介:课程所需基础和技术选型讲解,学完课程可 ...

随机推荐

  1. StarUML类图相关——关联、聚合、组合、泛化、依赖、实现

    在阅读设计模式相关的书籍,或者其他一些项目.相关博客等等,经常会遇到类图,它对于一个类的信息,如变量.方法及其可见性,类与类(接口)之间的继承关系.依赖关系.聚合关系.组合关系等,都可以比较形象得当地 ...

  2. Windbg 实践之符号篇

    How to display the size value 1)一开始不会加载,chksym 了一下就加载了. 2) 新版本已经可以显示size的大小了 3)?? 显示变量的类型 4)x std::v ...

  3. delphi http server

    unit Unit1; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System ...

  4. String StringBuffer和StringBuilder的区别和联系

    1:String,StringBuffer和StringBuilder概念 1.1:String String中使用字符串数组来存储字符串,但是是fianl来修饰的,所以String的内容不可改变. ...

  5. Mybatis实现联合查询(六)

    1. 疑问 在之前的章节中我们阐述了如何用Mybatis实现检查的查询,而我们实际的需求中,绝大部分查询都不只是针对单张数据表的简单查询,所以我们接下来要看一下Mybatis如何实现联合查询. 2. ...

  6. 树上问题&图论模板整理

    去除过水的模板,包括但不限于dijkstra(甚至堆优化都被过滤了).SPFA.kruskal.拓扑排序等. 欧拉回路:http://uoj.ac/problem/117 #include<bi ...

  7. usr/sbin/inetd

    root 4 0.0 1344 1204? S 17:09 0:10 /usr/sbin/inetd 运行 Internet 超级 服务器,它负责监听 Internet sockets 上的连接,并调 ...

  8. vue整合外部js

    vue引入外部jsimport { TrackLine } from "../../../../../static/js/trajectory.js";import { initM ...

  9. js中将json字符串转换成json对象

    在我们使用js请求后台控制器传回的结果result值的时候,经常会出现返回结果值为json字符串的情况,字符串无法在js中直接使用 返回样式栗子: 这是一个json字符串:result = " ...

  10. share团队冲刺5

    团队冲刺第五天 昨天:按钮操作,自定义按钮及各种原件样式 今天:设置按钮按下效果,界面布局 问题:无