高效稳定 · 简洁易用 · 灵活扩展
项目地址:lemon-echo微服务架构
1、服务采用rest + json返回数据,正常数据直接返回数据对象,提示码/异常统一封装ResultError返回,正常状况下controller只需返回正常数据对象就可以,service业务异常抛出由框架DefaultGlobalExceptionHandler统一处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 带结果返回实例 @GetMapping ("/menus" )public List<Menu> getMenus (Long projectId) { List<Menu> list = menuService.getMenus(projectId); return list; } 无结果返回实例: @PostMapping ("/code" )public void mobileCode (AuthType authType, String mobile) { mobileCodeService.sendMobileCode(authType, mobile); } 业务代码实例: public void sendMobileCode (final AuthType authType, final Long userId) { String mobile = userAuthService.getMobileByUserId(userId); if (StringUtils.isEmpty(mobile)) { throw new BizServiceException(F11615); } this .sendMobileCode(authType, mobile); } 异常码返回Json(http状态码大于300 ): { code: 11615 , msg: "未绑定手机号码" }
2、服务功能配置采用预配置方式,需继承基础功能包内的配置类并注解@Configuration
1 2 3 4 @Configuration public class MybatisConfig extends MybatisConfigurator {}
3、服务引入auth-rpc进行权限校验,在WebMvcConfig配置相应的权限拦截器即可实现,需要控制权限的接口仅需加上注解@RequestPermissions({xxx})
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 配置权限拦截: @Configuration public class WebMvcConfig extends WebMvcConfigurator { @Bean public PermissionInterceptor permissionInterceptor () { return new PermissionInterceptor(); } @Override public void addInterceptors (InterceptorRegistry registry) { super .addInterceptors(registry); registry.addInterceptor(permissionInterceptor()) .addPathPatterns("/portal/**" ); } } 接口权限配置 @RequestPermissions ({"user_freeze" })@PostMapping ("/portal/user/freeze" )public void freeze (Long userId, FreezeTime freezeTime, String reason) { userFreezeService.freeze(userId, freezeTime, reason); }
4、rabbit消息队列在实际开发中,绝大部分情况下仅需使用topic模式和延时模式,框架封装并简化了rabbit的topic模式和延时模式功能,生产和消费时的方法采用相同的方法,降低多种方法的配置和学习成本。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 消费端声明消息队列 @Configuration public class RabbitConfig extends RabbitConfigurator { @Override public Collection<QueueBinding> getQueueBinding () { List<QueueBinding> queueBindings = new ArrayList<>(); queueBindings.add(new QueueBinding(USER_UNFREEZE, "user.topic" , "user.unfreeze" )); return queueBindings; } } 消费端消费队列消息 @RabbitListener (queues = {USER_UNFREEZE})public void unfreezeUser (Long userId) { userAuthService.unfreeze(userId); } 生产端生产队列消息 public void unfreezeUser (Long userId) { rabbitTopicSender.send(USER_UNFREEZE, userId); }
注:
RoutingKey命名规则:生产服务名称.业务功能 如:USER_UNFREEZE = “user.unfreeze”;
Queue命名规则:消费服务名称_RoutingKey 如:USER_UNFREEZE = “auth_user.unfreeze”;
5、分布式锁采用redis特性作为锁凭证中心,封装了@DistributionLock和@LockKey降低对系统业务的侵入性,同时利用LockKey参数细分控制每个锁元素,增大服务的吞吐量。
1 2 3 4 5 帐户入账时锁定对应帐户类型+用户ID @DistributionLock public void increase (@LockKey AccountType accountType, @LockKey Long userId, AccountTradePo accountTrade) {}
6、利用@cache进行数据缓存,可选择使用本地caffeine缓存器和远程redis缓存器,根据业务灵活使用缓存可以保障业务对高效稳定。
1 2 3 4 5 6 7 8 9 @Cacheable (value="userCaches" , key="'user_' + #userId" , cacheManager="caffeineCacheManager" )public UserPo getUserInfoById (Long userId) {return userPo;} @Cacheable (value="userCaches" , key="'user_' + #userId" , cacheManager="redisCacheManager" )public UserPo getUserInfoById (Long userId) {return userPo;}
7、定时任务采用xxl-job分布式调度中心,利用统一调度策略(单点执行/分片执行),完成定时业务需求。
1 2 3 4 5 @XxlJob ("pendUnFreezeUserHandler" )public ReturnT<String> pendUnFreezeUserHandler (String param) throws Exception { XxlJobLogger.log("job exec unfreeze user." ); return ReturnT.SUCCESS; }
8、项目pom引入auth-rpc后,在WebMvcConfig使用拦截器设置需要的权限规则
1 2 3 4 5 6 7 8 9 10 11 @Bean public PermissionInterceptor permissionInterceptor () { return new PermissionInterceptor(); } @Override public void addInterceptors (InterceptorRegistry registry) { super .addInterceptors(registry); registry.addInterceptor(permissionInterceptor()).addPathPatterns("/portal/**" ); }
9、接口请求时经过授权检查会解析x-auth-token,在请求的header参数增加用户ID参数x-user-id,业务参数使用时可以直接获取改值。
1 2 3 4 @PostMapping ("/api/withdraw/apply" )public void applyWithdraw (@RequestHeader(value = AUTH_USER_ID) Long userId, Long bankCardId, BigDecimal coins) {}
10、接口请求数据均需要携带header参数:
1 2 3 4 5 6 7 8 9 10 AUTH_SCOPE = "x-auth-scope" ; AUTH_TOKEN = "x-auth-token" ; CLIENT_NAME = "x-client-name" ; CLIENT_IMEI = "x-client-imei" ; CLIENT_TYPE = "x-client-type" ; CLIENT_VERSION = "x-client-version" ; APP_VERSION = "x-app-version" ; APP_CHANNEL = "x-app-channel" ; BUNDLE_ID = "x-bundle-id" ; TIMESTAMP = "x-timestamp" ;