2.SpringCloud 服务注册与发现-Eureka

2.SpringCloud 服务注册与发现-Eureka

  • Eureka 工作原理
  • Eureka 搭建
  • Eureka 集群搭建
  • Eureka 集群原理
  • 服务注册与消费
    • 服务注册
    • 服务消费
  • RestTemplate
  • 负载均衡
  • 负载均衡原理

Eureka 工作原理

1. Eureka 两大组件

Eureka 采用 CS(Client/Server,客户端/服务器) 架构,它包括以下两大组件:

  • Eureka Server:Eureka 服务注册中心,主要用于提供服务注册功能。用来接收其他服务的注册。
    1. 服务注册,所有的服务都注册到 Eureka Server 上面来
    2. 提供注册表,Eureka Client 在调用服务时,需要获取这个注册表,一般来说,这个注册表会缓存下来,如果缓存失效,则直接获取最新的注册表
    3. 同步状态,Eureka Client 通过注册、心跳等机制,和 Eureka Server 同步当前客户端的状态
  • Eureka Client:Eureka 客户端,通常指的是微服务系统中各个微服务,主要用于和 Eureka Server 进行交互,并可以实现负载均衡等功能。
    1. 服务注册,服务提供者将自己注册到服务注册中心

    2. 服务续约,注册成功后,默认情况下,Eureka CLient 每隔 30 秒就要向 Eureka Server 发送一条心跳消息。如果 Eureka Server 连续 90 秒都有没有收到 Eureka Client 的续约消息,它会认为 Eureka Client 已经掉线了,会从当前的服务注册列表中剔除。

      # 服务续约时间 30s
      eureka.instance.lease-renewal-interval-in-seconds=30
      # 服务失效时间 90s
      eureka.instance.lease-expiration-duration-in-seconds=90
      
    3. 服务下线,当 Eureka Client 下线时,它会主动发送一条消息,告诉 Eureka Server

    4. 获取注册表信息,Eureka Client 从 Eureka Server 上获取服务的注册信息,并将其缓存在本地

      # 是否允许获取注册表信息
      eureka.client.fetch-registry=true
      # 定期更新注册表的时间间隔,默认 30 秒
      eureka.client.registry-fetch-interval-seconds=30
      

2. 工作原理
2.SpringCloud 服务注册与发现-Eureka_第1张图片

  • Eureka Server:注册中心,用于提供服务注册和发现功能。
  • Eureka Provider:服务提供者,它将自己提供的服务注册到服务注册中心,以供服务消费者发现。
  • Eureka Consumer:服务消费者,它可以从服务注册中心获取服务列表,调用所需的服务。

Eureka 搭建

1.创建一个普通的 Spring Boot 项目,创建时,勾选 Eureka Server依赖

>
    >org.springframework.cloud>
    >spring-cloud-starter-netflix-eureka-server>
>

2.在项目启动类上添加 @EnableEurekaServer 注解,标记该项目是一个 Eureka Server

@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {

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

}

3.在 application.properties 中添加基本配置信息

# 设置服务名称
spring.application.name=eureka
# 设置端口号
server.port=1111
# 默认情况下,Eureka Server 也是一个普通的微服务,自己也会注册进来
# register-with-eureka 设置为 false,表示当前项目不要注册到注册中心上
eureka.client.register-with-eureka=false
# 表示是否从 Eureka Server 上获取注册信息
eureka.client.fetch-registry=false

项目启动成功后,浏览器输入 http://localhost:1111 就可以查看 Eureka 后台管理页面了:
2.SpringCloud 服务注册与发现-Eureka_第2张图片

Eureka 集群搭建

使用了注册中心之后,所有的服务都要通过服务注册中心来进行信息交换。要保证注册中心的稳定性,Eureka 一般都是以集群的形式出现的。Eureka 集群,实际上就是启动多个 Eureka 实例,多个 Eureka 实例之间,互相注册,互相同步数据,共同组成一个 Eureka 集群

1.修改电脑的 hosts 文件

127.0.0.1 eurekaA eurekaB

2.新增两个eureka实例,为了方便,这里新增两个配置文件,再以jar包形式分别启动

