系统接口及调用情况

Spring Aop+Redis优雅的记录接口调用情况 某些场景下还是需要用到接口请求统计的,包括也可以做限流操作,大部分中间件的底层做监控,底层实现方式也差不了多少, 记得很多年前有道面试题,还被问到如何做接口的请求次数统计,以及限流策略。 阅读详情

系统接口及调用情况

说明:系统中经需要统计接口的调用情况,所以特此写了这个功能。(适用于spring boot框架)

1. 统计系统中有哪些接口及接口的功能

   思路,当服务器启动后统计系统的@requestMapper,并根据方法上的注解获取该接口的功能。

1.1 启动时注册一个监听器

SpringApplication app=new SpringApplication(Application.class);

app.addListeners(new ApplicationStartup());

app.run( args);

1.2 编写监听器中的业务逻辑

public class ApplicationStartup implements ApplicationListener<ContextRefreshedEvent>{

public void onApplicationEvent(ContextRefreshedEvent event) {

getAllUrls(event);

}

/**

 * 获取所有的url数据

 * @param event

 */

public void getAllUrls(ContextRefreshedEvent event) {

AbstractHandlerMethodMapping<RequestMappingInfo> objHandlerMethodMapping =(AbstractHandlerMethodMapping<RequestMappingInfo>) event.getApplicationContext().getBean("requestMappingHandlerMapping");

InterfaceMethodMapper interfaceMethodMapper=event.getApplicationContext().getBean(InterfaceMethodMapper.class);

Map<RequestMappingInfo, HandlerMethod> mapRet = objHandlerMethodMapping.getHandlerMethods();

//先删除所有数据

interfaceMethodMapper.deleteByExample(null);

if(mapRet!=null){

List<InterfaceMethod>InterfaceMethodList=new ArrayList<InterfaceMethod>();

Date date=new Date();

for(RequestMappingInfo info:mapRet.keySet()) {

HandlerMethod handlerMethod=mapRet.get(info);

//获取对应的类

String type=handlerMethod.getBeanType().getName();

//获取方法

Method method=handlerMethod.getMethod();

InterfaceDescript interfaceScript=method.getAnnotation(InterfaceDescript.class);

String name=method.getName();

//只对有该注解的方法做处理

if(interfaceScript!=null) {

String descript=interfaceScript.value();

//获取url

Set<String> urls=info.getPatternsCondition().getPatterns();

for(String url:urls) {

InterfaceMethod interfaceMethod=new InterfaceMethod();

interfaceMethod.setPath(url);

interfaceMethod.setClassName(type);

interfaceMethod.setMethodName(name);

interfaceMethod.setRmark(descript);

interfaceMethod.setCreateTime(date);

InterfaceMethodList.add(interfaceMethod);

}

}

}

if(InterfaceMethodList.size()>0) {

//将获取的url信息插入到数据库中

interfaceMethodMapper.insertList(InterfaceMethodList);

}

}

}

 

}

1.3 对应的数据库(mapper是通过工具直接生成的)

