SpringCloud使用Eureka服务注册中心

Eureka介绍

什么是服务治理?

Springcloud 封装了Netfix公司开发的Eureka模块来实现服务治理。在传统的RPC远程调用中,管理每个服务于服务之间依赖关系复杂,管理复杂,所以需要使用服务治理,管理服务于服务之间依赖关系,可以实现服务调用、负载均衡、容错等,实现服务发现与注册。

什么是服务注册与发现?

Eureka采用了CS的设计架构,Eureka Server服务端作为服务注册功能的服务器,它是服务注册中心。而系统中的其他微服务,使用Eureka  Client客户端连接到Eureka Server服务端并维持心跳的连接,这样系统的维护人员就可以通过ureka Server来监控系统中的各个微服务是否正常运行。

在服务注册与发现中,有一个注册中心。当服务器启动的时候,会把当前自己服务器的的信息,比如:服务地址通讯等以别名方式注册到注册中心上,另一方(消费者|生产者)以该别名的方式去注册中心上获取到实际的服务通讯地址,然后再实现本地RPC调用和RPC远程调用框架核心设计思想。这里的重点就是注册中心,因为注册中心管理每个服务于服务之间的依赖关系。在任何的RPC框架中,都会有一个注册中心,存放服务地址相关信息,也就是接口地址。

下图为Eureka的架构图:

Eureka 心跳检测的原理

我们知道 Eureka 注册中心是通过心跳检测机制来判断服务是否可用的,如果不可用,可能会把这个服务摘除。为什么是可能呢?因为 Eureka 有自我保护机制,如果达到自我保护机制的阀值,后续就不会自动摘除。

Eureka 心跳机制:每个服务每隔 30s 自动向 Eureka Server 发送一次心跳,Eureka Server 更新这个服务的最后心跳时间。如果 180 s 内(版本1.7.2)未收到心跳,则任务服务故障了。

Eureka 自我保护机制:如果上一分钟实际的心跳次数,比我们期望的心跳次数要小,就会触发自我保护机制,不会摘除任何实例。期望的心跳次数:服务实例数量 * 2 * 0.85。

Eureka 服务摘除机制:不是一次性将服务实例摘除,每次最多随机摘除 15%。如果摘除不完,1 分钟之后再摘除。

搭建Eureka服务端服务

新建一个module,是Eureka的服务。该module名称:cloud-eureka-server。

1、pom.xml添加依赖

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

<!--eureka-server, springcloud对一些常用模块都进行了版本管理,所以这里不需要引入版本号-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>

2、application.yml配置

server:
  port: 7001

spring:
  application:
    name: eureka-server-7001

# eureka配置
eureka:
  instance:
    hostname: eureka7001.com #eureka服务端的实例名称, 需要修改系hosts,添加ip和域名的解析配置
  client:
    register-with-eureka: false     #false表示不向注册中心注册自己。
    fetch-registry: false     #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
    service-url:
      #设置与eureka  server交互的地址和注册服务都需要依赖这个地址
      defaultZone: http://eureka7001.com:7001/eureka/  #单机就是指向自己

3、主启动类:因为是Eureka的服务端,所以要加该注解@EnableEurekaServer

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@EnableEurekaServer //开启Eureka Server
@SpringBootApplication
public class EurekaApp {
    public static void main(String[] args) {
        SpringApplication.run(EurekaApp.class, args);
    }
}

4、启动该项目,我们直接访问该地址http://localhost:7001/,端口7001就是该服务的端口,出现如下界面,说明启动成功。

现在Erueka的服务端已经启动成功,那我们现在就将我们的其他微服务注册到我们Erueka的注册中心。

搭建Eureka客户端服务

新建一个module。该module名称:cloud-user-service。

1、pom.xml添加依赖

<!--boot web-->
<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-client</artifactId>
</dependency>

2、application.yml配置

server:
  port: 9999

spring:
  application:
    name: cloud-user-service

#eureka配置
eureka:
  client:
    #表示是否将自己注册进eureka  默认为true
    register-with-eureka: true
    #是否从EurekaServer中抓取已有的注册信息,默认为true,单点无所谓,集群必须设置true才能和ribbon使用负载均衡
    fetch-registry: true
    service-url:
      #单机配置
      defaultZone: http://eureka7001.com:7001/eureka/

3、主启动类,要加注解@EnableEurekaClient 或 @EnableDiscoveryClient(推荐)

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@EnableDiscoveryClient //客户端开启服务注册和发现
//@EnableEurekaClient //该注解仅适用于注册中心是Eureka Server,我们使用另外一个注解
@SpringBootApplication
public class UserApp {
    public static void main(String[] args) {
        SpringApplication.run(UserApp.class, args);
    }
}

4、最后我们要先启动Eureka服务端的服务,然后再启动生产者。然后再访问:http://localhost:7001/,会出现如下的界面,我们可以看到服务列表中有就了cloud-user-service这个服务

