Spring Cloud Eureka

本文详细介绍了如何使用Spring Cloud Eureka搭建服务注册与发现中心,包括创建Eureka项目、编写服务提供者和消费者,以及Eureka的安全配置、集群设置和自我保护模式。此外,还探讨了元数据、健康检查URL和服务上下线监控,展示了Eureka在微服务架构中的关键作用。
摘要由CSDN通过智能技术生成

注册中心在微服务中是必不可少的一部分,主要用来实现服务治理功能。那么,所谓的服务治理又体现在哪些地方?对外,可以快速确定某一个服务的健康状态,其次,eureka本质上是存储了每个服务的注册信息,可以通过eureka提供的某些特定的Rest API快速获取某个服务的注册信息;对内,cloud定义了一些列微服务的开发规则,由于各服务之间需要快速相互访问,Ribbon(Spring Cloud Netflix组件之一)在转发的时候会获取注册中心的服务列表,然后根据对应的路由规则来选择一个服务给Feign(Spring Cloud Netflix组件之一)进行调用,不再需要开发者去编写繁琐的http调用代码。这就是eureka实现服务治理的一些体现。

本示例基于:Spring Cloud Netflix

Spring Cloud发展至今,已经形成了Spring Cloud Netflix和Spring Cloud Alibaba两大阵营以及其他小众开源阵营。犹记得前几年刚接触Spring Cloud,被这个版本各种折磨。

创建eureka项目

1、创建一个maven项目后引入如下依赖:

<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<!-- 注意和springcloud版本兼容,可在官网进行查看:https://spring.io/projects/spring-cloud -->
		<version>2.1.0.RELEASE</version>
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
		<!-- 注意和springboot版本兼容,可在官网进行查看:https://spring.io/projects/spring-cloud -->
		<spring-cloud.version>Greenwich.RELEASE</spring-cloud.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<!-- eureka依赖 -->
		<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
	</dependencies>

	<!-- spring cloud -->
	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

2、创建springboot配置文件

server.port=9999
spring.application.name=first-eureka


# 该应用为注册中心,所以要声明注册中心不要注册自己
eureka.client.register-with-eureka=false
# 由于注册中心的职责是维护服务实例,并不需要去检索服务,所以也设置为false
eureka.client.fetch-registry=false
# 覆盖eureka默认的监听地址
# 在使用非默认端口8761时,一定要指明。否则一旦有服务注册到eureka之后,就会抛出异常(因为默认在监听8761端口)
eureka.client.service-url.defaultZone=http://localhost:9999/eureka/

3、创建启动类

// 声明eureka服务端
@EnableEurekaServer
@SpringBootApplication
public class EurekaApp {

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

4、运行

启动项目后,访问localhost:8761,如下图

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MTM4MTg2Mw==,size_16,color_FFFFFF,t_70

 

编写服务提供者并注册到eureka

1、创建一个maven项目后引入如下依赖:

沿用eureka项目中的maven依赖

2、创建springboot配置文件

server.port=9001
spring.application.name=cloud-server0

# 指向eureka地址,在启动项目的时候就会将该服务注册到eureka中
eureka.client.service-url.defaultZone=http://localhost:9999/eureka/

3、创建启动类

// 声明这是一个eureka的客户端
@EnableEurekaClient
@SpringBootApplication
public class Server0App {

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

4、编写服务提供接口

@Controller
public class Server0Controller {

	@GetMapping("/server0/api0")
	@ResponseBody
	public String methodA() {
		return "第一个服务提供者:第一个接口";
	}
	
}

5、运行

启动项目后,可以在eureka中监听到此服务生产者,如下图:

20210513205802689.png

 

编写服务消费者

1、创建一个maven项目后引入如下依赖:

沿用eureka项目中的maven依赖

2、创建springboot配置文件

server.port=9002
spring.application.name=cloud-server1

# 指向eureka地址,在启动项目的时候就会将该服务注册到eureka中
eureka.client.service-url.defaultZone=http://localhost:9999/eureka/

3、创建启动类

//声明这是一个eureka的客户端
@EnableEurekaClient
@SpringBootApplication
public class Server1App {

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

4、配置基于http的远程调用工具RestTemplate

@Configuration
public class RestTemplateConfig {

	@Bean
	public RestTemplate initRestTemplate() {
		// 这里只是简单配置RestTemplate,工作中请勿这么操作!!!
		return new RestTemplate();
	}
	
}

5、编写服务消费接口

@Controller
public class Server1Controller {

	@Autowired
	private RestTemplate restTemplate;
	