CREATE TABLE `interface_method` (

  `path` varchar(200) NOT NULL COMMENT '路径',

  `class_name` varchar(200) NOT NULL COMMENT '类名',

  `method_name` varchar(100) NOT NULL COMMENT '方法名',

  `rmark` varchar(1000) NOT NULL COMMENT '备注',

  `create_time` datetime NOT NULL COMMENT '更新时间',

  PRIMARY KEY (`path`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

批量插入数据

  <insert id="insertList" parameterType="com.trilink.counter.entity.InterfaceMethod" >

    insert into interface_method (path, class_name, method_name,

      rmark, create_time)

    values

    <foreach collection="recordList" separator="," item="record">

     (#{record.path,jdbcType=VARCHAR}, #{record.className,jdbcType=VARCHAR}, #{record.methodName,jdbcType=VARCHAR},

      #{record.rmark,jdbcType=VARCHAR}, #{record.createTime,jdbcType=TIMESTAMP})

    </foreach>

  </insert>

 

 

2. 通过spring AOP拦截Controller对应的方法获取返回数据,以便于通过返回值获取响应数成功与否.

2.1 拦截器的设计

/***

 * aop拦截器获取返回值(返回对象里面有 errCode字段的可以做出处理)

 *

 * @author acer

 *

 */

@Aspect

@Component

public class ControllerInterceptor {

@Autowired

private InterfaceCounterMapper interfaceCounterMapper;

//用于存放请求数据

private static List<InterfaceCounter> list=new LinkedList<InterfaceCounter>();

//统计数量达到限制后批量写入数据库,以减轻数据库压力

public static final int UPDATESIZE=3;

/**

 * 定义拦截规则:拦截com.xjj.web.controller包下面的所有类中,有@RequestMapping注解的方法。

 */

@Pointcut("(execution(* com.trilink.common.controller..*(..)) || execution(* com.trilink.counter.controller..*(..))) and @annotation(org.springframework.web.bind.annotation.RequestMapping)")

public void controllerMethodPointcut() {

}

/**

 * 拦截器具体实现

 *

 * @param pjp

 * @return JsonResult(被拦截方法的执行结果,或需要登录的错误提示。)

 */

@Around("controllerMethodPointcut()") // 指定拦截器规则;也可以直接把“execution(* com.xjj.........)”写进这里

public Object Interceptor(ProceedingJoinPoint pjp) {

boolean isError = false;

Object result = null;

Gson gson = new Gson();

ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();

HttpServletRequest httpServletRequest = requestAttributes.getRequest();

//获取请求路径

String url=httpServletRequest.getRequestURI();

String appId=httpServletRequest.getParameter("appId");

Map<String, String[]>  par=httpServletRequest.getParameterMap();

//获取请求参数

String param=gson.toJson(par);

String errMsg;

int mcode=-1;

try {

result = pjp.proceed();

String json = gson.toJson(result);

Map<String, Object> reponse = gson.fromJson(json, Map.class);

Object code = reponse.get("errCode");

errMsg = (String)reponse.get("errMsg");

if (code != null) {

if (code instanceof Integer) {

mcode=(int)(code);

} else if (code instanceof String) {

mcode=Integer.parseInt((String)code);

} else if (code instanceof Double) {

Double cd=(Double)code;

mcode=cd.intValue();

} else if (code instanceof Long) {

mcode=(int)(code);

}

 

}

} catch (Throwable e) {

Map<String, Object> error = new HashMap<String, Object>();

error.put("errCode", -1);

error.put("errMsg", e.getMessage());

result = error;

mcode=-2;

isError = true;

errMsg= e.getMessage();

}

InterfaceCounter InterfaceCounter=new InterfaceCounter();

InterfaceCounter.setPath(url);

InterfaceCounter.setParam(param);

InterfaceCounter.setMessage(errMsg);

InterfaceCounter.setStatus(mcode);

InterfaceCounter.setCreateTime(new Date());

InterfaceCounter.setAppId(appId);

list.add(InterfaceCounter);

if(list.size()>=UPDATESIZE) {

interfaceCounterMapper.insertList(list);

list.removeAll(list);

}

return result;

}

}

3. 数据的展示

说明:数据主要以图表的形式展示,前台html页面通过ajax请求来与后台交互。

3.1 后台服务的编写

/**

 * 统计接口请求量

 * @author acer

 *

 */

@Controller

@RequestMapping("interfaceCounter")

public class InterfaceCounterController {

@Autowired

private InterfaceCounterService interfaceCounterService;

@RequestMapping("counterInterface")

@ResponseBody

@InterfaceDescript("统计接口请求量")

/**

 * 统计信息

 * @param type 类型,1:年,2:月,3:日,4:小时,5:分

 * @param year

 * @param month

 * @param day

 * @param hour

 * @param min

 * @return

 */

public Map<String,Object>counterInterface(Integer type,Integer year,Integer month,Integer day,Integer hour){

Map<String,Object>result=new HashMap<String,Object>();

if(type==null) {

type=1;

year=2010;

}

if(type==1) {

year=2010;

}

try {

result=interfaceCounterService.counterInterface(type,year,month,day,hour);

}catch(Exception e) {

result.put("errCode", 101);

result.put("errMsg", "查询失败:"+e.getMessage());

}

return result;

}

@RequestMapping("counterMathods")

@ResponseBody

@InterfaceDescript("某个接口的单位请求量")

/**

 *

 * @param type 类型,1:年,2:月,3:日,4:小时,5:分

 * @param year

 * @param month

 * @param day

 * @param hour

 * @return

 */

public Map<String,Object>counterMathods(Integer type,Integer year,Integer month,Integer day,Integer hour){

Map<String,Object>result=new HashMap<String,Object>();

if(type==null) {

type=1;

year=2010;

}

try {

result=interfaceCounterService.counterMathods(type,year,month,day,hour);

}catch(Exception e) {

result.put("errCode", 101);

result.put("errMsg", "查询失败:"+e.getMessage());

}

return result;

}

@RequestMapping("mathodsDetail")

@ResponseBody

@InterfaceDescript("所有接口的详细信息")

/**

 *

 * @param order 1位降序,2为升序(时间)

 * @param page默认值为1

 * @param rows

 * @return

 */

public Map<String,Object>mathodsDetail(Integer order,Integer page,Integer rows,String start,String end){

Map<String,Object>result=new HashMap<String,Object>();

if(page==null||page<1) {

page=1;

}

if(order==null||order<1) {

order=1;

}

if(rows==null||rows<1) {

rows=10;

}

try {

result=interfaceCounterService.mathodsDetail(order,page,rows,start,end);

}catch(Exception e) {

result.put("errCode", 101);

result.put("errMsg", "查询失败:"+e.getMessage());

}

return result;

}

}

 

@Service

public class InterfaceCounterService {

@Autowired

private InterfaceCounterMapper interfaceCounterMapper;

 

/**

 * 统计接口请求信息

 * @param type 类型,1:年,2:月,3:日,4:小时,5:分

 * @param year

 * @param month

 * @param day

 * @param hour

 * @param min

 * @return

 */

public Map<String, Object> counterInterface(Integer type,Integer year,Integer month,Integer day,Integer hour) {

Map<String,Object>result=new HashMap<String,Object>();

Calendar calendar=Calendar.getInstance();

List<Date> timeList=null;

if(type==1) {//年

timeList=getListYear(calendar.get(Calendar.YEAR),year);

}else if(type==2) {//月

timeList=getListMonth(year);

}else if(type==3) {//日

timeList=getListDay(year, month);

}else if(type==4) {//时

timeList=getListHour(year, month, day);

}else if(type==5) {//分

timeList=getListMinut(year, month, day, hour);

}

if(timeList==null||timeList.size()<1) {

result.put("errCode", 101);

result.put("errMsg", "输入的参数存在问题");

return result;

}else {

Date start=null;

Date end=null;

//总请求量

List<Integer>numberList=new ArrayList<Integer>();

//描述

List<Integer> decsList=new ArrayList<Integer>();

//成功请求量

List<Integer>successList=new ArrayList<Integer>();

for(int i=0;i<timeList.size()-1;i++) {

start=timeList.get(i);

end=timeList.get(i+1);

InterfaceCounterExample interfaceCounterExample =new InterfaceCounterExample();

InterfaceCounterExample.Criteria interfaceCounterExampleCriteria = interfaceCounterExample.createCriteria();

interfaceCounterExampleCriteria.andCreateTimeBetween(start, end);

int count=interfaceCounterMapper.countByExample(interfaceCounterExample);

numberList.add(count);

InterfaceCounterExample interfaceCounterExample2 =new InterfaceCounterExample();

InterfaceCounterExample.Criteria interfaceCounterExampleCriteria2 = interfaceCounterExample2.createCriteria();

interfaceCounterExampleCriteria2.andStatusEqualTo(0);

interfaceCounterExampleCriteria2.andCreateTimeBetween(start, end);

int countSuccess=interfaceCounterMapper.countByExample(interfaceCounterExample2);

successList.add(countSuccess);

//根据不同的类型返回不同的坐标数据

if(type==1) {//年

decsList.add(start.getYear()+1900);

}else if(type==2) {//月

decsList.add(start.getMonth()+1);

}else if(type==3) {//日

decsList.add(start.getDate());

}else if(type==4) {//时

decsList.add(start.getHours());

}else if(type==5) {//分

decsList.add(start.getMinutes());

}

}

result.put("dataY", numberList);

result.put("sdataY", successList);

result.put("dataX", decsList);

result.put("errCode", 0);

result.put("errMsg", "查询成功");

}

return result;

}

/**

 * 根据日期统计每个接口调用情况

 * @param type

 * @param year

 * @param month

 * @param day

 * @param hour

 * @return

 */

public Map<String, Object> counterMathods(Integer type, Integer year, Integer month, Integer day, Integer hour) {

Map<String,Object>result=new HashMap<String,Object>();

Date start=getFirstDate(type,year, month, day, hour);

Date end=getLastDate(type,year, month, day, hour);

List<InterfaceCounter> list=interfaceCounterMapper.countByDate(start, end);

List<String>dataY=new ArrayList<String>();

List<Integer>dataX=new ArrayList<Integer>();

for(InterfaceCounter item:list) {

dataY.add(item.getPath());

dataX.add(item.getNum());

}

result.put("dataY", dataY);

result.put("dataX", dataX);

result.put("errCode", 0);

result.put("errMsg", "查询成功");

return result;

}

public Map<String, Object> mathodsDetail(Integer order,Integer page, Integer rows,String start,String end) throws Exception {

Map<String,Object>result=new HashMap<String,Object>();

InterfaceCounterExample interfaceCounterExample = new InterfaceCounterExample();

InterfaceCounterExample.Criteria InterfaceCounterExampleCriteria=interfaceCounterExample.createCriteria();

if(order==1) {

interfaceCounterExample.setOrderByClause("create_time desc");

}else {

interfaceCounterExample.setOrderByClause("create_time asc");

}

//当时间不为空时根据时间段查询

if(start!=null&&!start.equals("")&&end!=null&&!end.equals("")) {

SimpleDateFormat fmt=new SimpleDateFormat("yyyy-MM-dd HH:mm");

Date startDate=fmt.parse(start);

Date endDate=fmt.parse(end);

InterfaceCounterExampleCriteria.andCreateTimeBetween(startDate, endDate);

}

PageHelper.startPage(page, rows);

List<InterfaceCounter> list=interfaceCounterMapper.selectByExample(interfaceCounterExample);

PageInfo<InterfaceCounter> pages=new PageInfo<>(list);

result.put("data", list);

result.put("tatal", pages.getTotal());

result.put("errCode", 0);

result.put("errMsg", "查询成功");

return result;

}

/**

 * 获取当前时间的第一时刻

 * @param type 1:年,2:月,3:日,4:小时

 * @param year

 * @param month

 * @param day

 * @param hour

 * @return

 */

private static Date getFirstDate(Integer type, Integer year, Integer month, Integer day, Integer hour) {

Calendar calendar=Calendar.getInstance();

if(type==1) {

calendar.set(Calendar.YEAR, year);

calendar.set(Calendar.MONTH, 0);

calendar.set(Calendar.DAY_OF_MONTH, 1);

calendar.set(Calendar.HOUR_OF_DAY, 0);

calendar.set(Calendar.MINUTE, 0);

calendar.set(Calendar.SECOND, 0);

return calendar.getTime();

}else if(type==2) {

calendar.set(Calendar.YEAR, year);

calendar.set(Calendar.MONTH, month-1);

calendar.set(Calendar.DAY_OF_MONTH, 1);

calendar.set(Calendar.HOUR_OF_DAY, 0);

calendar.set(Calendar.MINUTE, 0);

calendar.set(Calendar.SECOND, 0);

return calendar.getTime();

}else if(type==3) {

calendar.set(Calendar.YEAR, year);

calendar.set(Calendar.MONTH, month-1);

calendar.set(Calendar.DAY_OF_MONTH,day);

calendar.set(Calendar.HOUR_OF_DAY, 0);

calendar.set(Calendar.MINUTE, 0);

calendar.set(Calendar.SECOND, 0);

return calendar.getTime();

}else if(type==4||type==5) {

calendar.set(Calendar.YEAR, year);

calendar.set(Calendar.MONTH, month-1);

calendar.set(Calendar.DAY_OF_MONTH,day);

calendar.set(Calendar.HOUR_OF_DAY, hour);

calendar.set(Calendar.MINUTE, 0);

calendar.set(Calendar.SECOND, 0);

return calendar.getTime();

}

return null;

}

/**

 * 获取当前时间的第一时刻

 * @param type 1:年,2:月,3:日,4:小时

 * @param year

 * @param month

 * @param day

 * @param hour

 * @return

 */

private static Date getLastDate(Integer type, Integer year, Integer month, Integer day, Integer hour) {

Calendar calendar=Calendar.getInstance();

if(type==1) {

calendar.set(Calendar.YEAR, year+1);

calendar.set(Calendar.MONTH, 0);

calendar.set(Calendar.DAY_OF_MONTH, 1);

calendar.set(Calendar.HOUR_OF_DAY, 0);

calendar.set(Calendar.MINUTE, 0);

calendar.set(Calendar.SECOND, -1);

return calendar.getTime();

}else if(type==2) {

calendar.set(Calendar.YEAR, year);

calendar.set(Calendar.MONTH, month);

calendar.set(Calendar.DAY_OF_MONTH, 1);

calendar.set(Calendar.HOUR_OF_DAY, 0);

calendar.set(Calendar.MINUTE, 0);

calendar.set(Calendar.SECOND, -1);

return calendar.getTime();

}else if(type==3) {

calendar.set(Calendar.YEAR, year);

calendar.set(Calendar.MONTH, month-1);

calendar.set(Calendar.DAY_OF_MONTH,day+1);

calendar.set(Calendar.HOUR_OF_DAY, 0);

calendar.set(Calendar.MINUTE, 0);

calendar.set(Calendar.SECOND, -1);

return calendar.getTime();

}else if(type==4||type==5) {

calendar.set(Calendar.YEAR, year);

calendar.set(Calendar.MONTH, month-1);

calendar.set(Calendar.DAY_OF_MONTH,day);

calendar.set(Calendar.HOUR_OF_DAY, hour+1);

calendar.set(Calendar.MINUTE, 0);

calendar.set(Calendar.SECOND, -1);

return calendar.getTime();

}

return null;

}

/**

 * 获取年的列表

 * @param year

 * @return

 */

private static List<Date> getListYear(int current,int year) {

List<Date>list=new ArrayList<Date>();

Calendar calendar=Calendar.getInstance();

Date cur=calendar.getTime();

for(;current>=year;year++) {

calendar.set(Calendar.YEAR, year);

calendar.set(Calendar.MONTH, 0);

calendar.set(Calendar.DAY_OF_MONTH, 1);

calendar.set(Calendar.HOUR_OF_DAY, 0);

calendar.set(Calendar.MINUTE, 0);

calendar.set(Calendar.SECOND, 0);

list.add(calendar.getTime());

}

list.add(cur);

return list;

}

/**

 * 获取月列表

 * @param currentYear

 * @param year

 * @param month

 * @return

 */

private static List<Date>getListMonth(int year){

List<Date>list=new ArrayList<Date>();

Calendar calendar=Calendar.getInstance();

Date current=calendar.getTime();

int currentYear=calendar.get(Calendar.YEAR);

int end=0;

if(year<calendar.get(Calendar.YEAR)) {

end=11;

}else {

end=calendar.get(Calendar.MONTH);

}

for(int i=0;i<=end;i++) {

calendar.set(Calendar.YEAR, year);

calendar.set(Calendar.MONTH, i);

calendar.set(Calendar.DAY_OF_MONTH, 1);

calendar.set(Calendar.HOUR_OF_DAY, 0);

calendar.set(Calendar.MINUTE, 0);

calendar.set(Calendar.SECOND, 0);

list.add(calendar.getTime());

}

if(year>=currentYear) {

list.add(current);

}else {

calendar.set(Calendar.YEAR, year+1);

calendar.set(Calendar.MONTH, 0);

calendar.set(Calendar.DAY_OF_MONTH, 1);

calendar.set(Calendar.HOUR_OF_DAY, 0);

calendar.set(Calendar.MINUTE, 0);

calendar.set(Calendar.SECOND, -1);

list.add(calendar.getTime());

}

return list;

}

/**

 * 获取某个月对应的日

 * @param year

 * @param month

 * @return

 */

private static List<Date>getListDay(int year,int month){

List<Date>list=new ArrayList<Date>();

Calendar calendar=Calendar.getInstance();

Date current=calendar.getTime();

int day=calendar.get(Calendar.DAY_OF_MONTH);

int end=0;

Date endDay=calendar.getTime();

if(year<calendar.get(Calendar.YEAR)||(year==calendar.get(Calendar.YEAR)&&(month-1)<calendar.get(Calendar.MONDAY))) {

//获取本月最后一天

calendar.set(Calendar.YEAR, year);

calendar.set(Calendar.MONTH, month);

calendar.set(Calendar.DAY_OF_MONTH, 1);

calendar.set(Calendar.HOUR_OF_DAY, 0);

calendar.set(Calendar.MINUTE, 0);

calendar.set(Calendar.SECOND, -1);

endDay=calendar.getTime();

end=calendar.get(Calendar.DAY_OF_MONTH);

}else if(year==calendar.get(Calendar.YEAR)&&(month-1)==calendar.get(Calendar.MONDAY)){

endDay=current;

end=day;

}

for(int i=1;i<=end;i++) {

calendar.set(Calendar.YEAR, year);

calendar.set(Calendar.MONTH, month-1);

calendar.set(Calendar.DAY_OF_MONTH, i);

calendar.set(Calendar.HOUR_OF_DAY, 0);

calendar.set(Calendar.MINUTE, 0);

calendar.set(Calendar.SECOND, 0);

list.add(calendar.getTime());

}

list.add(endDay);

return list;

}

/**

 * 获取当天的时间列表信息

 * @param year

 * @param month

 * @param day

 * @return

 */

private static List<Date>getListHour(int year,int month,int day){

List<Date>list=new ArrayList<Date>();

Calendar calendar=Calendar.getInstance();

Date current=calendar.getTime();

//是当天时间

int end=0;

boolean isCurrent=false;

if(year==calendar.get(Calendar.YEAR)&&(month-1)==calendar.get(Calendar.MONTH)&&day==calendar.get(Calendar.DAY_OF_MONTH)) {

//获取现在时间

end=calendar.get(Calendar.HOUR_OF_DAY);

isCurrent=true;

}else {

end=23;

}

for(int i=0;i<=end;i++) {

calendar.set(Calendar.YEAR, year);

calendar.set(Calendar.MONTH, month-1);

calendar.set(Calendar.DAY_OF_MONTH, day);

calendar.set(Calendar.HOUR_OF_DAY, i);

calendar.set(Calendar.MINUTE, 0);

calendar.set(Calendar.SECOND, 0);

list.add(calendar.getTime());

}

if(isCurrent) {

list.add(current);

}else {

calendar.set(Calendar.YEAR, year);

calendar.set(Calendar.MONTH, month-1);

calendar.set(Calendar.DAY_OF_MONTH, day+1);

calendar.set(Calendar.HOUR_OF_DAY, 0);

calendar.set(Calendar.MINUTE, 0);

calendar.set(Calendar.SECOND, -1);

list.add(calendar.getTime());

}

return list;

}

/**

 * 获取当天小时列表

 * @param year

 * @param month

 * @param day

 * @param hour

 * @return

 */

private static List<Date>getListMinut(int year,int month,int day,int hour){

List<Date>list=new ArrayList<Date>();

Calendar calendar=Calendar.getInstance();

Date current=calendar.getTime();

int minut=0;

if(year==calendar.get(Calendar.YEAR)&&(month-1)==calendar.get(Calendar.MONTH)&&day==calendar.get(Calendar.DAY_OF_MONTH)&&hour==calendar.get(Calendar.HOUR_OF_DAY)) {

minut=calendar.get(Calendar.MINUTE);

}else {

minut=60;

}

//每5分钟统计一次

for(int i=0;i<=minut;i+=5) {

calendar.set(Calendar.YEAR, year);

calendar.set(Calendar.MONTH, month-1);

calendar.set(Calendar.DAY_OF_MONTH, day);

calendar.set(Calendar.HOUR_OF_DAY,hour);

calendar.set(Calendar.MINUTE, i);

calendar.set(Calendar.SECOND, 0);

list.add(calendar.getTime());

}

return list;

}

 

}

 

/**

 * 统计系统接口的数量和基本信息

 * @author acer

 *

 */

@Controller

@RequestMapping("interfaceMethod")

public class InterfaceMethodController {

@Autowired

private InterfaceMethodService interfaceMethodService;

@RequestMapping("/listMethod")

@ResponseBody

@InterfaceDescript("获取系统中的调用接口信息")

public Map<String,Object> listMethod(Integer page,Integer rows) {

if(page==null||page<=0) {

page=1;

}

if(rows==null||rows<=0) {

rows=10;

}

Map<String,Object>result=new HashMap<String,Object>();

try {

result=interfaceMethodService.listInterfaceMethod(page, rows);

}catch(Exception e) {

result.put("errCode", 101);

result.put("errMsg", "查询失败");

}

return result;

}

}

 

 

@Service

public class InterfaceMethodService {

@Autowired

private InterfaceMethodMapper interfaceMethodMapper;

/**

 * 查询接口信息

 * @return

 */

public Map<String,Object> listInterfaceMethod(Integer page,Integer rows){

Map<String,Object>result=new HashMap<String,Object>();

PageHelper.startPage(page, rows);

List<InterfaceMethod> list=interfaceMethodMapper.selectByExample(null);

PageInfo<InterfaceMethod> pageinfo=new PageInfo<>(list);

result.put("data", pageinfo.getList());

result.put("total", pageinfo.getTotal());

result.put("errCode", 0);

result.put("errMsg", "查询成功");

return result;

}

}

3.2数据表

CREATE TABLE `interface_counter` (

  `id` int(11) NOT NULL AUTO_INCREMENT,

  `path` varchar(200) CHARACTER SET utf8 NOT NULL COMMENT '请求的url',

  `status` int(11) DEFAULT '-1' COMMENT '请求状态码: 默认值为-1,0成功,大于0失败',

  `message` varchar(100) CHARACTER SET utf8 DEFAULT NULL COMMENT '响应信息',

  `create_time` datetime DEFAULT NULL COMMENT '请求时间',

  `param` varchar(255) CHARACTER SET utf8 DEFAULT NULL COMMENT '请求参数',

  `app_id` varchar(100) CHARACTER SET utf8 DEFAULT NULL COMMENT 'app的标识',

  PRIMARY KEY (`id`)

) ENGINE=InnoDB AUTO_INCREMENT=990 DEFAULT CHARSET=utf8mb4;

 

其中controller中使用了自定义注解,主要是为了获取接口功能信息。

 

3.3html页面的展示

 

<!DOCTYPE html>

<html>

<head>

<meta charset="UTF-8">

<title>系统接口信息</title>

<link href="/css/bootstrap.min.css" rel="stylesheet" /> 

<link href="/js/bootstrap-datetimepicker/css/bootstrap-datetimepicker.css" rel="stylesheet" media="screen">

<link href="/js/bootstrap-datetimepicker/css/bootstrap-datetimepicker.min.css" rel="stylesheet" media="screen">

</head>

<style>

.chart{

width:1000px;

height:500px;

}

</style>

<script type="text/javascript" src="/js/jquery-1.7.2.min.js"></script>

<script type="text/javascript" src="/js/echarts.min.js"></script>

<script type="text/javascript" src="js/bootstrap.min.js"></script>

<script type="text/javascript" src="/js/bootstrap-datetimepicker/js/locales/bootstrap-datetimepicker.zh-CN.js" charset="UTF-8"></script>

<script type="text/javascript" src="/js/bootstrap-datetimepicker/js/bootstrap-datetimepicker.min.js" charset="UTF-8"></script>

<body>

<div class="container">

<div class="row">

<div class="col-md-12" style="height:60px;"></div>

<div class="form-group col-md-5">

<label for="type" class="col-sm-3 control-label">视图方式:</label>

<div class="col-sm-6">

<select id="type" class="form-control">

<option value="5">分钟</option>

<option value="4">小时</option>

<option value="3" selected >日</option>

<option value="2">月</option>

<option value="1">年</option>

</select>

</div>

</div>

<div class="form-group col-md-5">

                <label for="dtp_input1" class="col-md-3 control-label">时间:</label>

                <div class="input-group date form_datetime col-md-6"  data-date-format="yyyy/MM/dd - hh:ii" data-link-field="dtp_input1">

                    <input id="dates" class="form-control" size="16" type="text" value="" readonly>

                    <span class="input-group-addon"><span class="glyphicon glyphicon-remove"></span></span>

<span class="input-group-addon"><span class="glyphicon glyphicon-th"></span></span>

                </div>

<input type="hidden" id="dtp_input1" value="" /><br/>

            </div>

            <div class="form-group col-md-2">

            <button class="btn btn-success" id="search">

            <span class="glyphicon glyphicon-search"></span>查询

        </button>

            </div>

            

<div class="col-lg-12">

<div id="chart" class="chart">

</div>

</div>

<div class="col-lg-12">

<div id="chart2" class="chart">

</div>

</div>

</div>

</div>

</body>

 

<script>

//对控件汉化

$.fn.datetimepicker.dates['zh-CN'] = {

days: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"],

daysShort: ["周日", "周一", "周二", "周三", "周四", "周五", "周六", "周日"],

daysMin:  ["日", "一", "二", "三", "四", "五", "六", "日"],

months: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"],

monthsShort: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"],

today: "今天",

suffix: [],

meridiem: ["上午", "下午"]

};

 

var pic=$('.form_datetime').datetimepicker({

format: "yyyy-mm-dd hh:ii",

autoclose: true,

todayBtn: true,

language:'zh-CN',

pickerPosition:"bottom-left"

});

$(function(){

var chart=$("#chart").get(0);

    var myChart=echarts.init(chart);

    //异步加载数据

    var date1=new Date();

    $('.form_datetime').data("datetimepicker").setDate(date1);

    var year1=date1.getYear()+1900;

    var month1=date1.getMonth()+1;

    var day1=date1.getDate();

    var hours1=date1.getHours();

    loadData(myChart,3,year1,month1,day1,hours1);

    

    var chart2=$("#chart2").get(0);

    var myChart2=echarts.init(chart2);

    loadMethodsData(myChart2,3,year1,month1,day1,hours1);

    /*

    $("#type").change(function(){

    var type=$(this).val();

    var  date=$('.form_datetime').data("datetimepicker").getDate();

    var year=date.getYear()+1900;

    var month=date.getMonth()+1;

    var day=date.getDate();

    var hours=date.getHours();

    loadData(myChart,type,year,month,day,hours);

    loadMethodsData(myChart2,type,year1,month1,day1,hours1);

    //用户获取时间数据

    });

    */

    

    $("#search").click(function(){

    var type=$("#type").val();

    var  date=new Date($("#dates").val());

    var year=date.getYear()+1900;

    var month=date.getMonth()+1;

    var day=date.getDate();

    var hours=date.getHours();

    loadData(myChart,type,year,month,day,hours);

    loadMethodsData(myChart2,type,year1,month1,day1,hours1);

    });

});

 

function loadMethodsData(myChart,type,year,month,day,hours){

var title="";

if(type==1){

title="年请求量"

}else if(type==2){

title="月请求量"

}else if(type==3){

title="日请求量"

}else if(type==4){

title="时请求量"

}

$.ajax({

url:'/interfaceCounter/counterMathods.do',

data:{'appId':10010,'type':type,'year':year,'month':month,'day':day,'hour':hours},

dataType:'json',

type:'post',

success:function(data){

if(data.errCode==0){

var dataX=data.dataX;

var dataY=data.dataY;

var option = {

dataZoom: {

        orient: 'vertical',      // 布局方式,默认为水平布局,可选为:

        backgroundColor: 'rgba(0,0,0,0)',       // 背景颜色

        dataBackgroundColor: '#eee',            // 数据背景颜色

        fillerColor: 'rgba(144,197,237,0.2)',   // 填充颜色

        handleColor: 'rgba(70,130,180,0.8)'     // 手柄颜色

    },

    title : {

        text: title

    },

    tooltip : {

        trigger: 'axis'

    },

    legend: {

        data:['接口请求']

    },

    toolbox: {

        show : true,

        feature : {

            mark : {show: true},

            dataView : {show: true, readOnly: false},

            magicType: {show: true, type: ['line', 'bar']},

            restore : {show: true},

            saveAsImage : {show: true}

        }

    },

    calculable : true,

    xAxis : [

        {

            type : 'value',

            boundaryGap : [0, 0.01]

        }

    ],

    yAxis : [

        {

            type : 'category',

            data : dataY

        }

    ],

    series : [

        {

            name:'接口请求',

            type:'bar',

            data:dataX

        }

    ]

};

myChart.setOption(option);

}

}

});

}

function loadData(myChart,type,year,month,day,hours){

//设置x周轴单位

var wei="hello";

if(type==1){

wei="年";

}else if(type==2){

wei="月";

}else if(type==3){

wei="日";

}else if(type==4){

wei="时";

}else if(type==5){

wei="分";

}

$.ajax({

url:'/interfaceCounter/counterInterface.do',

data:{'appId':10010,'type':type,'year':year,'month':month,'day':day,'hour':hours},

dataType:'json',

type:'post',

success:function(data){

  var chartDate=[];

  //var weight=[55.4,55.9,53];

  var weight=[];

  var weight2=[];

  var weight3=[];

  //对echarts做基础配置

  if(data.errCode==0){

  chartDate=data.dataX;

  weight=data.dataY;

  weight2=data.sdataY;

  for(var i=0;i<weight.length;i++){

  var temp=weight[i]-weight2[i];

  weight3.push(temp);

  }

  }

  

  option={

  toolbox: {

        show : true,

        feature : {

            mark : {show: true},

            dataView : {show: true, readOnly: false},

            magicType : {show: true, type: ['line', 'bar']},

            restore : {show: true},

            saveAsImage : {show: true}

        }

  },

  calculable : true,

  title:{

 text: '请求量走势图'

  },

  legend: {

        data:['总请求量','成功请求','失败请求']

   },

  tooltip : {

                          trigger: 'axis',

                      },

  label:{

    normal:{

        show:true,

        position:[0,-20],

        textStyle:{

            color:"#1daffa"

        }

  }

  },

  /*

  lineStyle:{

            normal:{

                color: 'rgba(29, 175, 250,1)',

            }

      },

      

      itemStyle:{

            normal:{

                color: 'rgba(29, 175, 250, 0.8)',

            }

      },

      */

  xAxis:[

  {

            type : 'category',

            boundaryGap : false,

            data :chartDate,

            axisLabel : {

                        formatter: '{value}'+wei

                    }

        }

  ],

        yAxis:{}

      ,

         series:[

            {

                name:"总请求量",

                type:"line",//折线图,还有pie(饼状图),

                data:weight,

                itemStyle:{

            normal:{

                color: 'rgba(255,0,0, 0.8)',

            }

      }

            },

            {

                name:"成功请求",

                type:"line",//折线图,还有pie(饼状图),

                data:weight2,

                itemStyle:{

            normal:{

                color: 'rgba(0,0,255, 0.8)',

            }

      }

            },

            {

                name:"失败请求",

                type:"line",//折线图,还有pie(饼状图),

                data:weight3,

                itemStyle:{

            normal:{

                color: 'rgba(0, 255, 0, 0.8)',

            }

      }

            }

        ]

    }

    myChart.setOption(option);

}

});

}

</script>

</html>

 

 

 

 

<!DOCTYPE html>

<html>

<head>

<meta charset="UTF-8">

<title>系统接口详细信息</title>

<link href="/css/bootstrap.min.css" rel="stylesheet" /> 

<link href="/js/bootstrap-datetimepicker/css/bootstrap-datetimepicker.css" rel="stylesheet" media="screen">

<link href="/js/bootstrap-datetimepicker/css/bootstrap-datetimepicker.min.css" rel="stylesheet" media="screen">

 

</head>

<body>

<div class="container">

<div class="row">

<div class="col-md-12" style="height:20px;"></div>

<div class="col-lg-12">

<div>

<div class="form-group col-md-5">

                <label for="dtp_input1" class="col-md-4 control-label">开始时间:</label>

                <div class="input-group date form_datetime col-md-8"  data-date-format="yyyy/MM/dd - hh:ii" data-link-field="dtp_input1">

                    <input class="form-control" id="startTime" size="16" type="text" value="" readonly>

                    <span class="input-group-addon"><span class="glyphicon glyphicon-remove"></span></span>

<span class="input-group-addon"><span class="glyphicon glyphicon-th"></span></span>

                </div>

            </div>

            <div class="form-group col-md-5">

                <label for="dtp_input2" class="col-md-4 control-label">结束时间:</label>

                <div class="input-group date form_datetime col-md-8"  data-date-format="yyyy/MM/dd - hh:ii" data-link-field="dtp_input1">

                    <input class="form-control" id="endTime" size="16" type="text" value="" readonly>

                    <span class="input-group-addon"><span class="glyphicon glyphicon-remove"></span></span>

<span class="input-group-addon"><span class="glyphicon glyphicon-th"></span></span>

                </div>

            </div>

            <div class="form-group col-md-2">

            <button class="btn btn-success" id="search">

            <span class="glyphicon glyphicon-search"></span>查询

            </button>

            <button class="btn btn-warning" id="reset">

            <span class="glyphicon glyphicon-remove-sign"></span>重置

            </button>

            </div>

</div>

<div>

<table id="table" class="table table-bordered">

<thead  align="center">

<tr>

<td>编号</td>

<td>请求路径</td>

<td>请求日期</td>

<td>状态码</td>

<td>返回信息(message)</td>

<td>请求参数</td>

</tr>

</thead>

<tbody align="center">

</tbody>

</table>

</div>

<div>

<ul class="pagination">

    <li><a class="first" href="#">«上一页</a></li>

    <li><a value="0" class="index disabled" href="#">1</a></li>

     <li><a class="total disabled" href="#">总数:</a></li>

    <li><a class="next" href="#">下一页»</a></li>

</ul>

</div>

</div>

</div>

</div>

</body>

<script type="text/javascript" src="/js/jquery-1.7.2.min.js"></script>

<script type="text/javascript" src="js/bootstrap.min.js"></script>

<script type="text/javascript" src="/js/bootstrap-datetimepicker/js/locales/bootstrap-datetimepicker.zh-CN.js" charset="UTF-8"></script>

<script type="text/javascript" src="/js/bootstrap-datetimepicker/js/bootstrap-datetimepicker.min.js" charset="UTF-8"></script>

<script>

$.fn.datetimepicker.dates['zh-CN'] = {

days: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"],

daysShort: ["周日", "周一", "周二", "周三", "周四", "周五", "周六", "周日"],

daysMin:  ["日", "一", "二", "三", "四", "五", "六", "日"],

months: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"],

monthsShort: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"],

today: "今天",

suffix: [],

meridiem: ["上午", "下午"]

};

$('.form_datetime').datetimepicker({

format: "yyyy-mm-dd hh:ii",

autoclose: true,

todayBtn: true,

language:'zh-CN',

pickerPosition:"bottom-left"

});

 

 

$(function(){

$("#search").click(function(){

var startTime=$("#startTime").val();

var endTime=$("#endTime").val();

$(".pagination .index").attr("value",0)

$(".pagination .index").html("当前"+0+"页");

createTable({'page':1,'rows':10,'start':startTime,'end':endTime},1);

});

$("#reset").click(function(){

$("#startTime").val("");

$("#endTime").val("");

});

$(".pagination .first").click(function(){

var index=parseInt($(".pagination .index").attr("value"))-1;

if(index>0){

var startTime=$("#startTime").val();

var endTime=$("#endTime").val();

createTable({'page':index,'rows':10,'start':startTime,'end':endTime},2);

}

});

$(".pagination .next").click(function(){

var index=parseInt($(".pagination .index").attr("value"))+1;

var tatal=parseInt($(".pagination .total").attr("value"));

if((index-1)*10<tatal){

var startTime=$("#startTime").val();

var endTime=$("#endTime").val();

createTable({'page':index,'rows':10,'start':startTime,'end':endTime},1);

}

});

createTable({'page':1,'rows':10},1);

});

function createTable(datas,type){

$.ajax({

url:'/interfaceCounter/mathodsDetail.do',

data:datas,

dataType:'json',

type:'post',

success:function(data){

if(data.errCode!=0){

$("#table tbody").html("<tr><td colspan='5' align='center'>无法获取数据</td></tr>");

}else{

var table="";

var index="";

if(type==1){

index=parseInt($(".pagination .index").attr("value"))+1;

}else{

index=parseInt($(".pagination .index").attr("value"))-1;

}

$(".pagination .index").attr("value",index)

$(".pagination .index").html("当前"+index+"页");

$(".pagination .total").html("共"+data.tatal+"条");

$(".pagination .total").attr("value",data.tatal);

for(var i=0;i<data.data.length;i++){

var item=data.data[i];

table+="<tr>";

table+="<td>"+item.id+"</td>";

table+="<td>"+item.path+"</td>";

table+="<td>"+item.createTime+"</td>";

table+="<td>"+item.status+"</td>";

table+="<td>"+item.message+"</td>";

table+="<td>"+item.param+"</td>";

table+="</tr>";

}

$("#table tbody").html(table);

}

}

});

}

</script>

</html>

 

 

 

<!DOCTYPE html>

<html>

<head>

<meta charset="UTF-8">

<title>系统接口信息</title>

<link href="/css/bootstrap.min.css" rel="stylesheet" /> 

</head>

<body>

<div class="container">

<div class="row">

<div class="col-md-12" style="height:20px;"></div>

<div class="col-lg-12">

<div>

<table id="table" class="table table-bordered">

<thead align="center">

<tr>

<td>编号</td>

<td>接口描述</td>

<td>请求路径</td>

<td>类名</td>

<td>方法名</td>

</tr>

</thead>

<tbody align="center">

</tbody>

</table>

</div>

</div>

</div>

</div>

</body>

<script type="text/javascript" src="/js/jquery-1.7.2.min.js"></script>

<script>

$(function(){

$.ajax({

url:'/interfaceMethod/listMethod.do',

data:{'page':1,'rows':1000},

dataType:'json',

type:'post',

success:function(data){

if(data.errCode!=0){

$("#table tbody").html("<tr><td colspan='5' align='center'>无法获取数据</td></tr>");

}else{

var $tbody=$("#table tbody");

var items=data.data;

var str="";

var index=0;

for(var i=0;i<items.length;i++){

index++;

var item=items[i];

str+="<tr>";

str+="<td>"+index+"</td>";

str+="<td>"+item.rmark+"</td>";

str+="<td>"+item.path+"</td>";

str+="<td>"+item.className+"</td>";

str+="<td>"+item.methodName+"</td>";

str+="</tr>";

}

$tbody.html(str);

}

}

});

});

</script>

</html>

 

3.4效果展示图

 

 

 

统计分析 01-统计分析功能(生成统计数据) 一、数据库设计 1、数据库 guli_statistics 2、数据表 guli_statistics.sql 二、创建微服务 1、在service模块下创建子模块 service_statistics 2、application.properties resources目录下创建文件 # 服务端口 server.port=8008 # 服务名 sprin... 阅读详情

相关推荐

HarmonyOS 实战开发 —— 基于Napi调用ArkTS/系统接口

场景二: 系统仅提供了ArkTS 异步接口,未提供对应的NDK接口,当伙伴使用C++ 代码实现业务逻辑时,部分系统能力需要依赖系统ArkTS 异步接口;系统仅提供了ArkTS 异步接口,未提供对应的NDK接口,当伙伴使用C++ 代码实现业务逻辑时,部分系统能力需要依赖系统ArkTS 异步接口;场景一:系统提供了ArkTS 接口,但未提供对应的NDK接口,当伙伴使用C++ 代码实现业务逻辑时,部分系统能力需要依赖系统ArkTS接口;通过创建线程安全函数的方式 调用系统的异步接口。例如: 获取设备的屏幕宽高。

CTrup的博客 795

java接口限流及调用统计

基于AOP和Redis实现对接口调用情况的接口及IP限流 需求描述 项目中有许多接口,现在我们需要实现一个功能对接口调用情况进行统计,主要功能如下: 需求一:实现对每个接口,每天的调用次数做记录; 需求二:如果某次调用抛出了异常信息,则记录下异常信息; 需求三:限流,限制单个IP一天内对一个接口的调用次数。 概要设计 因为需要对每个接口的调用情况进行统计,所以选择AOP来实现,将Controller层抽象为一个切面 @Before 执行业务操作前进行限流判断; @AfterReturn 如果正常返回则调用次

aw与你共同进步 5806

Napi助你轻松调用ArkTS系统接口

步骤四:获取方法并调用demo示例(获取/ets/common/ObjectUtil 的isNull方法,并调用isNull方法判断一个对象是否为null)系统仅提供了ArkTS 异步接口,未提供对应的NDK接口,当伙伴使用C++ 代码实现业务逻辑时,部分系统能力需要依赖系统ArkTS 异步接口;伙伴在 ArkTS/TS 侧已定义接口,当伙伴使用C++ 代码实现业务逻辑时,想直接使用已有的TS 接口;app应用在native侧调用 系统库/arkts模块的方法。例如: 获取设备的屏幕宽高。

CTrup的博客 865

SpringBoot统计接口被调用次数及限制调用

* 1、监控单个接口一天内的调用次数 * 2、如果抛出异常,则记录异常信息及发生时间 * 3、对单个IP进行限流,每天对每个接口的调用次数有限

曹俊的博客 4684

鸿蒙5.0实战案例:基于Napi调用ArkTS/系统接口

场景二: 系统仅提供了ArkTS 异步接口,未提供对应的NDK接口,当伙伴使用C++ 代码实现业务逻辑时,部分系统能力需要依赖系统ArkTS 异步接口;系统仅提供了ArkTS 异步接口,未提供对应的NDK接口,当伙伴使用C++ 代码实现业务逻辑时,部分系统能力需要依赖系统ArkTS 异步接口;场景一:系统提供了ArkTS 接口,但未提供对应的NDK接口,当伙伴使用C++ 代码实现业务逻辑时,部分系统能力需要依赖系统ArkTS接口;通过创建线程安全函数的方式 调用系统的异步接口。例如: 获取设备的屏幕宽高。

Maniu_666的博客 901

【鸿蒙实战开发】基于Napi调用ArkTS/系统接口

场景二: 系统仅提供了ArkTS 异步接口,未提供对应的NDK接口,当伙伴使用C++ 代码实现业务逻辑时,部分系统能力需要依赖系统ArkTS 异步接口;**场景一:**系统提供了ArkTS 接口,但未提供对应的NDK接口,当伙伴使用C++ 代码实现业务逻辑时,部分系统能力需要依赖系统ArkTS接口;场景一:系统提供了ArkTS 接口,但未提供对应的NDK接口,当伙伴使用C++ 代码实现业务逻辑时,部分系统能力需要依赖系统ArkTS接口;通过创建线程安全函数的方式 调用系统的异步接口。

CTrup的博客 816

API项目6:接口调用次数的统计 && 网关

本项目是一个面向开发者的 API 平台,提供 API 接口供开发者调用。用户通过注册登录,可以开通接口调用权限,并可以浏览和调用接口。每次调用都会进行统计,用户可以根据统计数据进行分析和优化。管理员可以发布接口、下线接口、接入接口,并可视化接口的调用情况和数据。本项目侧重于后端,涉及多种编程技巧和架构设计层面的知识。后端项目现在有 backend、interface,因为平台可以接入任何人开发的接口,不一定是同一个团队或者公司内部的项目。

Gus10124的博客 3488

鸿蒙(HarmonyOS)实战开发篇——基于Napi调用ArkTS/系统接口

场景二: 系统仅提供了ArkTS 异步接口,未提供对应的NDK接口,当伙伴使用C++ 代码实现业务逻辑时,部分系统能力需要依赖系统ArkTS 异步接口;系统仅提供了ArkTS 异步接口,未提供对应的NDK接口,当伙伴使用C++ 代码实现业务逻辑时,部分系统能力需要依赖系统ArkTS 异步接口;场景一:系统提供了ArkTS 接口,但未提供对应的NDK接口,当伙伴使用C++ 代码实现业务逻辑时,部分系统能力需要依赖系统ArkTS接口;通过创建线程安全函数的方式 调用系统的异步接口。例如: 获取设备的屏幕宽高。

lc748258的博客 1009

外部接口和内部接口区别

外部接口 对外部提供的接口(提供给第三方使用的接口) 例如:我们自己开发系统调用调用微信或支付宝支付接口,微信或支付宝供我们调用的接口为外部接口,外部接口需要,覆盖各种正常和异常情况。 内部接口 对自身系统提供的接口(供系统内部调用的接口) 一般为了尽早的介入测试,所以一般接口开发完成测试人员就可以进行内部接口测试。一般大公司可能要求比较高,需要考虑安全问题,那么内部接口测试也是一定要测的。对于小...

u013131716的博客 4万+

深度探讨:什么是API接口?API接口的类型,如何调用API接口?

API(Application Programming Interface,应用程序编程接口)是不同软件系统之间的 “桥梁”,它定义了一套规则和格式,让两个系统能标准化地交换数据或调用功能。简单说,就像餐厅的 “点餐系统”:顾客 - 顾客(调用方)不需要菜单(API 文档)选择菜品(功能);服务员(API 接口)把需求传给后厨(服务端);后厨做好后,服务员再把菜(返回数据)端给顾客。比如:当你用打车宝查快递时,支付宝就是通过快递公司的 API 接口获取物流信息的。

yuweide19761218的博客 1250

SpringBoot开发——统计接口调用耗时的几种方法

SpringBoot开发——统计接口调用耗时的几种方法

bjzhang75的博客 2488

restTemplate.postForObject调用接口,接口入参是@RequestParam的方案及其他使用

最近在调用其他系统的接口时,遇到的问题。由于接口文档给的写的模糊不清,导致接口一直没有调用通。后来索性要了代码权限自己看他写的接口。 一、接口入参是@RequestParam的post请求 1、接口方接口: 2、请求代码: header设置contentType为application/x-www-form-urlencoded 等同于URL后直接拼接参数。 //header HttpHeaders header = new HttpHeaders(); header.setContentT

ILoveController的博客 3250

arcgis插件 批量出图 按地块批量出图工具(动态文本、动态节点坐标表)

arcgis插件 批量出图 按地块批量出图工具。

坐听风雨 7067

鸿蒙开发5.0案例分析:基于Napi调用ArkTS/系统接口

系统仅提供了ArkTS 异步接口,未提供对应的NDK接口,当伙伴使用C++ 代码实现业务逻辑时,部分系统能力需要依赖系统ArkTS 异步接口;系统仅提供了ArkTS 异步接口,未提供对应的NDK接口,当伙伴使用C++ 代码实现业务逻辑时,部分系统能力需要依赖系统ArkTS 异步接口;:系统提供了ArkTS 接口,但未提供对应的NDK接口,当伙伴使用C++ 代码实现业务逻辑时,部分系统能力需要依赖系统ArkTS接口;通过创建线程安全函数的方式 调用系统的异步接口。例如: 获取设备的屏幕宽高。

2501_91726121的博客 932

RouterOS v7.6实战:5分钟搞定IPv6双栈配置(含光猫桥接避坑指南)

本文详细解析了在RouterOS v7.6系统中配置IPv6双栈网络的实战步骤,特别针对家庭网络光猫桥接模式下的常见问题提供了避坑指南。文章涵盖了从基础配置到企业级部署的进阶内容,包括IPv6前缀获取、DHCPv6客户端设置、路由通告以及安全优化,帮助用户充分利用v7.6版本在IPv6支持上的改进与优势。

embedding5hiker的博客 1282

别再写爬虫了!用Trae平台5分钟搞定一个能聊天的网页数据抓取Agent

本文介绍了如何利用Trae平台快速构建无需编程的网页数据抓取智能体,实现对话式数据采集。通过自然语言指令,用户可在5分钟内完成从数据抓取到分析的全流程,显著降低技术门槛和维护成本。文章详细解析了Trae智能体的核心能力,包括自然语言理解、智能页面解析、数据清洗和可视化配置,并提供了多个实战案例,展示其在竞品监控和舆情分析中的高效应用。

weixin_29038303的博客 717

SpringBoot中调用第三方接口的三种方式

使用SpringBoot跨系统调用接口的方案 一、简介 项目开发中存在系统之间互调问题,又不想用dubbo,这里提供几种SpringBoot方案: 1、使用Feign进行消费(推荐) 2、使用原始httpClient请求 3、使用RestTemplate方法 二、方案 方案一:使用Feign进行消费(推荐) 1、在maven中添加依赖 <dependency> <groupId>org.springframework.cloud</groupId> <artif

qq_43842093的博客 1万+

STM32G030C8T6 ADC+DMA实战:同时采集外部电压和芯片温度的完整代码流程

本文详细介绍了STM32G030C8T6芯片通过ADC+DMA实现外部电压和芯片内部温度采集的完整流程。从硬件设计到CubeMX配置,再到驱动模块化实现与系统优化,提供了高精度多通道采集系统的实战指南,特别适合嵌入式开发者在工业控制和环境监测项目中应用。

weixin_42653691的博客 467

prometheus自定义监控:监控接口的实时调用情况

当公共平台,我们需要实时监控各个业务系统对公共平台的调用情况时,可以在代码中自定义prometheus监控指标。 如下图中,维度使用tags来标记,我定义了两个维度,一个是关联系统的id: syskey 。一个是访问的表 tablename。 我需要按关联系统对每个表的调用情况统计: private void addCountMetric(String tableName) { Optional<String> opt = SecurityUtils.getCurr..

ankora的博客 9602

【深度解析TI InstaSPIN】无传感器FOC控制的核心方案

TI InstaSPIN通过“自动识别+通用观测器+开源工具链”的组合,解决了无传感器FOC控制开发难度大、周期长的核心痛点。其核心优势在于快速统一软件观测器的通用性与高精度,以及Fly Start闭环零速启动策略的高可靠性,可适配空调压缩机等家电电机的复杂运行需求。在空调外机双电机控制场景中,InstaSPIN与TI C2000系列MCU(F280013x/F28E12x)完美契合,借助MCU的高性能DSP内核与丰富外设,可实现双电机的精准同步控制,同时降低硬件成本与开发周期。

昔时扬尘处 323

YOLOv11架构革新:华为VanillaNet与BiFPN深度融合的突破性改进实战指南

购买即可解锁300+YOLO优化文章,并且还有海量深度学习复现项目,价格仅需两杯奶茶的钱,别人有的本专栏也有!

LXY_LOVE36的博客 136

OOP 中的 方法调用、接口、鸭式辩型、访问者模式

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_33730836的博客 202

修改legged_gym地形后仍然是平地,如何解决?

🏆 本文收录于《全栈Bug调优(实战版)》专栏,致力于分享我在项目实战过程中遇到的各类Bug及其原因,并提供切实有效的解决方案。无论你是初学者还是经验丰富的开发者,本文将为你指引出一条更高效的Bug修复之路,助你早日登顶,迈向财富自由的梦想🚀!同时,欢迎大家关注、收藏、订阅本专栏,更多精彩内容正在持续更新中。让我们一起进步,Up!Up!Up!    备注: 部分问题/难题源自互联网,经过精心筛选和整理,结合数位十多年大厂实战经验资深大佬经验总结所得,数条可行方案供所需之人参考。

**My Coding Family** 1563

Dubbo泛化调用:无需接口定义直接调用远程服务

在分布式系统开发中,服务调用通常依赖于接口定义文件的共享。当面临跨语言调用、接口频繁变更或动态服务编排等场景时,传统方式会带来接口版本管理复杂、开发效率低下等问题。Dubbo提供的泛化调用(Generic Invocation)机制打破了这一限制,允许消费者在不依赖服务接口类的情况下直接调用远程服务。本文将深入解析泛化调用的实现原理、使用场景及最佳实践,帮助开发者构建更灵活的分布式服务架构。 #...

gitblog_00129的博客 808

习题加餐4. 冒险者工会

这个问题通过贪心算法解决,展示了贪心算法在解决一些优化问题中的强大能力。关键在于如何识别贪心选择的性质,并通过数学证明确保这种方法能够达到全局最优解。通过对问题的深入理解和逻辑推理,我们可以设计出有效的算法来解决问题,同时也需要注意代码实现中的细节,如排序和双指针的使用,以确保算法的效率和正确性。贪心算法的应用这个问题展示了贪心算法在实际问题中的应用。贪心算法通过在每一步选择当前最优的解决方案,期望最终达到全局最优解。该问题中,每次选择能力最接近但足以完成最难任务的冒险者,就是贪心选择的体现。

tang7mj的博客 1349

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

上一篇: websocket消息推送
下一篇: 基于Java的阿里妈妈数据抓取技术
telrob
博客等级 码龄9年 20粉丝 15原创
评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值