application-a.properites 内如如下:

# 设置服务名称
spring.application.name=eureka
# 设置端口号
server.port=1111
eureka.instance.hostname=eurekaA
# 默认情况下,Eureka Server 也是一个普通的微服务,自己也会注册进来
# register-with-eureka 设置为 false,表示当前项目不要注册到注册中心上
eureka.client.register-with-eureka=true
# 表示是否从 Eureka Server 上获取注册信息
eureka.client.fetch-registry=true
# A 服务要注册到 B 上面
eureka.client.service-url.defaultZone=http://eurekaB:1112/eureka

application-b.properites 内如如下:

# 设置服务名称
spring.application.name=eureka
# 设置端口号
server.port=1112
eureka.instance.hostname=eurekaB
# 默认情况下,Eureka Server 也是一个普通的微服务,自己也会注册进来
# register-with-eureka 设置为 false,表示当前项目不要注册到注册中心上
eureka.client.register-with-eureka=true
# 表示是否从 Eureka Server 上获取注册信息
eureka.client.fetch-registry=true
# B 服务要注册到 A 上面
eureka.client.service-url.defaultZone=http://eurekaA:1111/eureka

3.对当前项目打包,打成 jar 包,分别启动

java -jar eureka-0.0.1-SNAPSHOT.jar --spring.profiles.active=a
java -jar eureka-0.0.1-SNAPSHOT.jar --spring.profiles.active=b

启动成功后,浏览器访问 http://127.0.0.1:1111/,就可以看到,两个服务之间互相注册,共同给组成一个集群。
2.SpringCloud 服务注册与发现-Eureka_第3张图片

Eureka 集群原理

2.SpringCloud 服务注册与发现-Eureka_第4张图片
在这个集群架构中,Eureka Server 之间通过 Replicate 进行数据同步,不同的 Eureka Server 之间不区分主从节点,所有节点都是平等的。节点之间,通过置顶 serviceUrl 来互相注册,形成一个集群,进而提高节点的可用性。

在 Eureka Server 集群中,如果有某一个节点宕机,Eureka Client 会自动切换到新的 Eureka Server 上。每一个 Eureka Server 节点,都会互相同步数据。

服务注册与消费

服务注册

服务注册就是把一个微服务注册到 Eureka Server 上,以传统springboot项目为例,添加Web 和 Eureka Discovery Client依赖

>
    >org.springframework.cloud>
    >spring-cloud-starter-netflix-eureka-client>
>

在 application.properties 中配置一下项目的注册地址

# provider 配置
spring.application.name=provider
server.port=1113
eureka.client.service-url.defaultZone=http://localhost:1111/eureka

启动 Eureka Server,待服务注册中心启动成功后,再启动 provider。
两者都启动成功后,浏览器输入 http://localhost:1111,就可以查看到 provider 的注册信息

服务消费

在 provider 中,提供一个 hello 接口

@RestController
public class HelloController {
    @GetMapping("/hello")
    public String hello() {
        return "hello javaboy";
   }
}

创建一个 consumer 项目,添加Web 和 Eureka Discovery Client依赖

# 消费者 consumer 配置
spring.application.name=consumer
server.port=1115
eureka.client.service-url.defaultZone=http://localhost:1111/eureka

consumer 中消费 provider 提供的接口:

  • DiscoveryClient:服务查询 ,可以根据服务名从 Eureka Server 上查询服务的详细信息
  • RestTemplate:HTTP调用
  • @LoadBalanced:负载均衡,给 RestTemplate 添加一个 @LoadBalanced 注解,开启负载均衡
@Bean
@LoadBalanced
RestTemplate restTemplate() {
    return new RestTemplate();
}
@Autowired
RestTemplate restTemplate;
@GetMapping("/hello2")
public String hello2() {
    return restTemplate.getForObject("http://provider/hello", String.class);
}