	@RequestMapping("/server1/api0")
	@ResponseBody
	public String methodA() {
		
		return  restTemplate.getForObject("http://localhost:9001/server0/api0", 
				String.class);
		
	}
	
}

6、运行

依次启动eureka、server0、server1,如下图:

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MTM4MTg2Mw==,size_16,color_FFFFFF,t_70

访问服务消费者接口:localhost:9002/server1/api0,如下图:

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MTM4MTg2Mw==,size_16,color_FFFFFF,t_70

7、体验Spring Cloud为微服务带来的便利性

在上面的服务消费中,就是一个普通的http调用,根本没有体验到Spring Cloud为微服务带来的便利性。于是,对服务消费者进行下面的改造。

RestTemplate配置中加一个@LoadBalanced注解

// @LoadBalanced注解会将当前restTemplate bean作为LoadBanlanceClient接口的实现类装配为Spring容器中
// 注意:使用了@LoadBalanced之后,服务之间的相互调用将不再支持ip+port的方式,只能以服务名称的方式相互访问
@LoadBalanced
@Bean
public RestTemplate initRestTemplate() {
	// 这里只是简单配置RestTemplate,工作中请勿这么操作!!!
    return new RestTemplate();
}

服务消费接口直接访问服务名称,如下:

@RequestMapping("/server1/api1")
	@ResponseBody
	public String methodB() {
		
		// ip+端口直接替换成服务提供者的名称
		return restTemplate.getForObject("http://cloud-server0/server0/api0", 
				String.class);
		
	}

重启服务消费者服务后,访问服务消费者接口:localhost:9002/server1/api1,会得到同样的结果,如下图:

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MTM4MTg2Mw==,size_16,color_FFFFFF,t_70

注意:使用了@LoadBalanced注解装配RestTemplate之后,服务之间的调用就只能使用服务名了,如果再使用ip+port方式,会抛出异常:java.lang.IllegalStateException: No instances available for xxx。

思考

为什么要有服务提供者和服务消费者?在一个微服务架构中,消费者在哪里体现呢,前端直接调用微服务接口,是不是客户端就是消费者呢?是不是每个服务提供者都要对应一个服务消费者呢,并且服务提供者向注册中心注册自己,服务消费者只负责发现服务?

上面的问题,本身就是一个伪命题。比如在一个微服务项目中,有A/B/C/D四个服务。并不是单纯的A(服务消费者)调用B(服务提供者)然后返回给客户端。而是A/B/C/D四个服务,统一由注册中心管理,A/B/C/D四个服务之间会相互调用。A调用B时,A作为服务消费者,B作为服务提供者,反过来,某一个业务场景需要B调用A时,B作为服务消费者,A作为服务提供者,服务提供者和服务消费者本身是一个相对的概念。ABCD和注册中心之间相互协作(此时就能体现注册中心的必要性了),形成一个闭环。此时,作为客户端,在调用各个服务的时候,不可能不停的切换ABCD的地址,此时再加入一个网关层,客户端直接访问网关,网关再去聚合各个服务,这样就形成了一个简单的微服务架构。(上面的示例,服务提供者和服务消费者,没有以provider和consumer去命名,就是防止在后续的学习中混淆概念。因为上面的server0和server1,谁是服务提供者,谁是服务消费,都是相对的)。

为eureka加上用户名和密码

eureka自带了一个web管理界面,方面查询注册到上面的服务实例。在实际应用中,注册中心如果使用了公文IP的话,直接访问是不安全的。

1、在eureka项目中,增加Spring Security依赖

<!-- spring security -->
        <dependency>
        	<groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

2、在eureka项目中的配置文件application.properties文件中,增加以下配置信息:

# spring security
spring.security.user.name=zepal
spring.security.user.password=123456

再次访问eureka地址:localhost:9999 就需要输入账号和密码了。在eureka开启认证之后,每个服务配置eureka注册地址的时候,也是需要加上用户名和密码的,如下:

# 指向eureka地址,在启动项目的时候就会将该服务注册到eureka中
# eureka.client.service-url.defaultZone=http://localhost:9999/eureka/
# 开启认证之后的注册地址
eureka.client.service-url.defaultZone=http://zepal:123456@localhost:9999/eureka/

如果配置上面的用户名和密码,那么会已默认的用户名:user,在启动项目时,security会默认分配一个随机密码,打印在控制台,如下:

20210513214350870.png

eureka集群

在实际生产环境中,是需要一个集群来保证eureka高可用的。实现的方式就是:每一台eureka都需要将自身指向其他每个eureka。

下面以2个节点为例展示搭建方式。假设目前有A和B两台机器,按照上面指出的实现方式,就需要:将A机器上的eureka注册到B机器的eureka上,同时需要将B机器上的eureka注册到A机器上。

如果是3台机器:A/B/C.那么就需要,将A注册到B和C上,同时,将B注册到A和C上,将C注册到A和B上。更多机器集群以此类推。

1、复制上面的eureka项目到虚拟机(土豪可以复制到另外一台电脑)。我这里两个项目的命名分别是:cloud-eureka0和cloud-eureka1。

2、通过配置:eureka.client.service-url.defaultZone将eureka0指向eureka1(注意:如果开启了Security,需要在url地址上填上用户名和密码),如下:

eureka0中的配置:

# eureka集群:如果是多个集群服务,用英文逗号分隔每个服务地址即可
eureka.client.service-url.defaultZone=http://zepal:123456@192.168.0.132:9998/eureka/

eureka1中的配置:

# eureka集群:如果是多个集群服务,用英文逗号分隔每个服务地址即可
eureka.client.service-url.defaultZone=http://zepal:123456@localhost:9999/eureka/

启动集群中所有eureka之后,访问eureka地址,如下图:

20210514214939482.png

另外,每个服务向eureka提交注册的地址,需要配置上整个eureka集群中所有的eureka地址:

# 开启认证之后的注册地址:向eureka集群注册服务,多个注册地址用英文逗号分隔
eureka.client.service-url.defaultZone=http://zepal:123456@192.168.0.132:9998/eureka/,http://zepal:123456@localhost:9999/eureka/

eureka自我保护

在开发环境中,经常会遇到如下警告:

20210514223351725.png

当长时间未访问服务或者更改服务名称的时候,就会出现这个警告。在生产环境中,可能会因为网络等因素,注册中心和服务之间无法正常通信,但此时服务其实是正常状态,如果关闭了eureka的"自我保护",那么会在默认时间后(当前版本是60000毫秒,即60s),移除这个无法和注册中心通信的服务。

生产环境时不推荐关闭eureka自我保护的。关闭eureka自我保护,在eureka项目中增加如下配置:

# 为false表示不开启"自我保护"
eureka.server.enable-self-preservation=false
# 失效服务清理间隔,当前版本默认是60000毫秒
eureka.server.eviction-interval-timer-in-ms=50000

