springcloud(暂时结束)

来源:互联网 发布:客户端软件开发 编辑:程序博客网 时间:2024/05/19 18:40

最近被人鄙视了,心情低落了很久,被嘲讽基础差,了解的不少,掌握的没几个。还有一些个人原因,暂时终结了springcloud学习,准备从最基础开始,重新学习总结,免得以后被人鄙视。

闲话不多说,最事情还是要有头有尾,把最后一点的eureka学习完整,也算是对springcloud有了一个概括上的了解,掌握还差的远。

首先学习服务注册,当服务启动时,要把自身的信息发送到服务中心。从上一章中我们学习到了springcloud利用的EurekaClient来获取服务。我们从这个接口入手。

该接口的实现类是DiscoveryClient,看到其注册服务的方法:

boolean register() throws Throwable {        logger.info(PREFIX + appPathIdentifier + ": registering service...");        EurekaHttpResponse<Void> httpResponse;        try {            httpResponse = eurekaTransport.registrationClient.register(instanceInfo);        } catch (Exception e) {            logger.warn("{} - registration failed {}", PREFIX + appPathIdentifier, e.getMessage(), e);            throw e;        }        if (logger.isInfoEnabled()) {            logger.info("{} - registration status: {}", PREFIX + appPathIdentifier, httpResponse.getStatusCode());        }        return httpResponse.getStatusCode() == 204;    }

继续追踪,可以看到最终调用的是AbstractJerseyEurekaHttpClient的方法:

 public EurekaHttpResponse<Void> register(InstanceInfo info) {        String urlPath = "apps/" + info.getAppName();        ClientResponse response = null;        try {            Builder resourceBuilder = jerseyClient.resource(serviceUrl).path(urlPath).getRequestBuilder();            addExtraHeaders(resourceBuilder);            response = resourceBuilder                    .header("Accept-Encoding", "gzip")                    .type(MediaType.APPLICATION_JSON_TYPE)                    .accept(MediaType.APPLICATION_JSON)                    .post(ClientResponse.class, info);            return anEurekaHttpResponse(response.getStatus()).headers(headersOf(response)).build();        } finally {            if (logger.isDebugEnabled()) {                logger.debug("Jersey HTTP POST {}/{} with instance {}; statusCode={}", serviceUrl, urlPath, info.getId(),                        response == null ? "N/A" : response.getStatus());            }            if (response != null) {                response.close();            }        }    }

看到其中serviceUrl就是我们配置的服务中心zone的地址,info就是该客户端的基本信息:



我们再看一下是什么地方调用了这个注册方法,InstanceInfoReplicator在这个类中,这个类实现了Runnable接口,在其run方法中会调用注册方法。

这个类又在DiscoveryClient初始化时启动线程。

 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);            // InstanceInfo replicator            instanceInfoReplicator = new InstanceInfoReplicator(                    this,                    instanceInfo,                    clientConfig.getInstanceInfoReplicationIntervalSeconds(),                    2); // burstSize            statusChangeListener = new ApplicationInfoManager.StatusChangeListener() {                @Override                public String getId() {                    return "statusChangeListener";                }                @Override                public void notify(StatusChangeEvent statusChangeEvent) {                    if (InstanceStatus.DOWN == statusChangeEvent.getStatus() ||                            InstanceStatus.DOWN == statusChangeEvent.getPreviousStatus()) {                        // log at warn level if DOWN was involved                        logger.warn("Saw local status change event {}", statusChangeEvent);                    } else {                        logger.info("Saw local status change event {}", statusChangeEvent);                    }                    instanceInfoReplicator.onDemandUpdate();                }            };            if (clientConfig.shouldOnDemandUpdateStatusChange()) {                applicationInfoManager.registerStatusChangeListener(statusChangeListener);            }            instanceInfoReplicator.start(clientConfig.getInitialInstanceInfoReplicationIntervalSeconds());        } else {            logger.info("Not registering with Eureka server per configuration");        }    }

