服务注册与发现及其优雅停服

在Spring Cloud中,开发Eureka Client组件还是非常方便的。

一、服务注册(服务提供者)

pom依赖:

<dependencies>
    <!-- eureka client -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <!-- actuator监控信息
         actuator 组件是Spring Boot的监控组件,actuator一旦应用,在启动的时候,会发布一系列的URL服务。包含一个shutdown服务,代表优雅关闭。
         当Spring Boot 应用中的actuator组件接收到shutdown请求的时候,会触发优雅关闭。
         如果当前应用中有Eureka Client的集成,则会触发Eureka Client向Eureka Server发起一个shutdown优雅停服的请求-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <!-- web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>
<build>
    <finalName>microservicecloud</finalName>
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <filtering>true</filtering><!--开启过滤-->
        </resource>
    </resources>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-resources-plugin</artifactId>
            <configuration>
                <delimiters>
                    <delimit>$</delimit><!---解析以$开始和$结尾且在src/main/resources目录下的配置信息-->
                </delimiters>
            </configuration>
        </plugin>
    </plugins>
</build>
View Code

全局配置:

  在配置Eureka Server信息时,建议将Eureka Server集群中的所有节点依次配置,Eureka Client在注册服务的时候,会根据节点列表依次访问Eureka Server集群节点,只要注册成功,后续Eureka Server节点不再访问注册。虽然Eureka Server集群各节点可以相互发现服务,但是Eureka Server集群中每个节点对服务的管理都使用连带责任,及从某Eureka Server节点发现服务A,如果这个Eureka Server节点宕机,则A服务同时从服务列表中删除。

# 定义SpringBoot应用的名称,建议必须提供。在SpringCloud中,对服务的最大粒度的管理是使用应用命名的
# 最好是一个应用一个名称,在Consumer角色开发的时候,比较容易查找Provider
spring.application.name=appService-name
server.port=8888
#eureka微服务实例名称修改,不采用默认的, 默认是spring.application.name的大写
eureka.instance.instance-id=appService-minsoft

# 配置Eureka Server的地址信息,如果是Eureka Server集群,多个节点使用逗号','分割。
# 如果开启了安全认证,使用HTTP Bacic格式提供用户名和密码。
# 如果Eureka Server是一个集群,那么配置Eureka Server节点信息的时候,建议将所有的Eureka Server节点信息都配置上
# 实际上,只配置一个Eureka Server节点其实就可以了,但是,Eureka Server对服务的管理有连带责任。如果只配置一个Eureka Server节点,那么会导致级联删除的风险,可能导致服务不可靠
# 如果配置了多个Eureka Server节点,Eureka不会将当期的服务同时注册到所有Eureka Server节点上
# 从第一个配置的Eureka Server节点开始注册,如果注册成功,后续的Eureka Server节点不再重复注册
# 每30秒,Eureka Client发送一个心跳到Eureka Server上,如果心跳没有反馈,则从已配置的Eureka Server节点列表的下一个服务节点继续注册。
# 这样做可以保证服务的可靠性,降低服务连带责任导致的服务不可靠。
# 如果多个Eureka Client需要注册,建议Eureka Server的服务列表顺序是随机排列的。
# 如:有Eureka Server s1,s2,s3,有Eureka Client c1,c2,c3。
# 那么在c1上配置的Eureka Server列表建议是s1,s2,s3,在c2上配置的是s2,s3,s1,在c3上配置的是s3,s1,s2,这样可以更好的利用Eureka Server集群的特性。
# 因为Eureka Server和Eureka Client对心跳的监测都是3*间隔时间的,所以会有服务列表数据的不同步可能。
# 所以在CAP原则上,Eureka Server是保证AP原则,放弃C原则的。
# 注意:多个地址之间不要有多余的空格, 只有一个逗号
eureka.client.serviceUrl.defaultZone=http://admin:1234@eurekaserver1:8761/eureka/,http://admin:1234@eurekaserver2:8761/eureka/

#启动所有端点, 也可以设置部分启动, 如:env,beans。默认是health, info
management.endpoints.web.exposure.include=*
## 启用shutdown,优雅停服功能,配置actuator的优雅关闭
## actuator 组件监听shutdown请求地址的时候,要求请求的method必须是POST
## shutdown的请求地址是使用:@PostMapping或@RequestMapping(method=RequestMethod.POST)
management.endpoint.shutdown.enabled=true

