Spring Cloud 快速上手之 Eureka 服务注册

Spring Cloud 快速上手之 Eureka 服务注册

简介

本文主要介绍 Spring Cloud 中的 Eureka 服务注册中心。

image.png

手机用户请横屏获取最佳阅读体验,REFERENCES中是本文参考的链接,如需要链接和更多资源,可以扫码加入『知识星球』(文末)获取长期知识分享服务。

准备工作

版本

简单的RestTemplate调用

消费端服务调用

@RestController
public class UserController {

    @Autowired
    private IUserService userService;

    @GetMapping("/user/{id}")
    public User findById(@PathVariable Long id) {
        return userService.findById(id);
    }
}

//---

@Service
public class UserServiceImpl implements IUserService {

    @Autowired
    private RestTemplate restTemplate;

    @Override
    public User findById(Long id) {
        return this.restTemplate.getForObject("http://localhost:8000/"+id,User.class);
    }

}

@Configuration
public class RpcConfig {

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

返回结果

{
  "id": 1,
  "account": "account0",
  "userName": "x_user_0",
  "age": 18
}

Spring Boot Actuator

 <!--状态监控-->
 <dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-actuator</artifactId>
 </dependency>
.

从日志打印的端点信息进入,查看状态监控返回信息

//获取健康指标
HTTP/1.1 200 
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Sun, 03 May 2020 13:53:14 GMT

{
  "status": "UP"
}

//获取应用信息
GET http://localhost:8000/actuator/info

HTTP/1.1 200 
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Sun, 03 May 2020 13:54:41 GMT

{}

//查询端点信息
GET http://localhost:8000/actuator

HTTP/1.1 200 
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Sun, 03 May 2020 13:55:56 GMT

{
  "_links": {
    "self": {
      "href": "http://localhost:8000/actuator",
      "templated": false
    },
    "health": {
      "href": "http://localhost:8000/actuator/health",
      "templated": false
    },
    "info": {
      "href": "http://localhost:8000/actuator/info",
      "templated": false
    }
  }
}

Response code: 200; Time: 51ms; Content length: 227 bytes

可以通过在 yml 中补充 info 节点信息:

info:
  app:
    name: @project.artifactId@
    encoding: @project.build.sourceEncoding@
    java:
      source: @java.version@
      target: @java.version@

效果如下:

GET http://localhost:8000/actuator/info

HTTP/1.1 200 
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Sun, 03 May 2020 14:01:48 GMT

{
  "app": {
    "name": "ms-provider-user-v1",
    "encoding": "UTF-8",
    "java": {
      "source": "1.8.0_181",
      "target": "1.8.0_181"
    }
  }
}

Response code: 200; Time: 182ms; Content length: 108 bytes

小结

我们通过RestTemplate直接完成服务端的接口调用,但是对于远端应用地址的硬编码,往往会带来很多问题:

  • 无法解决 IP 地址或端口变更的场景。
  • 无法进行动态伸缩,不支持多实例。

服务注册与发现

Eureka Server

编写单点服务注册中心

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

/**
 * 核心启动类
 */
@EnableEurekaServer
@SpringBootApplication
public class MsDiscoveryEurekaApplication {

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

}

server:
  port: 8010

eureka:
  client:
    # 表示是否将自己注册到 Eureka Server,默认为 true
    register-with-eureka: false
    # 表示是否从 Eureka Server 获取注册信息,默认为 true
    fetch-registry: false
    service-url:
      defaultZone: http://localhost:8010/eureka/

spring:
  application:
    name: ms-discovery-eureka

访问 http://localhost:8010/

.

此处配置的是一个单点的 Eureka Server,不需要同步其他的 Eureka Server 节点的数据,因而 fetch-registry: false

Eureka Client

注册微服务

  • 启动类
@SpringBootApplication
public class MsProviderUserV2Application {

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

}

  • 配置
server:
  port: 8011

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/db_yier?characterEncoding=UTF-8&rewriteBatchedStatements=true
    username: root
    password: Abc123++
    driver-class-name: com.mysql.jdbc.Driver
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
    database-platform: org.hibernate.dialect.MySQL5Dialect

