Spring Cloud学习02-服务注册与发现

Spring Cloud学习02-服务注册与发现

Spring Cloud简介

Spring Cloud是一个基于Spring Boot实现的云应用开发工具,它为基于JVM的云应用开发中的配置管理、服务发现、断路器、智能路由、微代理、控制总线、全局锁、决策竞选、分布式会话和集群状态管理等操作提供了一种简单的开发方式。

Spring Cloud包含了多个子项目(针对分布式系统中涉及的多个不同开源产品),比如:Spring Cloud Config、Spring Cloud Netflix、Spring Cloud CloudFoundry、Spring Cloud AWS、Spring Cloud Security、Spring Cloud Commons、Spring Cloud Zookeeper、Spring Cloud CLI等项目。

微服务架构

“微服务架构”在这几年非常的火热,以至于关于微服务架构相关的产品社区也变得越来越活跃(比如:netflix、dubbo),Spring Cloud也因Spring社区的强大知名度和影响力也被广大架构师与开发者备受关注。

那么什么是“微服务架构”呢?简单的说,微服务架构就是将一个完整的应用从数据存储开始垂直拆分成多个不同的服务,每个服务都能独立部署、独立维护、独立扩展,服务与服务间通过诸如RESTful API的方式互相调用。

对于“微服务架构”,大家在互联网可以搜索到很多相关的介绍和研究文章来进行学习和了解。也可以阅读始祖Martin Fowler的《Microservices》,本文不做更多的介绍和描述。

服务注册与发现

在简单介绍了Spring Cloud和微服务架构之后,下面回归本文的主旨内容,如何使用Spring Cloud搭建服务注册与发现模块。

这里我们会用到Spring Cloud Netflix,该项目是Spring Cloud的子项目之一,主要内容是对Netflix公司一系列开源产品的包装,它为Spring Boot应用提供了自配置的Netflix OSS整合。通过一些简单的注解,开发者就可以快速的在应用中配置一下常用模块并构建庞大的分布式系统。它主要提供的模块包括:服务发现(Eureka),断路器(Hystrix),智能路有(Zuul),客户端负载均衡(Ribbon)等。

所以,我们这里的核心内容就是服务发现模块:Eureka。

Eureka简介

EurekaNetflix开发的,一个基于REST服务的,服务注册与发现的组件

它主要包括两个组件:Eureka Server和Eureka Client

Eureka Client:一个Java客户端,用于简化与Eureka Server的交互(通常就是微服务中的客户端和服务端)

Eureka Server:提供服务注册和发现的能力(通常就是微服务中的注册中心)

各个微服务启动时,会通过Eureka Client向Eureka Server注册自己,Eureka Server会存储该服务的信息

也就是说,每个微服务的客户端和服务端,都会注册到Eureka Server,这就衍生出了微服务相互识别的话题

同步:每个Eureka Server同时也是Eureka Client(逻辑上的)

多个Eureka Server之间通过复制的方式完成服务注册表的同步,形成Eureka的高可用

识别:Eureka Client会缓存Eureka Server中的信息

即使所有Eureka Server节点都宕掉,服务消费者仍可使用缓存中的信息找到服务提供者(笔者已亲测)

续约:微服务会周期性(默认30s)地向Eureka Server发送心跳以Renew(续约)自己的信息(类似于heartbeat)

续期:Eureka Server会定期(默认60s)执行一次失效服务检测功能

它会检查超过一定时间(默认90s)没有Renew的微服务,发现则会注销该微服务节点

Spring Cloud已经把Eureka集成在其子项目Spring Cloud Netflix里面

关于Eureka配置的最佳实践,可参考:https://github.com/spring-cloud/spring-cloud-netflix/issues/203

更多介绍,可参考:http://cloud.spring.io/spring-cloud-static/Camden.SR4/#spring-cloud-eureka-server

下面我们动手来做一些尝试。

创建服务注册中心

创建一个基础的Spring Boot工程,工程目录如下:


并在pom.xml中引入需要的依赖内容:

