1. 程式人生 > >統計Controller、Service、Repository消耗時間

統計Controller、Service、Repository消耗時間

1、監控

package com.cloud.config;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;

/**   
 * @Title: UserMonitor.java 
 * @ProjectName com.spring.pro.thread.pool
 * @Description:  
 * @author ybwei   
 * @date 2018年12月11日 上午10:44:36    
 */
@Aspect
@Component
public class MonitorAop {

    private static Logger logger = LoggerFactory.getLogger(MonitorAop.class);
    private ThreadLocal<StopWatch> t=new ThreadLocal<StopWatch>();

    @Pointcut("execution(* com.cloud.controller..*.*(..))")
    public void controllerPerformance() {
    }

    @Pointcut("execution(* com.cloud.serviceImpl..*.*(..))")
    public void servicePerformance() {
    }

    @Pointcut("execution(* com.cloud.mapper..*.*(..))")
    public void repositoryPerformance() {
    }

    @Before("controllerPerformance()")
    public void startWatch() {
    	StopWatch stopWatch = new StopWatch("controller");
    	t.set(stopWatch);
    }

    @After("controllerPerformance()")
    public void endWatch() {
        logger.info(t.get().prettyPrint());
        t.remove();;
    }

    @Around("servicePerformance() || repositoryPerformance() ")
    public Object watchPerformance(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("---------------------------");
        try {
            //如果是一層一層的,這裡只能統計到到下一層需要的時間,因為返回值後沒有統計,也就是說只能統計平行的呼叫
            if (t.get().isRunning()){
                t.get().stop();
            }
            t.get().start(joinPoint.getSignature().toString());
        } catch (IllegalStateException e) {
            logger.error("watch start error:",e);
        }

        Object proceed = joinPoint.proceed();

        try {
            if (t.get().isRunning()){
                t.get().stop();
            }
        } catch (IllegalStateException e) {
            logger.error("watch end error:",e);
        }

        return proceed;

    }

}

2、controller

import javax.annotation.Resource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.cloud.entity.User;
import com.cloud.service.UserService;

/**
 * @ClassName: UserController 
 * @Description:  
 * @author ybw 
 * @date 2017年4月14日 下午3:24:50 
 */
@RestController
@RequestMapping("/user")
public class UserController {

	private Logger logger=LoggerFactory.getLogger(getClass());
	@Resource(name="userService")
	private UserService userService=null;
	
	@GetMapping("/getUser")
	public User getUser(int id){
		User user=userService.getUser(id);
		logger.info("user:{}",user);
		return user;
	}
	
	
	/**
	 * @param id
	 * @return
	 * @author weiyb 
	 */
	@GetMapping("/getUser2")
	public ResponseEntity<User> getUser2(int id){
		User user=userService.getUser2(id);
		logger.info("user:{}",user);
		return new ResponseEntity<User>(user, HttpStatus.OK);
	}
}

3、service

package com.cloud.service;

import com.cloud.entity.User;

/**
 * @ClassName: UserService 
 * @Description:  
 * @author ybw 
 * @date 2017年4月10日 下午1:43:08 
 */
public interface UserService {

	/**
	 * @param id
	 * @return
	 */
	public User getUser(int id);

	public User getUser2(int id);
}

 

package com.cloud.serviceImpl;

import java.util.Random;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import com.cloud.entity.User;
import com.cloud.mapper.UserMapper;
import com.cloud.service.UserService;

/**
 * @ClassName: UserServiceImpl
 * @Description:
 * @author weiyb
 * @date 2017年6月19日 下午5:03:11
 */
@Component("userService")
public class UserServiceImpl implements UserService {

	@Autowired
	private UserMapper userMapper;

	@Override
	@Transactional
	public User getUser(int id) {
		User user = new User();
		user.setName("張三");
		user.setAge(12);
		userMapper.insert(user);
		return userMapper.selectByPrimaryKey(id);
	}

	@Override
	public User getUser2(int id) {
		Random r = new Random();
		User user = userMapper.selectByPrimaryKey(id);
		user.setName("張三2" + r.nextInt());
		user.setAge(r.nextInt());
		userMapper.updateByPrimaryKeySelective(user);
		return userMapper.selectByPrimaryKey(id);
	}

}

4、repository

package com.cloud.mapper;

import java.util.List;

import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.DeleteProvider;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.InsertProvider;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.SelectProvider;
import org.apache.ibatis.annotations.Update;
import org.apache.ibatis.annotations.UpdateProvider;
import org.apache.ibatis.type.JdbcType;

import com.cloud.entity.User;
import com.cloud.entity.UserExample;

public interface UserMapper {
    /**
     *
     * @mbg.generated 2018-12-11
     */
    @SelectProvider(type=UserSqlProvider.class, method="countByExample")
    long countByExample(UserExample example);

    /**
     *
     * @mbg.generated 2018-12-11
     */
    @DeleteProvider(type=UserSqlProvider.class, method="deleteByExample")
    int deleteByExample(UserExample example);

    /**
     *
     * @mbg.generated 2018-12-11
     */
    @Delete({
        "delete from user",
        "where id = #{id,jdbcType=INTEGER}"
    })
    int deleteByPrimaryKey(Integer id);

    /**
     *
     * @mbg.generated 2018-12-11
     */
    @Insert({
        "insert into user (name, age)",
        "values (#{name,jdbcType=VARCHAR}, #{age,jdbcType=INTEGER})"
    })
    @Options(useGeneratedKeys=true,keyProperty="id")
    int insert(User record);