 在服务项目中,加入以下依赖和配置:

<!-- actuator依赖 -->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

 

# 配置关闭eureka自我保护,让eureka移除失效的服务更快速
# 开启健康检查
eureka.client.healthcheck.enabled=true
# 默认30s:表示当前项目发送心跳给eureka的频率
eureka.instance.lease-renewal-interval-in-seconds=5
# 默认90s:表示eureka至上一次收到当前项目的心跳之后,等待下一次心跳的超时时间,在这个时间内若没有收到下一次心跳,则移除该服务实例
eureka.instance.lease-expiration-duration-in-seconds=10

自定义注册在eureka中的服务实例id

服务在注册到eureka中的时候,默认的格式如下:

20210518211714949.png

它的组成是:"主机名:服务名称:服务端口"。

接下来,就把它改造成"服务名:服务所在IP:端口号",这样方便一目了然具体服务的信息,一看就知道是哪个服务,在哪台机器,端口是多少。在服务实例项目中(不是eureka项目),加入以下配置:

# 自定义eureka注册信息,${}表达式是引用其他配置项
eureka.instance.instance-id=${spring.application.name}:${spring.cloud.client.ip-address}:${spring.application.instance_id:${server.port}}
eureka.instance.prefer-ip-address=true

重启服务后,在eureka管理界面中可以看到如下信息:

20210518212802340.png

自定义实例的跳转链接

在eureka管理界面中,点击服务实例所携带的链接后,会自动跳转下面的地址:ip+端口/actuator/info,在整合actuator监控之后,会看到实例相关的运行信息。如果需要自定义跳转其他链接,可以在服务配置文件中加入以下配置:

# 自定义服务跳转链接
eureka.instance.status-page-url=http://www.baidu.com

重启服务后,点击eureka中的服务实例链接,就会跳转到指定的路径。

元数据使用(即各服务在eureka的注册信息)

eureka提供的元数据有两种类型。其一:是框架已经定好的标准元数据,比如主机名、IP地址、端口号、状态页和监控检测等;其二:自定义元数据(即自定义配置项),这个自定义元数据有一定局限性,配置项的key不能随便定义,需要遵从规则,使用eureka.instance.metadata-map.xxx进行配置。比如,在其中一个服务实例中,增加如下配置:

# 自定义服务元数据
eureka.instance.metadata-map.zepal=cloud-eureka

当开发者拿到这些注册在eureka中的服务信息之后,就可以快速进行其他一些个性化的功能扩展了。

通过Rest  API获取元数据

访问eureka项目的url:localhost:9999/eureka/apps/cloud-server0(该url的ip和端口是eureka项目的ip和端口,cloud-sever0是注册在eureka中的服务之一),结果如下(其中一部分):

返回结果默认是以xml形式,如果需要json格式,只需要在请求头中加入:Content-Type:application/json和Accept:application/json。

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MTM4MTg2Mw==,size_16,color_FFFFFF,t_70

通过EurekaClient获取元数据

eureka已经为EurekaClient注册了Spring Bean,所以直接注入使用它即可。如下:

@Controller
public class GetMetadataController0 {

