Spring Cloud踩坑(二)SpringCloud 坑集

feign  作为 springcloud 微服务 内部通信的组件  还是有很多坑的

坑1、 

Load balancer does not have available server for client

这是因为 默认的eureka 启动时  相关的服务端还没有来得及往 eureka 服务端注册    或者 eureka server中没有注册 相关的服务   

坑2、看了很多 教程   说 feign  类上的 @RequestMapping 不会被加入 接口映射   没错 想想也应该是这样  但是为了解决这个问题   我在接口方法上加入映射路径是否可行呢

可以做个 测试   我这里有 2个服务  MESSAGE-SERVICE 是服务提供者  SMS-SERVICE 是 服务消费者      feignware 单独模块 用来统一提供 相互调用的api  生产中建议feign集中配置   一个eureka server  

MESSAGE-SERVICE  提供接口 :

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

    @PostMapping("/get")
    public User getUser(@RequestBody User user) {
        System.out.println("i am  message-service <<<<<<<<<<<<<<<<<<<<");
        System.out.println(user);
        return user;
    }

}

feignware :      这样写  直接加入 类一级的/msg

@FeignClient(value = "MEMBER-SERVICE")
public interface UserService {
      @PostMapping("/msg/get")
     User  get(@RequestBody User user);
}

SMS-SERVICE 调用接口:

@RestController
@RequestMapping("/sms")
public class RemoteController {
  @Resource
    private UserService userService;
  @GetMapping("/go")
  public User test(User user){
      System.out.println(">>>>>>>>>>>");
      User result=userService.get(user);
      System.out.println(result);
      return result;
  }

}

流程是这样的 :     

执行结果:  请求成功

 

去掉了 feignware 中 /msg 一级后 测试 结果:

结果说明: feign  类上的 @RequestMapping 不会被加入 接口映射  是对的     如果 想加入正好上面是个解决方案

坑3、    feign 调用是 post 请求    还是上面的例子 测试看看   先说明一句  看到有的教程上说 

feign只支持 @RequestMapping     这种说法是不对  上面已经有例子证明了这一点  起码上  @GetMapping @PostMapping 是支持的    其他 springcloud版本没有试过

 我们先三者都采用@GetMapping  看看结果: 

405  到底是哪里不支持 get呢   来几组测试:

sms  get   feignware get   message  get    结果   405 

sms  get   feignware post   message  get    结果   405 

sms  post   feignware post   message  get    结果   405   

sms  post   feignware get  message  post 结果   200

sms  post   feignware post   message   post  结果   200   

sms  get   feignware post   message   post  结果   200   

sms  get   feignware get   message   post  结果   200   

结果总结一下   就是  @RequestBody 调用提供方 一定要用post        feign包  跟消费方  无所谓

坑4、       无法扫描到引用包的feign接口

在实际的生产中 我们服务模块是有很多的, 如果 A的接口 B要调用 我们声明一次feigin客户端 api    然后C也要调A的接口  我们还要声明一次feigin客户端api   如果还有更多  就会有很多重复代码  为了提高代码复用,我们往往单独声明一个包来引入feigin 的api  其他包如果调用的化引入到工程中就行了   但是 问题来了 引用包无法被直接扫描到  我们知道 一个微服务模块 如果需要开启feign调用功能 需要加上

@EnableFeignClients 

源码:

/*
 * Copyright 2013-2015 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.cloud.netflix.feign;

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 {

	/**
	 * Alias for the {@link #basePackages()} attribute. Allows for more concise annotation
	 * declarations e.g.: {@code @ComponentScan("org.my.pkg")} instead of
	 * {@code @ComponentScan(basePackages="org.my.pkg")}.
	 * @return the array of 'basePackages'.
	 */
	String[] value() default {};

	/**
	 * Base packages to scan for annotated components.
	 * <p>
	 * {@link #value()} is an alias for (and mutually exclusive with) this attribute.
	 * <p>
	 * Use {@link #basePackageClasses()} for a type-safe alternative to String-based
	 * package names.
	 *
	 * @return the array of 'basePackages'.
	 */
	String[] basePackages() default {};

	/**
	 * Type-safe alternative to {@link #basePackages()} for specifying the packages to
	 * scan for annotated components. The package of each class specified will be scanned.
	 * <p>
	 * Consider creating a special no-op marker class or interface in each package that
	 * serves no purpose other than being referenced by this attribute.
	 *
	 * @return the array of 'basePackageClasses'.
	 */
	Class<?>[] basePackageClasses() default {};

	/**
	 * A custom <code>@Configuration</code> for all feign clients. Can contain override
	 * <code>@Bean</code> definition for the pieces that make up the client, for instance
	 * {@link feign.codec.Decoder}, {@link feign.codec.Encoder}, {@link feign.Contract}.
	 *
	 * @see FeignClientsConfiguration for the defaults
	 */
	Class<?>[] defaultConfiguration() default {};

	/**
	 * List of classes annotated with @FeignClient. If not empty, disables classpath scanning.
	 * @return
	 */
	Class<?>[] clients() default {};
}

     其中basePackages  声明 扫描  feignclient 注解所在的包的包路径  声明后就能扫描到你在该包下@FeignClient 标记的 feign接口

