統計Controller、Service、Repository消耗時間
阿新 • • 發佈:2018-12-21
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開發者中得到很好的普及。