亚洲乱色熟女一区二区三区丝袜,天堂√中文最新版在线,亚洲精品乱码久久久久久蜜桃图片,香蕉久久久久久av成人,欧美丰满熟妇bbb久久久

LOGO OA教程 ERP教程 模切知識(shí)交流 PMS教程 CRM教程 開(kāi)發(fā)文檔 其他文檔  
 
網(wǎng)站管理員

如何設(shè)計(jì)一個(gè)安全的對(duì)外接口,老司機(jī)總結(jié)了這幾點(diǎn)...

admin
2024年1月13日 16:40 本文熱度 1714

做接口需要考慮的問(wèn)題

什么是接口

接口無(wú)非就是客戶端請(qǐng)求你的接口地址,并傳入一堆該接口定義好的參數(shù),通過(guò)接口自身的邏輯處理,返回接口約定好的數(shù)據(jù)以及相應(yīng)的數(shù)據(jù)格式。

接口怎么開(kāi)發(fā)

接口由于本身的性質(zhì),由于和合作方對(duì)接數(shù)據(jù),所以有以下幾點(diǎn)需要在開(kāi)發(fā)的時(shí)候注意:

1.定義接口入?yún)ⅲ簩懞媒涌谖臋n

2.定義接口返回?cái)?shù)據(jù)類型:一般都需要封裝成一定格式,確定返回json還是xml報(bào)文等

見(jiàn)如下返回?cái)?shù)據(jù)定義格式:

package com.caiex.vb.model;
 
import java.io.Serializable;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlType;
 
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "Result", propOrder = { "resultCode""resultMsg" })
public class Result implements Serializable {
 private static final long serialVersionUID = 10L;
 protected int resultCode;
 protected String resultMsg;
 
 public int getResultCode() {
  return this.resultCode;
 }
 
 public void setResultCode(int value) {
  this.resultCode = value;
 }
 
 public String getResultMsg() {
  return this.resultMsg;
 }
 
 public void setResultMsg(String value) {
  this.resultMsg = value;
 }
}
package com.caiex.vb.model;
 
import java.io.Serializable;
 
public class Response implements Serializable {
 
 private static final long serialVersionUID = 2360867989280235575L;
 
 private Result result;
 
 private Object data;
 
 public Result getResult() {
  if (this.result == null) {
   this.result = new Result();
  }
  return result;
 }
 
 public void setResult(Result result) {
  this.result = result;
 }
 
 public Object getData() {
  return data;
 }
 
 public void setData(Object data) {
  this.data = data;
 }
 
}

3.確定訪問(wèn)接口的方式,get or post等等,可以根據(jù)restful接口定義規(guī)則RESTful API:RESTful API

4.定義一套全局統(tǒng)一并通用的返回碼,以幫助排查問(wèn)題;

 public static int NO_AGENT_RATE = 1119;  //未找到兌換率
 
 public static int SCHEME_COMMIT_FAIL = 4000;  //方案提交失敗
 
 public static int SCHEME_CONFIRMATION = 4001;  //方案確認(rèn)中
 
 public static int SCHEME_NOT_EXIST = 4002;  //方案不存在
 
 public static int SCHEME_CANCEL= 4005;  //方案不存在
 
 //。。。。

5.統(tǒng)一的異常處理:應(yīng)該每個(gè)系統(tǒng)都需要一套統(tǒng)一的異常處理

package com.caiex.vb.interceptor;
 
import javax.servlet.http.HttpServletRequest;
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
 
import com.caiex.vb.model.Response;
 
