当前位置: 首页 > news >正文

vs2013 网站开发科技企业网站模板

vs2013 网站开发,科技企业网站模板,福建高能建设工程有限公司网站,温州公司建设网站文章摘要 本文中将教会您如何快速使用Openfeign#xff0c;包括Opengfeign的基础配置、接口调用、接口重试、拦截器实现、记录接口日志信息到数据库 文章目录 文章摘要一、Openfeign初步定义二、Openfeign快速入门1.引入maven坐标2.启动类增加EnableFeignClients注解3.定义fei…文章摘要 本文中将教会您如何快速使用Openfeign包括Opengfeign的基础配置、接口调用、接口重试、拦截器实现、记录接口日志信息到数据库 文章目录 文章摘要一、Openfeign初步定义二、Openfeign快速入门1.引入maven坐标2.启动类增加EnableFeignClients注解3.定义feign客户端并实现接口调用3.定义feign远程调用演示 三、Openfeign常用配置1.配置请求连接超时2.配置Openfeign接口调用重试机制3.配置Openfeign请求拦截器4.Openfeign接口日志保存到数据库 一、Openfeign初步定义 OpenFeign 是一个基于 Spring 的声明式、模板化的 HTTP 客户端它简化了编写 Web 服务客户端的过程。用户只需创建一个接口并添加相应的注解即可实现对远程服务的调用。OpenFeign 是 Spring Cloud 的一部分它支持 Spring MVC 的注解如 RequestMapping使得使用 HTTP 请求访问远程服务就像调用本地方法一样直观和易于维护。Openfeign底层默认使用JDK提供的HttpURLConnection进行通信(源码参考类feign.Default)使用Openfeign可以快速的帮我们完成第三方接口调用的实现简化开发流程。 二、Openfeign快速入门 1.引入maven坐标 因为我使用的SpringBoot是2.2.10.RELEASE版本因此在引入Openfeign依赖时也就引入与SpringBoot一致的版本具体版本号根据您的SpringBoot版本来定。 dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-openfeign/artifactIdversion2.2.1.RELEASE/version /dependency2.启动类增加EnableFeignClients注解 启动类增加 EnableFeignClients 用于在启动时加载我们定义的所有使用注解 FeignClient 定义的feign客户端并把feign客户端注册到 IOC 容器中。EnableFeignClients注解中的basePackages用于配置扫描哪些包下来的类这里我配置com.hl.by.remote包下的类 此时openfeign最基础的配置就已经配置完成了现在可以自定义feign客户端使用openfeign调用远程接口。 3.定义feign客户端并实现接口调用 feign的客户端需要使用 FeignClient 注解进行标识这样在扫描时才知道这是一个feign客户端。FeignClient 最常用的就两个属性一个是name用于给客户端定义一个唯一的名称另一个就是url用于定义该客户端调用的远程地址。 这里的 ${open-feign.api-url} 是通过springBoot通过读取yaml或者properties文件获取的配置。这里我是在yaml中配置了该值: 因此客户端feign-remote-service调用的远程地址就是http://127.0.0.1:8080。在定义好客户端后可以在客户端中加入对应的接口比如我在客户端feign-remote-service中增加了一个saveSku的方法当调用该方法时将发送一个请求方式是POSTrequestBody的类型是FeignRemoteRequestVO返回对象类型是ResponseResult请求地址是http://127.0.0.1:8080/hl-template/mock/saveSku 的请求。 3.定义feign远程调用演示 由于我定义的feign客户端的调用地址是本地因此我在本地定义了一个saveSku接口用于演示feign远程调用的效果 我使用Postman 调用接口feign然后通过feign这个接口使用刚刚定义的feign客户端进行远程调用saveSku方法。 通过feignRemoteService.saveSku将调用到第三方接口可以看到已经通过feign客户端调用到了远程方法saveSku 现在您已经学会了使用Openfegn来进行最基础的远程调用。可以看出使用Openfeign以后我们只用定义客户端即可剩下的具体调用实现交给Openfeign就可以了。 三、Openfeign常用配置 本小节将对Openfeign常用配置进行演示包括连接超时配置、接口调用、接口重试、拦截器实现其中会涉及部分少量源码但重点是教会您如何使用 Openfeign采用动态代理的方式实现接口调用通过ReflectiveFeign.invoke 方法找到具体SynchronousMethodHandler。SynchronousMethodHandler是每个feign客户端中每个具体调用方法的最终实现。通过调用SynchronousMethodHandler中的invoke方法进行远程接口调用。 1.配置请求连接超时 Openfeign中常用的基础信息维护在抽象类Feign中维护着Feign调用时的日志等级、客户端调用Client、重试机制、日志实现、请求编码器encoder、响应解码器decoder等。可以看到Feign采用Builder构建者模式对每个属性都设置了最初的值。而请求超时的配置则由类Options维护。 Options类中属性只有三个connectTimeoutMillis配置连接超时时间readTimeoutMillis连接成功后等待响应超时时间followRedirects是否允许重定向。默认的Options设置连接超时是10秒等待响应超时是60秒不允许重定向。 因此如果我们要自定义Openfeign的连接响应超时时间则只需要自己声明一个Options注入到spring容器中去即可替换Openfeign默认的配置。我在yaml中配置了openfeign的接口连接超时信息只需要创建一个配置类读取配置信息放入Options中即可。 代码如下: Data Component ConfigurationProperties(prefix open-feign) RequiredArgsConstructor public class FeignConfig {private Integer connectTimeoutMillis;private Integer readTimeoutMillis;private boolean followRedirects;private Integer maxAttempts;private Long period;private Long maxPeriod;/*** 配置openfeign调用接口时的超时时间和等待超时时间** return Request.Options*/Beanpublic Request.Options options() {return new Request.Options(connectTimeoutMillis, readTimeoutMillis, followRedirects);} 通过启动项目打断点就可以看到我们自定义的Options是否生效可以看到我们自定的配置已经生效并设置到了Openfeign中 2.配置Openfeign接口调用重试机制 Openfeign接口调用重试默认是由Retryer接口中的Default来实现Default里默认设置每次接口调用失败随机休眠100毫秒~1000毫秒最多重试5次。 如果我们要自定义Openfeign的重试机制的话可以像Retryer.Default一样实现Retryer接口这里我自定义了一个接口重试机制名叫FeignRetry相比于Retryer.Default只是增加了错误信息打印。首先依然先在yaml配置文件中配置重试相关信息。配置了接口调用失败时休眠一秒进行重试最多重试3次 open-feign:api-url: http://127.0.0.1:8088connectTimeoutMillis: 5000 #超时连接超时时间readTimeoutMillis: 30000 #连接成功后等待接口响应时间超时时间followRedirects: false #是否允许重定向maxAttempts: 3 #接口调用失败重试次数,调用失败通常指的是接口网络异常period: 1000 #接口调用失败后周期多少毫秒后重新尝试调用maxPeriod: 1000 #接口调用失败后最大周期多少毫秒后重新尝试调用通过FeignConfig读取配置信息将配置信息加载到FeignRetry中 Data Component ConfigurationProperties(prefix open-feign) RequiredArgsConstructor public class FeignConfig {private Integer connectTimeoutMillis;private Integer readTimeoutMillis;private boolean followRedirects;private Integer maxAttempts;private Long period;private Long maxPeriod;private final InterfaceLogMapper interfaceLogMapper;/*** 配置将重试N次每次间隔M秒钟重试条件为连接超时或请求失败** return Retryer*/Beanpublic Retryer feignRetryer() {return new FeignRetry(period, maxPeriod, maxAttempts);}/*** 配置openfeign调用接口时的超时时间和等待超时时间** return Request.Options*/Beanpublic Request.Options options() {return new Request.Options(connectTimeoutMillis, readTimeoutMillis, followRedirects);}FeignRetry代码实现如下: import feign.FeignException; import feign.RetryableException; import feign.Retryer; import org.slf4j.Logger; import org.slf4j.LoggerFactory;import static java.util.concurrent.TimeUnit.SECONDS;/*** Author: Greyfus* Create: 2024-03-10 00:56* Version: 1.0.0* Description:自定义feign请求时重试机制,与Default相比只是增加了调用日志纪录机制*/ public class FeignRetry implements Retryer {private static final Logger LOGGER LoggerFactory.getLogger(FeignRetry.class);//最大重试次数private final int maxAttempts;//调用频率private final long period;//最大频率private final long maxPeriod;//尝试的次数int attempt;//纪录失眠次数long sleptForMillis;public FeignRetry() {this(1000, SECONDS.toMillis(1), 3);}public FeignRetry(long period, long maxPeriod, int maxAttempts) {this.period period;this.maxPeriod maxPeriod;this.maxAttempts maxAttempts;this.attempt 1;}protected long currentTimeMillis() {return System.currentTimeMillis();}public void continueOrPropagate(RetryableException e) {if (e instanceof FeignException) {//打印异常FeignException remoteException e;LOGGER.error(Feign request【{}】 attempt 【{}】 times,status:【{}】,errorMessage:{}, remoteException.request().url(), attempt, remoteException.status(), remoteException.getMessage());}if (attempt maxAttempts) {throw e;}long interval;if (e.retryAfter() ! null) {interval e.retryAfter().getTime() - currentTimeMillis();if (interval maxPeriod) {interval maxPeriod;}if (interval 0) {return;}} else {interval nextMaxInterval();}try {Thread.sleep(interval);} catch (InterruptedException ignored) {Thread.currentThread().interrupt();throw e;}sleptForMillis interval;}long nextMaxInterval() {long interval (long) (period * Math.pow(1.5, attempt - 1));return interval maxPeriod ? maxPeriod : interval;}Overridepublic Retryer clone() {return new FeignRetry(period, maxPeriod, maxAttempts);} } 3.配置Openfeign请求拦截器 openfeign提供了一个RequestInterceptor接口凡是实现该接口的类并注入到Spring容器中后在feign进行远程调用之前会调用实现该接口的所有类。源码可以参考SynchronousMethodHandler中的targetRequest方法targetRequest是在执行远程调用之前进行调用。Openfeign请求拦截器的作用通常是用于设置公共属性比如给请求设置请求头认证信息等。 这里我自定义了一个名叫FeignRequestInterceptor 的拦截器并使用Component注入到容器中。当我调用远程接口时通过debug模式可以看到该拦截器的apply方法被调用。 /*** Author: DI.YIN* Date: 2024/3/13 13:43* Version: 1.0.0* Description: openfeign请求拦截器在发送请求前进行拦截**/ Component public class FeignRequestInterceptor implements RequestInterceptor {Overridepublic void apply(RequestTemplate requestTemplate) {System.out.println(你好);} } 4.Openfeign接口日志保存到数据库 Openfeign提供了请求拦截器但是没有提供响应拦截器这导致我们无法纪录接口的响应信息到数据库。为了实现该功能我翻遍了SynchronousMethodHandler这个类的所有源码信息。我们先简单的对SynchronousMethodHandler源码进行分析再来说如何实现将接口日志信息存入数据库。SynchronousMethodHandler的invoke是整个openfeign远程调用了的核心部分其最核心的是executeAndDecode方法用于构建请求参数、请求方式等信息。 进入executeAndDecode方法后第一个执行targetRequest方法targetRequest就是循环遍历RequestInterceptor请求拦截器调用每个RequestInterceptor实现类的apply方法。 执行完拦截器后判断当前日志等级是否为不为NONE如果当前日志等级不为NONE则通过logger.logRequest打印日志。此时可以通过request获取到请求信息、请求地址、请求头等信息。 response client.execute(request, options); 则是真正调用远程接口的地方openfeing默认采用HttpURLConnection进行远程接口调用。如果调用异常则logger.logIOException打印异常信息如果调用成功则通过logger.logAndRebufferResponse打印异常信息。 通过对源码的分析我们知道当日志等级不为NONE时openfeign会打印接口请求信息到控制台如果接口调用失败调用logIOException将错误信息打印到控制台接口调用成功则调用logAndRebufferResponse将响应信息打印到控制台。因此我打算自定义一个日志对象将接口信息存入数据库。 第一步创建接口日志信息表用于存放接口日志信息: CREATE TABLE interface_log ( log_id bigint(20) NOT NULL AUTO_INCREMENT COMMENT ‘日志主键’, remote_sys_code varchar(50) COLLATE utf8mb4_bin DEFAULT NULL COMMENT ‘远程系统编码’, remote_sys_name varchar(50) COLLATE utf8mb4_bin DEFAULT NULL COMMENT ‘远程系统名称’, request_time datetime NOT NULL COMMENT ‘请求时间’, response_time datetime DEFAULT NULL COMMENT ‘响应时间’, request_method varchar(10) COLLATE utf8mb4_bin NOT NULL COMMENT ‘请求方式 GET PUT POST’, request_url varchar(200) COLLATE utf8mb4_bin NOT NULL COMMENT ‘请求地址’, request_headers_message longtext COLLATE utf8mb4_bin COMMENT ‘请求头信息’, request_message longtext COLLATE utf8mb4_bin COMMENT ‘请求信息’, response_message longtext COLLATE utf8mb4_bin COMMENT ‘响应信息’, status varchar(50) COLLATE utf8mb4_bin NOT NULL COMMENT ‘接口状态’, PRIMARY KEY (log_id) USING BTREE ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_bin ROW_FORMATDYNAMIC COMMENT‘日志记录表’; 第二步创建接口日志对象 import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.hl.by.domain.base.BaseDomain; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.RequiredArgsConstructor;import java.util.Date;/*** Author: DI.YIN* Date: 2024/3/13 16:05* Version: 1.0.0* Description: 日志实体类**/ Data RequiredArgsConstructor TableName(interface_log) public class InterfaceLogDomain extends BaseDomain {TableField(exist false)private static final long serialVersionUID 2588400601329175428L;ApiModelProperty(value 日志主键)TableId(type IdType.AUTO)private Long logId;ApiModelProperty(value 远程系统编码)TableField(value remote_sys_code)private String remoteSysCode;ApiModelProperty(value 远程系统名称)TableField(value remote_sys_name)private String remoteSysName;ApiModelProperty(value 请求时间)TableField(value request_time)private Date requestTime;ApiModelProperty(value 响应时间)TableField(value response_time)private Date responseTime;ApiModelProperty(value 请求方式)TableField(value request_method)private String requestMethod;ApiModelProperty(value 请求地址)TableField(value request_url)private String requestUrl;ApiModelProperty(value 请求头内容)TableField(value request_headers_message)private String requestHeaders;ApiModelProperty(value 请求内容)TableField(value request_message)private String requestMessage;ApiModelProperty(value 响应内容)TableField(value response_message)private String responseMessage;ApiModelProperty(value 接口状态)TableField(value status)private String status;} 第三步创建日志Mapper包含对日志表的增删改查功能这里我使用MyabtisPlus框架因此实现相对简单。您可根据自己的实际情况实现Mapper。 /*** Author: DI.YIN* Date: 2024/3/13 16:34* Version: 1.0.0* Description: 接口日志**/ public interface InterfaceLogMapper extends BaseMapperInterfaceLogDomain { }第四步自定义日志对象FeignRemoteLogger package com.hl.by.common.feign;import com.hl.by.domain.log.InterfaceLogDomain; import com.hl.by.domain.log.InterfaceLogStatus; import com.hl.by.persistence.dao.log.InterfaceLogMapper; import feign.Logger; import feign.Request; import feign.Response; import feign.Util; import lombok.RequiredArgsConstructor;import java.io.IOException; import java.util.Collection; import java.util.Date; import java.util.Map;import static feign.Util.UTF_8; import static feign.Util.decodeOrDefault;/*** Author: DI.YIN* Date: 2024/3/13 15:36* Version: 1.0.0* Description: Feign接口远程调用接口日志记录**/ RequiredArgsConstructor public class FeignRemoteLogger extends Logger {private final InterfaceLogMapper logMapper;/*** 具体日志显示格式再次方法打印因为要将日志写入数据库因此该地方就不打印日志了** param configKey* param format* param args*/Overrideprotected void log(String configKey, String format, Object... args) {}/*** 在调用之前调用该方法将请求数据写入数据库** param configKey* param logLevel* param request*/Overrideprotected void logRequest(String configKey, Level logLevel, Request request) {InterfaceLogDomain interfaceLogDomain new InterfaceLogDomain();//从header中获取系统编码interfaceLogDomain.setRemoteSysCode(request.headers().get(SYS_CODE) null || request.headers().get(SYS_CODE).isEmpty() ? UNKNOWN : request.headers().get(SYS_CODE).toArray()[0].toString());//从header中获取系统名称interfaceLogDomain.setRemoteSysName(request.headers().get(SYS_NAME) null || request.headers().get(SYS_NAME).isEmpty() ? UNKNOWN : request.headers().get(SYS_NAME).toArray()[0].toString());interfaceLogDomain.setRequestTime(new Date());interfaceLogDomain.setStatus(InterfaceLogStatus.DOING.getCode());interfaceLogDomain.setRequestMethod(request.httpMethod().name());//请求方式interfaceLogDomain.setRequestUrl(request.url()); //请求地址MapString, CollectionString headers request.headers();//请求头if (request.requestBody() ! null) {interfaceLogDomain.setRequestMessage(request.requestBody().asString());//请求内容}//存入数据库logMapper.insert(interfaceLogDomain);//放入线程缓存中,等待请求完成或者异常时从线程缓存中拿出日志对象回填数据FeignRequestContextHolder.setThreadLocalInterfaceLog(interfaceLogDomain);}/*** 当配置了接口重试机制时再接口重试之前调用该方法** param configKey* param logLevel*/Overrideprotected void logRetry(String configKey, Level logLevel) {}/*** 当接口调用异常时会调用该接口如果配置了重试机制该方法在logRetry之前调用** param configKey* param logLevel* param ioe* param elapsedTime* return*/Overrideprotected IOException logIOException(String configKey, Level logLevel, IOException ioe, long elapsedTime) {//从线程缓存中获取日志信息用于信息回填存入数据库InterfaceLogDomain threadLocalInterfaceLog FeignRequestContextHolder.getThreadLocalInterfaceLog();if (threadLocalInterfaceLog ! null) {threadLocalInterfaceLog.setStatus(InterfaceLogStatus.ERROR.getCode());//回填响应状态threadLocalInterfaceLog.setResponseTime(new Date()); //回填响应时间threadLocalInterfaceLog.setResponseMessage(ioe.getMessage());try {logMapper.updateById(threadLocalInterfaceLog);} catch (Throwable throwable) {//清除线程缓存FeignRequestContextHolder.clear();}}return ioe;}/*** 接口调用成功后调用该方法记录日志信息** param configKey* param logLevel* param response* param elapsedTime* return* throws IOException*/Overrideprotected Response logAndRebufferResponse(String configKey, Level logLevel, Response response, long elapsedTime) throws IOException {//从线程缓存中获取日志信息用于信息回填存入数据库InterfaceLogDomain threadLocalInterfaceLog FeignRequestContextHolder.getThreadLocalInterfaceLog();if (threadLocalInterfaceLog ! null) {try {threadLocalInterfaceLog.setResponseTime(new Date());//回写接口响应时间threadLocalInterfaceLog.setStatus(InterfaceLogStatus.SUCCESS.getCode());//回写接口日志状态int status response.status();int bodyLength;if (response.body() ! null !(status 204 || status 205)) {// HTTP 204 No Content ...response MUST NOT include a message-body// HTTP 205 Reset Content ...response MUST NOT include an entity//获取响应内容byte[] bodyData Util.toByteArray(response.body().asInputStream());//内容长度bodyLength bodyData.length;if (bodyLength 0) {String responseMessage decodeOrDefault(bodyData, UTF_8, Binary data);threadLocalInterfaceLog.setResponseMessage(responseMessage);}return response.toBuilder().body(bodyData).build();}} finally {logMapper.updateById(threadLocalInterfaceLog);FeignRequestContextHolder.clear();}}return response;} } FeignRequestContextHolder 用于缓存每个线程当前所拥有的日志信息 /*** Author: Greyfus* Create: 2024/3/13 15:36* Version: 1.0.0* Description: 用于维护openfeign当前线程请求的请求日志对象该对象在请求之前进行缓存在请求结束或者异常后进行拿出回写日志信息保存到数据库*/ package com.hl.by.common.feign;import com.hl.by.domain.log.InterfaceLogDomain;public class FeignRequestContextHolder {private static final ThreadLocalInterfaceLogDomain THREAD_LOCAL_INTERFACE_LOG new ThreadLocal();/*** 获取当前线程缓存的日志信息** return*/public static InterfaceLogDomain getThreadLocalInterfaceLog() {return THREAD_LOCAL_INTERFACE_LOG.get();}/*** 设置当前线程接口请求的日志信息** param interfaceLogDomain*/public static void setThreadLocalInterfaceLog(InterfaceLogDomain interfaceLogDomain) {THREAD_LOCAL_INTERFACE_LOG.set(interfaceLogDomain);}/*** 清除数据*/public static void clear() {THREAD_LOCAL_INTERFACE_LOG.remove();} } InterfaceLogStatus 枚举维护接口请求状态 package com.hl.by.domain.log;/*** Author: DI.YIN* Date: 2024/3/13 16:31* Version: 1.0.0* Description: 接口日志状态**/ public enum InterfaceLogStatus {SUCCESS(S),ERROR(E),DOING(D),;private String code;InterfaceLogStatus(String code) {this.code code;}public String getCode() {return code;} } 第五步设置日志等级并注入自定义日志对象 Data Component ConfigurationProperties(prefix open-feign) RequiredArgsConstructor public class FeignConfig {private Integer connectTimeoutMillis;private Integer readTimeoutMillis;private boolean followRedirects;private Integer maxAttempts;private Long period;private Long maxPeriod;Autowiredprivate InterfaceLogMapper interfaceLogMapper;/*** 参考SynchronousMethodHandler源码executeAndDecode方法中当日志等级不等于Logger.Level.NONE才触发日志显示** return 返回日志等级openfeign根据日志等级来显示请求响应日志*/Beanpublic Logger.Level level() {return Logger.Level.FULL;}/*** 配置openfeign自定义请求日志记录将请求日志存入MQ或者数据库** return*/Beanpublic Logger feignRemoteLogger() {return new FeignRemoteLogger(interfaceLogMapper);}}您仅需要将以上代码复制到您的项目中就可以正常运行现在让我们来测试一下效果。我用Postman调用本地接口本地接口通过openfeign调用远程接口进行保存商品。 数据库的接口日志表已经成功保存本次请求的信息:
http://www.dnsts.com.cn/news/19897.html

