feign简单使用
在调用服务的时候,我们常用是RestTemplate,但是这种方式调用时需要凭借url,如果服务的地址或者端口号有变化,我们需要跟着做变动,给开发工作带来不必要的麻烦,
这里引入Feign调用服务接口,这是微服务之间经常使用的调用关联方服务的一种,和Dubbo类似,后期调用就类似于本地方法调用同样的道理
需要的依赖
<!--引入feign依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
<!--客户端--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
列出我使用的springboot版本和springcloud版本
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Hoxton.SR3</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>2.1.0.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.1.RELEASE</version> <relativePath/> </parent>
启动类中需要的注解
@SpringBootApplication //开启定时任务支持 @EnableAsync() //设置redis共享 @EnableRedisHttpSession //开启事务支持 @EnableTransactionManagement //开启服务发现 @EnableDiscoveryClient // 开启Feign @EnableFeignClients
方法声明
package com.java.api; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; /** * @author yourheart * @Description * @create 2022-09-07 23:33 */ @FeignClient(name="ip-service") public interface QueryIpClient { @RequestMapping("/queryIpCity/{ip}") @ResponseBody public String queryIpCity(@PathVariable String ip); }
如何使用
package com.java.service.impl; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.java.api.QueryIpClient; import com.java.bean.Paramter; import com.java.service.ParamterService; import com.java.service.QueryIp; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.ui.Model; import org.springframework.web.client.RestTemplate; import javax.servlet.http.HttpServletRequest; /** * @author yourheart * @Description * @create 2021-06-07 0:05 */ @Service public class QueryIpServiceImpl implements QueryIp { private static final Logger logger= LoggerFactory.getLogger(QueryIpServiceImpl.class); @Autowired private ParamterService paramterService; @Autowired private QueryIpClient queryIpClient; /** * 判断ip * * @param model * @param remoteHost * @param localAddr */ @Override public void judgeIp(Model model, String remoteHost, String localAddr) { logger.info("remoteHost:{}",remoteHost); Paramter paramter=new Paramter(); paramter.setParamterType("IpAddress"); paramter.setParamterCode(remoteHost); String queryByCode = paramterService.queryByCode(paramter); model.addAttribute("icp", queryByCode); } /** * 查询ip地址详细信息 * @param ip * @return */ @Override public String queryCityByIp(String ip) { logger.info("【Feign方式调用】【查询ip地址详细信息】入参:{}",ip); String forObject = null; try { forObject =queryIpClient.queryIpCity(ip); logger.info("【Feign方式调用】【查询ip地址详细信息】返回参数:{}",forObject); } catch (Exception e) { logger.error("【Feign方式调用】【调用获取ip信息接口异常】"); return "中国上海上海市"; } JSONObject jsonObject = JSON.parseObject(forObject); String city=jsonObject.getString("address"); logger.info("【Feign方式调用】【查询ip地址详细信息】返回参数:{}",city); return city; } }
application.properties文件配置
spring.application.name=pingan-service #注册到eureka注册中心,如果是注册到集群就用逗号连接多个,单实例写上一个就好 eureka.client.service-url.defaultZone=http://127.0.0.1:8761/eureka
当然你要确保调用的服务是使用了springcloud的形式,或者包装了springcloud的形式
下面给出我调用的服务中配置情况
<?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> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.5.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.ip</groupId> <artifactId>ip-service</artifactId> <version>1.0-SNAPSHOT</version> <properties> <java.version>1.8</java.version> <skipTests>true</skipTests> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Hoxton.SR3</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>2.1.0.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <!--客户端--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!--tomcat容器--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--添加fastjson依赖--> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.7</version> </dependency> <!--lombok依赖--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.16</version> </dependency> <!--mysql驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> </dependency> <!--springboot整合mybatis--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.2</version> </dependency> <!-- 热部署模块 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> <!-- 这个需要为 true 热部署才有效 --> </dependency> <!--java爬虫需要的jar包--> <dependency> <groupId>org.jsoup</groupId> <artifactId>jsoup</artifactId> <version>1.12.2</version> </dependency> <!--判断空的用法 --> <dependency> <groupId>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>2.6</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> <version>3.10.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.lionsoul</groupId> <artifactId>ip2region</artifactId> <version>1.7.2</version> </dependency> <!--RateLimiter的底层是基于令牌桶算法来实现的,来自谷歌的Guava包中--> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>28.0-jre</version> </dependency> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.8.6</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <!--编译插件定义编译细节--> <!-- <plugin>--> <!-- <groupId>org.apache.maven.plugins</groupId>--> <!-- <artifactId>maven-compiler-plugin</artifactId>--> <!-- <version>3.1</version>--> <!-- <configuration>--> <!-- <source>1.8</source>--> <!-- <target>1.8</target>--> <!-- <encoding>utf-8</encoding>--> <!-- <!–告诉编译器,编译的时候记录下形参的真实名称–>--> <!-- <compilerArgs>--> <!-- <arg>-parameters</arg>--> <!-- </compilerArgs>--> <!-- </configuration>--> <!-- </plugin>--> </plugins> <finalName>server0825</finalName> </build> </project>
package com.ip; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; /** * @author yourheart * @Description * @create 2021-06-06 10:34 */ @SpringBootApplication @EnableDiscoveryClient public class CloudPingAnApplication { public static void main(String[] args) { SpringApplication.run(CloudPingAnApplication.class,args); } }
package com.ip.controller.front; import com.alibaba.fastjson.JSONObject; import com.ip.service.QueryIpService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; /** * @author yourheart * @Description * @create 2021-06-06 10:59 */ @Controller @RequestMapping("/queryIp") @Slf4j public class QueryIpController { @Autowired private QueryIpService queryIpService; @RequestMapping("/queryIpCity/{ip}") @ResponseBody public String queryIpCity(@PathVariable String ip){ long a = System.currentTimeMillis(); JSONObject ipCityByIp = queryIpService.getIpCityByIp(ip); String jsonString = ipCityByIp.toJSONString(); long b = System.currentTimeMillis(); log.info("接口调用耗时:{}",(b-a)+"ms"); return jsonString; } }
其中
queryIpService.getIpCityByIp(ip);可以自己写实现
同时对于调用方式给出使用测试类方式测试
结果报错
feign.RetryableException: iZuf627hz8vloz1wtzgxzdZ executing GET http://ip-service/queryIpCity/116.230.165.108 at feign.FeignException.errorExecuting(FeignException.java:249) at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:120) at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:80) at feign.ReflectiveFeign$FeignInvocationHandler.invoke(ReflectiveFeign.java:100) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:56) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:175) at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212) at com.java.ApiTest.test(ApiTest.java:27) at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:74) at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84) at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75) at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86) at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97) at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190) Caused by: java.net.UnknownHostException: iZuf627hz8vloz1wtzgxzdZ at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:184) at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172) at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392) at java.net.Socket.connect(Socket.java:589) at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:480) at feign.Client$Default.convertResponse(Client.java:78) at feign.Client$Default.execute(Client.java:74) at org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer.execute(FeignLoadBalancer.java:93) at org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer.execute(FeignLoadBalancer.java:56) at com.netflix.client.AbstractLoadBalancerAwareClient$1.call(AbstractLoadBalancerAwareClient.java:104) at com.netflix.loadbalancer.reactive.LoadBalancerCommand$3$1.call(LoadBalancerCommand.java:303) at com.netflix.loadbalancer.reactive.LoadBalancerCommand$3$1.call(LoadBalancerCommand.java:287) at rx.internal.util.ScalarSynchronousObservable$3.call(ScalarSynchronousObservable.java:231) at rx.internal.util.ScalarSynchronousObservable$3.call(ScalarSynchronousObservable.java:228) at rx.Observable.unsafeSubscribe(Observable.java:10327) at rx.internal.operators.OnSubscribeConcatMap$ConcatMapSubscriber.drain(OnSubscribeConcatMap.java:286) at rx.internal.operators.OnSubscribeConcatMap$ConcatMapSubscriber.onNext(OnSubscribeConcatMap.java:144) at com.netflix.loadbalancer.reactive.LoadBalancerCommand$1.call(LoadBalancerCommand.java:185) at com.netflix.loadbalancer.reactive.LoadBalancerCommand$1.call(LoadBalancerCommand.java:180) at rx.Observable.unsafeSubscribe(Observable.java:10327) at rx.internal.operators.OnSubscribeConcatMap.call(OnSubscribeConcatMap.java:94) at rx.internal.operators.OnSubscribeConcatMap.call(OnSubscribeConcatMap.java:42) at rx.Observable.unsafeSubscribe(Observable.java:10327) at rx.internal.operators.OperatorRetryWithPredicate$SourceSubscriber$1.call(OperatorRetryWithPredicate.java:127) at rx.internal.schedulers.TrampolineScheduler$InnerCurrentThreadScheduler.enqueue(TrampolineScheduler.java:73) at rx.internal.schedulers.TrampolineScheduler$InnerCurrentThreadScheduler.schedule(TrampolineScheduler.java:52) at rx.internal.operators.OperatorRetryWithPredicate$SourceSubscriber.onNext(OperatorRetryWithPredicate.java:79) at rx.internal.operators.OperatorRetryWithPredicate$SourceSubscriber.onNext(OperatorRetryWithPredicate.java:45) at rx.internal.util.ScalarSynchronousObservable$WeakSingleProducer.request(ScalarSynchronousObservable.java:276) at rx.Subscriber.setProducer(Subscriber.java:209) at rx.internal.util.ScalarSynchronousObservable$JustOnSubscribe.call(ScalarSynchronousObservable.java:138) at rx.internal.util.ScalarSynchronousObservable$JustOnSubscribe.call(ScalarSynchronousObservable.java:129) at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48) at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48) at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48) at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) at rx.Observable.subscribe(Observable.java:10423) at rx.Observable.subscribe(Observable.java:10390) at rx.observables.BlockingObservable.blockForSingle(BlockingObservable.java:443) at rx.observables.BlockingObservable.single(BlockingObservable.java:340) at com.netflix.client.AbstractLoadBalancerAwareClient.executeWithLoadBalancer(AbstractLoadBalancerAwareClient.java:112) at org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient.execute(LoadBalancerFeignClient.java:83) at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:110) ... 21 more
下面给出解决方案,见后续补充地址,其实这里使用Feign调用并没有成功,拿到的事服务器的实例名,并不是服务的信息
下面给Feign添加日志打印
package com.java.config; import com.java.log.MyFeignLogger; import feign.Logger; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @Description: * @Author: Yourheart * @Create: 2022/9/16 13:30 */ @Configuration public class FeignConfig { /** * NONE【性能最佳,适用于生产】:不记录任何日志(默认值) * BASIC【适用于生产环境追踪问题】:仅记录请求方法、URL、响应状态代码以及执行时间 * HEADERS:记录BASIC级别的基础上,记录请求和响应的header。 * FULL【比较适用于开发及测试环境定位问题】:记录请求和响应的header、body和元数 据 * @return */ @Bean public Logger.Level feignLogLevel(){ return Logger.Level.FULL; } }
#设置Feign的日志级别 logging.level.com.java.config.FeignConfig = DEBUG
报错显示如下
调整服务提供者的配置文件为
eureka.instance.prefer-ip-address=true eureka.instance.hostname=127.0.0.1 eureka.instance.instance-id=${eureka.instance.hostname}:${server.port}
然后记录调用的时候,路径要写完整
@FeignClient(value="ip-service-query") @RequestMapping("/queryIp") public interface QueryIpClient { @RequestMapping("/queryIpCity/{ip}") @ResponseBody public String queryIpCity(@PathVariable String ip); }