  application:
    name: ms-provider-user-v2

eureka:
  client:
    service-url:
      # 如果此处是注册到 Eureka Server 集群的话,建议多配几个节点,以便适应某些极端场景
      defaultZone: http://localhost:8010/eureka/
  instance:
        # 表示将自己的 IP 注册到 Eureka Server,若不配置,默认为 false,表示注册微服务所在操作系统的 hostname 到 Eureka Server
    prefer-ip-address: true

info:
  app:
    name: @project.artifactId@
    encoding: @project.build.sourceEncoding@
    java:
      source: @java.version@
      target: @java.version@

将项目ms-provider-user-v2注册到 Eureka Server 服务中心。

.

同理,将消费者注册到服务中心

.

Eureka Server不同,Eureka Client无需在在启动类上加注解@EnableEurekaClient注解[1]

Eureka Server 的高可用

构建双节点集群

  • 修改 Hosts:Mac vim /etc/hosts
.
  • 修改配置
spring:
  application:
    name: ms-discovery-eureka-ha
---
spring:
  profiles: peer1
server:
  port: 8010
eureka:
  instance:
    hostname: peer1
  client:
    service-url:
      defaultZone: http://peer2:8020/eureka/

---
spring:
  profiles: peer2
server:
  port: 8020
eureka:
  instance:
    hostname: peer2
  client:
    service-url:
      defaultZone: http://peer1:8010/eureka/


  • 启动profiles:peer1profiles:peer2
.
.

Eureka Server 集群精简配置

spring:
  application:
    name: ms-discovery-eureka-ha

eureka:
  client:
    service-url:
      defaultZone: http://peer2:8020/eureka/,http://peer1:8010/eureka/

---
spring:
  profiles: peer1
server:
  port: 8010
eureka:
  instance:
    hostname: peer1

---
spring:
  profiles: peer2
server:
  port: 8020
eureka:
  instance:
    hostname: peer2


用户认证

为注册中心的访问增加权限认证控制

  • 引入Spring Security
 <!--登录认证-->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-security</artifactId>
</dependency>
  • 配置认证信息
server:
  port: 8010

eureka:
  client:
    register-with-eureka: false
    fetch-registry: false
    service-url:
      defaultZone: http://admin:Abc123++@localhost:8010/eureka/

spring:
  application:
    name: ms-discovery-eureka-authentication
  security:
    user:
      name: admin
      password: Abc123++
  • 访问 localhost:8010,会跳转到登录界面
.

服务注册到带有安全认证的注册中心

  • Spring Boot 2.0 配置不兼容修改
/*
 * @ProjectName: 编程学习
 * @Copyright:   2019 HangZhou Helios Dev, Ltd. All Right Reserved.
 * @address:     微信搜索公众号「架构探险之道」获取更多资源。
 * @date:        2020/5/4 9:50 下午
 * @description: 本内容仅限于编程技术学习使用,转发请注明出处.
 */
package com.yido.ms.discovery.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;

/**
 * <p>
 *
 * </p>
 *
 * @author Helios
 * @date 2020/5/4 9:50 下午
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * 高版本的丢弃了
     *
     * security:
     *   basic:
     *    enabled: true
     * 配置,应该使用以下方式开启
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //方式1
        // Configure HttpSecurity as needed (e.g. enable http basic).
        //http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER);
        //http.csrf().disable();
        //注意:为了可以使用 http://user:{user}:user:{password}@host:{host}:host:{port}/eureka/ 这种方式登录,所以必须是httpBasic,
        //如果是form方式,不能使用url格式登录
        //http.authorizeRequests().anyRequest().authenticated().and().httpBasic();

        //方式2
        //默认情况下,将其添加到classpath后,会对每个请求进行CSRF检查。Eureka并不会生成CSRF token,
        // 所以需要关掉对/eureka/*路径下的检查:
        // 关闭csrf
        http.csrf().ignoringAntMatchers("/eureka/**");
        super.configure(http);
    }
}

  • YAML 配置
server:
  port: 8010

eureka:
  client:
    register-with-eureka: false
    fetch-registry: false
    service-url:
      defaultZone: http://admin:Abc123++@localhost:8010/eureka/

spring:
  application:
    name: ms-discovery-eureka-authentication
  security:
    user:
      name: admin
      password: Abc123++
  • 注册成功
.

Eureka 元数据

元数据配置

server:
  port: 8012

spring:
  application:
    name: ms-consumer-user-v2-metadata

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8010/eureka/
  instance:
    prefer-ip-address: true
    # 定义实例元数据信息
    metadata-map:
      my-metadata: 自定义信息

查询接口

/**
 * 查询当前实例信息
 * @param instanceId
 * @return
 */
@GetMapping("/instances/{instanceId}")
public List<ServiceInstance> instances(@PathVariable String instanceId) {
    //为空,返回自身实例信息
    if (StringUtils.isEmpty(instanceId)) {
        return this.discoveryClient.getInstances("ms-consumer-user-v2-metadata");
    }
    return this.discoveryClient.getInstances(instanceId);
}

演示

.

端点信息

示例