    /**
     *
     * @mbg.generated 2018-12-11
     */
    @InsertProvider(type=UserSqlProvider.class, method="insertSelective")
    @Options(useGeneratedKeys=true,keyProperty="id")
    int insertSelective(User record);

    /**
     *
     * @mbg.generated 2018-12-11
     */
    @SelectProvider(type=UserSqlProvider.class, method="selectByExample")
    @Results({
        @Result(column="id", property="id", jdbcType=JdbcType.INTEGER, id=true),
        @Result(column="name", property="name", jdbcType=JdbcType.VARCHAR),
        @Result(column="age", property="age", jdbcType=JdbcType.INTEGER)
    })
    List<User> selectByExample(UserExample example);

    /**
     *
     * @mbg.generated 2018-12-11
     */
    @Select({
        "select",
        "id, name, age",
        "from user",
        "where id = #{id,jdbcType=INTEGER}"
    })
    @Results({
        @Result(column="id", property="id", jdbcType=JdbcType.INTEGER, id=true),
        @Result(column="name", property="name", jdbcType=JdbcType.VARCHAR),
        @Result(column="age", property="age", jdbcType=JdbcType.INTEGER)
    })
    User selectByPrimaryKey(Integer id);

    /**
     *
     * @mbg.generated 2018-12-11
     */
    @UpdateProvider(type=UserSqlProvider.class, method="updateByExampleSelective")
    int updateByExampleSelective(@Param("record") User record, @Param("example") UserExample example);

    /**
     *
     * @mbg.generated 2018-12-11
     */
    @UpdateProvider(type=UserSqlProvider.class, method="updateByExample")
    int updateByExample(@Param("record") User record, @Param("example") UserExample example);

    /**
     *
     * @mbg.generated 2018-12-11
     */
    @UpdateProvider(type=UserSqlProvider.class, method="updateByPrimaryKeySelective")
    int updateByPrimaryKeySelective(User record);

    /**
     *
     * @mbg.generated 2018-12-11
     */
    @Update({
        "update user",
        "set name = #{name,jdbcType=VARCHAR},",
          "age = #{age,jdbcType=INTEGER}",
        "where id = #{id,jdbcType=INTEGER}"
    })
    int updateByPrimaryKey(User record);
}

UserSqlProvider.java、UserExample、User省略。用mybatis-generator即可生成。

5、測試

package com.spring.pro.thread.api;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

import com.cloud.ProviderApplication;

/**
 * @Title: ThreadApi.java
 * @ProjectName com.spring.pro.thread.pool
 * @Description:
 * @author ybwei
 * @date 2018年12月11日 下午12:16:27
 */
@RunWith(value = SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = ProviderApplication.class)
public class ThreadApi {
	@Autowired
	private WebApplicationContext wac;
	private MockMvc mockMvc;

	@Before
	public void setUp() {
		mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
	}

	@Test
	public void testUsersConcurrent() throws Exception {

		ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS,
				new ArrayBlockingQueue<Runnable>(5));
		// 不能超過15
		for (int i = 0; i < 12; i++) {
			executor.execute(() -> {
				try {
					mockMvc.perform(MockMvcRequestBuilders.get("/user/getUser?id=1"))
							.andExpect(MockMvcResultMatchers.status().isOk());
					mockMvc.perform(MockMvcRequestBuilders.get("/user/getUser2?id=1"))
							.andExpect(MockMvcResultMatchers.status().isOk());
				} catch (Exception e) {
					e.printStackTrace();
				}
			});
		}

		// 等待其他執行緒執行,方便檢視控制檯列印結果
		TimeUnit.SECONDS.sleep(1000);
	}
}

6、注意

spring的controller是多執行緒的。當訪問請求是高併發時,計數要保證每個執行緒都是獨立,不會相互干擾,所以使用ThreadLocal。

6.1 ThreadLocal簡介

ThreadLocal為解決多執行緒程式的併發問題提供了一種新的思路。

使用這個工具類可以很簡潔地編寫出優美的多執行緒程式。當使用ThreadLocal維護變數時,ThreadLocal為每個使用該變數的執行緒提供獨立的變數副本,所以每一個執行緒都可以獨立地改變自己的副本,而不會影響其它執行緒所對應的副本。從執行緒的角度看,目標變數就象是執行緒的本地變數,這也是類名中“Local”所要表達的意思。

6.2 ThreadLocal類介面

  • void set(T value)設定當前執行緒的執行緒區域性變數的值。
  • public T get()該方法返回當前執行緒所對應的執行緒區域性變數。
  • public void remove()將當前執行緒區域性變數的值刪除,目的是為了減少記憶體的佔用,該方法是JDK 5.0新增的方法。需要指出的是,當執行緒結束後,對應該執行緒的區域性變數將自動被垃圾回收,所以顯式呼叫該方法清除執行緒的區域性變數並不是必須的操作,但它可以加快記憶體回收的速度。
  • protected T initialValue()返回該執行緒區域性變數的初始值,該方法是一個protected的方法,顯然是為了讓子類覆蓋而設計的。這個方法是一個延遲呼叫方法,線上程第1次呼叫get()或set(Object)時才執行,並且僅執行1次。ThreadLocal中的預設實現直接返回一個null。

在Java中編寫執行緒區域性變數的程式碼相對來說要笨拙一些,因此造成執行緒區域性變數沒有在Java開發者中得到很好的普及。