前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring Cloud系列之Commons - 2. 服务发现 - 如何通过配置文件配置服务实例?

Spring Cloud系列之Commons - 2. 服务发现 - 如何通过配置文件配置服务实例?

作者头像
干货满满张哈希
发布2021-04-12 14:09:17
7900
发布2021-04-12 14:09:17
举报
文章被收录于专栏:干货满满张哈希

Spring Cloud Commons 主要包括如下模块的接口和默认实现:

image
image

其中的限流策略以及重试策略是没有天然带的,但是其他模块的实现一般会带上这些功能。我们先从服务发现相关接口开始分析

服务发现相关

核心接口DiscoveryClient

DiscoveryClient

代码语言:javascript
复制
public interface DiscoveryClient extends Ordered {
	int DEFAULT_ORDER = 0;
	//描述
    String description();
    //通过 serviceId 获取服务实例
    List getInstances(String serviceId);
    //获取所有服务的名称
	List getServices();
	@Override
	default int getOrder() {
		return DEFAULT_ORDER;
	}
}

DiscoveryClient 扩展了 Ordered 接口,这个和之前提到的@Order注解的作用是一样的。

服务实例的信息包括:

image
image
代码语言:javascript
复制
public interface ServiceInstance {

	//实例id,并不是必须的
	default String getInstanceId() {
		return null;
	}

	//服务id,用于区分不同微服务
	String getServiceId();

	//服务实例提供服务的地址
	String getHost();

	//服务实例提供服务的端口
	int getPort();

	//是否使用的是 HTTPS
	boolean isSecure();

	//提供服务的 URI 地址
	URI getUri();

	//一些元数据信息
	Map getMetadata();

	//使用的传输协议,例如 http,https 等等
	default String getScheme() {
		return null;
	}

}

Spring Cloud 从 Feinchley 版本之后,越来越重视异步 Reactor 编程与 WebFlux,所以所有同步的接口基本上都有对应的异步接口,这里的DiscoveryClient对应的就是ReactiveDiscoveryClient:

代码语言:javascript
复制
public interface ReactiveDiscoveryClient extends Ordered {
    int DEFAULT_ORDER = 0;
    //描述
    String description();
    //通过 serviceId 获取服务实例,这里返回的是 Flux,究竟如何使用会在后面的例子中详细阐明
    Flux getInstances(String serviceId);
    //获取所有服务的名称,这里返回的是 Flux,究竟如何使用会在后面的例子中详细阐明
    Flux getServices();
    @Override
    default int getOrder() {
        return 0;
    }
}

如何通过配置文件配置服务实例?

使用 SimpleDiscoveryClientSimpleReactiveDiscoveryClient

假设要调用的微服务的域名是固定的,我们可以直接通过将这些域名写入配置文件。这个场景一般发生在:

  • 基于 Kubernetes ingress nginx 与 coredns 的内网域名解析负载均衡
  • 外网统一提供服务的域名

我们通过一个例子来说明下 SimpleDiscoveryClientSimpleReactiveDiscoveryClient,这里的代码可以从这里下载,首先引入依赖:

代码语言:javascript
复制
    org.springframework.boot
    spring-boot-starter-parent
    2.4.1



    
        org.springframework.cloud
        spring-cloud-commons
    
    
        org.springframework.boot
        spring-boot-starter-actuator
    
    
        org.springframework.boot
        spring-boot-starter-web
    
    
        org.springframework.boot
        spring-boot-starter-webflux
    
    
        junit
        junit
        4.12
    
    
        org.springframework.boot
        spring-boot-starter-test
    



    
        
            org.springframework.cloud
            spring-cloud-dependencies
            2020.0.0
            pom
            import

编写application.yml,这里列举了SimpleDiscoveryClientSimpleReactiveDiscoveryClient所有可能的配置:

代码语言:javascript
复制
spring:
  cloud:
    discovery:
      client:
        # SimpleDiscoveryClient与SimpleReactiveDiscoveryClient的配置
        simple:
          instances:
            #微服务1
            service1:
              #实例1
              - host: instance1 #地址
                port: 8080 #端口
                instanceId: 'instance1:8080' #可以不填,实例id
              #实例2
              - uri: 'https://instance2:443' #指定了 scheme 为 https,host 为 instance2,端口为443
            #微服务2
            service2:
              #实例3
              - host: instance3 #地址
                port: 80 #端口
                instanceId: ${spring.cloud.discovery.client.simple.instances.service2[0].host}:${spring.cloud.discovery.client.simple.instances.service2[0].port} #可以不填,实例id
              #实例4
              - uri: 'https://instance4:8080' #指定了 scheme 为 https,host 为 instance4,端口为8080
          # 指定 SimpleDiscoveryClient的排序顺序为1,默认是0,越小越优先
          order: 1
# actuator 配置
management:
  endpoint:
    health:
      # health 接口总是输出详细信息
      show-details: always
  endpoints:
    jmx:
      exposure:
        # jmx 不暴露任何 actuator 接口
        exclude: '*'
    web:
      exposure:
        # http 暴露所有 actuator 接口
        include: '*'

我们配置了四个不同实例:

image
image

我们可以直接通过 uri 配置,也可以具体配置其中的 host,port 和 isSecure,两者是等价的。例如:

代码语言:javascript
复制
spring:
  cloud:
    discovery:
      client:
        # SimpleDiscoveryClient与SimpleReactiveDiscoveryClient的配置
        simple:
          instances:
            #微服务1
            service1:
              #实例1
              - host: instance1 #地址
                port: 8080 #端口

等价于

代码语言:javascript
复制
spring:
  cloud:
    discovery:
      client:
        # SimpleDiscoveryClient与SimpleReactiveDiscoveryClient的配置
        simple:
          instances:
            #微服务1
            service1:
              #实例1
              - uri: http://instance1:8080

instanceId 不一定需要指定,serviceId 会根据实例配置的上一级取,就算自己配置了,例如:

代码语言:javascript
复制
spring:
  cloud:
    discovery:
      client:
        # SimpleDiscoveryClient与SimpleReactiveDiscoveryClient的配置
        simple:
          instances:
            #微服务1
            service1:
              #实例1
              - host: instance1 #地址
                port: 8080 #端口
                serviceId: service2 #无效,实际还是service1

serviceId 还是实际合理的那个,也就是 service1。

这些机制在后面的源码分析就会理解了。 我们的测试代码会用到 Spring Boot 的事件机制,也就是在 ApplicationContext 到某一生命周期的时候,这些事件会被发布出来,由实现了对应事件的ApplicationListener接口的 Bean 消费,Spring boot 中,事件主要包括:

  • ApplicationStartingEvent:这个是spring boot应用一开始启动时,发出的事件,只是用来标识,应用开始启动了,一般没什么用
  • ApplicationEnvironmentPreparedEvent:这个是在创建好Environment(通过上下文配置,判断到底创建StandardServletEnvironment(针对Servlet环境),StandardReactiveWebEnvironment(针对Reactive环境)还是StandardEnvironment(针对无servlet环境))之后发出的事件。
  • ApplicationContextInitializedEvent: 这个是在创建好Context并调用ApplicationContextInitializer初始化context之后发布这个事件,在加载bean信息之前
  • ApplicationPreparedEvent:加载bean信息之后,但是还没有创建bean的时候,发步这个事件。这个事件是和调用ApplicationContextAware设置ApplicationContext一起进行的,可以看出,setApplicationContext方法里面不能去获取bean,因为bean可能还没有初始化完成
  • ApplicationStartedEvent: 加载初始化各种需要的bean并依赖注入之后,在运行ApplicationRunner做一些用户自定义的初始化操作之前,会发布这个事件。
  • ApplicationReadyEvent:运行ApplicationRunner做一些用户自定义的初始化操作之后,会发布这个事件。

我们使用ApplicationReadyEventApplicationListener确保所有的DiscoveryClient都初始化完成并可以使用作为测试类。

编写测试类: TestSimpleDiscoveryClient