#访问信息可以使用IP地址
eureka.instance.prefer-ip-address=true
# 对该微服务进行简单的信息介绍, 随便配置
info.app.name=appService-name
info.company.name=myApplication
info.build.artifactId=$project.artifactId$
info.build.version=$project.version$
View Code

  建议:如果有多个服务功能需要注册,那么在设置Eureka Server信息的时候,推荐异序排列。如:现在有3个工程A、B、C需要注册服务到Eureka Server集群中,集群节点有三个,分别是e1、e2、e3,那么在工程中推荐配置为,A工程配置-e1,e2,e3,B工程配置e2,e3,e1,C工程配置e3,e1,e2。这样可以更好的利用Eureka Server集群的特性。

  这里使用的是8888端口,需要在防火墙打开。

启动类:

  需要在启动类上新增注解@EnableEurekaClient,代表当前应用开启Eureka客户端,应用启动后,会自动将服务注册到Eureka Server中。

  @EnableEurekaClient和@EnableDiscoveryClient的区别?@EnableDiscoveryClient注解是基于spring-cloud-commons依赖,并且在classpath中实现;而@EnableEurekaClient注解是基于spring-cloud-netflix依赖,只能为eureka作用。

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

@EnableDiscoveryClient
@SpringBootApplication
public class AppService {
    public static void main(String[] args) {
        SpringApplication.run(AppService.class, args);
    }
}

对外接口: 

@Controller
public class TestApplicationServiceController {

    @RequestMapping("/test")
    @ResponseBody
    public List<Map<String, Object>> test(){
        List<Map<String, Object>> result = new ArrayList<>();
        for(int i = 0; i < 3; i++){
            Map<String, Object> data = new HashMap<>();
            data.put("id", i+1);
            data.put("name", "test name " + i);
            data.put("age", 20+i);
            result.add(data);
        }
        return result;
    }
}

二、服务发现(服务消费者)

在Spring Cloud中,服务消费方代码的开发确实比较麻烦,并不像Dubbo那么直接注入服务接口代理对象,通过代理对象方法直接访问远程服务。在Spring Cloud中,微服务的提供是通过REST风格提供的,也就是服务的调用是基于HTTP协议的,所以在服务调用上比较麻烦。

pom依赖:

  同服务提供者。

全局配置:

  在Eureka Server中,对服务的管理是基于spring应用名称的,所以不同的服务推荐使用不同的应用名称。

# 定义SpringBoot应用的名称,建议必须提供。
spring.application.name=app-client
server.port=8111

# 任何Eureka Client都必须注册。如果没有配置Eureka Server节点列表,则注册失败。Eureka client无法正常启动。
# 注意:多个地址之间不要有多余的空格, 只有一个逗号
eureka.client.serviceUrl.defaultZone=http://admin:1234@192.168.178.5:8761/eureka/,http://admin:1234@192.168.178.6:8761/eureka/

# 设置负载均衡策略 appservice-name为调用的服务的名称
# 没有配置全部服务的负载均衡策略的方式。因为不是每个服务都可以使用相同负载均衡策略的。
# 如:搜索服务和注册服务就不能使用相同的负载均衡策略。
appservice-name.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule

# 配置服务列表,其中appservice-name代表要访问的服务的应用名,如果有多个服务结点组成集群,多个节点的配置信息使用逗号','分隔。
# 配置服务列表,需要配置要调用的服务的名字和服务所在的位置。
# 服务的名字,就是Application Service(服务提供者)中配置的spring.application.name。
# 服务的位置,就是服务的所在ip和端口。
# 如果服务位置有多个,也就是服务集群,那么使用逗号','分割多个服务列表信息。
appservice-name.ribbon.listOfServers=192.168.178.5:8888
View Code

启动类:

@EnableDiscoveryClient
@SpringBootApplication
public class AppClient {
    public static void main(String[] args) {
        SpringApplication.run(AppClient.class, args);
    }
}

请求调用类(并不是只有这一种方式):

/**
 * 在这里开发Eureka Client中的Application Client角色。就是consumer服务的消费者。
 * 服务消费者需要在注册中心中发现服务列表的。且同时将自己注册到注册中心的服务列表中。(参考如下截图)
 * 
 * consumer在消费provider的时候,是通过LoadBalancer来实现的。
 * LoadBalancer简介 : 是Eureka client内置的一个负载均衡器。复杂在发现的服务列表中选择服务应用,获取服务的IP和端口。
 * 实现服务的远程调用。
 * 
 * application client代码开发相比较dubbo的consumer开发麻烦很多。
 * 
 */
