如何在 Spring-Cloud 中将 ConsulDiscoveryClient 与 Zuul 和 Sidecar 一起使用

Posted

技术标签:

【中文标题】如何在 Spring-Cloud 中将 ConsulDiscoveryClient 与 Zuul 和 Sidecar 一起使用【英文标题】:How do I use the ConsulDiscoveryClient with Zuul and Sidecar in Spring-Cloud 【发布时间】:2015-12-13 02:41:09 【问题描述】:

我正在尝试将 Spring-Cloud 微服务架构与来自 Netflix 的 Sidecar 和 Zuul 组件一起使用 an example from kbastani on github

将部署服务的环境已经有一个正在运行的 consul 服务,可以用于服务发现和配置。这应该意味着我可以从示例中消除 config-service 和 eureka。

我现在正在尝试使用以下代码将 api-gateway 微服务连接到 Consul:

@SpringBootApplication
@EnableSidecar
@EnableDiscoveryClient
public class GatewayApplication 

    @Autowired
    @Qualifier("consulDiscoveryClient")
    DiscoveryClient discovery;

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

这会导致以下异常:

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'gatewayApplication.MyZuulProxyConfiguration': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private org.springframework.cloud.client.discovery.DiscoveryClient org.springframework.cloud.netflix.zuul.ZuulProxyConfiguration.discovery; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [org.springframework.cloud.client.discovery.DiscoveryClient] is defined: expected single matching bean but found 2: consulDiscoveryClient,discoveryClient
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:334)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1214)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:543)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:305)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:301)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:196)
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:368)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1123)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1018)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:510)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:305)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:301)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:201)
    at org.springframework.boot.context.embedded.ServletContextInitializerBeans.getOrderedBeansOfType(ServletContextInitializerBeans.java:209)
    at org.springframework.boot.context.embedded.ServletContextInitializerBeans.addServletContextInitializerBeans(ServletContextInitializerBeans.java:85)
    at org.springframework.boot.context.embedded.ServletContextInitializerBeans.<init>(ServletContextInitializerBeans.java:73)
    at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.getServletContextInitializerBeans(EmbeddedWebApplicationContext.java:234)
    at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.selfInitialize(EmbeddedWebApplicationContext.java:221)
    at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.access$000(EmbeddedWebApplicationContext.java:84)
    at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext$1.onStartup(EmbeddedWebApplicationContext.java:206)
    at org.springframework.boot.context.embedded.tomcat.TomcatStarter.onStartup(TomcatStarter.java:54)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5156)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1408)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1398)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    ... 1 more
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private org.springframework.cloud.client.discovery.DiscoveryClient org.springframework.cloud.netflix.zuul.ZuulProxyConfiguration.discovery; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [org.springframework.cloud.client.discovery.DiscoveryClient] is defined: expected single matching bean but found 2: consulDiscoveryClient,discoveryClient
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:571)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331)
    ... 32 more
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [org.springframework.cloud.client.discovery.DiscoveryClient] is defined: expected single matching bean but found 2: consulDiscoveryClient,discoveryClient
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1079)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:967)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:543)
    ... 34 more

这个服务的 pom.xml 是:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <artifactId>api-gateway-microservice</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <parent>
        <groupId>nl.quby.nxt</groupId>
        <artifactId>springCloudSpike</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zuul</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-netflix-sidecar</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-consul-all</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

bootstrap.yml:

spring:
  application:
    name: gateway
  cloud:
    consul:
      host: consul
      port: 8500
      config:
        enabled: true

encrypt:
  failOnError: false

application.yml:

server:
  port: 10000

endpoints:
  restart:
    enabled: true
  shutdown:
    enabled: true
  health:
    sensitive: false

谁能阐明我如何将 Spring Cloud 与 Consul 一起用作发现和配置服务来创建 API 网关?

【问题讨论】:

为什么要与 consul 一起使用 sidecar? Sidecar 存在的原因是 eureka 是基于 JVM 的。由于 consul 不是,它本身就是一个边车。 感谢您的反应。我的错误,我的印象是 Sidecar 是 API-Gateway 的必要部分。实际上 Zuul 就是这样。 【参考方案1】:

你应该使用@EnalbeZuulProxy,sidecar 用于非 java 服务作为服务发现客户端。 @EnalbeZuulProxy 还将为您启用发现客户端。

此外,您的错误清楚地表明您正在尝试通过接口自动连接DiscoveryClient,并且您的上下文中已经有两个。一个是 spring-boot 自动配置,另一个是您创建的。

您不应该自己创建客户端,因为如果您的类路径上有适当的依赖项,自动配置将为您完成。

工作领事客户示例:

https://github.com/spring-cloud/spring-cloud-consul/tree/master/spring-cloud-consul-sample

【讨论】:

非常感谢您的回答。使用@EnableZuulProxy 而不是自己尝试连接 DiscoveryClient 确实完成了这项工作。尝试自己连接它是对 Spring Cloud 无法选择自身的反应。我还删除了 @EnableSidecar 注释和 Sidecar 作为上面@spencergibb 建议的依赖项。

以上是关于如何在 Spring-Cloud 中将 ConsulDiscoveryClient 与 Zuul 和 Sidecar 一起使用的主要内容,如果未能解决你的问题,请参考以下文章

如何在共享库中管理 spring-cloud 引导属性?

不正确的字符串值:列的“\\xA0Consu ...”即使列具有 utf8mb4 编码

Spring-Cloud的版本是如何定义的

如何保证 spring-boot 和 spring-cloud版本一致

spring-boot 指标与 spring-cloud 指标

Spring-Cloud之Feign