@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {
 
 private  Logger  logger = LoggerFactory.getLogger(this.getClass()); 
 
    /**
     * 所有異常報(bào)錯(cuò)
     * @param request
     * @param exception
     * @return
     * @throws Exception
     */

    @ExceptionHandler(value=Exception.class)  
    public Response allExceptionHandler(HttpServletRequest request,  
            Exception exceptionthrows Exception  
    
{  
     logger.error("攔截到異常:", exception);
        Response response = new Response();
        response.setData(null);
        response.getResult().setResultCode(9999);
        response.getResult().setResultMsg("系統(tǒng)繁忙");
        return response;  
    }  
 
}

6.攔截器鏈設(shè)置:合作方訪問(wèn)接口的時(shí)候,會(huì)根據(jù)你接口定義好的傳參訪問(wèn)你的接口服務(wù)器,但是會(huì)存在接口參數(shù)類型錯(cuò)誤或者格式不對(duì),必傳參數(shù)沒(méi)傳的問(wèn)題,甚至一些惡意請(qǐng)求,都可以通過(guò)攔截器鏈進(jìn)行前期攔截,避免造成接口服務(wù)的壓力。

學(xué)習(xí)資料:Java進(jìn)階視頻資源

還有很重要的一點(diǎn),加簽驗(yàn)簽也可以在攔截器設(shè)置。繼承WebMvcConfigurerAdapter實(shí)現(xiàn)springboot的攔截器鏈。實(shí)現(xiàn)HandlerInterceptor方法編寫業(yè)務(wù)攔截器。

package com.caiex.vb.interceptor;
 
 
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
 
import com.alibaba.fastjson.JSON;
import com.caiex.redis.service.api.RedisApi;
import com.caiex.vb.model.Response;
import com.caiex.vb.utils.CaiexCheckUtils;
 
@Component
public class SignInterceptor extends BaseValidator implements HandlerInterceptor{
 
 private Logger logger = LogManager.getLogger(this.getClass());
 
 @Resource
 private RedisApi redisApi;
 
 
 public void afterCompletion(HttpServletRequest arg0,
   HttpServletResponse arg1, Object arg2, Exception arg3)

   throws Exception 
{
  // TODO Auto-generated method stub
  
 }
 
 public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1,
   Object arg2, ModelAndView arg3)
 throws Exception 
{
  // TODO Auto-generated method stub
  
 }
 
 public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1,
   Object arg2)
 throws Exception 
{
  if(isTestIpAddr(arg0)){
   return true;
  }
  String securityKey = redisApi.hGet("securityKey", arg0.getParameter("agentid"));
  if(StringUtils.isEmpty(securityKey)){
   Response response = new Response();
   response.setData(null);
   response.getResult().setResultCode(8001);
   response.getResult().setResultMsg("缺少私鑰, 渠道號(hào):" + arg0.getParameter("agentid"));
   logger.error("缺少私鑰, 渠道號(hào):" + arg0.getParameter("agentid"));
   InterceptorResp.printJson(arg1, response);
   return false;
  }
  
  if(StringUtils.isEmpty(arg0.getParameter("sign")) || !arg0.getParameter("sign").equals(CaiexCheckUtils.getSign(arg0.getParameterMap(), securityKey))){
   Response response = new Response();
   response.setData(null);
   response.getResult().setResultCode(3203);
   response.getResult().setResultMsg("參數(shù)簽名認(rèn)證失敗");
   logger.error("參數(shù)簽名認(rèn)證失?。?quot; + JSON.toJSONString(arg0.getParameterMap()) + " securityKey = " + securityKey);
   InterceptorResp.printJson(arg1, response);
   return false;
  }else{
   return true;
  }
  
 }
 
}
package com.caiex.oltp.config;
 
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
 
import com.caiex.oltp.interceptor.APILimitRateValidator;
import com.caiex.oltp.interceptor.CommonValidator;
import com.caiex.oltp.interceptor.DDSAuthValidator;
import com.caiex.oltp.interceptor.QueryPriceParamsValidator;
import com.caiex.oltp.interceptor.TradeParamsValidator;
 
 
@EnableWebMvc
@Configuration
@ComponentScan
public class WebAppConfigurer extends WebMvcConfigurerAdapter {
 
   @Bean
   CommonValidator commonInterceptor() {
         return new CommonValidator();
     }
 
