项目介绍:
项目一:是一个基于微服务架构的生产级在线教育项目,是一个B2C类型的教育网站,分为两个端:后台管理端、 用户端(PC网站)。主要包含了在线教育中核心的学习辅助系统、考试系统,电商类项目的促销优惠系统;项目中涉猎技术广泛,涉及到SpringCloud Alibaba组件,非关系型数据库mongoDB
以及最常见的Redis
,集成了ElasticSearch
搜索技术,还有消息中间件RabbitMQ
及定时任务组件Xxl-Job
分布式锁组件redisson
。最终项目基于docker
容器搭建使用Graylog日志
及SKywalking
进行分布式链路追踪;
整体架构
技术架构
项目核心流程
模块情况
微服务名称 | 功能描述 |
---|---|
tj-parent | 父工程 |
tj-common | 通用工程 |
tj-message | 消息中心 |
tj-gateway | 网关 |
tj-auth | 权限服务 |
tj-user | 用户服务 |
tj-pay | 支付服务 |
tj-course | 课程服务 |
tj-exam | 考试服务 |
tj-search | 搜索服务 |
tj-trade | 交易服务 |
tj-learning | 学习服务 |
tj-promotion | 促销服务 |
tj-media | 媒资服务 |
tj-data | 数据服务 |
tj-remark | 评价服务 |
实体类规范
- DTO:数据传输对象,在客户端与服务端间传递数据,例如微服务之间的请求参数和返回值、前端提交的表单
- PO:持久层对象,与数据库表一一对应,作为查询数据库时的返回值
- VO:视图对象,返回给前端用于封装页面展示的数据
- QUERY:查询对象,一般是用于封装复杂查询条件
依赖注入(spring推荐的构造器注入方式)
Spring提供了依赖注入的功能,方便我们管理和使用各种Bean,常见的方式有:
- 字段注入(
@Autowired 或 @Resource
) - 构造函数注入
- set方法注入
在以往代码中,我们经常利用Spring提供的@Autowired
注解来实现依赖注入:
Spring推荐的是基于构造函数注入
Lombok提供了一个注解@RequiredArgsConstructor
,可以帮我们生成构造函数。代码不会那么臃肿
全局异常处理
在项目运行过程中,或者业务代码流程中,可能会出现各种类型异常,为了加以区分,我们定义了一些自定义异常对应不同场景:在开发业务的过程中,如果出现对应类型的问题,应该优先使用这些自定义异常。
@RestControllerAdvice
@Slf4j
public class CommonExceptionAdvice {
@ExceptionHandler(DbException.class)
public Object handleDbException(DbException e) {
log.error("mysql数据库操作异常 -> ", e);
return processResponse(e.getStatus(), e.getCode(), e.getMessage());
}
@ExceptionHandler(CommonException.class)
public Object handleBadRequestException(CommonException e) {
log.error("自定义异常 -> {} , 状态码:{}, 异常原因:{} ",e.getClass().getName(), e.getStatus(), e.getMessage());
log.debug("", e);
return processResponse(e.getStatus(), e.getCode(), e.getMessage());
}
@ExceptionHandler(FeignException.class)
public Object handleFeignException(FeignException e) {
log.error("feign远程调用异常 -> ", e);
return processResponse(e.status(), e.status(), e.contentUTF8());
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
String msg = e.getBindingResult().getAllErrors()
.stream().map(ObjectError::getDefaultMessage)
.collect(Collectors.joining("|"));
log.error("请求参数校验异常 -> {}", msg);
log.debug("", e);
return processResponse(400, 400, msg);
}
@ExceptionHandler(BindException.class)
public Object handleBindException(BindException e) {
log.error("请求参数绑定异常 ->BindException, {}", e.getMessage());
log.debug("", e);
return processResponse(400, 400, "请求参数格式错误");
}
@ExceptionHandler(NestedServletException.class)
public Object handleNestedServletException(NestedServletException e) {
log.error("参数异常 -> NestedServletException,{}", e.getMessage());
log.debug("", e);
return processResponse(400, 400, "请求参数异常");
}
@ExceptionHandler(ConstraintViolationException.class)
public Object handViolationException(ConstraintViolationException e) {
log.error("请求参数异常 -> ConstraintViolationException, {}", e.getMessage());
return processResponse( HttpStatus.OK.value(), HttpStatus.BAD_REQUEST.value(),
e.getConstraintViolations().stream().map(ConstraintViolation::getMessage).distinct().collect(Collectors.joining("|"))
);
}
@ExceptionHandler(Exception.class)
public Object handleRuntimeException(Exception e) {
log.error("其他异常 uri : {} -> ", WebUtils.getRequest().getRequestURI(), e);
return processResponse(500, 500, "服务器内部异常");
}
private Object processResponse(int status, int code, String msg){
// 1.标记响应异常已处理(避免重复处理)
WebUtils.setResponseHeader(Constant.BODY_PROCESSED_MARK_HEADER, "true");
// 2.如果是网关请求,http状态码修改为200返回,前端基于业务状态码code来判断状态
// 如果是微服务请求,http状态码基于异常原样返回,微服务自己做fallback处理
return WebUtils.isGatewayRequest() ?
R.error(code, msg).requestId(MDC.get(Constant.REQUEST_ID_HEADER))
: ResponseEntity.status(status).body(msg);
}
}
技能掌握:(远程调试)
添加一个新的启动项:
在新建的Configuration中填写信息:
远程调试的部署脚本
- 仅仅本地配置还不够,我们还需要在虚拟机中部署时,添加一段配置到部署脚本中,这段配置IDEA已经提供给我们了
java -jar -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 xx.jar
运行Jenkins部署脚本
虚拟机中暴露远程调试的端口
debug打断点调试
评论( 0 )