代码语言:javascript
复制
/**
 * 通过消费 ApplicationReadyEvent 来确保 DiscoveryClient 初始化完成并可用
 */
@Slf4j
@Component
public class TestSimpleDiscoveryClient implements ApplicationListener {


    /**
     * 初始化的方法返回类型是 DiscoveryClient 并且不是 Primary,这里只能通过 @Resource 自动装载不能通过 @Autowired
     * 这里不排除以后返回类型修改为 SimpleDiscoveryClient 的可能性
     * @see org.springframework.cloud.client.discovery.simple.SimpleDiscoveryClientAutoConfiguration
     */
    @Resource
    private SimpleDiscoveryClient simpleDiscoveryClient;

    @Override
    public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
        List services = simpleDiscoveryClient.getServices();
        services.forEach(serviceId -> {
            log.info("{}: {}", serviceId, simpleDiscoveryClient.getInstances(serviceId));
        });
    }
}

TestSimpleReactiveDiscoveryClient

代码语言:javascript
复制
/**
 * 通过消费 ApplicationReadyEvent 来确保 DiscoveryClient 初始化完成并可用
 */
@Slf4j
@Component
public class TestSimpleReactiveDiscoveryClient implements ApplicationListener {
    @Autowired
    private SimpleReactiveDiscoveryClient simpleReactiveDiscoveryClient;

    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        simpleReactiveDiscoveryClient.getServices().subscribe(serviceId -> {
            simpleReactiveDiscoveryClient.getInstances(serviceId).collect(Collectors.toList()).subscribe(serviceInstances -> {
                log.info("{}: {}", serviceId, serviceInstances);
            });
        });
    }
}

启动类:DiscoveryClientMain

代码语言:javascript
复制
@SpringBootApplication
public class DiscoveryClientMain {
    public static void main(String[] args) {
        SpringApplication.run(DiscoveryClientMain.class, args);
    }
}

启动后,可以看到日志:

代码语言:javascript
复制
2021-01-19 09:38:05.646  INFO 6168 --- [           main] .h.s.c.i.s.d.s.TestSimpleDiscoveryClient : service2: [DefaultServiceInstance{instanceId='instance3:80', serviceId='service2', host='instance3', port=80, secure=false, metadata={}}, DefaultServiceInstance{instanceId='null', serviceId='service2', host='instance4', port=8080, secure=true, metadata={}}]
2021-01-19 09:38:05.647  INFO 6168 --- [           main] .h.s.c.i.s.d.s.TestSimpleDiscoveryClient : service1: [DefaultServiceInstance{instanceId='instance:8080', serviceId='service1', host='instance1', port=8080, secure=false, metadata={}}, DefaultServiceInstance{instanceId='null', serviceId='service1', host='instance2', port=443, secure=true, metadata={}}]
2021-01-19 09:38:05.913  INFO 6168 --- [           main] .s.d.s.TestSimpleReactiveDiscoveryClient : service2: [DefaultServiceInstance{instanceId='instance3:80', serviceId='service2', host='instance3', port=80, secure=false, metadata={}}, DefaultServiceInstance{instanceId='null', serviceId='service2', host='instance4', port=8080, secure=true, metadata={}}]
2021-01-19 09:38:05.913  INFO 6168 --- [           main] .s.d.s.TestSimpleReactiveDiscoveryClient : service1: [DefaultServiceInstance{instanceId='instance:8080', serviceId='service1', host='instance1', port=8080, secure=false, metadata={}}, DefaultServiceInstance{instanceId='null', serviceId='service1', host='instance2', port=443, secure=true, metadata={}}]
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/01/27 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 服务发现相关
    • 如何通过配置文件配置服务实例?
    相关产品与服务
    负载均衡
    负载均衡(Cloud Load Balancer,CLB)提供安全快捷的流量分发服务,访问流量经由 CLB 可以自动分配到云中的多台后端服务器上,扩展系统的服务能力并消除单点故障。负载均衡支持亿级连接和千万级并发,可轻松应对大流量访问,满足业务需求。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档