   @Bean
   DDSAuthValidator ddsAuthInterceptor() {
         return new DDSAuthValidator();
     }
 
   @Bean
   QueryPriceParamsValidator queryPriceParamsInterceptor() {
         return new QueryPriceParamsValidator();
     }
 
   @Bean
   TradeParamsValidator tradeParamsInterceptor() {
         return new TradeParamsValidator();
     }
   
  @Bean
   APILimitRateValidator aPILimitRateInterceptor() {
         return new APILimitRateValidator();
     }
 
 
     @Override
     public void addInterceptors(InterceptorRegistry registry) {
      
      //訪問(wèn)速率限制
      registry.addInterceptor(aPILimitRateInterceptor())
      .addPathPatterns("/*/*");
      //.addPathPatterns("/price/getPriceParam");
 
      //參數(shù)簽名認(rèn)證
         registry.addInterceptor(ddsAuthInterceptor())
         .addPathPatterns("/tradeState/*")
         .addPathPatterns("/recycle/*")
         .addPathPatterns("/matchInfo/*")
         .addPathPatterns("/price/tradeTicketParam");
         
         //公共參數(shù)檢查
         registry.addInterceptor(commonInterceptor())
         .addPathPatterns("/price/tradeTicketParam")
         .addPathPatterns("/tradeState/*")
         .addPathPatterns("/recycle/*");
         
         //詢價(jià)參數(shù)校驗(yàn)
         registry.addInterceptor(queryPriceParamsInterceptor())
         .addPathPatterns("/price/getPriceParam");
         
         //交易參數(shù)檢查
         registry.addInterceptor(tradeParamsInterceptor())
         .addPathPatterns("/price/tradeTicketParam");
         
         super.addInterceptors(registry);
     }
}

7.token令牌和sign數(shù)字簽名實(shí)現(xiàn)數(shù)據(jù)保密性。

創(chuàng)建令牌(Token)

為保證請(qǐng)求的合法性,我們提供第三方創(chuàng)建令牌接口,某些接口需要通過(guò)token驗(yàn)證消息的合法性,以免遭受非法攻擊。

token過(guò)期時(shí)間目前暫時(shí)定為1天,由于考慮到合作方往往是分布式環(huán)境,多臺(tái)機(jī)器都有可能申請(qǐng)token,為了降低合作方保證token一致性的難度,調(diào)用接口創(chuàng)建token成功以后一分鐘以內(nèi),再次請(qǐng)求token返回的數(shù)據(jù)是一樣的。

獲取私鑰

獲取用于數(shù)字簽名的私鑰,第三方獲取的私鑰需妥善保存,并定期更新,私鑰只參與數(shù)字簽名,不作為參數(shù)傳輸。

數(shù)字簽名方式:

參數(shù)簽名;簽名方式:所有值不為null的參數(shù)(不包括本參數(shù))均參與數(shù)字簽名,按照“參數(shù)名+參數(shù)值+私鑰”的格式得到一個(gè)字符串,再將這個(gè)字符串MD5一次就是這個(gè)參數(shù)的值。(示例:h15adc39y9ba59abbe56e057e60f883g),所以需要先獲取私鑰。

驗(yàn)簽方式:

將用戶的所有非null參數(shù)放入定義好排序規(guī)則的TreeSet中進(jìn)行排序,再用StringBuilder按照按照“參數(shù)名+參數(shù)值+私鑰”的格式得到一個(gè)字符串(私鑰從redis拿),再將這個(gè)字符串MD5一次就是這個(gè)參數(shù)的值。將這個(gè)值與用戶傳來(lái)的sign簽名對(duì)比,相同則通過(guò),否則不通過(guò)。

private String createToken(){
  String utk = "Msk!D*"+System.currentTimeMillis()+"UBR&FLP";
  logger.info("create token   --- "+Md5Util.md5(utk));
  return Md5Util.md5(utk);
 }

8.接口限流