注意,上述代码中,restTemplate.getForObject(“http://provider/hello”, String.class);第一个参数URL,是服务名称而不是具体的服务地址。使用RestTemplate,其实是封装了服务查询和URL拼接的过程。

// 根据服务名查询服务的详细信息
List list = discoveryClient.getInstances(“provider”);
// 手动实现负载均衡 (count++) % list.size()
ServiceInstance instance = list.get((count++) % list.size());

RestTemplate

RestTemplate 是从 Spring3.0 开始支持的一个 Http 请求工具,提供了常见的 REST 请求方法模板,例如 GET、POST、PUT、DELETE 请求以及一些通用的请求执行方法 exchange 和 execute 方法。

GET 请求

  • getForObject() 返回的是一个对象,这个对象就是服务端返回的具体值。
  • getForEntity() 返回的是一个ResponseEntity,还包含了 Http 响应头的数据。

POST 请求

  • postForObject()
  • postForEntity()
  • postForLocation() 执行完post 请求之后,进行重定向,比如 注册后重定向到登录页面,注意,重定向的地址,一定要写成绝对路径,不要写相对路径,否则在 consumer 中调用时会出问题

PUT 请求

  • put()

可接受两种类型的参数,key/value 形式以及 JSON 形式。

DELETE 请求

  • delete()

有两种方式来传递参数,key/value 形式或者 PathVariable(参数放在路径中)

restTemplate.delete("http://provider/user1?id={1}", 99);
restTemplate.delete("http://provider/user2/{1}", 99);

负载均衡

  • 服务端负载均衡,就是传统的 Nginx 的方式。调用的客户端并不知道具体是哪一个 Server 提供的服务,它也不关心。
  • 客户端负载均衡,调用的客户端本身是知道所有 Server 的详细信息的,当需要调用 Server 上的接
    口的时候,客户端从自身所维护的 Server 列表中,根据提前配置好的负载均衡策略,自己挑选一个
    Server 来调用。

负载均衡原理

在 RestTemplate 中,要想使用负载均衡功能,只需要给 RestTemplate 实例上添加 @LoadBalanced注解即可,此时,RestTemplate 就会自动具备负载均衡功能,这个负载均衡就是客户端负载均衡。

整体上来说,这个功能的实现就是三个核心点:

  1. 查询注册的服务列表;
  2. 选择一个可以调用的服务;
  3. 根据 2 中所选择的服务,重构请求 URL 地址。

RibbonLoadBalancerClient部分源码如下:

public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint) throws IOException {
	//1.加载服务列表
    ILoadBalancer loadBalancer = this.getLoadBalancer(serviceId);
    //2.选择一个服务
    Server server = this.getServer(loadBalancer, hint);
    if (server == null) {
        throw new IllegalStateException("No instances available for " + serviceId);
    } else {
        RibbonLoadBalancerClient.RibbonServer ribbonServer = new RibbonLoadBalancerClient.RibbonServer(serviceId, server, this.isSecure(server, serviceId), this.serverIntrospector(serviceId).getMetadata(server));
        return this.execute(serviceId, (ServiceInstance)ribbonServer, (LoadBalancerRequest)request);
    }
}
//3.重构URI
public URI reconstructURI(ServiceInstance instance, URI original) {
    Assert.notNull(instance, "instance can not be null");
    String serviceId = instance.getServiceId();
    RibbonLoadBalancerContext context = this.clientFactory.getLoadBalancerContext(serviceId);
    URI uri;
    Server server;
    if (instance instanceof RibbonLoadBalancerClient.RibbonServer) {
        RibbonLoadBalancerClient.RibbonServer ribbonServer = (RibbonLoadBalancerClient.RibbonServer)instance;
        server = ribbonServer.getServer();
        uri = RibbonUtils.updateToSecureConnectionIfNeeded(original, ribbonServer);
    } else {
        server = new Server(instance.getScheme(), instance.getHost(), instance.getPort());
        IClientConfig clientConfig = this.clientFactory.getClientConfig(serviceId);
        ServerIntrospector serverIntrospector = this.serverIntrospector(serviceId);
        uri = RibbonUtils.updateToSecureConnectionIfNeeded(original, clientConfig, serverIntrospector, server);
    }
    return context.reconstructURIWithServer(server, uri);
}

你可能感兴趣的:(SpringCloud,eureka,spring,cloud,微服务)