相关文章:

  • 建设五证在那个网站可以查企业网站优化包括哪三个层面
  • 鹤壁网站设计什么是网络营销战略
  • 上海建站网络科技怎样保证网站的安全
  • 电商网站建设服务凡科小程序登录
  • 网站搭建的步骤wordpress 提权攻击
  • 网站设计制作平台哪个好甘肃再就业建设集团网站
  • 建筑网建设通网站作用前几年做那个网站致富
  • 建立一个网站沈阳做网站软件
  • 行业协会网站织梦模板自建站有哪些
  • 那个网站可以免费建站网站域名登陆
  • 国外网站代做你做的网站会不会被人模仿
  • 学网站建设的软件网络销售渠道
  • 网站上海备案查询哈尔滨百姓网免费发布信息
  • 2014网站设计网站系统繁忙怎么办
  • 广告素材网站哪个比较好祝贺公司网站上线
  • 北京建设工程主管部门网站wordpress获取用户位置
  • 刷数据网站怎么推广做货源的网站
  • 网站如何设计方案wordpress hotnews syntax error
  • 网站开发 法律声明wordpress可视化功能
  • 企业网站开发哪家好网站出现建设中
  • 网站开发人员属于什么设计方案
  • 建站之星免费wordpress的数据库有多大
  • 莆田网站建设培训建设网站的各种问题
  • 网站怎么做图片动态图片不显示不出来普通网站成微网站开发
  • 安阳市网站建设西昌手机网站建设成都彩钢顶防水
  • 河北省建设机械协会是正规网站吗软件技术方案
  • 佛山模板网站建设ftp网站服务器
  • 网站维护是什么专业桂林两江四湖在哪里
  • 青岛外贸网站建站公司官网源码下载
  • 制作网站的图片素材建设厅网站账户名忘了怎么查