spring cloud 使用nacos当注册中心,以及服务间调用问题

本以为这是一个很简单的事情,因为之前一直使用他当注册中心,界面看起来也比较友好,翻看官方也有spring cloud文档,万万没想到真用起来就坑了,从下午一直debug加改源码到现在
正常我们使用

@FeignClient(value = "paasUserFacade", path = "/im/user")
public interface IPaasUserFacade extends IBaseController<PaasUserRequestModel> {
}

这些就可以调用了,但是nacos不行,因为注册到nacos上面的之后lemur-paas这个项目的id,并没有passUserFacade的这个所以所以一直报服务找不到,然后就看官方文档

@SpringBootApplication
@EnableDiscoveryClient
public class NacosConsumerApplication {

    @LoadBalanced
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

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

    @RestController
    public class TestController {

        private final RestTemplate restTemplate;

        @Autowired
        public TestController(RestTemplate restTemplate) {this.restTemplate = restTemplate;}

        @RequestMapping(value = "/echo/{str}", method = RequestMethod.GET)
        public String echo(@PathVariable String str) {
            return restTemplate.getForObject("http://service-provider/echo/" + str, String.class);
        }
    }
}

这种方案如何行,这不是反人类,然后就进入慢慢的源码寻找之旅
1.以为ribbonRequest 没有设置serviceKey导致的问题,自己set了下依然不行

FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(
					this.delegate, request, uriWithoutHost);

			IClientConfig requestConfig = getClientConfig(options, clientName);
			return lbClient(clientName)
					.executeWithLoadBalancer(ribbonRequest, requestConfig).toResponse();

2.加入contextId 启动不起来因为contextId会作为类的Alise,spring会禁止多个类叫一个名字

@FeignClient(value = "paasUserFacade", contextId = "lemur-paas", path = "/im/user")
public interface IPaasUserFacade extends IBaseController<PaasUserRequestModel> {

}

按照提示加上了

spring:
	main:
    	allow-bean-definition-overriding: true

启动测试,发现依然拿到是 paasUserFacade,此路依然不通
3.查看注册的时候如何获取的类FeignClientsRegistrar

private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
      String className = annotationMetadata.getClassName();
      BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);
      this.validate(attributes);
      definition.addPropertyValue("url", this.getUrl(attributes));
      definition.addPropertyValue("path", this.getPath(attributes));
      String name = this.getName(attributes);
      definition.addPropertyValue("name", name);
      String contextId = this.getContextId(attributes);
      definition.addPropertyValue("contextId", contextId);
      definition.addPropertyValue("type", className);
      definition.addPropertyValue("decode404", attributes.get("decode404"));
      definition.addPropertyValue("fallback", attributes.get("fallback"));
      definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
      definition.setAutowireMode(2);
      String alias = contextId + "FeignClient";
      AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
      boolean primary = (Boolean)attributes.get("primary");
      beanDefinition.setPrimary(primary);
      String qualifier = this.getQualifier(attributes);
      if (StringUtils.hasText(qualifier)) {
          alias = qualifier;
      }

      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);
      validateFallback(annotation.getClass("fallback"));
      validateFallbackFactory(annotation.getClass("fallbackFactory"));
  }

  static void validateFallback(final Class clazz) {
      Assert.isTrue(!clazz.isInterface(), "Fallback class must implement the interface annotated by @FeignClient");
  }

  static void validateFallbackFactory(final Class clazz) {
      Assert.isTrue(!clazz.isInterface(), "Fallback factory must produce instances of fallback classes that implement the interface annotated by @FeignClient");
  }

  String getName(Map<String, Object> attributes) {
      String name = (String)attributes.get("serviceId");
      if (!StringUtils.hasText(name)) {
          name = (String)attributes.get("name");
      }

      if (!StringUtils.hasText(name)) {
          name = (String)attributes.get("value");
      }

      name = this.resolve(name);
      return getName(name);
  }

  private String getContextId(Map<String, Object> attributes) {
      String contextId = (String)attributes.get("contextId");
      if (!StringUtils.hasText(contextId)) {
          return this.getName(attributes);
      } else {
          contextId = this.resolve(contextId);
          return getName(contextId);
      }
  }

  static String getName(String name) {
      if (!StringUtils.hasText(name)) {
          return "";
      } else {
          String host = null;

          try {
              String url;
              if (!name.startsWith("http://") && !name.startsWith("https://")) {
                  url = "http://" + name;
              } else {
                  url = name;
              }

              host = (new URI(url)).getHost();
          } catch (URISyntaxException var3) {
          }

          Assert.state(host != null, "Service id not legal hostname (" + name + ")");
          return name;
      }
  }

发现url是用的context但是怎么都是不生效的,然后继续debug,查看到底哪里获取的url
发现在FeignClientFactoryBean 有如下代码

<T> T getTarget() {
		FeignContext context = this.applicationContext.getBean(FeignContext.class);
		Feign.Builder builder = feign(context);

		if (!StringUtils.hasText(this.url)) {
			iif (!this.name.startsWith("http")) {
				this.url = "http://" + this.name;
			} else {
				this.url = this.name;
			}
			this.url += cleanPath();
			return (T) loadBalance(builder, context,
					new HardCodedTarget<>(this.type, this.name, this.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 load 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 (T) targeter.target(this, builder, context,
				new HardCodedTarget<>(this.type, this.name, url));
	}

这个就决定了我们访问地址的方法 我在if (!StringUtils.hasText(this.url)) { 之后加入了

	if (StringUtils.hasText(this.contextId) && !this.name.startsWith("http")){
		this.url =  "http://" + this.contextId;
	}else if (StringUtils.hasText(this.contextId)){
		this.url =  this.contextId;
	} 

优先判断contextId,来获取负载的地址,这样就解决了问题,虽然自己改动了源码 ,但依然不nacos官网和各种demo里面的方式好多了,而且等spring哪天兼容了nacos也就把自己改的类删除了就完成了.接口类还是使用原来的就可以了

顺便说一句:url为啥会使用绝对路径,就是因为url非空状态下回返回tartge的直接代理

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值