  • http://localhost:8010/eureka/apps
.

其余参见Eureka REST operations

用途

  • 获取微服务注册信息
  • 通过 xml 和 json 注册(或注销)非 jvm 微服务

自我保护模式

.

保护模式主要用于一组客户端和Eureka Server之间存在网络分区场景下的保护。一旦进入保护模式,Eureka Server将会尝试保护其服务注册表中的信息,不再删除服务注册表中的数据(也就是不会注销任何微服务)。

默认情况下,如果Eureka Server在一定时间内没有接收到某个微服务实例的心跳,Eureka Server将会注销该实例(默认90秒)。但是当网络分区故障发生时,微服务与Eureka Server之间无法正常通信,以上行为可能变得非常危险了(因为微服务本身其实是健康的,此时本不应该注销这个微服务)。

Eureka通过“自我保护模式”来解决这个问题:当Eureka Server节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。一旦进入该模式,Eureka Server就会保护服务注册表中的信息,不再删除服务注册表中的数据(也就是不会注销任何微服务)。当网络故障恢复后,该Eureka Server节点会自动退出自我保护模式。

综上,自我保护模式是一种应对网络异常的安全保护措施。它的架构哲学是宁可同时保留所有微服务(健康的微服务和不健康的微服务都会保留),也不盲目注销任何健康的微服务。使用自我保护模式,可以让Eureka集群更加的健壮、稳定。

自我保护的条件:

一般情况下,微服务在Eureka上注册后,会30秒定期发送心跳,Eureka 通过心跳来判断微服务是否健康,同时会定期删除超过90秒没有发送心跳的服务。

有2种情况会导致Eureka Server收不到微服务的心跳,

  1. 是微服务自身原因所致,比如故障或关闭;

  2. 是微服务与eureka之间的网络出现故障。

通常(微服务自身的故障关闭)只会导致个别服务出现故障,一般不会出现大面积的故障,而(网络故障)通常会导致Eureka Server在短时间内无法收到大批心跳。

考虑到这个区别,Eureka设定了一个阀值,当判断挂掉的服务的数量超过阀值时,Eureka Server认为很大程度上出现了网络故障,将不再删除心跳过期的服务。

那这个阀值是多少呢?

Eureka Server在运行期间,会统计心跳失败的比例在15分钟之内是否低于85%。换句话就是:默认情况下启用自我保留,启用自我保留的默认阈值大于当前注册表大小的15%。

关闭自我保护模式(生产上不建议)

server:
  port: 8010

eureka:
  client:
    register-with-eureka: false
    fetch-registry: false
    service-url:
      defaultZone: http://localhost:8010/eureka/
  # 关闭自我保护模式
  server:
    enable-self-preservation: false
spring:
  application:
    name: ms-discovery-eureka

健康检查

引入 spring-boot-starter-actuator

 <!--状态监控-->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

开启健康检查

server:
  port: 8012

spring:
  application:
    name: ms-consumer-user-v2

eureka:
  client:
    # 健康检查
    healthcheck:
      enabled: true
    service-url:
      defaultZone: http://localhost:8010/eureka/
  instance:
    prefer-ip-address: true

查询状态

.

在开启健康检查后,应用程序可以将自己的健康状态传播到 Eureka Server。

REFERENCES

脚注


获取更多

扫码关注架构探险之道,回复"源码",获取本文相关源码

架构探险之道

扫码加入知识星球,获取更多珍贵笔记、视频、电子书的等资源。

知识星球

  1. 在 Spring Cloud Edgware 以及高版本中,只需要添加相关依赖即可。

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