	@Autowired
	private EurekaClient eurekaClient;
	
	@GetMapping("/server0/metadata/getByEurekaClient")
	@ResponseBody
	public Object methodA() {
		// 获取服务名为cloud-server0的元数据
		return eurekaClient.getInstancesByVipAddress("cloud-server0", false);
	}
	
}

然后访问该接口,可以得到同样的元数据。

通过DiscoveryClient获取元数据

同样的,Eureka也同样为DiscoveryClient注册了Spring Bean,编写获取元数据代码,如下:

@Controller
public class GetMetadataController1 {

	// 注意:DiscoveryClient的类路径为:org.springframework.cloud.client.discovery.DiscoveryClient
	@Autowired
	private DiscoveryClient discoveryClient;
	
	@GetMapping("/server0/metadata/getByDiscoveryClient")
	@ResponseBody
	public Object methodA() {
		// 获取服务名为cloud-server0的元数据
		return discoveryClient.getInstances("cloud-server0");
	}
	
}

然后访问该接口,可以得到同样的元数据。

定义健康检查的url

集成了actuator之后,通过元数据拿到的健康检查url如下:

20210518224902346.png

如果开发者为某个服务项目定义了url访问前缀,那么就需要在当前项目中,更改健康检查的url路径,如下:

# 项目访问url前缀
server.servlet.context-path=/zepal
# 如果开发者为项目定义的url访问前缀,那么就需要重新定义健康检查url
eureka.instance.health-check-url-path=${server.servlet.context-path}/actuator/info
eureka.instance.status-page-url-path=${server.servlet.context-path}/actuator/health

然后获取到的元数据如下:

20210518225323950.png

服务上下线监控

在某些特定的需求下,我们需要对服务的上下线进行监控,然后服务上线或者下线,都可以整合一些邮件、短信、电话等通知方式。

目前eureka提供了以下事件:

1、EurekaInstanceCanceledEvent   服务下线事件,这个服务下线一定是服务被移除,从关闭服务到该事件相应和eureka自我保护移除服务间隔事件配置有关(如果没有配置,不要傻等)

2、EurekaInstanceRegisteredEvent   服务注册事件

3、EurekaInstanceRenewedEvent    服务续约事件(即eureka和服务之间的心跳续约)

4、EurekaRegistryAvailableEvent      注册中心启动事件

5、EurekaServerStartedEvent     Eureka Server启动事件

在eureka项目中编写如下监听事件代码:

@Service
public class EurekaStateChangeListener {

	// 服务下线事件,从关闭服务到该事件响应和eureka自我保护移除服务间隔事件配置有关(如果没有配置,不要傻等)
	@EventListener
	public void listen(EurekaInstanceCanceledEvent event) {
		System.err.println(event.getServerId() + "===" + event.getAppName() + "===服务下线");
	}
	
	// 服务注册事件
	@EventListener
	public void listen(EurekaInstanceRegisteredEvent event) {
		InstanceInfo inst = event.getInstanceInfo();
		System.err.println(inst.getAppName() + "===进行注册");
	}
	
	// 服务续约事件(即eureka和服务之间的心跳续约)
	@EventListener
	public void listen(EurekaInstanceRenewedEvent event) {
		System.err.println(event.getServerId() + "===" + event.getAppName() + "===服务续约");
	}
	
	// 注册中心启动事件
	@EventListener
	public void listen(EurekaRegistryAvailableEvent event) {
		System.err.println(" 注册中心启动 ");
	}
	
	// Eureka Server启动事件
	@EventListener
	public void listen(EurekaServerStartedEvent event) {
		System.err.println(" eureka server 启动 ");
	}
	
}

启动eureka项目,可见看到以下监听事件被执行:

20210518230341226.png

再启动一个服务,可以看到以下监听事件被执行:

20210518230500917.png

然后关闭已经注册的服务,可以看到以下监听事件被执行:

20210518230736101.png

 

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值