一、OpenFeign介绍
前面在使用Ribbon+RestTemplate时,利用RestTemplate对http请求的封装处理,形成了一套模版化的调用方法。但是在实际开发中,对于服务依赖的调用可能不止一处,往往一个接口会被多处调用。所有Feign在此基础上做了进一步封装,由他来帮助我们定义和实现依赖服务接口的定义。在Feign的实现下,我们只需创建一个接口并使用注解的方式来配置它(以前是Dao接口上面标注Mapper注解,现在是一个微服务接口上面标注一个Feign注解即可),集可完成对服务提供方的接口绑定,简化了使用Spring Cloud Ribbon时,自动封装服务调用客户端的开发量。
Feign旨在式编写Java Http客户端变得更容易。
Feign集成了Ribbon,利用Ribbon维护了服务列表信息,并且通过轮询实现了客户端的负载均衡。而与Ribbon不同的是,通过feign只需要定义服务绑定接口且以声明式的方法,优雅而简单的实现了服务调用
git地址:https://github.com/spring-cloud/spring-cloud-openfeign
Feign与OpenFeign的区别
1)Feign是Spring Cloud组件中一个轻量级RESTful的HTTP服务客户端,Feign内置了Ribbon,用来做客户端负载均衡,去调用服务注册中心的服务。Feign的使用方式是:使用Feign的注解定义接口,调用接口,就可以调用服务注册中心的服务
二、OpenFeign使用
-
cloud-consumer-order8002
pom.xml
<dependencies> <!--open feign--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <!-- eureka client --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> </dependencies>
application.yml
# 端口
server:
port: 8002
spring:
application:
name: cloud-order
#安全认证
security:
user:
name: root
password: root
eureka:
client:
# 表示将自己注册进Eureka Server默认为true
register-with-eureka: true
# 是否从Eureka Server抓去已有的注册信息,默认是true
fetch-registry: true
# 设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址
service-url:
#设置同时向三个eureka注册
defaultZone: http://${spring.security.user.name}:${spring.security.user.password}@eureka1.com:8887/eureka
instance:
#instance.getUri()获取到的就是这个值(默认为主机名称+端口 / 这里修改为IP地址+端口)
instance-id: ${spring.cloud.client.ip-address}:${server.port}
#访问路径是否显示IP地址
prefer-ip-address: true
#开启优雅停服
#(必须通过POST请求向Eureka Client发起一个shutdown请求。请求路径为:http://ip:port/shutdown。可以通过任意技术实现,如:HTTPClient,AJAX等。)
management:
endpoint:
shutdown:
enabled: true
App
1 package com.sdkj; 2 3 import com.sdkj.myrule.rule2.MyBalancedRule; 4 import org.springframework.boot.SpringApplication; 5 import org.springframework.boot.autoconfigure.SpringBootApplication; 6 import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 7 import org.springframework.cloud.netflix.eureka.EnableEurekaClient; 8 import org.springframework.cloud.netflix.ribbon.RibbonClient; 9 import org.springframework.cloud.openfeign.EnableFeignClients; 10 11 /** 12 * @Author wangshuo 13 * @Date 2022/5/19, 19:46 14 * Please add a comment 15 */ 16 //标识为eureka client端 17 @EnableEurekaClient/*注意这个是client和提供者server不一样*/ 18 @SpringBootApplication(scanBasePackages = {"com.sdkj"}) 19 //开启服务发现 20 @EnableDiscoveryClient 21 //开启feign客户端 22 @EnableFeignClients 23 //使用自定义的Ribbon规则 24 @RibbonClient(name = "CLOUD-PAYMENT-SERVICE", configuration = MyBalancedRule.class) 25 public class App { 26 27 public static void main(String[] args) { 28 SpringApplication.run(App.class); 29 } 30 }
PaymentFeignService
1 package com.sdkj.service; 2 3 import com.sdkj.response.CommonReturnType; 4 import org.springframework.cloud.openfeign.FeignClient; 5 import org.springframework.stereotype.Component; 6 import org.springframework.web.bind.annotation.GetMapping; 7 import org.springframework.web.bind.annotation.PathVariable; 8 import org.springframework.web.bind.annotation.RequestMapping; 9 import org.springframework.web.bind.annotation.ResponseBody; 10 11 /** 12 * @Author wangshuo 13 * @Date 2022/5/23, 9:35 14 * 使用open feign调用消费者 15 */ 16 @Component 17 @FeignClient("CLOUD-PAYMENT-SERVICE") 18 public interface PaymentFeignService { 19 20 @GetMapping(value = "/payment/findById?id={id}") 21 public CommonReturnType findPaymentById(@PathVariable Long id); 22 }
OrderController
1 package com.sdkj.controller; 2 3 import com.sdkj.myrule.rule3.LoadBalancer; 4 import com.sdkj.response.CommonReturnType; 5 import com.sdkj.service.PaymentFeignService; 6 import org.springframework.beans.factory.annotation.Autowired; 7 import org.springframework.cloud.client.ServiceInstance; 8 import org.springframework.cloud.client.discovery.DiscoveryClient; 9 import org.springframework.stereotype.Controller; 10 import org.springframework.web.bind.annotation.*; 11 import org.springframework.web.client.RestTemplate; 12 13 import java.util.List; 14 15 /** 16 * @Author wangshuo 17 * @Date 2022/5/19, 19:52 18 * Please add a comment 19 */ 20 @Controller 21 @RequestMapping(value = "/order") 22 @CrossOrigin(origins = {"*"}, allowCredentials = "true") 23 public class OrderController { 24 25 private static final String url = "http://CLOUD-PAYMENT-SERVICE"; 26 @Autowired 27 private RestTemplate restTemplate; 28 @Autowired 29 private LoadBalancer loadBalancer; 30 @Autowired 31 private DiscoveryClient discoveryClient; 32 @Autowired 33 private PaymentFeignService paymentFeignService; 34 35 @RequestMapping("/getPaymentById") 36 @ResponseBody 37 public CommonReturnType getById(Long id) { 38 //不要忘记把参数传过去 39 return restTemplate.getForObject(url + "/payment/findById/?id=" + id, CommonReturnType.class); 40 } 41 42 @RequestMapping("/getPort") 43 @ResponseBody 44 public String getPort() { 45 46 return restTemplate.getForObject(url + "/payment/getPort", String.class); 47 } 48 49 @RequestMapping("/getLBPort") 50 @ResponseBody 51 public String getLbPort() { 52 53 List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE"); 54 if (instances == null || instances.size() == 0) 55 return null; 56 ServiceInstance instance = loadBalancer.getInstance(instances); 57 return restTemplate.getForObject(instance.getUri() + "/payment/getPort", String.class); 58 } 59 60 //使用open feign 61 @GetMapping("/findPaymentById/{id}") 62 @ResponseBody 63 public CommonReturnType findPaymentById(@PathVariable Long id){ 64 65 return paymentFeignService.findPaymentById(id); 66 } 67 }
-
cloud-provider-payment8001 / cloud-provider-payment9001
PaymentController
1 package com.sdkj.controller; 2 3 import com.sdkj.dataobject.PaymentDO; 4 import com.sdkj.error.BusinessException; 5 import com.sdkj.error.EnumBusinessError; 6 import com.sdkj.response.CommonReturnType; 7 import com.sdkj.service.PaymentService; 8 import lombok.extern.slf4j.Slf4j; 9 import org.apache.commons.lang.StringUtils; 10 import org.springframework.beans.factory.annotation.Autowired; 11 import org.springframework.beans.factory.annotation.Value; 12 import org.springframework.cloud.client.ServiceInstance; 13 import org.springframework.cloud.client.discovery.DiscoveryClient; 14 import org.springframework.stereotype.Controller; 15 import org.springframework.web.bind.annotation.*; 16 17 /** 18 * @Author wangshuo 19 * @Date 2022/5/19, 9:08 20 * Please add a comment 21 */ 22 @Controller 23 @RequestMapping(value = "/payment") 24 @CrossOrigin(origins = {"*"}, allowCredentials = "true") 25 @Slf4j 26 public class PaymentController extends BaseController { 27 28 @Autowired 29 PaymentService paymentService; 30 //注入服务发现 31 @Autowired 32 DiscoveryClient discoveryClient; 33 @Value("${server.port}") 34 private String port; 35 36 @RequestMapping("/findById") 37 @ResponseBody 38 public CommonReturnType findById(Long id) throws BusinessException { 39 40 if (StringUtils.isEmpty(id.toString())) 41 throw new BusinessException(EnumBusinessError.REGISTER_OTP_ERROR);//参数不合法 42 PaymentDO payment = paymentService.getPaymentById(id); 43 return CommonReturnType.create(payment); 44 } 45 46 @RequestMapping(value = "/getDiscovery") 47 @ResponseBody 48 public Object getDiscovery() { 49 50 //获取服务列表 51 for (String service : discoveryClient.getServices()) { 52 log.info("发现service:" + service); 53 } 54 //获取服务实例集合 55 for (ServiceInstance instance : discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE")) { 56 log.info("发现服务CLOUD-PAYMENT-SERVICE:" + 57 " serviceId = " + instance.getServiceId() + 58 " host = " + instance.getHost() + 59 " port = " + instance.getPort() + 60 " uri = " + instance.getUri()); 61 } 62 return this.discoveryClient; 63 } 64 65 @RequestMapping("/getPort") 66 @ResponseBody 67 public String getPort() { 68 return "server port = " + port; 69 } 70 }
三、设置logback日志与feign日志
-
application.yml
# 端口
server:
port: 8002
spring:
application:
name: cloud-order
#安全认证
security:
user:
name: root
password: root
eureka:
client:
# 表示将自己注册进Eureka Server默认为true
register-with-eureka: true
# 是否从Eureka Server抓去已有的注册信息,默认是true
fetch-registry: true
# 设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址
service-url:
#设置同时向三个eureka注册
defaultZone: http://${spring.security.user.name}:${spring.security.user.password}@eureka1.com:8887/eureka
instance:
#instance.getUri()获取到的就是这个值(默认为主机名称+端口 / 这里修改为IP地址+端口)
instance-id: ${spring.cloud.client.ip-address}:${server.port}
#访问路径是否显示IP地址
prefer-ip-address: true
#开启优雅停服
#(必须通过POST请求向Eureka Client发起一个shutdown请求。请求路径为:http://ip:port/shutdown。可以通过任意技术实现,如:HTTPClient,AJAX等。)
management:
endpoint:
shutdown:
enabled: true
#logback
logging:
config: classpath:log/logback.xml
-
logback.xml
<configuration> <!--本文主要输出日志为控制台日志,系统日志,sql日志,异常日志--> <!-- %m输出的信息,%p日志级别,%t线程名,%d日期,%c类的全名,,,, --> <!--控制台--> <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d %p (%file:%line\)- %m%n</pattern> <charset>UTF-8</charset> </encoder> </appender> <!--系统info级别日志--> <!--<File> 日志目录,没有会自动创建--> <!--<rollingPolicy>日志策略,每天简历一个日志文件,或者当天日志文件超过64MB时--> <!--encoder 日志编码及输出格式--> <appender name="fileLog" class="ch.qos.logback.core.rolling.RollingFileAppender"> <File>log/file/fileLog.log</File> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>log/file/fileLog.log.%d.%i</fileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <!-- or whenever the file size reaches 64 MB --> <maxFileSize>64 MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> </rollingPolicy> <encoder> <pattern> %d %p (%file:%line\)- %m%n </pattern> <charset>UTF-8</charset> <!-- 此处设置字符集 --> </encoder> </appender> <!--sql日志--> <appender name="sqlFile" class="ch.qos.logback.core.rolling.RollingFileAppender"> <File>log/sql/sqlFile.log</File> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>log/sql/sqlFile.log.%d.%i</fileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <!-- or whenever the file size reaches 64 MB --> <maxFileSize>64 MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> </rollingPolicy> <!--对记录事件进行格式化。负责两件事,一是把日志信息转换成字节数组,二是把字节数组写入到输出流。--> <encoder> <!--用来设置日志的输入格式--> <pattern> %d %p (%file:%line\)- %m%n </pattern> <charset>UTF-8</charset> <!-- 此处设置字符集 --> </encoder> </appender> <!--异常日志--> <appender name="errorFile" class="ch.qos.logback.core.rolling.RollingFileAppender"> <File>log/error/errorFile.log</File> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>log/error/errorFile.%d.log.%i</fileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <!-- or whenever the file size reaches 64 MB --> <maxFileSize>64 MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> </rollingPolicy> <!--对记录事件进行格式化。负责两件事,一是把日志信息转换成字节数组,二是把字节数组写入到输出流。--> <encoder> <!--用来设置日志的输入格式--> <pattern> %d %p (%file:%line\)- %m%n </pattern> <charset>UTF-8</charset> <!-- 此处设置字符集 --> </encoder> <!-- 日志都在这里 过滤出 error 使用 try {}catch (Exception e){} 的话异常无法写入日志,可以在catch里用logger.error()方法手动写入日志 --> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>ERROR</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> </appender> <!-- 日志输出级别 --> <!--All\DEBUG\INFO\WARN\ERROR\FATAL\OFF--> <!--打印info级别日志,分别在控制台,fileLog,errorFile输出 异常日志在上面由过滤器过滤出ERROR日志打印 --> <root level="INFO"> <appender-ref ref="fileLog" /> <appender-ref ref="console" /> <appender-ref ref="errorFile" /> </root> <!--打印sql至sqlFile文件日志--> <logger name="com.dolphin.mapper" level="DEBUG" additivity="false"> <appender-ref ref="console" /> <appender-ref ref="sqlFile" /> </logger> </configuration>
-
FeignConfig
1 package com.sdkj.config; 2 3 import feign.Logger; 4 import org.springframework.context.annotation.Bean; 5 import org.springframework.context.annotation.Configuration; 6 7 /** 8 * @Author wangshuo 9 * @Date 2022/5/23, 19:08 10 * 配置feign日志级别 11 */ 12 @Configuration 13 public class FeignConfig { 14 15 @Bean 16 public Logger.Level feignLoggerLevel() { 17 /* 18 Feign有一下日志级别: 19 NONE:默认的,不显示任何日志 20 BASIC:仅记录请求方法、URL、响应状态码及执行时间 21 HEADERS:出了BASIC中定义的信息之外,还有请求和响应的头信息 22 FULL:除了HEADERS中定义的信息之外,还有请求和响应的正文及元素 23 */ 24 return Logger.Level.BASIC; 25 } 26 }
本文来自博客园,作者:荣慕平,转载请注明原文链接:https://www.cnblogs.com/rongmuping/articles/16301439.html