[Eureka] Introduction and principles SpringCloud

In order to develop effective and efficient business logic clear, more and more projects using distributed systems. Distributed most important thing is the registry. Eureka is a registered center SpringCloud native offered to look a wave of it.

Getting over the speed of light

Server

The introduction of dependence:

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

Add comments to the class start @EnableEurekaServer

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

Configure the look yml file:

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

Start the project EurekaApplication, browser to access http: // localhost: 8331 /, Euerka server build success.

Now things have not registered to come.

Client

The introduction of dependence:

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

This time startup class notes changed, @ EnableDiscoveryClient

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

Next is the configuration file:

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

Then start the project, after a while, refresh the page:

With just a registry that served. This is called first-service registered with the registry, both can be called a producer, can also be called a consumer because it can provide an interface for other services, you can also call other services provided by the interface. In short, whether producers or consumers, which are called the Client, use @EnableDiscoveryClient comment. I accidentally point into this annotation inside, we found that there is a parameter boolean autoRegister() default true. This is whether the project has been started, the service is automatically registered to the registry. The default is Auto.

In addition to @EnableDiscoveryClient this annotation, you also can use another comment @EnableEurekaClient. The same effect, if it is registered to do Eureka Center, it is recommended to use @EnableEurekaClient, if other registries words (such as Ali nacos), recommended @EnableDiscoveryClient.

principle

To run up a micro-service, form a distributed system, as a registration center and where the service should achieve what needs:

  1. Service can successfully registered with the registry.
  2. By registering a service center you can know what registry services can use, and this process needs to ensure real-time.
  3. Registry services require real-time to know whether they were alive.

一个服务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工作原理,有助于帮助我们更好的理解分布式系统中的注册中心,为了将来学习了解其他注册中心提供理论基础。

Guess you like

Origin www.cnblogs.com/pjjlt/p/11019247.html