```

<parent>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-parent</artifactId>

<version>1.5.3.RELEASE</version>

<relativePath/></parent>

<properties>

<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

<java.version>1.8</java.version>

</properties>

<dependencyManagement>

<dependencies>

<dependency>

<groupId>org.springframework.cloud</groupId>

<artifactId>spring-cloud-dependencies</artifactId>

<version>Dalston.RELEASE</version>

<type>pom</type>

<scope>import</scope>

</dependency>

</dependencies>

</dependencyManagement>

<dependencies>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter</artifactId>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-test</artifactId>

<scope>test</scope>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-actuator</artifactId>

</dependency>

<dependency>

<groupId>org.springframework.cloud</groupId>

<artifactId>spring-cloud-starter-config</artifactId>

</dependency>

org.springframework.cloud

spring-cloud-starter-eureka

--><dependency>

<groupId>org.springframework.cloud</groupId>

<artifactId>spring-cloud-starter-eureka-server</artifactId>

</dependency>

</dependencies>

```

通过@EnableEurekaServer注解启动一个服务注册中心提供给其他应用进行对话。这一步非常的简单,只需要在一个普通的Spring Boot应用中添加这个注解就能开启此功能,比如下面的例子:

@SpringBootApplication

@EnableEurekaServer

public classApplicaiton {

public static voidmain(String[] args) {

//SpringApplication.run(Applicaiton.class,args);

//new SpringApplicationBuilder(Applicaiton.class).web(true).run(args);

newSpringApplicationBuilder(Applicaiton.class).run(args);

}

}

在默认设置下,该服务注册中心也会将自己作为客户端来尝试注册它自己,所以我们需要禁用它的客户端注册行为,只需要在application.properties中问增加如下配置:

server.port=1111

eureka.client.register-with-eureka=false#设置是否从注册中心获取注册信息(缺省true),因为这是一个单点的EurekaServer

#不需要同步其他EurekaServer节点的数据,故设为falseeureka.client.fetch-registry=false#设置是否将自己作为客户端注册到注册中心(缺省true#这里为不需要(查看@EnableEurekaServer注解的源码,会发现它间接用到了@EnableDiscoveryClient#在未设置defaultZone的情况下,注册中心在本例中的默认地址就是http://127.0.0.1:1100/eureka/

#但奇怪的是,启动注册中心时,控制台还是会打印这个地址的节点:http://localhost:8761/eureka/

#而实际服务端注册时,要使用1100端口的才能注册成功,8761端口的会注册失败并报告异常--说法不正确

#打印信息如下:2017-05-24 11:16:49.148INFO 14800 --- [main] c.n.eureka.DefaultEurekaServerContext: Initializing ...

