精华内容
下载资源
问答
  • 文章目录 前言 ...本篇主要是配置Feign调用过程的日志输出的干货,如今微服务是占据了主流市场(SpringCloud/SpringCloudAlibaba),成百上千的服务产生的调用更是不计其数,但还是有许多伙伴...

    文章目录


    前言

    本篇主要是配置Feign调用过程的日志输出的干货,如今微服务是占据了主流市场(SpringCloud/SpringCloudAlibaba),成百上千的服务产生的调用更是不计其数,但还是有许多伙伴没有意识到服务调用过程的日志输出的重要性。因协助开发公司的部分系统时发现该问题,有些是用aop来打印其过程产生的收发报文,其实这种方式非常不适宜。今天我们也是主要讲述如何优雅的打印Feign的调用过程的日志。

    一、先上调用过程的日志效果图

    二、简单三步配置

    1.引入依赖包(第一个依赖根据架构引入,下方第二个依赖版本自选)

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-feign</artifactId>
    </dependency>
    <dependency>
        <groupId>io.github.openfeign.form</groupId>
        <artifactId>feign-form-spring</artifactId>
        <version>3.2.2</version>
    </dependency>

    2.实现配置类

    @Component
    public class FeignBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
            if (containsBeanDefinition(beanFactory, "feignContext", "eurekaAutoServiceRegistration")) {
                BeanDefinition bd = beanFactory.getBeanDefinition("feignContext");
                bd.setDependsOn("eurekaAutoServiceRegistration");
            }
        }
    
    
        private boolean containsBeanDefinition(ConfigurableListableBeanFactory beanFactory, String... beans) {
            return Arrays.stream(beans).allMatch(b -> beanFactory.containsBeanDefinition(b));
        }
    }
    @Configuration
    public class FeignSupportConfig {
    
        @Scope("prototype")
        @Primary
        @Configuration
        class MultipartSupportConfig {
            @Autowired
            private ObjectFactory<HttpMessageConverters> messageConverters;
            @Bean
            public Encoder feignFormEncoder() {
                return new SpringFormEncoder(new SpringEncoder(messageConverters));
            }
        }
    
        @Bean
        Logger.Level feignLoggerLevel() {
            return Logger.Level.FULL;
        }
    
    }

    3.logback配置(可能一些项目没有做logback的配置,此步流程自行百度)在Loggers标签里加入Feign接口所在的包结构

    <Loggers>
        <logger name="com.xxx.xxx" level="DEBUG" />
    </Loggers>

    总结


    特别是生产环境,快速的解决问题是必备技能,日志是查找bug的重要依据,所以日志的配置是真的很重要。

    展开全文
  • feign调用过程

    2021-09-21 20:41:15
  • Spring Cloud Feign 调用过程分析

    千次阅读 2019-11-05 09:35:41
    通过这两个组件我们暂时可以完成服务注册和可配置负载均衡的服务调用。今天我们要学习的是Feign,那么Feign解决了什么问题呢? 相对于Eureka,Ribbon来说,Feign的地位好像不是那么重要,Feign是一个声...

    前面已经学习了两个Spring Cloud 组件:

    • Eureka:实现服务注册功能;
    • Ribbon:提供基于RestTemplate的HTTP客户端并且支持服务负载均衡功能。

    通过这两个组件我们暂时可以完成服务注册和可配置负载均衡的服务调用。今天我们要学习的是Feign,那么Feign解决了什么问题呢?

    相对于Eureka,Ribbon来说,Feign的地位好像不是那么重要,Feign是一个声明式的REST客户端,它的目的就是让REST调用更加简单。通过提供HTTP请求模板,让Ribbon请求的书写更加简单和便捷。另外,在Feign中整合了Ribbon,从而不需要显式的声明Ribbon的jar包。

    前面在使用Ribbon+RestTemplate时,利用RestTemplate对http请求的封装处理,形成了一套模版化的调用方法。但是在实际开发中,由于对服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以通常都会针对每个微服务自行封装一些客户端类来包装这些依赖服务的调用。所以,Feign在此基础上做了进一步封装,由他来帮助我们定义和实现依赖服务接口的定义。在Feign的实现下,我们只需创建一个接口并使用注解的方式来配置它(以前是Dao接口上面标注Mapper注解,现在是一个微服务接口上面标注一个Feign注解即可),即可完成对服务提供方的接口绑定,简化了使用Spring Cloud Ribbon时,自动封装服务调用客户端的开发量。

    Feign的使用:

    1.引入依赖:

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    

    另外,我们需要添加spring-cloud-dependencies

    <?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.2.0.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.rickiyang.learn</groupId>
        <artifactId>feign-consumer</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>feign-consumer</name>
        <description>Demo project for Spring Boot</description>
    
        <properties>
            <java.version>1.8</java.version>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
            <spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-openfeign</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-eureka</artifactId>
            </dependency>
    
        </dependencies>
    
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-dependencies</artifactId>
                    <version>${spring-cloud.version}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    </project>
    

    2.接下来,我们需要在主类中添加 @EnableFeignClients

    package com.rickiyang.learn;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    import org.springframework.cloud.openfeign.EnableFeignClients;
    
    @EnableDiscoveryClient
    @EnableFeignClients
    @SpringBootApplication
    public class FeignConsumerApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(FeignConsumerApplication.class, args);
        }
    
    }
    

    3.再来看一下用Feign写的HTTP请求的格式:

    package com.rickiyang.learn.service;
    
    import com.rickiyang.learn.entity.Person;
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.*;
    
    /**
     * @author: rickiyang
     * @date: 2019/10/5
     * @description:
     */
    @FeignClient(name= "eureka-client")
    public interface HelloRemote {
    
    
        @RequestMapping(value = "/hello/{name}")
        String hello(@PathVariable(value = "name") String name);
    
    
        @PostMapping(value ="/add",produces = "application/json; charset=UTF-8")
        String addPerson(@RequestBody Person person);
    
        @GetMapping("/getPerson/{id}")
        String getPerson(@PathVariable("id") Integer id);
    
    }
    

    用FeignClient注解申明要调用的服务是哪个,该服务中的方法都有我们常见的Restful方式的API来声明,这种方式大家是不是感觉像是在写Restful接口一样。

    代码示例:点击这里

    note:

    示例代码的正确打开方式:先启动服务端,然后启动一个client端,再次启动 feign-consumer,调用 feign-consumer中的接口即可。

    还记得在Ribbon学习的时候使用RestTemplate发起HTTP请求的方式吗:

    restTemplate.getForEntity("http://eureka-client/hello/" + name, String.class).getBody();
    

    将整个的请求URL和参数都放在一起,虽然没有什么问题,总归不是那么优雅。使用Feign之后你可以使用Restful方式进行调用,写起来也会更加清晰。

    Feign调用过程分析

    上面简单介绍了Feign的使用方式,大家可以结合着代码示例运行一下,了解基本的使用方式。接下来我们一起分析Feign的调用过程,我们带着两个问题去跟踪:

    1.请求如何被Feign 统一托管;

    2.Feign如何分发请求。

    这两个问题应该就涵盖了Feign的功能,下面就出发去一探究竟。

    我们还和以前一样从一个入口进行深入,首先看启动类上的 @EnableFeignClients 注解:

    package org.springframework.cloud.openfeign;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    import org.springframework.context.annotation.Import;
    
    /**
     * Scans for interfaces that declare they are feign clients (via {@link FeignClient
     * <code>@FeignClient</code>}). Configures component scanning directives for use with
     * {@link org.springframework.context.annotation.Configuration
     * <code>@Configuration</code>} classes.
     *
     * @author Spencer Gibb
     * @author Dave Syer
     * @since 1.0
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    @Import(FeignClientsRegistrar.class)
    public @interface EnableFeignClients {
    
        //等价于basePackages属性,更简洁的方式
    	String[] value() default {};
        //指定多个包名进行扫描
    	String[] basePackages() default {};
    
        //指定多个类或接口的class,扫描时会在这些指定的类和接口所属的包进行扫描
    	Class<?>[] basePackageClasses() default {};
    
    	 //为所有的Feign Client设置默认配置类
    	Class<?>[] defaultConfiguration() default {};
    
    	 //指定用@FeignClient注释的类列表。如果该项配置不为空,则不会进行类路径扫描
    	Class<?>[] clients() default {};
    }
    
    

    注释上说了该注解用于扫描 FeignClient 声明的类。我们用 FeignClient 注解去声明一个 Eureka 客户端,那么猜想这里应该是取到我们声明的Eureka client名称,然后去访问Eureka server获取服务提供者。

    同样的,为所有Feign Client 也支持文件属性的配置,如下 :

    feign:
      client:
        config:                                         
        # 默认为所有的feign client做配置(注意和上例github-client是同级的)
          default:                                      
            connectTimeout: 5000                        # 连接超时时间
            readTimeout: 5000                           # 读超时时间设置  
    

    注 : 如果通过Java代码进行了配置,又通过配置文件进行了配置,则配置文件的中的Feign配置会覆盖Java代码的配置。

    但也可以设置feign.client.defalult-to-properties=false,禁用掉feign配置文件的方式让Java配置生效。

    注意到类头声明的 @Import 注解引用的 FeignClientsRegistrar 类,这个类的作用是在 EnableFeignClients 初始化的时候扫描该注解对应的配置。

    接着看 FeignClient 注解:

    package org.springframework.cloud.openfeign;
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    import org.springframework.core.annotation.AliasFor;
    
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface FeignClient {
    
        //指定Feign Client的名称,如果项目使用了 Ribbon,name属性会作为微服务的名称,用于服务发现
    	@AliasFor("name")
    	String value() default "";
    	//用serviceId做服务发现已经被废弃,所以不推荐使用该配置
    	@Deprecated
    	String serviceId() default "";
    	//指定Feign Client的serviceId,如果项目使用了 Ribbon,将使用serviceId用于服务发现,但上面可以看到serviceId做服务发现已经被废弃,所以也不推荐使用该配置
    	@AliasFor("value")
    	String name() default "";
    	//为Feign Client 新增注解@Qualifier
    	String qualifier() default "";
        //请求地址的绝对URL,或者解析的主机名
    	String url() default "";
        //调用该feign client发生了常见的404错误时,是否调用decoder进行解码异常信息返回,否则抛出FeignException
    	boolean decode404() default false;
         //Feign Client设置默认配置类
    	Class<?>[] configuration() default {};
        //定义容错的处理类,当调用远程接口失败或超时时,会调用对应接口的容错逻辑,fallback 指定的类必须实现@FeignClient标记的接口。实现的法方法即对应接口的容错处理逻辑
    	Class<?> fallback() default void.class;
        //工厂类,用于生成fallback 类示例,通过这个属性我们可以实现每个接口通用的容错逻辑,减少重复的代码
    	Class<?> fallbackFactory() default void.class;
        //定义当前FeignClient的所有方法映射加统一前缀
    	String path() default "";
        //是否将此Feign代理标记为一个Primary Bean,默认为ture
    	boolean primary() default true;
    }
    
    

    同样在 FeignClientsRegistrar 类中也会去扫描 FeignClient 注解对应的配置信息。我们直接看 FeignClientsRegistrar 的逻辑:

    class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
    		ResourceLoaderAware, EnvironmentAware {
    
    	// patterned after Spring Integration IntegrationComponentScanRegistrar
    	// and RibbonClientsConfigurationRegistgrar
    
    	private ResourceLoader resourceLoader;
    
    	private Environment environment;
    
    	public FeignClientsRegistrar() {
    	}
    
    	@Override
    	public void setResourceLoader(ResourceLoader resourceLoader) {
    		this.resourceLoader = resourceLoader;
    	}
    
       //在这个重载的方法里面做了两件事情:
       //1.将EnableFeignClients注解对应的配置属性注入
       //2.将FeignClient注解对应的属性注入
    	@Override
    	public void registerBeanDefinitions(AnnotationMetadata metadata,
    			BeanDefinitionRegistry registry) {
            //注入EnableFeignClients注解对应的配置属性
    		registerDefaultConfiguration(metadata, registry);
           //注入FeignClient注解对应的属性
    		registerFeignClients(metadata, registry);
    	}
    
       /**
       * 拿到 EnableFeignClients注解 defaultConfiguration 字段的值
       * 然后进行注入
       *
       **/
    	private void registerDefaultConfiguration(AnnotationMetadata metadata,
    			BeanDefinitionRegistry registry) {
    		Map<String, Object> defaultAttrs = metadata
    				.getAnnotationAttributes(EnableFeignClients.class.getName(), true);
    
    		if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
    			String name;
    			if (metadata.hasEnclosingClass()) {
    				name = "default." + metadata.getEnclosingClassName();
    			}
    			else {
    				name = "default." + metadata.getClassName();
    			}
    			registerClientConfiguration(registry, name,
    					defaultAttrs.get("defaultConfiguration"));
    		}
    	}
    
    	public void registerFeignClients(AnnotationMetadata metadata,
    								 BeanDefinitionRegistry registry) {
            // 获取ClassPath扫描器
            ClassPathScanningCandidateComponentProvider scanner = getScanner();
            // 为扫描器设置资源加载器
            scanner.setResourceLoader(this.resourceLoader);
    
            Set<String> basePackages;
            // 1. 从@EnableFeignClients注解中获取到配置的各个属性值
            Map<String, Object> attrs = metadata
                    .getAnnotationAttributes(EnableFeignClients.class.getName());
            // 2. 注解类型过滤器,只过滤@FeignClient   
            AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
                    FeignClient.class);
            // 3. 从1. 中的属性值中获取clients属性的值        
            final Class<?>[] clients = attrs == null ? null
                    : (Class<?>[]) attrs.get("clients");
            if (clients == null || clients.length == 0) {
                // 扫描器设置过滤器且获取需要扫描的基础包集合
                scanner.addIncludeFilter(annotationTypeFilter);
                basePackages = getBasePackages(metadata);
            }else {
                // clients属性值不为null,则将其clazz路径转为包路径
                final Set<String> clientClasses = new HashSet<>();
                basePackages = new HashSet<>();
                for (Class<?> clazz : clients) {
                    basePackages.add(ClassUtils.getPackageName(clazz));
                    clientClasses.add(clazz.getCanonicalName());
                }
                AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {
                    @Override
                    protected boolean match(ClassMetadata metadata) {
                        String cleaned = metadata.getClassName().replaceAll("\\$", ".");
                        return clientClasses.contains(cleaned);
                    }
                };
                scanner.addIncludeFilter(
                        new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));
            }
    
            // 3. 扫描基础包,且满足过滤条件下的接口封装成BeanDefinition
            for (String basePackage : basePackages) {
                Set<BeanDefinition> candidateComponents = scanner
                        .findCandidateComponents(basePackage);
                // 遍历扫描到的bean定义        
                for (BeanDefinition candidateComponent : candidateComponents) {
                    if (candidateComponent instanceof AnnotatedBeanDefinition) {
                        // 并校验扫描到的bean定义类是一个接口
                        AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
                        AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
                        Assert.isTrue(annotationMetadata.isInterface(),
                                "@FeignClient can only be specified on an interface");
    
                        // 获取@FeignClient注解上的各个属性值
                        Map<String, Object> attributes = annotationMetadata
                                .getAnnotationAttributes(
                                        FeignClient.class.getCanonicalName());
    
                        String name = getClientName(attributes);
                        // 可以看到这里也注册了一个FeignClient的配置bean
                        registerClientConfiguration(registry, name,
                                attributes.get("configuration"));
                        // 注册bean定义到spring中
                        registerFeignClient(registry, annotationMetadata, attributes);
                    }
                }
            }
    	}
    
       /**
       * 注册bean
       **/
    	private void registerFeignClient(BeanDefinitionRegistry registry,
           AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
            // 1.获取类名称,也就是本例中的FeignService接口
            String className = annotationMetadata.getClassName();
    
            // 2.BeanDefinitionBuilder的主要作用就是构建一个AbstractBeanDefinition
            // AbstractBeanDefinition类最终被构建成一个BeanDefinitionHolder
            // 然后注册到Spring中
            // 注意:beanDefinition类为FeignClientFactoryBean,故在Spring获取类的时候实际返回的是
            // FeignClientFactoryBean类
            BeanDefinitionBuilder definition = BeanDefinitionBuilder
                .genericBeanDefinition(FeignClientFactoryBean.class);
            validate(attributes);
    
            // 3.添加FeignClientFactoryBean的属性,
            // 这些属性也都是我们在@FeignClient中定义的属性
            definition.addPropertyValue("url", getUrl(attributes));
            definition.addPropertyValue("path", getPath(attributes));
            String name = getName(attributes);
            definition.addPropertyValue("name", name);
            definition.addPropertyValue("type", className);
            definition.addPropertyValue("decode404", attributes.get("decode404"));
            definition.addPropertyValue("fallback", attributes.get("fallback"));
            definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
            definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
    
            // 4.设置别名 name就是我们在@FeignClient中定义的name属性
            String alias = name + "FeignClient";
            AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
    
            boolean primary = (Boolean)attributes.get("primary"); // has a default, won't be null
    
            beanDefinition.setPrimary(primary);
    
            String qualifier = getQualifier(attributes);
            if (StringUtils.hasText(qualifier)) {
                alias = qualifier;
            }
    
            // 5.定义BeanDefinitionHolder,
            // 在本例中 名称为FeignService,类为FeignClientFactoryBean
            BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, new String[] { alias });
            BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
    	}
    
    	private void validate(Map<String, Object> attributes) {
    		AnnotationAttributes annotation = AnnotationAttributes.fromMap(attributes);
    		// This blows up if an aliased property is overspecified
    		// FIXME annotation.getAliasedString("name", FeignClient.class, null);
    		Assert.isTrue(
    			!annotation.getClass("fallback").isInterface(),
    			"Fallback class must implement the interface annotated by @FeignClient"
    		);
    		Assert.isTrue(
    			!annotation.getClass("fallbackFactory").isInterface(),
    			"Fallback factory must produce instances of fallback classes that implement the interface annotated by @FeignClient"
    		);
    	}
    
       ......
       ......
       ......
    }
    

    在这里做了两件事情:

    1. 将EnableFeignClients注解对应的配置属性注入;
    2. 将FeignClient注解对应的属性注入。

    生成FeignClient对应的bean,注入到Spring 的IOC容器。

    在registerFeignClient方法中构造了一个BeanDefinitionBuilder对象,BeanDefinitionBuilder的主要作用就是构建一个AbstractBeanDefinition,AbstractBeanDefinition类最终被构建成一个BeanDefinitionHolder 然后注册到Spring中。

    beanDefinition类为FeignClientFactoryBean,故在Spring获取类的时候实际返回的是FeignClientFactoryBean类。

    FeignClientFactoryBean作为一个实现了FactoryBean的工厂类,那么每次在Spring Context 创建实体类的时候会调用它的getObject()方法。

    public Object getObject() throws Exception {
        FeignContext context = applicationContext.getBean(FeignContext.class);
        Feign.Builder builder = feign(context);
    
        if (!StringUtils.hasText(this.url)) {
            String url;
            if (!this.name.startsWith("http")) {
                url = "http://" + this.name;
            }
            else {
                url = this.name;
            }
            url += cleanPath();
            return loadBalance(builder, context, new HardCodedTarget<>(this.type, this.name, url));
        }
        if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
            this.url = "http://" + this.url;
        }
        String url = this.url + cleanPath();
        Client client = getOptional(context, Client.class);
        if (client != null) {
            if (client instanceof LoadBalancerFeignClient) {
                // not lod balancing because we have a url,
                // but ribbon is on the classpath, so unwrap
                client = ((LoadBalancerFeignClient)client).getDelegate();
            }
            builder.client(client);
        }
        Targeter targeter = get(context, Targeter.class);
        return targeter.target(this, builder, context, new HardCodedTarget<>(
            this.type, this.name, url));
    }
    

    这里的getObject()其实就是将@FeinClient中设置value值进行组装起来,此时或许会有疑问,因为在配置FeignClientFactoryBean类时特意说过并没有将Configuration传过来,那么Configuration中的属性是如何配置的呢?看其第一句是:

    FeignContext context = applicationContext.getBean(FeignContext.class);
    

    从Spring容器中获取FeignContext.class的类,我们可以看下这个类是从哪加载的。点击该类查看被引用的地方,可以找到在FeignAutoConfiguration类中有声明bean:

    @Configuration
    @ConditionalOnClass(Feign.class)
    @EnableConfigurationProperties({FeignClientProperties.class, FeignHttpClientProperties.class})
    public class FeignAutoConfiguration {
    
    	@Autowired(required = false)
    	private List<FeignClientSpecification> configurations = new ArrayList<>();
    
    	@Bean
    	public HasFeatures feignFeature() {
    		return HasFeatures.namedFeature("Feign", Feign.class);
    	}
    
    	@Bean
    	public FeignContext feignContext() {
    		FeignContext context = new FeignContext();
    		context.setConfigurations(this.configurations);
    		return context;
    	}
        ......
        ......
        ......
            
    }     
    

    从上面的代码中可以看到在set属性的时候将 FeignClientSpecification 类型的类全部加入此类的属性中。还记得在上面分析registerFeignClients方法的时候里面有一行代码调用:registerClientConfiguration()方法:

    private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
                                             Object configuration) {
        BeanDefinitionBuilder builder = BeanDefinitionBuilder
            .genericBeanDefinition(FeignClientSpecification.class);
        builder.addConstructorArgValue(name);
        builder.addConstructorArgValue(configuration);
        registry.registerBeanDefinition(
            name + "." + FeignClientSpecification.class.getSimpleName(),
            builder.getBeanDefinition());
    }
    

    在注册BeanDefinition的时候, configuration 其实也被作为参数,传给了 FeignClientSpecification。 所以这时候在FeignContext中是带着configuration配置信息的。

    至此我们已经完成了配置属性的装配工作,那么是如何执行的呢?我们可以看getObject()最后一句可以看到返回了Targeter.target的方法。

    return targeter.target(this, builder, context, new HardCodedTarget<>(this.type, this.name, url));
    

    那么这个Targeter是哪来的?我们还是看上面的FeignAutoConfiguration类,可以看到其中有两个Targeter类,一个是DefaultTargeter,一个是HystrixTargeter。当配置了feign.hystrix.enabled = true的时候,Spring容器中就会配置HystrixTargeter此类,如果为false那么Spring容器中配置的就是DefaultTargeter

    我们以DefaultTargeter为例介绍一下接下来是如何通过创建代理对象的:

    class DefaultTargeter implements Targeter {
    
    	@Override
    	public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
    						Target.HardCodedTarget<T> target) {
    		return feign.target(target);
    	}
    }
    
    public static class Builder {
    
        public <T> T target(Target<T> target) {
          return build().newInstance(target);
        }
    
        public Feign build() {
          SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
              new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
                                                   logLevel, decode404);
          ParseHandlersByName handlersByName =
              new ParseHandlersByName(contract, options, encoder, decoder,
                                      errorDecoder, synchronousMethodHandlerFactory);
          return new ReflectiveFeign(handlersByName, invocationHandlerFactory);
        }
     }
    

    在target方法中有个参数:Feign.Builder:

    public static class Builder {
    
        private final List<RequestInterceptor> requestInterceptors =
            new ArrayList<RequestInterceptor>();
        private Logger.Level logLevel = Logger.Level.NONE;
        private Contract contract = new Contract.Default();
        private Client client = new Client.Default(null, null);
        private Retryer retryer = new Retryer.Default();
        private Logger logger = new NoOpLogger();
        private Encoder encoder = new Encoder.Default();
        private Decoder decoder = new Decoder.Default();
        private ErrorDecoder errorDecoder = new ErrorDecoder.Default();
        private Options options = new Options();
        private InvocationHandlerFactory invocationHandlerFactory =
            new InvocationHandlerFactory.Default();
        private boolean decode404;
        
        ......
        ......
        ......
    }
    

    构建feign.builder时会向FeignContext获取配置的Encoder,Decoder等各种信息 。Builder中的参数来自于配置文件的 feign.client.config里面的属性。

    查看ReflectiveFeign类中newInstance方法是返回一个代理对象:

    public <T> T newInstance(Target<T> target) {
        //为每个方法创建一个SynchronousMethodHandler对象,并放在 Map 里面
        Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
        Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
        List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
    
        for (Method method : target.type().getMethods()) {
            if (method.getDeclaringClass() == Object.class) {
                continue;
            } else if(Util.isDefault(method)) {
                //如果是 default 方法,说明已经有实现了,用 DefaultHandler
                DefaultMethodHandler handler = new DefaultMethodHandler(method);
                defaultMethodHandlers.add(handler);
                methodToHandler.put(method, handler);
            } else {
                //否则就用上面的 SynchronousMethodHandler
                methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
            }
        }
        // 设置拦截器
        // 创建动态代理,factory 是 InvocationHandlerFactory.Default,创建出来的是 
        // ReflectiveFeign.FeignInvocationHanlder,也就是说后续对方法的调用都会进入到该对象的 inovke 方法
        InvocationHandler handler = factory.create(target, methodToHandler);
        // 创建动态代理对象
        T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[]{target.type()}, handler);
    
        for(DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
            defaultMethodHandler.bindTo(proxy);
        }
        return proxy;
    }
    

    这个方法大概的逻辑是:

    1. 根据target,解析生成MethodHandler对象;
    2. MethodHandler对象进行分类整理,整理成两类:default 方法和 SynchronousMethodHandler 方法;
    3. 通过jdk动态代理生成代理对象,这里是最关键的地方;
    4. DefaultMethodHandler绑定到代理对象。

    最终都是执行了SynchronousMethodHandler拦截器中的invoke方法:

    @Override
      public Object invoke(Object[] argv) throws Throwable {
        RequestTemplate template = buildTemplateFromArgs.create(argv);
        Retryer retryer = this.retryer.clone();
        while (true) {
          try {
            return executeAndDecode(template);
          } catch (RetryableException e) {
            retryer.continueOrPropagate(e);
            if (logLevel != Logger.Level.NONE) {
              logger.logRetry(metadata.configKey(), logLevel);
            }
            continue;
          }
        }
      }
    

    invoke方法方法首先生成 RequestTemplate 对象,应用 encoder,decoder 以及 retry 等配置,下面有一个死循环调用:executeAndDecode,从名字上看就是执行调用逻辑并对返回结果解析。

    Object executeAndDecode(RequestTemplate template) throws Throwable {
        //根据  RequestTemplate生成Request对象
        Request request = targetRequest(template);
    
        if (logLevel != Logger.Level.NONE) {
            logger.logRequest(metadata.configKey(), logLevel, request);
        }
    
        Response response;
        long start = System.nanoTime();
        try {
            // 调用client对象的execute()方法执行http调用逻辑,
            //execute()内部可能设置request对象,也可能不设置,所以需要response.toBuilder().request(request).build();这一行代码
            response = client.execute(request, options);
            // ensure the request is set. TODO: remove in Feign 10
            response.toBuilder().request(request).build();
        } catch (IOException e) {
            if (logLevel != Logger.Level.NONE) {
                logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));
            }
            // IOException的时候,包装成 RetryableException异常,上面的while循环 catch里捕捉的就是这个异常
            throw errorExecuting(request, e);
        }
        //统计 执行调用花费的时间
        long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
    
        boolean shouldClose = true;
        try {
            if (logLevel != Logger.Level.NONE) {
                response =
                    logger.logAndRebufferResponse(metadata.configKey(), logLevel, response, elapsedTime);
                // ensure the request is set. TODO: remove in Feign 10
                response.toBuilder().request(request).build();
            }
            //如果元数据返回类型是 Response,直接返回回去即可,不需要decode()解码
            if (Response.class == metadata.returnType()) {
                if (response.body() == null) {
                    return response;
                }
                if (response.body().length() == null ||
                    response.body().length() > MAX_RESPONSE_BUFFER_SIZE) {
                    shouldClose = false;
                    return response;
                }
                // Ensure the response body is disconnected
                byte[] bodyData = Util.toByteArray(response.body().asInputStream());
                return response.toBuilder().body(bodyData).build();
            }
            //主要对2xx和404等进行解码,404需要特别的开关控制。其他情况,使用errorDecoder进行解码,以异常的方式返回
            if (response.status() >= 200 && response.status() < 300) {
                if (void.class == metadata.returnType()) {
                    return null;
                } else {
                    return decode(response);
                }
            } else if (decode404 && response.status() == 404 && void.class != metadata.returnType()) {
                return decode(response);
            } else {
                throw errorDecoder.decode(metadata.configKey(), response);
            }
        } catch (IOException e) {
            if (logLevel != Logger.Level.NONE) {
                logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime);
            }
            throw errorReading(request, response, e);
        } finally {
            if (shouldClose) {
                ensureClosed(response.body());
            }
        }
    }
    

    这里主要就是使用:client.execute(request, options) 来发起调用,下面基本都是处理返回结果的逻辑。到此我们的整个调用生态已经解析完毕。

    我们可以整理一下上面的分析:

    首先调用接口为什么会直接发送请求?

    原因就是Spring扫描了@FeignClient注解,并且根据配置的信息生成代理类,调用的接口实际上调用的是生成的代理类。

    其次请求是如何被Feign接管的?

    1. Feign通过扫描@EnableFeignClients注解中配置包路径,扫描@FeignClient注解并将注解配置的信息注入到Spring容器中,类型为FeignClientFactoryBean
    2. 然后通过FeignClientFactoryBeangetObject()方法得到不同动态代理的类并为每个方法创建一个SynchronousMethodHandler对象;
    3. 为每一个方法创建一个动态代理对象, 动态代理的实现是 ReflectiveFeign.FeignInvocationHanlder,代理被调用的时候,会根据当前调用的方法,转到对应的 SynchronousMethodHandler

    这样我们发出的请求就能够被已经配置好各种参数的Feign handler进行处理,从而被Feign托管。

    请求如何被Feign分发的?

    上一个问题已经回答了Feign将每个方法都封装成为代理对象,那么当该方法被调用时,真正执行逻辑的是封装好的代理对象进行处理,执行对应的服务调用逻辑。

    展开全文
  • 使用springCloud的Eureka+Feign实现两个微服务之间的调用)一.简介二.需求三.设计思想四.实现4-1.创建Eureka服务端4-2.创建宠物微服务客户端4-3.创建用户微服务端五.测试六.总结 一.简介 对于微服务,本人自己最近也...

    一.简介

    对于微服务,本人自己最近也是在摸索之中,下面这篇算是sprinCloud入门级,通过这个案例,至少会知道什么是微服务。

    二.需求

    在实现之前要先清楚想干嘛实现什么东西,带着思路去实现,这样才能知道为什么要这么做。假设现在有个基于微服务设计的宠物管理系统,系统被拆分成多个服务,其中有两个服务为用户服务、宠物服务。现在的需求是用户服务想要查看宠物服务的信息,比如用户A想要查看有哪些种类的宠物狗。

    三.设计思想

    先创建一个Eureka注册中心服务端用与管理其他Eureka注册中心客户端的信息,再创建两个Eureka客户端,一个是用户服务客户端(用户微服务)另一个是宠物服务客户端(宠物微服务),此时用户服务查看宠物服务,即用户服务为请求方,宠物服务是响应方,springCloud中称用户服务为消费方(consumer),宠物服务属于供应方(provider),这两个微服务是使用springCloud的Feign组件实现通信的(这里暂不谈这种通信方式的好坏,想了解更多通信方式可以搜索webService)。

    四.实现

    4-1.创建Eureka服务端

    1.打开Intellij IDEA新建一个项目,File -->New -->Project。
    在这里插入图片描述

    2.选择Spring Initializr,然后点击Next按钮。耐心等待,可能会很慢,如果老是失败就把网址换成阿里云提供的网址,更改Default处的为下面的Custom,网址输入https://start.aliyun.com/
    在这里插入图片描述

    3.修改项目信息,这是弹出的界面,修改其中的信息
    在这里插入图片描述

    我修改了这四个地方,按要求修改就行,修改后点击Next.
    在这里插入图片描述

    4.勾选需要的组件
    2处Eurake Server表示这是个注册中心服务端,版本此处我选择的是最高版,这个要特别注意,之后的版本尽量选择和这个版本统一,而且推荐不要选择最新版。
    在这里插入图片描述

    点击Finish
    在这里插入图片描述

    等待依赖的下载完成,下面是pom文件,如果出现报错,就将版本号声明下,否则就不用做修改。
    在这里插入图片描述

    5.增加注解
    找到启动类,增加注解@EnableEurekaServer
    在这里插入图片描述

    6.编辑配置文件,推荐将properties改为yml格式

    输入以下信息,注意格式,完成后点击启动按钮
    可以直接复制下面这段信息的,也可以照着截图自己改,因为我后面更新了下这个配置信息(之前图片太模糊了),所以截图和下面这段信息有所误差。

    server:
      port: 8000
    eureka:
      client:
        service-url:
    #这是注册中心服务端的地址,告诉别的服务我在这个位置,之后将别的服务信息注册到这个位置来
          defaultZone: http://localhost:8000/eureka/
    #表示是否从Eureka Server获取注册的服务信息,这就是个服务端,没必要自己获取自己
        fetch-registry: false
    #表示是否将当前服务注册到Eureka Server 一样的道理没必要自己保管自己的信息    
        register-with-eureka: false
    

    在这里插入图片描述

    打开浏览器,输入 localhost:8000,注意不是输入配置文件中的localhost:8000/eureka/
    在这里插入图片描述

    4-2.创建宠物微服务客户端

    1.创建步骤和上面类似
    在这里插入图片描述

    2.修改信息
    在这里插入图片描述

    3.勾选组件,注意要选择Web组件或者其它能够持久运行的。不然会注册失败,我试过只勾选eureka client组件项目启动不起来的。
    在这里插入图片描述

    在这里插入图片描述

    点击Finish
    在这里插入图片描述

    4.打开启动类,添加注解@EnableEurekaClient。
    这里说明下EnableEurekaClient和@EnableDisCoveryClient都是可以的,springCloud中的discovery service有多种实现(Eureka、Zookeeper等),@EnableDiscoveryClient基于spring-cloud-commons,@EnableEurekaClient基于spring-cloud-netflix。
    就是如果选用的注册中心是Eureka,那么就推荐@EnableEurekaClient,如果是其他的注册中心,那么推荐使用@EnableDiscoveryClient。
    在这里插入图片描述

    5.修改配置文件,添加如下信息。

    server:
      port: 1002
    #指定项目对外的名字,为方便记忆与项目名一致就行了
    spring:
      application:
        name: eureka-pet-client
    #将此服务注册到注册中心的服务端中,此处的地址为Eureka服务端对外公开的地址
    eureka:
      client:
        service-url:
          defaultZone: http://localhost:8000/eureka/
    

    在这里插入图片描述

    启动项目(下面是旧图很模糊,信息也没来的即改不用管)
    在这里插入图片描述
    刷新Eureka服务端网页,可以看到已经将服务注册进去了。
    在这里插入图片描述

    6.上面的步骤成功了,就可编写宠物微服务的功能代码了。
    把项目结构先贴出来
    在这里插入图片描述

    先建个实体类

    package com.springcloudbase.eurekapetclient.entity;
    public class Dog {
        private String name;
        private Integer age;
        /**
         * 种类
         */
        private String type;
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public Integer getAge() {
            return age;
        }
        public void setAge(Integer age) {
            this.age = age;
        }
        public String getType() {
            return type;
        }
        public void setType(String type) {
            this.type = type;
        }
    }
    

    建个DogService接口

    package com.springcloudbase.eurekapetclient.service;
    import com.springcloudbase.eurekapetclient.entity.Dog;
    import java.util.List;
    public interface DogService {
        /**
         * 查询所有狗的信息
         * @return
         */
        List<Dog> queryDogs();
    }
    

    创建实现类实现查询方法

    package com.springcloudbase.eurekapetclient.service.impl;
    
    import com.springcloudbase.eurekapetclient.entity.Dog;
    import com.springcloudbase.eurekapetclient.service.DogService;
    import java.util.ArrayList;
    import java.util.List;
    @Service
    public class DogServiceImpl implements DogService {
        @Override
        public List<Dog> queryDogs() {
            //模拟数据库中的数据
            Dog dog01 = new Dog();
            dog01.setName("小黑");
            dog01.setAge(1);
            dog01.setType("中华田园犬");
            Dog dog02 = new Dog();
            dog02.setName("小白");
            dog02.setAge(2);
            dog02.setType("哈士奇");
            List<Dog> dogList = new ArrayList<>();
            dogList.add(dog01);
            dogList.add(dog02);
            return dogList;
        }
    }
    

    新建DogController类

    package com.springcloudbase.eurekapetclient.controller;
    import com.springcloudbase.eurekapetclient.entity.Dog;
    import com.springcloudbase.eurekapetclient.service.DogService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RestController;
    import java.util.List;
    
    @RestController
    public class DogController {
        @Autowired
        private DogService dogService;
    
        /**
         * 若遵循RestFul风格,查询应该用Get请求才对,但是Feign不支持Get请求传递POJO对象,
         * 所以此处我采用了Post的请求方式
         * @param dog
         * @return
         */
        @PostMapping("queryDogs")
        public List<Dog> queryDogs(Dog dog){
           return dogService.queryDogs();
        }
    }
    

    到pom文件中加入Feign的依赖,并刷新Maven。

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    

    到启动类中加入@EnableFeignClients注解

    项目结构如下,重启项目,项目结构如下
    在这里插入图片描述

    4-3.创建用户微服务客户端

    创建步骤和宠物微服务端几乎一样,只贴图了。
    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述
    启动类中添加@EnableEurekaClient注解

    配置文件信息如下

    server:
      port: 1001
    #对外的项目名,为方便记忆就和项目名一致
    spring:
      application:
        name: eureka-user-client
    #将此服务注册到注册中心,地址填写注册中心服务端中的地址
    eureka:
      client:
        service-url:
          defaultZone: http://localhost:8000/eureka/
    

    在这里插入图片描述

    启动项目
    刷新服务端的网页,用户微服务客户端信息已经被注册到服务端了。
    在这里插入图片描述

    接下来编写功能代码 实现调用用宠物微服务端的方法
    先到pom文件中引入Feign依赖,再到启动类上加入@EnableFeignClients,步骤同前面一模一样。

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    

    到启动类中加入@EnableFeignClients注解
    先贴出项目结构
    在这里插入图片描述

    创建Dog实体类,同上,直接复制前面创建过的Dog实体类然后粘贴,修改下包名。
    创建一个FeignClient接口用于调用宠物微服务中的方法。

    package com.springcloudbase.eurekauserclient.client;
    import com.springcloudbase.eurekauserclient.entity.Dog;
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PostMapping;
    import java.util.List;
    
    /**
     * eureka-pet-client 为供应方对外公开的项目名,即yml中配置的application-name
     */
    @FeignClient("eureka-pet-client")
    public interface DogClient {
    
        /**
         * queryDogs为 供应方的Controller地址
         * @return
         */
        @PostMapping("queryDogs")
        public List<Dog> queryDogs(Dog dog);
    }
    

    创建Controller

    package com.springcloudbase.eurekauserclient.controller;
    import com.springcloudbase.eurekauserclient.client.DogClient;
    import com.springcloudbase.eurekauserclient.entity.Dog;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    import java.util.List;
    
    @RestController
    public class UserController {
        @Autowired
        private DogClient dogClient;
        @GetMapping("userQueryDogs")
        public List<Dog> userQueryDogs(Dog dog){
            return dogClient.queryDogs(dog);
        }
    }
    

    重启项目,刷新Eureka服务端网页,可以看到两条注册信息了,现在这两个服务是部署在同一台机器上的,等学会了docker后就可以模拟出部署在不同机器上了,还可以为Eureka服务端做集群以提高可用性。
    在这里插入图片描述

    五.测试

    使用postman或者直接在浏览器上输入 localhost:1001/userQueryDogs,可以看到用户服务成功调用了宠物服务中的查询方法。这个网址是用户服务的网址,但是却调用了宠物服务中的方法。
    在这里插入图片描述

    六.总结

    之前听微服务是不是觉得很牛逼的样子?其实这些框架让我们建项目越来越简单。在敲代码搭环境之前,首先搞清楚自己要干嘛,为什么这么做,如果不知道自己为什么要做这些步骤,即使照着别人的敲,效率是非常低的,如果知道了思想清楚步骤,照着做一两遍基本就可以记住了。POM文件报错,很可能是网络环境不好(并不是真的网速慢)导致jar包其实并没有下载下来。

    展开全文
  • Feign调用过程和调用示例

    千次阅读 2017-11-14 15:06:44
    1.PathVariable的url客户端调用示例1.1controller实现@Autowired QueryRemote queryRemote;@RequestMapping("/query/{name}") public String query(@PathVariable String name){ return queryRemote.query(name); }...
  • 一、fegin 调用端,从rootContext中获取xid,添加header请求头。 seata对fegin的client作了一层wrap,在execute http请求的基础上,前置添加了http header。 二、服务端,从header请求头中获取xid,绑定到root...
  • feign调用过程注意事项

    千次阅读 2019-03-25 13:58:00
    feign调用过程注意事项 Feign是Netflix开发的声明式、模板化的HTTP客户端, Feign可以帮助我们更快捷、优雅地调用HTTP API。在Spring Cloud中,使用Feign非常简单——创建一个接口,并在接口上添加...
  • 在使用feign 调用过程中出现了java.lang.NullPointerException: null 空指针错误, 根据debug 信息可以看到是由 (ServletRequestAttributes)RequestContextHolder.getRequestAttributes()报出来的,可以看出主要...
  • 世界上并没有完美的程序,但是我们并不因此而沮丧,因为写程序就是一个不断追求完美的过程。 @FeignClient("test") @RequestMapping("/test") public interface testService { @GetMapping(value = "/test/{id}/{...
  • RPC:Remote Produce Call远程过程调用,类似的还有RMI。自定义数据格式,基于原生TCP通信,速度快,效率高。早期的webservice,现在热门的dubbo,都是RPC的典型。(代表dubbo) Http:http其实是一种网络传输协议,...
  • fegin接口出现有的请求能够请求通过,有的请求就发现出现调用接口500的情况。
  • Feign的调用原理及其源码分析目录概述架构特性设计思路实现思路分析Feign是如何进行服务调用的拓展实现相关工具如下:实验效果:(解决思路)分析:小结:参考资料和推荐阅读 LD is tigger forever,CG are not ...
  • XID, RootContext.getXID()) 注意: 1、RootContext的事务ID生成必须是依赖切面,若未切入成功则无法生成事务ID 2、RootContext的本质为localThread,如果在一个容器不同线程做事务绑定,可以调用官方提供的方法Root...
  • 最近在阅读SpringCloud核心组件的相关源码,先归纳一张图总览整个调用过程,后续逐步拆解各个组件的实现机制。 分为两个步骤: 1、Spring初始化Feign的过程; 2、Feign接口调用过程
  • Feign的使用以及调用过程分析 在进行微服务之间的调用的时候,我们本质上都是通过http请求来进行的(参数处理,返回结果处理),在使用Feign之前我们都是使用的RestTemplate来完成这些工作的,类似于下面的这种方式...
  •   虽然此篇文章介绍Feign客户端调用工具,但是我们也要知道SpringCloud 中支持两种客户端调用工具: ①RestTemplate(基本上不用)、②Feign客户端工具(项目开发中用的较多)。这篇文章重点介绍Feign客户端的调用工具...
  • spring-cloud-openfeign是我们项目中使用的依赖,而spring-cloud-openfeign又引入了feign依赖,这里我们只看调用过程,真正的调用过程在 Feign源码 中,请看:SynchronousMethodHandler.java 调用时,会走这里...
  • 使用Fegin+Consul

    2020-07-03 17:05:21
    Feign:微服务之间互相调用 Consul:服务注册中心 <!-- consul 服务注册和发现功能 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-...
  • Spring Cloud Feign 服务间调用 -超时

    万次阅读 2018-06-20 16:04:21
    一、概述 上一章已经看到 Eureka client 是如何注册了 Eureka Server 中去的,在本文中将讲解 服务间如何进行调用,在上一章已经提到 microserver-user-api会对外提供服务进行服务间调用,在本章将用到 ...
  • springcloud中feignclient远程过程调用demo

    万次阅读 2017-12-25 15:47:35
    使用@FeignClient注解接口,来远程调用服务,在controller中调用接口的方法来实现远程调用 接口代码片段 (调用远程服务来验证登录) @FeignClient(name = "service",url = "http://localhost:7000") public ...
  • Feign原理 (图解)

    万次阅读 多人点赞 2020-07-28 21:17:51
    1.1简介:Feign远程调用的 Feign远程调用,核心就是通过一系列的封装和处理,将以JAVA注解的方式定义的远程调用API接口,最终转换成HTTP的请求形式,然后将HTTP的请求的响应结果,解码成JAVA Bean,放回给调用...
  • SpringCloud之远程调用Feign

    千次阅读 2018-12-10 16:17:14
    在上篇文章中,我们使用了Ribbon的负载均衡功能,大大简化了远程调用时的代码: String baseUrl = &quot;http://user-service/user/&quot;; User user = this.restTemplate.getForObject(baseUrl + id, User...
  • 使用springcloud的过程中,微服务间调用的情况是非常多的,其中springcloud有提供feign调用的方式,来实现服务间的通信。 对于post请求大多都是使用RequestBody注解来表示请求参数,基本上不会存在什么问题,但是...
  • feign调用调用者(consumer)抛出如下异常: com.netflix.hystrix.exception.HystrixRuntimeException: LoginServer#saveToken(TbJwtToken) failed and fallback failed. at com.netflix.hystrix.AbstractCommand$...
  • 2.feign服务间调用数据超过10M会报错,报错如下; Could not read document: UT000020 : Connection terminated as request was larger than 10485769; nested exception is java.io.Exception:UT000020 : ...
  • 在这里,Feign接口的代理对象就被创建出来了,在使用接口过程实际调用的这是这个代理对象的InvocationHandler.invoke()方法。 代理对象的构建主要有以下内容 1、为了创建Feign的远程接口的代理对象,Feign提供了自己...
  • 转载:原文 总结: 1.pom添加依赖 2.application中填写正确的eureka配置 ...Eureka中配置的地址不对,导致消费者调用提供者方法时候,出现链接超时, 这边需要改一下提供者的Eureka的接口配置 正文如下 一...
  • --服务调用--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> 在启动类上...
  • 最近在远程调用过程中出现了: Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured. 但是实际上我并没有做数据库相关的任何操作,出现此错误导致...
  • 踩坑现场: 日志信息报错为空指针异常 排坑过程: 断点去查看源码: ...因此fegin调用服务时,response会返回null 解决方法: 去检查配置文件中的Eureka服务地址是否正确或者开启服务器

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,773
精华内容 709
关键字:

fegin调用过程