有時(shí)候服務(wù)器壓力真的太大,以防交易接口被擠死,就可以對(duì)一些其他不影響主要業(yè)務(wù)功能并且計(jì)算量大的接口做限流處理。RateLimit--使用guava來(lái)做接口限流,當(dāng)接口超過(guò)指定的流量時(shí),就不處理該接口的請(qǐng)求。詳細(xì)可看RateLimit。也可參考其他限流框架。

9.協(xié)議加密,http升級(jí)成https;

為什么要升級(jí)呢,為了保證數(shù)據(jù)的安全性。當(dāng)使用https訪問(wèn)時(shí),數(shù)據(jù)從客戶端到服務(wù)斷,服務(wù)端到客戶端都加密,即使黑客抓包也看不到傳輸內(nèi)容。當(dāng)然還有其他好處,這里不多講。但這也是開(kāi)發(fā)接口項(xiàng)目需要注意的一個(gè)問(wèn)題。

如何提高接口的高并發(fā)和高可用

接口開(kāi)發(fā)好了,接下來(lái)就討論接口的可用性問(wèn)題。首先我們要將高并發(fā)和高可用區(qū)分一下,畢竟高可用是在可用的情況,只是很慢或者效率不高。其實(shí)也可以歸為一類問(wèn)題,但是不重要啦,重要的是怎么提高你寫的接口的訪問(wèn)速度和性能。

接口的高并發(fā)解決方案(其實(shí)沒(méi)有唯一答案,業(yè)界針對(duì)不同業(yè)務(wù)也有很多不同的方法)

當(dāng)訪問(wèn)一個(gè)接口獲取數(shù)據(jù)時(shí),發(fā)現(xiàn)返回很慢,或者總是超時(shí),如果排除網(wǎng)絡(luò)的原因,那就是接口服務(wù)器壓力太大,處理不過(guò)來(lái)了。在世界杯期間,我們查看后臺(tái)日志總是connection by reset和borker pipe和一些超時(shí)問(wèn)題。

這時(shí)候,你可能遇到了高并發(fā)和高可用問(wèn)題。但是,不管遇到什么問(wèn)題,都不能臆斷和亂改,你得需要找到慢的原因,才能對(duì)癥下藥,亂改可能會(huì)導(dǎo)致其他問(wèn)題的出現(xiàn)。首先,解決高并發(fā)問(wèn)題的三個(gè)方向是負(fù)載均衡,緩存和集群。

學(xué)習(xí)資料:Java進(jìn)階視頻資源

負(fù)載均衡

我們使用的是阿里云服務(wù)器的負(fù)載均衡,后臺(tái)分布式服務(wù)管理,我們運(yùn)維小哥哥搭建了一套k8s,可以自由在k8s上擴(kuò)展服務(wù)節(jié)點(diǎn),各個(gè)服務(wù)結(jié)點(diǎn)也能隨內(nèi)存的使用自動(dòng)漂移,不用多說(shuō),k8s真的很厲害,感興趣的同學(xué)可以詳細(xì)去學(xué)。那么問(wèn)題來(lái)了,阿里云的負(fù)載均衡怎么對(duì)應(yīng)到k8s的負(fù)載均衡呢?

這個(gè)涉及到了k8s的service暴露的一些特點(diǎn),簡(jiǎn)單說(shuō)就是k8s把所有集群的服務(wù)都通過(guò)指定的內(nèi)部負(fù)載均衡,在指定的服務(wù)器上暴露,然后我們又把這幾個(gè)服務(wù)器接在阿里云負(fù)載均衡下,這個(gè)涉及的細(xì)節(jié)和配置很多。當(dāng)然,除nginx外,還有其他負(fù)載均衡解決方案,軟件硬件都有,硬件如f5等。

阿里云的nginx負(fù)載均衡,我們使用的是加權(quán)輪詢策略,其實(shí)輪詢是最低效的方式;