坑5、  无法扫描到引入包的服务降级实现,大多数情况 我们要对feignClient接口  显式声明一个fallback 以便进行服务降级   但是如果你的feignclient 接口 不在 springboot 的启动类的子类  会无法启动 显示

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean 
with name 'com.xxx.feign.api.UserService': 
FactoryBean threw exception on object creation; 
nested exception is java.lang.IllegalStateException:
 No fallback instance of type class com.xxx.feign.api.UserServiceHystrix found for feign client MEMBER-SERVICE

也就是你feign 接口的实现类  无法被注入

 

先看一下 源码:

/*
 * Copyright 2013-2016 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.cloud.netflix.feign;

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;

/**
 * Annotation for interfaces declaring that a REST client with that interface should be
 * created (e.g. for autowiring into another component). If ribbon is available it will be
 * used to load balance the backend requests, and the load balancer can be configured
 * using a <code>@RibbonClient</code> with the same name (i.e. value) as the feign client.
 *
 * @author Spencer Gibb
 * @author Venil Noronha
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FeignClient {

	/**
	 * The name of the service with optional protocol prefix. Synonym for {@link #name()
	 * name}. A name must be specified for all clients, whether or not a url is provided.
	 * Can be specified as property key, eg: ${propertyKey}.
	 */
	@AliasFor("name")
	String value() default "";

	/**
	 * The service id with optional protocol prefix. Synonym for {@link #value() value}.
	 *
	 * @deprecated use {@link #name() name} instead
	 */
	@Deprecated
	String serviceId() default "";

	/**
	 * The service id with optional protocol prefix. Synonym for {@link #value() value}.
	 */
	@AliasFor("value")
	String name() default "";
	
	/**
	 * Sets the <code>@Qualifier</code> value for the feign client.
	 */
	String qualifier() default "";

	/**
	 * An absolute URL or resolvable hostname (the protocol is optional).
	 */
	String url() default "";

	/**
	 * Whether 404s should be decoded instead of throwing FeignExceptions
	 */
	boolean decode404() default false;

	/**
	 * A custom <code>@Configuration</code> for the feign client. Can contain override
	 * <code>@Bean</code> definition for the pieces that make up the client, for instance
	 * {@link feign.codec.Decoder}, {@link feign.codec.Encoder}, {@link feign.Contract}.
	 *
	 * @see FeignClientsConfiguration for the defaults
	 */
	Class<?>[] configuration() default {};

	/**
	 * Fallback class for the specified Feign client interface. The fallback class must
	 * implement the interface annotated by this annotation and be a valid spring bean.
	 */
	Class<?> fallback() default void.class;

	/**
	 * Define a fallback factory for the specified Feign client interface. The fallback
	 * factory must produce instances of fallback classes that implement the interface
	 * annotated by {@link FeignClient}. The fallback factory must be a valid spring
	 * bean.
	 *
	 * @see feign.hystrix.FallbackFactory for details.
	 */
	Class<?> fallbackFactory() default void.class;

	/**
	 * Path prefix to be used by all method-level mappings. Can be used with or without
	 * <code>@RibbonClient</code>.
	 */
	String path() default "";

	/**
	 * Whether to mark the feign proxy as a primary bean. Defaults to true.
	 */
	boolean primary() default true;

}

  * Fallback class for the specified Feign client interface. The fallback class must
     implement the interface annotated by this annotation and be a valid spring bean. *

fallback 上会有这样的注释    说的是  声明feign客户端接口 的降级类  而且 这个降级类必须实现 该feign 接口   并且必须是一个可用的spring bean

 如果你仅仅在这个实现类上加入spring bean 声明注解 比如 @Component   你会发现依然 无法注入 来大致猜想一下流程     熟悉springboot 的 应该清楚   springboot  启动的时候  会扫描其main类 所在包的子包进行 bean 实例化     如果不在子包 默认是扫描不到的   那么如何扫描到呢  声明扫描的路径 也就是需要在main类上使用注解@ComponentScan 注解  但是 如果 我们仅仅声明了 feign 降级实现的路径 你会发现 main类的子包无法扫描到了  所以 此处应该 

@ComponentScan(basePackages = {"main 所在的包","降级类所在的包"})

配置好后  我们写一个降级类:

@Component
public class UserServiceHystrix implements UserService {
    @Override
    public User get(User user) {
        System.out.println("<><><><><><><><><><> MEMBER-SERVICE 挂了<><><><><><><><><><> ");
         user.setAge(20017);
         user.setGender("male");
         user.setName("服务挂了");
        return user;
    }
}

 

然后我们测试一下 启动消费方  不启动 提供方MEMBER-SERVICE  发现熔断可用:

原文地址:https://my.oschina.net/10000000000/blog/1591147

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值