精华内容
下载资源
问答
  • @RestController ... * 使这个Controller中的接口一个统一的前缀 */ @RequestMapping("/prefix") public class ParaController { /** * http://127.0.0.1:8080/prefix/firstrequest * @return */ @
    @RestController
    /**
     * RestController
     * 在普通Controller的基础上有Restful的能力
     */
    /***
     * 使这个Controller中的接口都有一个统一的前缀
     */
    @RequestMapping("/prefix")
    public class ParaController {
    
        /**
         * http://127.0.0.1:8080/prefix/firstrequest
         * @return
         */
        @GetMapping({"/firstrequest"})
        public String firstRequest(){
            return "test";
        }
    
        /**
         * http://127.0.0.1:8080/prefix/requestpara?num=1
         * @param num
         * @return
         */
        @GetMapping({"/requestpara"})
        public String requestpara(@RequestParam Integer num){
            return "para from request:" + num;
        }
    
        /**
         *http://127.0.0.1:8080/prefix/para/9
         * @param num
         * @return
         */
        @GetMapping({"/para/{num}"})
        public String pathpara(@PathVariable Integer num){
            return "para from request:" + num;
        }
    
        /**
         * 两个url都可以调用同一个接口
         * http://127.0.0.1:8080/prefix/multiurl1?num=9
         * http://127.0.0.1:8080/prefix/multiurl2?num=9
         * @param num
         * @return
         */
        @GetMapping({"/multiurl1","/multiurl2"})
        public String multiurl(@RequestParam Integer num){
            return "para from request:" + num;
        }
    
        /**
         * http://127.0.0.1:8080/prefix/required
         * 接口参数可以为空,为空时使用默认值
         * 默认值必须是字符串,哪怕参数是一个整型
         * @param num
         * @return
         */
        @GetMapping({"/required"})
        public String required(@RequestParam(required = false,defaultValue = "5") Integer num){
            return "para from request:" + num;
        }
    }
    

    当我们在 application.properties 中做如下配置后,相当于为我们当前的应用接口加了一个统一的前缀

    server.servlet.context-path=/first
    

    再次访问上面的接口时,则需要

    http://127.0.0.1:8080/first/prefix/firstrequest
    
    展开全文
  • 项目中需要把连接服务器的... 服务可以只对外提供接口,具体功能在服务内部实现3. 增加项目代码的耦合性打开几个我使用的第三方sdk的jar包看看:这是微信的语音识别sdk的jar包的内容我们一般开发一个sdk当然是定义远...

    项目中需要把连接服务器的部分做成一个service并生成一个jar模块。其他产品就可通过这个包来快速的开发连接服务器的应用软件。做成一个service的优点是:

    1. 在后台运行,可以一直保持与服务器的连接

    2. 服务可以只对外提供接口,具体功能在服务内部实现

    3. 增加项目代码的耦合性

    打开几个我使用的第三方sdk的jar包看看:

    934246808bc6

    934246808bc6

    这是微信的语音识别sdk的jar包的内容

    934246808bc6

    我们一般开发一个sdk当然是定义远程的service,也就是通过bingservice启动的方式来启动的服务。

    同时,应用和服务通信的方式有Messenger和aidl,具体区别看别人的文章吧。

    我们这里是开发一个sdk,因此选择了使用aidl,当然如果你的sdk并不包含任何服务,就没有这回事儿了。

    现在以一个例子描述创建一个服务并调用的过程。例子的功能是设置一个数字的值到服务里,然后在从服务里读出来。主界面用于放置了两个按键用于触发以上两个动作,调用的结果在logcat里显示。

    首先创建一个工程,,然后在项目src下面创建一个包,用于放置服务的代码以及一个包用于放置aidl文件。包内依次放置了服务类和aidl文件。创建完之后,内容如下。

    其中CloudServiceInterface.aidl文件内容如下:

    packagecom.rayleigh.aidl;

    interfaceCloudServiceInterface {

    void setInfo(intperson);

    int  getInfo();

    }

    CloudService.java文件文件内容如下:

    packagecom.rayleigh.cloud;

    importcom.rayleigh.aidl.CloudServiceInterface;

    import android.app.Service;

    importandroid.content.Intent;

    importandroid.os.IBinder;

    importandroid.util.Log;

    publicclass CloudService extends Service {

    private int testdata = 0;

    public CloudService() {

    }

    private static final StringTAG="Test";

    @Override

    public void onCreate()

    {

    Log.i(TAG, "ServiceonCreate--->");

    //TODO:register broadcast

    TODO: 2016/12/18  or start do something

    super.onCreate();

    }

    @Override

    public int onStartCommand(Intent intent,int flags, int startId) {

    // return super.onStartCommand(intent,flags, startId);

    return START_STICKY;

    }

    @Override

    public void onDestroy()

    {

    Log.i(TAG, "ServiceonDestroy--->");

    //TODO: unregister broadcast

    super.onDestroy();

    }

    @Override

    public boolean onUnbind(Intent intent) {

    Log.i(TAG, "Serviceonunbind--->");

    return super.onUnbind(intent);

    }

    @Override

    public void onRebind(Intent intent) {

    Log.i(TAG, "Serviceonrebond--->");

    super.onRebind(intent);

    }

    @Override

    public IBinder onBind(Intent intent) {

    // TODO: Return the communicationchannel to the service.

    // throw new UnsupportedOperationException("Notyet implemented");

    return mBinder;

    }

    private final CloudServiceInterface.StubmBinder = new CloudServiceInterface.Stub() {

    @Override

    public void setInfo(int person)  {

    testdata = person;

    }

    @Override

    public int getInfo()   {

    return testdata;

    }

    };

    }

    然后点击project->clean,来编译项目。就会在gen目录里生成了CloudServiceInterface.aidl所用对应的CloudServiceInterface.java。如下:

    然后在AndroidMannifest.xml里面添加服务声明:

    android:name="com.rayleigh.cloud.CloudService"

    android:process=":remote"

    >

    android:name="com.rayleigh.cloud.CloudService"/>

    然后在MainActivity.java里面定义服务对象:

    CloudServiceInterfacemService;

    private ServiceConnectionserviceConnection =new ServiceConnection() {

    @Override

    publicvoid onServiceConnected(ComponentName componentName,IBinder iBinder) {

    Log.e("on","on service connected");

    mService = CloudServiceInterface.Stub.asInterface(iBinder);

    }

    @Override

    publicvoid onServiceDisconnected(ComponentName componentName) {

    Log.e("on","on service disconnected");

    mService = null;

    }

    };

    然后调用bindService函数启用服务。我定义成函数如下:

    private void connectionToService() {

    Intent intent=newIntent(MainActivity.this,CloudService.class);

    this.getApplicationContext().bindService(intent,serviceConnection,Context.BIND_AUTO_CREATE);

    }

    在onCreate里面调用此函数。

    最后onCreate里面的内容如下:

    @Override

    protected void onCreate(BundlesavedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);

    startBtn = (Button)findViewById(R.id.StartBtn);

    stopBtn = (Button) findViewById(R.id.StopBtn);

    startBtn.setOnClickListener(listener);

    stopBtn.setOnClickListener(listener);

    connectionToService();

    }

    其中按键点击响应函数如下:

    private View.OnClickListener listener = newView.OnClickListener()

    {

    @Override

    public void onClick(Viewv)

    {

    switch (v.getId())

    {

    case R.id.StartBtn:

    if (mService != null){

    try {

    mService.setInfo(3);

    Log.e("mainactivity","setvalue success");

    } catch (Exceptione){

    }

    } else {

    Log.e("mainactivity","mServiceis null");

    }

    break;

    case R.id.StopBtn:

    if (mService != null){

    try {

    int a= mService.getInfo();

    Log.e("mainactivity","getvalue is :"+ a);

    } catch (Exceptione) {

    }

    } else {

    Log.e("mainactivity","mServiceis null");

    }

    break;

    default:

    break;

    }

    }

    };

    分别点击两个按键来,设置数字到服务里,和从服务里获取数据

    这个例子很简单,看代码就可以了

    展开全文
  • 前言 继续总结吧,没有面试就继续夯实自己的基础,前阵子的在面试过程中遇到的...1.4.1 服务提供者不可用 硬件故障:如网络故障、硬盘损坏等。 程序的 bug:如算法需要占用大量 CPU 的计算时间导致 CPU 使用率过高。

    前言

    继续总结吧,没有面试就继续夯实自己的基础,前阵子的在面试过程中遇到的各种问题陆陆续续都会总结出来分享给大家,这次要说的也是面试中被问到的一个高频的问题,我当时其实没答好,因为很早之前是看过springboot启动过程的源码,但是时间隔得有点久了(两年多没用过springboot),所以当时也没答好。这次好好总结这部分知识。

    造成雪崩的真实场景

    1.4.1 服务提供者不可用

    • 硬件故障:如网络故障、硬盘损坏等。

    • 程序的 bug:如算法需要占用大量 CPU 的计算时间导致 CPU 使用率过高。

    • 缓存击穿:比如应用刚重启,短时间内缓存是失效的,导致大量请求直接访问到了数据库,数据库不堪重负,服务不可用。

    • 秒杀和大促:服务短时间承载不了那么多请求量。

    1.4.2 重试加大流量

    • 用户连续重试:比如用户看到界面上没有响应,所以又操作了一遍,结果又增加了一倍请求量。

    • 程序重试机制:比如代码中有多次重试的逻辑,一次失败后,过几秒后再重试,重试个三次就取消重试,走异常处理分支了。也是增加了请求量。

    五、如何防止雪崩

    方案

    出问题前预防:限流、主动降级、隔离

    出问题后修复:熔断、被动降级

    「本篇主要来讲解熔断机制。」 后续几篇会讲解其他方案。

    六、熔断原理和算法

    1.6.1 熔断概念

    熔断这个概念来源于电路系统中的保险丝熔断。当电流过大时,保险丝熔断,防止因电流过大损坏电器元器件,或因电流过大,导致元器件热度过高,发生火灾。

    「物理公式」 电功率 P = I^2 * R,I 代表电流,元器件的电阻 R 不变的情况下,电流越大,电功率约大,电阻做的电功大部分都用来发热了,所以电功率越大,发热越严重。(还好高中物理没忘。)

    放到我们系统中,怎么理解熔断?

    如果在某段时间内,调用某个服务非常慢甚至超时,就可以将这个服务熔断,后续其他服务再调用这个服务就直接返回,告诉其他服务:「“已经熔断了,你别调用我了,过段时间再来试下吧。”」

    1.6.2 如何熔断

    「熔断有个原则」 一段时间内,统计失败的次数或者失败请求的占比超过一定阈值,就进行熔断。

    详细的原理如下图所示:

    1.6.3 统计请求的算法

    • 请求访问到后台服务后,首先判断熔断开关是否打开。

    • 如果熔断开关已打开,则表明当前请求不能被处理。

    • 如果熔断开关未打开,则判断时间窗口是否已满。

    • 如果时间窗口未满,则请求桶中的请求数加 1。

    • 如果返回的响应有异常,则失败桶的失败数加 1,如果返回的响应没有异常,则成功桶的成功数加 1

    • 如果时间窗口已满,则开始判断是否需要熔断。

    1.6.4 熔断的恢复算法

    • 当熔断后,开关切换到断开状态

    • 过一段时间后,开关切换为半断开状态(Half-Open)。半断开状态下,允许对应用程序的一定数量的请求可以去调用服务,如果调用成功,则认为服务可以正常访问了,于是将开关切换为闭合状态

    • 如果半断开状态下,还是有调用失败的情况,则认为服务还没有恢复,开关从半断开状态切换到断开状态

    1.6.5 统计失败率的时间窗口

    • 时间窗口可以比喻为人坐在窗户边,看外面来往的车辆,一定时间内从窗户外经过的车辆。

    • 每次请求,都会判断时间窗口是否已满(如5分钟),如果时间窗口已满,则重新开始计时,且清理请求数/成功数/失败数。

    • 注意:第一次开始的起始时间默认为当前时间。

    1.6.6 尝试恢复服务的时间窗口

    • 开关为断开的状态,经过一定时间后,比如 1 分钟,设置为半断开的状态,尝试发送请求检测服务是否恢复。

    • 如果已恢复,则切换状态为关闭状态。如果未恢复,则切换状态为断开的状态,经过 1 分钟后,重复上面的步骤。

    • 这里的时间窗口可以根据环境的运行状态进行动态调整,比如第一次是 1 分钟,第二次是 3 分钟,第三次是 10 分钟。

    七、熔断中间件

    肯定有人会问了,你这上面讲的原理,难道还真的自己去写这套算法?

    「答案:是的,项目中我们自己造了一个轮子:熔断器。」

    但这里我不推荐大家这么做。市面上还有更优秀的开源组件供大家使用,比如阿里系的 Sentinel(推荐),Netflix 的 Hystrix(已停止更新)。

    当然 Sentinel 就不在这篇讲了,后续奉上~

    总结

    以上是字节二面的一些问题,面完之后其实挺后悔的,没有提前把各个知识点都复习到位。现在重新好好复习手上的面试大全资料(含JAVA、MySQL、算法、Redis、JVM、架构、中间件、RabbitMQ、设计模式、Spring等),现在起闭关修炼半个月,争取早日上岸!!!!

    下面给大家分享下我的面试大全资料,如果你也有需要,可以戳这里即可免费领取我的这份复习资料

    • 第一份是我的后端JAVA面试大全

    image.png

    后端JAVA面试大全

    • 第二份是MySQL+Redis学习笔记+算法+JVM+JAVA核心知识整理

    字节二面拜倒在“数据库”脚下,闭关修炼半个月,我还有机会吗?

    MySQL+Redis学习笔记算法+JVM+JAVA核心知识整理

    • 第三份是Spring全家桶资料

    字节二面拜倒在“数据库”脚下,闭关修炼半个月,我还有机会吗?

    …(img-0x7y8JMz-1623900217769)]

    MySQL+Redis学习笔记算法+JVM+JAVA核心知识整理

    • 第三份是Spring全家桶资料

    [外链图片转存中…(img-CIPY9g06-1623900217770)]

    MySQL+Redis学习笔记算法+JVM+JAVA核心知识整理

    展开全文
  • 博主之前做过恒丰银行代收付系统(相当于支付接口),包括现在的oltpapi交易接口和虚拟业务的对外提供数据接口。总之,当你做了很多项目写了很多代码的时候,就需要回过头来,多总结总结,这样你会看到更多之前写...

    >>号外:关注“Java精选”公众号,回复“2021面试题”,领取免费资料!“Java精选面试题”小程序,3000+ 道面试题在线刷,最新、最全 Java 面试题!

    博主之前做过恒丰银行代收付系统(相当于支付接口),包括现在的oltpapi交易接口和虚拟业务的对外提供数据接口。总之,当你做了很多项目写了很多代码的时候,就需要回过头来,多总结总结,这样你会看到更多之前写代码的时候看不到的东西,也能更明白为什么要这样做。

    做接口需要考虑的问题

    什么是接口

    接口无非就是客户端请求你的接口地址,并传入一堆该接口定义好的参数,通过接口自身的逻辑处理,返回接口约定好的数据以及相应的数据格式。

    接口怎么开发

    接口由于本身的性质,由于和合作方对接数据,所以有以下几点需要在开发的时候注意:

    1、定义接口入参:写好接口文档

    2、定义接口返回数据类型:一般都需要封装成一定格式,确定返回json还是xml报文等

    见如下返回数据定义格式:

    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、确定访问接口的方式,get or post等等,可以根据restful接口定义规则RESTful API。

    4、定义一套全局统一并通用的返回码,以帮助排查问题;

    public static int NO_AGENT_RATE = 1119;  //未找到兑换率
    
    public static int SCHEME_COMMIT_FAIL = 4000;  //方案提交失败
    
    public static int SCHEME_CONFIRMATION = 4001;  //方案确认中
    
    public static int SCHEME_NOT_EXIST = 4002;  //方案不存在
    
    public static int SCHEME_CANCEL= 4005;  //方案不存在
    
    //。。。。

    5、统一的异常处理:应该每个系统都需要一套统一的异常处理

    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()); 
     
        /**
         * 所有异常报错
         * @param request
         * @param exception
         * @return
         * @throws Exception
         */
        @ExceptionHandler(value=Exception.class)  
        public Response allExceptionHandler(HttpServletRequest request,  
                Exception exception) throws Exception  
        {  
        	logger.error("拦截到异常:", exception);
            Response response = new Response();
            response.setData(null);
            response.getResult().setResultCode(9999);
            response.getResult().setResultMsg("系统繁忙");
            return response;  
        }  
     
    }

    6、拦截器链设置

    合作方访问接口的时候,会根据你接口定义好的传参访问你的接口服务器,但是会存在接口参数类型错误或者格式不对,必传参数没传的问题,甚至一些恶意请求,都可以通过拦截器链进行前期拦截,避免造成接口服务的压力。还有很重要的一点,加签验签也可以在拦截器设置。继承WebMvcConfigurerAdapter实现springboot的拦截器链。实现HandlerInterceptor方法编写业务拦截器。

    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("缺少私钥, 渠道号:" + arg0.getParameter("agentid"));
    			logger.error("缺少私钥, 渠道号:" + 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("参数签名认证失败");
    			logger.error("参数签名认证失败:" + 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) {
    	    	
    	    	//访问速率限制
    	    	registry.addInterceptor(aPILimitRateInterceptor())
    	    	.addPathPatterns("/*/*");
    	    	//.addPathPatterns("/price/getPriceParam");
     
    	    	//参数签名认证
    	        registry.addInterceptor(ddsAuthInterceptor())
    	        .addPathPatterns("/tradeState/*")
    	        .addPathPatterns("/recycle/*")
    	        .addPathPatterns("/matchInfo/*")
    	        .addPathPatterns("/price/tradeTicketParam");
    	        
    	        //公共参数检查
    	        registry.addInterceptor(commonInterceptor())
    	        .addPathPatterns("/price/tradeTicketParam")
    	        .addPathPatterns("/tradeState/*")
    	        .addPathPatterns("/recycle/*");
    	        
    	        //询价参数校验
    	        registry.addInterceptor(queryPriceParamsInterceptor())
    	        .addPathPatterns("/price/getPriceParam");
    	        
    	        //交易参数检查
    	        registry.addInterceptor(tradeParamsInterceptor())
    	        .addPathPatterns("/price/tradeTicketParam");
    	        
    	        super.addInterceptors(registry);
    	    }
    }

    7、token令牌和sign数字签名实现数据保密性。

     创建令牌(Token)

    为保证请求的合法性,我们提供第三方创建令牌接口,某些接口需要通过token验证消息的合法性,以免遭受非法攻击。

    token过期时间目前暂时定为1天,由于考虑到合作方往往是分布式环境,多台机器都有可能申请token,为了降低合作方保证token一致性的难度,调用接口创建token成功以后一分钟以内,再次请求token返回的数据是一样的。

     获取私钥

    获取用于数字签名的私钥,第三方获取的私钥需妥善保存,并定期更新,私钥只参与数字签名,不作为参数传输。

    数字签名方式:

    参数签名;签名方式:所有值不为null的参数(不包括本参数)均参与数字签名,按照“参数名+参数值+私钥”的格式得到一个字符串,再将这个字符串MD5一次就是这个参数的值。(示例:h15adc39y9ba59abbe56e057e60f883g),所以需要先获取私钥。

    验签方式:

    将用户的所有非null参数放入定义好排序规则的TreeSet中进行排序,再用StringBuilder按照按照“参数名+参数值+私钥”的格式得到一个字符串(私钥从redis拿),再将这个字符串MD5一次就是这个参数的值。将这个值与用户传来的sign签名对比,相同则通过,否则不通过。

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

    8、接口限流

    有时候服务器压力真的太大,以防交易接口被挤死,就可以对一些其他不影响主要业务功能并且计算量大的接口做限流处理。RateLimit--使用guava来做接口限流,当接口超过指定的流量时,就不处理该接口的请求。详细可看RateLimit。也可参考其他限流框架。

    9、协议加密,http升级成https;

    为什么要升级呢,为了保证数据的安全性。当使用https访问时,数据从客户端到服务断,服务端到客户端都加密,即使黑客抓包也看不到传输内容。当然还有其他好处,这里不多讲。但这也是开发接口项目需要注意的一个问题。

    如何提高接口的高并发和高可用

    接口开发好了,接下来就讨论接口的可用性问题。首先我们要将高并发和高可用区分一下,毕竟高可用是在可用的情况,只是很慢或者效率不高。其实也可以归为一类问题,但是不重要啦,重要的是怎么提高你写的接口的访问速度和性能。

    1、接口的高并发解决方案(其实没有唯一答案,业界针对不同业务也有很多不同的方法)

    当访问一个接口获取数据时,发现返回很慢,或者总是超时,如果排除网络的原因,那就是接口服务器压力太大,处理不过来了。在世界杯期间,我们查看后台日志总是connection by reset和borker pipe和一些超时问题。这时候,你可能遇到了高并发和高可用问题。但是,不管遇到什么问题,都不能臆断和乱改,你得需要找到慢的原因,才能对症下药,乱改可能会导致其他问题的出现。首先,解决高并发问题的三个方向是负载均衡,缓存和集群。

    1)负载均衡

    我们使用的是阿里云服务器的负载均衡,后台分布式服务管理,我们运维小哥哥搭建了一套k8s,可以自由在k8s上扩展服务节点,各个服务结点也能随内存的使用自动漂移,不用多说,k8s真的很厉害,感兴趣的同学可以详细去学。那么问题来了,阿里云的负载均衡怎么对应到k8s的负载均衡呢?这个涉及到了k8s的service暴露的一些特点,简单说就是k8s把所有集群的服务都通过指定的内部负载均衡,在指定的服务器上暴露,然后我们又把这几个服务器接在阿里云负载均衡下,这个涉及的细节和配置很多。当然,除nginx外,还有其他负载均衡解决方案,软件硬件都有,硬件如f5等。

    阿里云的nginx负载均衡,我们使用的是加权轮询策略,其实轮询是最低效的方式;

    这就是最基本的负载均衡实例,但这不足以满足实际需求;目前Nginx服务器的upstream模块支持6种方式的分配:

    负载均衡策略

    轮询默认方式
    weight权重方式
    ip_hash依据ip分配方式
    least_conn最少连接方式
    fair(第三方)响应时间方式
    url_hash(第三方)依据URL分配方式

    2)集群

    首先,通过排查问题,发现是oltpapi接口服务处理请求很慢,大量请求过来,总是超时和中断连接,这时候,我们想着最简单的方法就是加机器,给oltp接口服务多加几台机器。嗯,一切都很完美,如预期进行,但是加到一定数量,你发现,怎么不起效果,异步响应还是很慢,或者更直观的说,消息队列出现了严重的消息堆积。这时候,你发现出现了新的问题或者瓶颈,这个问题已经不是说加oltp服务器能解决了,那么,就需要去重新定位问题。发现是消息堆积,消息堆积就是生产者过快,导致消费者消费不过来,这时候,你就需要增加消费者的消费数量。给风控系统多加几台机器,让消费者和生产者达到一定平衡。这里有个误区,你可能以为是rocketmq的broker数量过少,增加broker数量,其实当消费者和生产者保持一样的速度时,消息肯定不对堆积,按照原始的broker数量就足够。但是增加broker也会使得消息得到尽快的处理,提升一定效率。

    3)缓存

    当加机器不能解决问题时,或者说没那么多服务器可使用时,那么就要重代码层面解决高并发问题。Redis 是一个高性能的key-value数据库,当获取数据从数据库拿很慢时,就可以存储到redis,从redis取值。

    1、用ConcurrentHashMap缓存对象,并设置过期时间
    2、redis缓存数据,结合spring定时任务定时获取不会经常改动的key
    3、提高使用redis的效率:比如使用mGet一次获取多个key
    4、…等

    2、接口高可用问题

    高可用问题应该上升到整个服务的架构问题上,就是说在搭建整体系统是就应该考虑到。高可用问题是以单点故障,访问速度慢的问题为主导。见  服务高可用

    1、redis主从分布式(redis的单点故障和访问速度的提高和主从备份)
    2、分布式dubbo服务的zookeeper主从集群
    3、strom的主从集群
    4、…等

    总结

    下面对接口开发服务做一些总结:

    1、是拉还是推:

    当接口作为数据源时,还要考虑数据是让合作方主动过来拉还是数据有变化就推送呢,当然是推的效果更好,但是如何有效的推数据,不推重复数据等都是需要根据实际业务考虑的问题。

    2、多台分布式服务器上,怎么保证交易的幂等和订单的唯一性

    当接口服务和合作方都处于分布式情况下,就很容易出现一个订单号申请多次交易请求,但是根据幂等性,一张彩票只能交易一次,并且每次不管何时请求,结果都应该一样不会改变。这种情况下,我们怎么保证唯一性呢,我们需要把该订单和订单状态存redis,每次请求时去看是否订单已存在。但可能这次交易不成功,下次这张票还可以继续交易,可以生成新的订单号啊。redis的setNX是一个很好的解决方案,意思是当存在该key时,返回false,当没有时,该key和value插入成功。用作检查订单是否正在提交,如果是,则阻塞本次请求,避免重复提交 ,可以设置过期时间3s。提交之前锁定订单,防止重复提交。

    3、处理时间超过10s,自动返回该订单交易失败

    总之,博主发现,在高并发场景下,导致服务崩溃的原因还是redis和数据库,可能是redis读写太慢,或者数据库的一些sql使用不当,或者没建索引导致读写很慢。总之,这是一条很漫长的路,我们都需要慢慢积累经验和学习前人更优秀的解决办法。

    作者:xiaolizh

    blog.csdn.net/xiaolizh/article/details/83011031

    往期精选  点击标题可跳转

    Spring Boot 框架中如何使用 AOP 防止重复提交?(附源码)

    Google 出品 Java 编码风格规范,强烈推荐,权威又科学!

    Spring Cloud 项目中实现推送消息到 RabbitMQ 消息中间件

    程序员网站 Stack Overflow 被收购!以后“抄代码”难道要付费了?

    为什么阿里规范需要在 @Transactional 事务注解中指定 rollbackFor?

    拒绝 kill -9 强制停止,如何优雅停止 Spring Boot 服务?

    数据库连接池到底应该设置多大?响应时间从 100ms 优化到 3ms!

    IntelliJ IDEA 中使用热部署 JRebel 神器,开发效率提升一倍!

    MySQL 中 update 语句双引号错位引发的“血案”,这会长记性了!

    程序员缺乏工作经验的 7 种表现,你有没有中招?及早避坑!

    点个赞,就知道你“在看”!

    展开全文
  • 最近个项目需要对外提供一个接口提供公网域名进行访问,而且接口和交易订单有关,所以安全性很重要;这里整理了一下常用的一些安全措施以及具体如何去实现。 安全措施 个人觉得安全措施大体来看主要在两个方面,...
  • 前言: 日常开发中我们一般都会做对外接口的统一数据返回模板,以下是我所采用的数据返回模板,分享出来目的是欢迎大家学习和帮助改进。以下,Enjoy:DataResult.java(数据模板类):/*** @Auther: 折戟沉沙* @...
  • 接口需要考虑的问题 什么是接口 接口无非就是客户端请求你的接口地址,并传入一堆该接口定义好的参数,通过接口自身的逻辑处理,返回接口约定好的数据以及...3、确定访问接口方式,get or post等等,可以根据restf
  • 最近个项目需要对外提供一个接口提供公网域名进行访问,而且接口和交易订单有关,所以安全性很重要;这里整理了一下常用的一些安全措施以及具体如何去实现。 安全措施 个人觉得安全措施大体来看主要在两个方面,...
  • Java中的接口

    2021-03-22 14:19:55
    接口明确地描述了系统对外提供的所有服务,清晰地把系统的实现细节与接口分离,系统的实现细节由接口的实现类负责实现,接口负责对外描述系统提供的服务,对内描述系统应该具备的功能;b.接口和抽象类都不能够被实例...
  • 常见的接口调用方式有三种(设计接口的时候要考虑选用哪种接口) 1、http接口:http是一种网络传输协议,基于TCP。(等价于:http+json) 现在浏览器客户端与服务器端通信基本都是采用http协议。 SpringCloud框架,...
  • 一个公司需要拓展业务时,内部...像这种对外的api接口往往对安全性严格要求,本文整理了一下常用的api鉴权方式以及安全措施(如不当之处,请路过的大佬指正)。 对外API接口的安全性设计及鉴权方式 API鉴权方式 ...
  • 经常使用JavaScript的同学应该注意过类似于下面这样结构的脚本语言著名的第三方类库jQuery就是用到这样的语法...首先,小括号在语句中起到”表达式组合分块“作用,并且每个分组都会返回值,在控制台下运行执行下...
  • *******************************提供接口**************************************//通过request的输入流来获取调用接口传递过来的参数// 遍历request的流public String getRequestStreamString(InputStream is) {...
  • 基于PHP5.5 编写对外API接口的交互流程与后续版本维护小黄牛当前最新版本 - V1.0.0.1作者 - 小黄牛初衷说明写这个DEMO的初衷是为了让自己更好的熟悉接口开发流程,以及与朋友们的日常交流学习,期望在技术层面有所...
  • 每个独立的服务背后,可能是一个集群在对外提供服务。这就会碰到一个问题,整个系统是由多个服务(子系统)组成的,数据需要在各个服务中不停流转。如果数据在各个子系统中传输时,速度过慢,就会形成瓶颈,降低整个...
  • 问题:对某个对外暴露的接口加一个限制:调用者一分钟之内调用次数不能超过100次,如果超过100次就直接返回给调用者失败的信息。给调用者一个SECRET,每次调用者需要调用接口的时候,都需要把这个SECRET带过来(为了安全...
  • 接口其实就是提供了一种统一”协议”,接口中的属性也属于“协议”中的成员。它们是公共的,静态的,最终的常量。相当于全局常量。抽象类是不“完全”的类,相当于是接口和具体类的一个中间层。即满足接口的抽象,也...
  • 对外接口的设计原则

    2021-04-05 10:15:54
    接口安全性1.1 接口外部处理措施1.1.1 接口权限隔离1.1.2 接口限流1.1.3 使用 POST 请求1.1.4 数据加密传输1.2 接口内部处理措施1.2.1 记录请求日志1.2.2 参数合法校验1.2.3 调用失败告警2. 接口幂等性2.1 Token ...
  • 一直以来做对外的接口文档都比较原始,基本上都是手写的文档传来传去,最近发现了一个新玩具,可以在接口上省去不少麻烦。...本文主要记录我用swagger2做对外接口的两种方式,方面后面查阅。使用...
  • 在java中,接口是一切实现方法的来源,任何实现都离开不了接口,那么,一个标准的java接口写法是怎样的呢?下面,就让小编带你一起了解一下吧。(一)接口1、什么是接口接口就是一些方法特征的集合。一些功能的定义。...
  • 对外接口,需要校验一下是否相应权限,简单的一个小代码。 res加密util; /** * @description: AES加密解密工具 * @author:mic * @create: **/ public class AESUtil { /** * AES加密字符串 * * @...
  • 最近公司业务需要对外提供接口,之前没有什么对外接口开发经验所以最近找了很多文章,恶补了一下对外接口开发知识,这篇算是自己开发接口的一个总结吧。下图我设计接口的大体流程 对外接口的安全问题 1.传输过程的...
  • 但比如 Web service 技术对外提供的一个公共接口,需要通过soapUI 等工具对其进行测试。 UI层的自动化测试,这个大家应该再熟悉不过了,大部分测试人员的大部分工作都是对UI层的功能进行测试。例如,我们不断重复的...
  • 为开发者分配AccessKey(开发者标识,确保唯一)和SecretKey(用于接口加密,确保不易被穷举,生成算法不易被猜测)。 2、防止篡改 参数签名: (1)按照请求参数名的字母升序排列非空请求参数(包含AccessKey),...
  • 设计一个笔记本电脑类,属性随意,并且进行属性私有化,对外提供公开的set和get方法。 设计一个可插拔的接口:InsertDrawable,该接口有什么方法自行定义。 设计一个鼠标类,实现InsertDrawable接口,并实现...
  • 多个类实现了相似的方法的时候,就可以对它们提取出一个接口,告诉其他人“可以通过同一种行为来操控我们”。当然,你认为接口的抽象方法必须全部实现很麻烦的话,这时就可以用Adapter,提取为同一个抽象类,因为...
  • 软件系统之间的接口是实现一个系统跟另外系统进行信息交互的桥梁,接口一般分为两种:程序内部的接口和系统对外接口,软件接口的通常分为两类:webservice接口和http api接口 webservice接口 webService接口是走...
  • 最后,我只是不建议在对外提供接口的出入参中使用枚举,并不是说彻底不要用枚举,我之前很多文章也提到过,枚举很多好处,我在代码中也经常使用。所以,切不可因噎废食。 当然,文中的观点仅代表我个人,具体是...
  • 本文详细介绍了Python中通过requests库来请求外部接口
  • 编辑: 从Java 8开始,接口中现在允许使用静态方法。例子如下:public interface IXMLizable{static T newInstanceFromXML(Element e);Element toXMLElement();}当然这行不通。 但是为什么不呢?可能的问题之一是,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 223,861
精华内容 89,544
关键字:

对外提供接口的方式有哪些