2017-05-24 11:16:49.156INFO 14800 --- [main] c.n.eureka.cluster.PeerEurekaNodes: Adding new peer nodes [http://localhost:1111/eureka/]eureka.client.serviceUrl.defaultZone=http://localhost:${server.port}/eureka/#实际测试:若修改尾部的eureka为其他的,比如/myeureka,注册中心启动没有问题,但服务端在注册时会失败#报告异常:com.netflix.discovery.shared.transport.TransportException:cannot execute request on any known servereureka.server.enable-self-preservation=falseeureka.server.eviction.interval-timer-in-ms=4000

为了与后续要进行注册的服务区分,这里将服务注册中心的端口通过server.port属性设置为1111。

启动工程后,访问:http://localhost:1111/

可以看到下面的页面,其中还没有发现任何服务


创建服务提供方

下面我们创建提供服务的客户端,并向服务注册中心注册自己。

假设我们有一个提供计算功能的微服务模块,我们实现一个RESTful API,通过传入两个参数a和b,最后返回a + b的结果。

首先,创建一个基本的Spring Boot应用,在pom.xml中,加入如下配置:

<parent>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-parent</artifactId>

<version>1.5.3.RELEASE</version>

<relativePath/></parent>

<properties>

<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

<java.version>1.8</java.version>

</properties>

<dependencyManagement>

<dependencies>

<dependency>

<groupId>org.springframework.cloud</groupId>

<artifactId>spring-cloud-dependencies</artifactId>

<version>Dalston.RELEASE</version>

<type>pom</type>

<scope>import</scope>

</dependency>

</dependencies>

</dependencyManagement>

<dependencies>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter</artifactId>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-test</artifactId>

<scope>test</scope>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-actuator</artifactId>

</dependency>

<dependency>

<groupId>org.springframework.cloud</groupId>

<artifactId>spring-cloud-starter-config</artifactId>

</dependency>

<dependency>

<groupId>org.springframework.cloud</groupId>

<artifactId>spring-cloud-starter-eureka</artifactId>

</dependency>

org.springframework.cloud

spring-cloud-starter-eureka-server

-->

</dependencies>

<build>

<plugins>

<plugin>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-maven-plugin</artifactId>

</plugin>

</plugins>

</build>

其次,实现/add请求处理接口,通过DiscoveryClient对象,在日志中打印出服务实例的相关内容。

@RestController

public classComputerController {

private finalLoggerlogger= Logger.getLogger(getClass());

@Autowired

privateDiscoveryClientdiscoveryclient;

@Autowired

privateEurekaClienteurekaClient;

@RequestMapping(value="/add",method= RequestMethod.GET)

publicInteger add(@RequestParamInteger a,@RequestParamInteger b) {

//ServiceInstance instance = client.getLocalServiceInstance();

InstanceInfo instance =eurekaClient.getNextServerFromEureka("compute-service",false);

Integer r = a + b;

logger.info("/add, host:"+instance.getHostName() +",serviceId:"+ instance.getInstanceId() +",result:"+ r);

returnr;

}

}

最后在主类中通过加上@EnableEurekaClient注解,该注解能激活Eureka中的DiscoveryClient实现,才能实现Controller中对服务信息的输出。

@SpringBootApplication

@EnableEurekaClient

public classComputeServiceApplication {

public static voidmain(String[] args) {

//new SpringApplicationBuilder(ComputeServiceApplication.class).web(true).run(args);

newSpringApplicationBuilder(ComputeServiceApplication.class).run(args);

}

}

我们在完成了服务内容的实现之后,再继续对application.properties做一些配置工作,具体如下:

server.port=2222

spring.application.name=compute-serviceeureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/

eureka.instance.lease-renewal-interval-in-seconds=10eureka.instance.lease-expirtion-duration-in-seconds=30eureka.client.healthcheck.enabled=true

通过spring.application.name属性,我们可以指定微服务的名称后续在调用的时候只需要使用该名称就可以进行服务的访问。

eureka.client.serviceUrl.defaultZone属性对应服务注册中心的配置内容,指定服务注册中心的位置。

为了在本机上测试区分服务提供方和服务注册中心,使用server.port属性设置不同的端口。

启动该工程后,再次访问:http://localhost:1111/

可以看到,我们定义的服务被注册了。


验证

由于发布的微服务所暴露出去的都是HTTP的接口,所以验证的话,可以在浏览器访问下面的地址:

http:127.0.0.1:2222/add?a=3&b=13


目前为止,我们完成了Spring Cloud

Netflix Eureka搭建注册中心的基本示例,不过也只是尝尝鲜

因为它还存在着很多问题,比如

什么是自我保护模式

服务提供方关闭之后,在注册中心看到的状态还是UP

注册中心的服务提供方显示的名字,是不是可以自定义

等等吧,这些问题,请参见Eureka进阶篇

参考:http://blog.didispace.com/springcloud1/

http://jadyer.cn/2017/01/16/springcloud-eureka/

https://springcloud.cc/spring-cloud-dalston.html#_spring_cloud_netflix

eureka-server的配置文件:

server.port=1111

#设置是否从注册中心获取注册信息(缺省true),因为这是一个单点的EurekaServer

#不需要同步其他EurekaServer节点的数据,故设为false

eureka.client.register-with-eureka=false

#设置是否将自己作为客户端注册到注册中心(缺省true)

#这里为不需要(查看@EnableEurekaServer注解的源码,会发现它间接用到了@EnableDiscoveryClient)

#在未设置defaultZone的情况下,注册中心在本例中的默认地址就是http://127.0.0.1:1100/eureka/

#但奇怪的是,启动注册中心时,控制台还是会打印这个地址的节点:http://localhost:8761/eureka/

#而实际服务端注册时,要使用1100端口的才能注册成功,8761端口的会注册失败并报告异常--说法不正确

eureka.client.fetch-registry=false

#实际测试:若修改尾部的eureka为其他的,比如/myeureka,注册中心启动没有问题,但服务端在注册时会失败

#报告异常:com.netflix.discovery.shared.transport.TransportException:cannot execute request on any known server

eureka.client.serviceUrl.defaultZone=http://localhost:${server.port}/eureka/

#关闭自我保护模式(缺省为打开)

eureka.server.enable-self-preservation=false

#续期时间,即扫描失败服务的间隔时间(缺省为:60*1000ms)

eureka.server.eviction.interval-timer-in-ms=4000


<parent>

<groupId>org.springframework.bootgroupId>

<artifactId>spring-boot-starter-parentartifactId>

<version>1.5.3.RELEASEversion>

<relativePath/>parent>

<properties>

<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>

<project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>

<java.version>1.8java.version>

properties>

<dependencyManagement>

<dependencies>

<dependency>

<groupId>org.springframework.cloudgroupId>

<artifactId>spring-cloud-dependenciesartifactId>

<version>Dalston.RELEASEversion>

<type>pomtype>

<scope>importscope>

dependency>

dependencies>

dependencyManagement>

<dependencies>

<dependency>

<groupId>org.springframework.bootgroupId>

<artifactId>spring-boot-starterartifactId>

dependency>

<dependency>

<groupId>org.springframework.bootgroupId>

<artifactId>spring-boot-starter-testartifactId>

<scope>testscope>

dependency>

<dependency>

<groupId>org.springframework.bootgroupId>

<artifactId>spring-boot-starter-actuatorartifactId>

dependency>

<dependency>

<groupId>org.springframework.cloudgroupId>

<artifactId>spring-cloud-starter-configartifactId>

dependency>

org.springframework.cloud

spring-cloud-starter-eureka

--><dependency>

<groupId>org.springframework.cloudgroupId>

<artifactId>spring-cloud-starter-eureka-serverartifactId>

dependency>

dependencies>

<build>

<plugins>

<plugin>

<groupId>org.springframework.bootgroupId>

<artifactId>spring-boot-maven-pluginartifactId>

plugin>

plugins>

build>


eureka-client配置文件:

server.port=2222

#指定发布的微服务名(以后调用时,只需该名称即可访问该服务)

spring.application.name=compute-service

#指定服务注册中心的地址

eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/

#修改显示的微服务名为IP:端口

eureka.instance.instance-id=${spring.cloud.client.ipAddress}:${server.port}

#eureka.instance-id:${spring.application.name}

#设置微服务调用地址为IP优先(缺省为false)

eureka.instance.prefer-ip-address=true

#eureka.instance.ip-address=192.168.6.16

#心跳时间,即服务续约间隔时间(缺省为30s)

eureka.instance.lease-renewal-interval-in-seconds=10

#发呆时间,即服务续约到期时间(缺省为90s)

eureka.instance.lease-expirtion-duration-in-seconds=30

#开启健康检查(依赖spring-boot-starter-actuator)

eureka.client.healthcheck.enabled=true


pom.xml文件:

<parent>

<groupId>org.springframework.bootgroupId>

<artifactId>spring-boot-starter-parentartifactId>

<version>1.5.3.RELEASEversion>

<relativePath/>parent>

<properties>

<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>

<project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>

<java.version>1.8java.version>

properties>

<dependencyManagement>

<dependencies>

<dependency>

<groupId>org.springframework.cloudgroupId>

<artifactId>spring-cloud-dependenciesartifactId>

<version>Dalston.RELEASEversion>

<type>pomtype>

<scope>importscope>

dependency>

dependencies>

dependencyManagement>

<dependencies>

<dependency>

<groupId>org.springframework.bootgroupId>

<artifactId>spring-boot-starterartifactId>

dependency>

<dependency>

<groupId>org.springframework.bootgroupId>

<artifactId>spring-boot-starter-testartifactId>

<scope>testscope>

dependency>

<dependency>

<groupId>org.springframework.bootgroupId>

<artifactId>spring-boot-starter-actuatorartifactId>

dependency>

<dependency>

<groupId>org.springframework.cloudgroupId>

<artifactId>spring-cloud-starter-configartifactId>

dependency>

<dependency>

<groupId>org.springframework.cloudgroupId>

<artifactId>spring-cloud-starter-eurekaartifactId>

dependency>

org.springframework.cloud

spring-cloud-starter-eureka-server

-->

dependencies>

<build>

<plugins>

<plugin>

<groupId>org.springframework.bootgroupId>

<artifactId>spring-boot-maven-pluginartifactId>

plugin>

plugins>

build>

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容