@RestController
public class TestApplicationClientController {

    /**
     * ribbon负载均衡器,其中记录了从Eureka Server中获取的所有服务信息。 
     * 这些服务的信息是IP和端口等。应用名称,域名,主机名等信息。
     */
    @Autowired
    private LoadBalancerClient loadBalancerClient;

    /**
     * 通过HTTP协议,发起远程服务调用,实现一个远程的服务消费。
     * @return
     */
    @GetMapping
    public List<Map<String, Object>> test() {

        // 通过spring应用命名,获取服务实例ServiceInstance对象
        // ServiceInstance 封装了服务的基本信息,如 IP,端口
        /*
         * 在Eureka中,对所有注册到Eureka Server中的服务都称为一个service instance服务实例。
         * 一个服务实例,就是一个有效的,可用的,provider单体实例或集群实例。
         * 每个service instance都和spring application name对应。
         * 可以通过spring application name查询service instance
         */
        ServiceInstance si = this.loadBalancerClient.choose("appservice-name");
        // 拼接访问服务的URL
        StringBuilder sb = new StringBuilder();
        // http://192.168.178.6:8761/test
        sb.append("http://").append(si.getHost())
            .append(":").append(si.getPort()).append("/test");

        System.out.println("本次访问的service是: " + sb.toString());
        
        // SpringMVC RestTemplate,用于快速发起REST请求的模板对象。
        /*
         * RestTemplate是SpringMVC提供的一个用于发起REST请求的模板对象。
         * 基于HTTP协议发起请求的。
         * 发起请求的方式是exchange。需要的参数是: URL, 请求方式, 请求头, 响应类型,【URL rest参数】。
         */
        RestTemplate rt = new RestTemplate();

        /*
         * 创建一个响应类型模板。
         * 就是REST请求的响应体中的数据类型。
         * ParameterizedTypeReference - 代表REST请求的响应体中的数据类型。
         */
        ParameterizedTypeReference<List<Map<String, Object>>> type =
                new ParameterizedTypeReference<List<Map<String, Object>>>() {
        };

        /*
         * ResponseEntity:封装了返回值信息,相当于是HTTP Response中的响应体。
         * 发起REST请求。
         */
        ResponseEntity<List<Map<String, Object>>> response =
                rt.exchange(sb.toString(), HttpMethod.GET, null, type);
        /*
         * ResponseEntity.getBody() - 就是获取响应体中的java对象或返回数据结果。
         */
        List<Map<String, Object>> result = response.getBody();

        return result;
    }

}
View Code

LoadBanlancerClient中包含了所有的服务注册信息。

三、优雅关闭服务(优雅停服)

在Spring Cloud中,可以通过HTTP请求的方式,通知Eureka Client优雅停服,这个请求一旦发送到Eureka Client,那么Eureka Client会发送一个shutdown请求到Eureka Server,Eureka Server接收到这个shutdown请求后,会在服务列表中标记这个服务的状态为down,同时Eureka Client应用自动关闭。这个过程就是优雅停服。

如果使用了优雅停服,则不需要再关闭Eureka Server的服务保护模式。

POM依赖:优雅停服是通过Eureka Client发起的,所以需要在Eureka Client中增加新的依赖,这个依赖是autuator组件,添加下述依赖即可。

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

修改全局配置文件:Eureka Client默认不开启优雅停服功能,需要在全局配置文件中新增如下内容:

#启动所有端点, 也可以设置部分启动, 如:env,beans。默认是health, info
management.endpoints.web.exposure.include=*
# 启用shutdown,优雅停服功能
management.endpoint.shutdown.enabled=true

发起shutdown请求:

  必须通过POST请求向Eureka Client发起一个shutdown请求。请求路径为:http://ip:port/actuator/shutdown。可以通过任意技术实现,如:HTTPClient、form表单,AJAX等。
  建议使用优雅停服方式来关闭Application Service/Application Client服务。

参考:https://www.cnblogs.com/jing99/p/11576133.html

原文地址:https://www.cnblogs.com/myitnews/p/12416506.html