我们再新建一个module。该module名称:cloud-order-service。

步骤跟上面创建cloud-user-service一样,application.yml配置如下:

server:
  port: 8888

spring:
  application:
    name: cloud-order-service

#eureka配置
eureka:
  client:
    #表示是否将自己注册进eureka  默认为true
    register-with-eureka: true
    #是否从EurekaServer中抓取已有的注册信息,默认为true,单点无所谓,集群必须设置true才能和ribbon使用负载均衡
    fetch-registry: true
    service-url:
      #单机配置
      defaultZone: http://eureka7001.com:7001/eureka/

启动cloud-order-service,访问 http://localhost:7001/,看到该服务也注册上去了。

搭建Eureka集群

我们只是简单的搭建了Eureka的单机版,但是在真正的生产环境上,是远远不够的,微服务RPC远程服务调用最核心的就是高可用,如果一台Eureka宕机了,那我们整个服务就不能使用了,所以就需要我们的集群版,实现负载均衡与故障容错。

我们参考cloud-eureka-server,新建module我们命名为cloud-eureka-server02,这样可以防止与cloud-eureka-server区别开来。(其实实际开发中不用这么模块,只需要修改配置文件,重新打包接口)

1、cloud-eureka-server02的搭建过程

1)添加相关的依赖

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

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

2)application.yml

server:
  port: 7002

spring:
  application:
    name: eureka-server-7002

# eureka配置
eureka:
  instance:
    hostname: eureka7002.com #eureka服务端的实例名称, 需要修改系hosts,添加ip和域名的解析配置
  client:
    register-with-eureka: false     #false表示不向注册中心注册自己。
    fetch-registry: false     #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
    service-url:
      #设置与eureka  server交互的地址和注册服务都需要依赖这个地址
      defaultZone: http://eureka7001.com:7001/eureka/  #集群就是指向其他的eureka

3)启动类添加注解@EnableEurekaServer

2、cloud-eureka-server的yml修改配置

server:
  port: 7001

spring:
  application:
    name: eureka-server-7001

# eureka配置
eureka:
  instance:
    hostname: eureka7001.com #eureka服务端的实例名称, 需要修改系hosts,添加ip和域名的解析配置
  client:
    register-with-eureka: false     #false表示不向注册中心注册自己。
    fetch-registry: false     #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
    service-url:
      #设置与eureka  server交互的地址和注册服务都需要依赖这个地址
      defaultZone: http://eureka7002.com:7002/eureka/  #集群就是指向其他的eureka

3、eureka 客户端修改(我们的微服务)

将yml中的配置读改为:

defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka

例如:cloud-user-service的application.yml完整配置

server:
  port: 9999

spring:
  application:
    name: cloud-user-service

#eureka配置
eureka:
  client:
    #表示是否将自己注册进eureka  默认为true
    register-with-eureka: true
    #是否从EurekaServer中抓取已有的注册信息,默认为true,单点无所谓,集群必须设置true才能和ribbon使用负载均衡
    fetch-registry: true
    service-url:
      #集群配置
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka

现在我们要先启动Eureka端口为7001的服务,然后再启动Eureka端口为7002的服务,再启动我们的其他微服务。

先看一下Eureka端口为7001的服务,看到服务都已经成功注册到了Eureka服务中心。如下图:

再看看一下Eureka端口为7002的服务,看到服务都已经成功注册到了Eureka服务中心。如下图:

微服务集群搭建

1、我们参照之前搭建的端口为8888的服务cloud-order-service,现在我们搭建cloud-order-service02服务,端口为8899。(实际开发中,不用搭建cloud-order-service02项目的,我们只修改相应的配置文件,重新构建打包即可)

cloud-order-service02的application.yml配置:

server:
  port: 8899 # 如果集群中的服务都是部署在不同的机器,端口可以不用修改

spring:
  application:
    name: cloud-order-service # 集群的服务名都是这个, 只有一个

#eureka配置
eureka:
  client:
    #表示是否将自己注册进eureka  默认为true
    register-with-eureka: true
    #是否从EurekaServer中抓取已有的注册信息,默认为true,单点无所谓,集群必须设置true才能和ribbon使用负载均衡
    fetch-registry: true
    service-url:
      #集群配置
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
  #instance:
    # 我使用的eureka client版本是3.1.1, 该值默认是ip:应用名:端口
    #instance-id: ${spring.application.name}:${server.port}
    #prefer-ip-address: true

cloud-order-service的application.yml配置:

server:
  port: 8888 # 如果集群中的服务都是部署在不同的机器,端口可以不用修改

spring:
  application:
    name: cloud-order-service # 集群的服务名都是这个, 只有一个