如果需要向eureka注册服务,则开启心跳检测,并注册服务。


接下来看服务中心的代码,服务中心的启动类EurekaBootStrap,有以下方法开始初始化:

 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);        }    }

在initEurekaServerContect()方法中初始化了两个类的对象:PeerAwareInstanceRegistry和PeerEurekaNodes

分别看这两个类:这连个类的作用就是服务注册和多个服务中心的服务同步。

看PeerAwareInstanceRegistry这个类的方法:

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);    }

就是注册服务和同步服务到其他的中心,我们看一看到其父类中维护了一个map,保存了所有的服务。

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


上面说客户端的时候,客户端是发送请求到服务器端,服务器端肯定有一个接口用来接受请求,我们找到
ApplicationResource这个类,就是用来接收请求的。

 @POST    @Consumes({"application/json", "application/xml"})    public Response addInstance(InstanceInfo info,                                @HeaderParam(PeerEurekaNode.HEADER_REPLICATION) String isReplication) {        logger.debug("Registering instance {} (replication={})", info.getId(), isReplication);        // validate that the instanceinfo contains all the necessary required fields        if (isBlank(info.getId())) {            return Response.status(400).entity("Missing instanceId").build();        } else if (isBlank(info.getHostName())) {            return Response.status(400).entity("Missing hostname").build();        } else if (isBlank(info.getAppName())) {            return Response.status(400).entity("Missing appName").build();        } else if (!appName.equals(info.getAppName())) {            return Response.status(400).entity("Mismatched appName, expecting " + appName + " but was " + info.getAppName()).build();        } else if (info.getDataCenterInfo() == null) {            return Response.status(400).entity("Missing dataCenterInfo").build();        } else if (info.getDataCenterInfo().getName() == null) {            return Response.status(400).entity("Missing dataCenterInfo Name").build();        }        // handle cases where clients may be registering with bad DataCenterInfo with missing data        DataCenterInfo dataCenterInfo = info.getDataCenterInfo();        if (dataCenterInfo instanceof UniqueIdentifier) {            String dataCenterInfoId = ((UniqueIdentifier) dataCenterInfo).getId();            if (isBlank(dataCenterInfoId)) {                boolean experimental = "true".equalsIgnoreCase(serverConfig.getExperimental("registration.validation.dataCenterInfoId"));                if (experimental) {                    String entity = "DataCenterInfo of type " + dataCenterInfo.getClass() + " must contain a valid id";                    return Response.status(400).entity(entity).build();                } else if (dataCenterInfo instanceof AmazonInfo) {                    AmazonInfo amazonInfo = (AmazonInfo) dataCenterInfo;                    String effectiveId = amazonInfo.get(AmazonInfo.MetaDataKey.instanceId);                    if (effectiveId == null) {                        amazonInfo.getMetadata().put(AmazonInfo.MetaDataKey.instanceId.getName(), info.getId());                    }                } else {                    logger.warn("Registering DataCenterInfo of type {} without an appropriate id", dataCenterInfo.getClass());                }            }        }        registry.register(info, "true".equals(isReplication));        return Response.status(204).build();  // 204 to be backwards compatible    

最终如果注册成功,则返回了204,和我们在实际应用中看到的日志信息是完全一样的。

eureka的一些配置信息都在DefaultEurekaClientConfig这里,默认值之类的可以来这里查找。

例如

 @Override    public int getEurekaServerReadTimeoutSeconds() {        return configInstance.getIntProperty(                namespace + EUREKA_SERVER_READ_TIMEOUT_KEY, 8).get();    }
等等。


springcloud就暂时放一放了,恶补一下基础,接下来准备先从jvm开始,好好系统的学习一遍。同时也再过一遍设计模式,准备把每个设计模式用java实现一遍加深印象。

代码会一点一点的上传到github上。不要再被鄙视了。https://github.com/Begro/acm.git



原创粉丝点击