這就是最基本的負(fù)載均衡實(shí)例,但這不足以滿足實(shí)際需求;目前Nginx服務(wù)器的upstream模塊支持6種方式的分配:

負(fù)載均衡策略

輪詢默認(rèn)方式
weight權(quán)重方式
ip_hash依據(jù)ip分配方式
least_conn最少連接方式
fair(第三方)響應(yīng)時(shí)間方式
url_hash(第三方)依據(jù)URL分配方式

集群

首先,通過(guò)排查問(wèn)題,發(fā)現(xiàn)是oltpapi接口服務(wù)處理請(qǐng)求很慢,大量請(qǐng)求過(guò)來(lái),總是超時(shí)和中斷連接,這時(shí)候,我們想著最簡(jiǎn)單的方法就是加機(jī)器,給oltp接口服務(wù)多加幾臺(tái)機(jī)器。

嗯,一切都很完美,如預(yù)期進(jìn)行,但是加到一定數(shù)量,你發(fā)現(xiàn),怎么不起效果,異步響應(yīng)還是很慢,或者更直觀的說(shuō),消息隊(duì)列出現(xiàn)了嚴(yán)重的消息堆積。這時(shí)候,你發(fā)現(xiàn)出現(xiàn)了新的問(wèn)題或者瓶頸,這個(gè)問(wèn)題已經(jīng)不是說(shuō)加oltp服務(wù)器能解決了,那么,就需要去重新定位問(wèn)題。發(fā)現(xiàn)是消息堆積,消息堆積就是生產(chǎn)者過(guò)快,導(dǎo)致消費(fèi)者消費(fèi)不過(guò)來(lái),這時(shí)候,你就需要增加消費(fèi)者的消費(fèi)數(shù)量。給風(fēng)控系統(tǒng)多加幾臺(tái)機(jī)器,讓消費(fèi)者和生產(chǎn)者達(dá)到一定平衡。

這里有個(gè)誤區(qū),你可能以為是rocketmq的broker數(shù)量過(guò)少,增加broker數(shù)量,其實(shí)當(dāng)消費(fèi)者和生產(chǎn)者保持一樣的速度時(shí),消息肯定不對(duì)堆積,按照原始的broker數(shù)量就足夠。但是增加broker也會(huì)使得消息得到盡快的處理,提升一定效率。

緩存

當(dāng)加機(jī)器不能解決問(wèn)題時(shí),或者說(shuō)沒(méi)那么多服務(wù)器可使用時(shí),那么就要重代碼層面解決高并發(fā)問(wèn)題。Redis 是一個(gè)高性能的key-value數(shù)據(jù)庫(kù),當(dāng)獲取數(shù)據(jù)從數(shù)據(jù)庫(kù)拿很慢時(shí),就可以存儲(chǔ)到redis,從redis取值。

  • 用ConcurrentHashMap緩存對(duì)象,并設(shè)置過(guò)期時(shí)間
  • redis緩存數(shù)據(jù),結(jié)合spring定時(shí)任務(wù)定時(shí)獲取不會(huì)經(jīng)常改動(dòng)的key
  • 提高使用redis的效率:比如使用mGet一次獲取多個(gè)key
  • ....等

接口高可用問(wèn)題

高可用問(wèn)題應(yīng)該上升到整個(gè)服務(wù)的架構(gòu)問(wèn)題上,就是說(shuō)在搭建整體系統(tǒng)是就應(yīng)該考慮到。高可用問(wèn)題是以單點(diǎn)故障,訪問(wèn)速度慢的問(wèn)題為主導(dǎo)。見(jiàn)  服務(wù)高可用

  • redis主從分布式(redis的單點(diǎn)故障和訪問(wèn)速度的提高和主從備份)
  • 分布式dubbo服務(wù)的zookeeper主從集群
  • strom的主從集群
  • ...等

總結(jié)

下面對(duì)接口開(kāi)發(fā)服務(wù)做一些總結(jié):

1.是拉還是推:

當(dāng)接口作為數(shù)據(jù)源時(shí),還要考慮數(shù)據(jù)是讓合作方主動(dòng)過(guò)來(lái)拉還是數(shù)據(jù)有變化就推送呢,當(dāng)然是推的效果更好,但是如何有效的推數(shù)據(jù),不推重復(fù)數(shù)據(jù)等都是需要根據(jù)實(shí)際業(yè)務(wù)考慮的問(wèn)題。

2.多臺(tái)分布式服務(wù)器上,怎么保證交易的冪等和訂單的唯一性

當(dāng)接口服務(wù)和合作方都處于分布式情況下,就很容易出現(xiàn)一個(gè)訂單號(hào)申請(qǐng)多次交易請(qǐng)求,但是根據(jù)冪等性,一張彩票只能交易一次,并且每次不管何時(shí)請(qǐng)求,結(jié)果都應(yīng)該一樣不會(huì)改變。這種情況下,我們?cè)趺幢WC唯一性呢,我們需要把該訂單和訂單狀態(tài)存redis,每次請(qǐng)求時(shí)去看是否訂單已存在。但可能這次交易不成功,下次這張票還可以繼續(xù)交易,可以生成新的訂單號(hào)啊。

redis的setNX是一個(gè)很好的解決方案,意思是當(dāng)存在該key時(shí),返回false,當(dāng)沒(méi)有時(shí),該key和value插入成功。用作檢查訂單是否正在提交,如果是,則阻塞本次請(qǐng)求,避免重復(fù)提交 ,可以設(shè)置過(guò)期時(shí)間3s。提交之前鎖定訂單,防止重復(fù)提交。

3.處理時(shí)間超過(guò)10s,自動(dòng)返回該訂單交易失敗

總之,在高并發(fā)場(chǎng)景下,導(dǎo)致服務(wù)崩潰的原因還是redis和數(shù)據(jù)庫(kù),可能是redis讀寫太慢,或者數(shù)據(jù)庫(kù)的一些sql使用不當(dāng),或者沒(méi)建索引導(dǎo)致讀寫很慢。


該文章在 2024/1/13 16:40:19 編輯過(guò)
關(guān)鍵字查詢
相關(guān)文章
正在查詢...
點(diǎn)晴ERP是一款針對(duì)中小制造業(yè)的專業(yè)生產(chǎn)管理軟件系統(tǒng),系統(tǒng)成熟度和易用性得到了國(guó)內(nèi)大量中小企業(yè)的青睞。
點(diǎn)晴PMS碼頭管理系統(tǒng)主要針對(duì)港口碼頭集裝箱與散貨日常運(yùn)作、調(diào)度、堆場(chǎng)、車隊(duì)、財(cái)務(wù)費(fèi)用、相關(guān)報(bào)表等業(yè)務(wù)管理,結(jié)合碼頭的業(yè)務(wù)特點(diǎn),圍繞調(diào)度、堆場(chǎng)作業(yè)而開(kāi)發(fā)的。集技術(shù)的先進(jìn)性、管理的有效性于一體,是物流碼頭及其他港口類企業(yè)的高效ERP管理信息系統(tǒng)。
點(diǎn)晴WMS倉(cāng)儲(chǔ)管理系統(tǒng)提供了貨物產(chǎn)品管理,銷售管理,采購(gòu)管理,倉(cāng)儲(chǔ)管理,倉(cāng)庫(kù)管理,保質(zhì)期管理,貨位管理,庫(kù)位管理,生產(chǎn)管理,WMS管理系統(tǒng),標(biāo)簽打印,條形碼,二維碼管理,批號(hào)管理軟件。
點(diǎn)晴免費(fèi)OA是一款軟件和通用服務(wù)都免費(fèi),不限功能、不限時(shí)間、不限用戶的免費(fèi)OA協(xié)同辦公管理系統(tǒng)。
Copyright 2010-2025 ClickSun All Rights Reserved