#eureka配置
eureka:
  client:
    #表示是否将自己注册进eureka  默认为true
    register-with-eureka: true
    #是否从EurekaServer中抓取已有的注册信息,默认为true,单点无所谓,集群必须设置true才能和ribbon使用负载均衡
    fetch-registry: true
    service-url:
      #集群配置
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
  #instance:
    # 我使用的eureka client版本是3.1.1, 该值默认是ip:应用名:端口
    #instance-id: ${spring.application.name}:${server.port}
    #prefer-ip-address: true

启动后,我们看到服务名为cloud-order-service的有两个实例,端口分别是8888和8899。

2、服务间的调用

说明:cloud-user-service调用cloud-order-service集群。

1)cloud-user-service

创建RestTemplate实例:

//如果是集群服务, 调用时使用了服务名请求,则必须添加@LoadBalanced注解,使用ip请求不用添加
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
    return new RestTemplate();
}

UserController.java

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private RestTemplate restTemplate;

    //服务端单机配置
    //public static final String ORDER_URL = "http://localhost:8888";
    //服务端集群配置
    public static final String ORDER_URL = "http://CLOUD-ORDER-SERVICE";

    @RequestMapping("/findOrdersByUserId")
    public Map<String, Object> findOrdersByUserId(@RequestParam("userId") Long userId) {
        return restTemplate.getForObject(ORDER_URL + "/order/findOrdersByUserId?userId={userId}", HashMap.class, userId);
    }
}

2)cloud-order-service

@RestController
@RequestMapping("/order")
public class OrderController {

    @RequestMapping("/findOrdersByUserId")
    public Map<String, Object> findOrdersByUserId(@RequestParam("userId") Long userId) {
        Map<String, Object> resultMap = new HashMap();
        resultMap.put("code", 200);
        resultMap.put("port", 8888);
        return resultMap;
    }
}

3)cloud-order-service02

@RestController
@RequestMapping("/order")
public class OrderController {

    @RequestMapping("/findOrdersByUserId")
    public Map<String, Object> findOrdersByUserId(@RequestParam("userId") Long userId) {
        Map<String, Object> resultMap = new HashMap();
        resultMap.put("code", 200);
        resultMap.put("port", 8899);
        return resultMap;
    }
}

相关服务都启动后,请求访问 http://localhost:9999/user/findOrdersByUserId?userId=1 

我们不断刷新页面,会出现:

说明集群服务进行了负载均衡。

我们查看注解@LoadBalanced的源码:

/**
 * Annotation to mark a RestTemplate or WebClient bean to be configured to use a
 * LoadBalancerClient.
 * @author Spencer Gibb
 */
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {

}

用于标记要配置为使用LoadBalancerClient的RestTemplate bean的。

public interface ServiceInstanceChooser {
	ServiceInstance choose(String serviceId);
}
public interface LoadBalancerClient extends ServiceInstanceChooser {
	<T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;
	<T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException;	
	URI reconstructURI(ServiceInstance instance, URI original);
}

LoadBalancerClient是一个接口,该接口中有四个方法,我们来大概看一下这几个方法的作用:

  • ServiceInstance choose(String serviceId)根据传入的服务名serviceId从客户端负载均衡器中挑选一个对应服务的实例。
  • T execute() ,使用从负载均衡器中挑选出来的服务实例来执行请求。
  • URI reconstructURI(ServiceInstance instance, URI original)表示为系统构建一个合适的URI。第一个参数ServiceInstance实例是一个带有host和port的具体服务实例,第二个参数URI则是使用逻辑服务名定义为host和port的URI,而返回的URI则是通过ServiceInstance的服务实例详情拼接出的具体的host:port形式的请求地址。一言以蔽之,就是把类似于http://HELLO-SERVICE/hello这种地址转为类似于http://195.124.207.128/hello地址(IP地址也可能是域名)。

DiscoveryClient获取注册的服务

private Logger logger = LoggerFactory.getLogger(UserController.class);

//org.springframework.cloud.client.discovery.DiscoveryClient
@Autowired
private DiscoveryClient discoveryClient;

//获取服务信息
@RequestMapping("/discovery")
public Object discovery() {
    List<String> services = discoveryClient.getServices();
    for (String s : services) {
        logger.info("********注册到eureka中的服务中有:" + services);
    }
    List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-ORDER-SERVICE");
    for (ServiceInstance s : instances) {
        logger.info("当前服务的实例有" + s.getServiceId() + "\t" + s.getHost() + "\t" + s.getPort() + "\t" + s.getUri());
    }
    return this.discoveryClient;
}

控制台输出:

********注册到eureka中的服务中有:[cloud-user-service, cloud-order-service]
********注册到eureka中的服务中有:[cloud-user-service, cloud-order-service]
当前服务的实例有CLOUD-ORDER-SERVICE	192.168.1.103	8888	http://192.168.1.103:8888
当前服务的实例有CLOUD-ORDER-SERVICE	192.168.1.103	8899	http://192.168.1.103:8899

 

posted @ 2022-05-03 12:30  残城碎梦  阅读(189)  评论(0编辑  收藏  举报