微服務架構案例(04):中間件集成,公共服務封裝
- 2019 年 11 月 4 日
- 筆記
本文源碼:GitHub·點這裡 || GitEE·點這裡
更新進度(共6節):
04:中間件集成,公共服務管理
一、中間件簡介
中間件是基礎軟體的一類, 屬於復用性極高的軟體。處於作業系統軟體與應用程式的之間。是一種獨立的系統軟體,也可以是公共的服務程式,分散式架構系統藉助中間件,可以在不同的技術之間共享資源,或者不同的服務直接傳遞資訊。中間件位作業系統之上,管理電腦資源和網路通訊。是連接兩個獨立應用程式或獨立系統的軟體,例如:
- 消息隊列中間件,在兩個服務之間進行非同步的消息傳遞;
- 數據快取中間件,快取整合系統的熱點數據,提高程式的響應速度;
- Nginx中間件,提供負載均衡,服務代理,等功能;
二、公共服務簡介
公共服務,顧名思義就是系統內通用的服務,例如用戶身份驗證,消息發送,監控預警,網關服務等。
該案例的中間件和公共服務,都是基於Feign
介面統一的方式提供服務。
三、中間件集成
1、消息中間件
RocketMq
簡介
RocketMq
是一款分散式、隊列模型的消息中間件,有兩個核心角色:消息生產者和消息消費者。作為高並發系統的核心組件之一,能夠幫助業務系統解構提高系統穩定性。
- 應用流程
- 消息生產者
@Component public class MsgSendService { @Resource private ProducerConfig producerConfig ; public void sendMsg (MsgWrap msgWrap) { producerConfig.sendMsg(msgWrap.getGroup(),msgWrap.getTopic(), msgWrap.getTag(),msgWrap.getContent()); } }
- 消息消費者
@Component @Consumer(group = MsgRoute.husky_group_1, topic = MsgRoute.husky_topic_1 , tag = MsgRoute.husky_tag_1) public class UserSearchListener implements MsgReadService { @Resource private BookEsAnalyFeign bookEsAnalyFeign ; @Override public void readMsg(String msg) throws Exception { LOGGER.info("【用戶搜索消息監聽 Msg】:{}",msg) ; // 轉發請求數據分析服務 bookEsAnalyFeign.sendBookEsMsg(msg); } }
- 提供
Feign
介面
@RestController public class UserSearchController implements UserSearchFeign { @Resource private SendMsgService sendMsgService ; @Override public void sendBookSearch(String msgContent) { MsgWrap msgWrap = new MsgWrap() ; msgWrap.setContent(msgContent); msgWrap.setGroup(MsgRoute.husky_group_1); msgWrap.setTopic(MsgRoute.husky_topic_1); msgWrap.setTag(MsgRoute.husky_tag_1); sendMsgService.sendMsg(msgWrap); } }
2、快取中間件
Redis
簡介
Redis
是一個基於記憶體的高性能key-value
資料庫。對高並發系統提供各種場景的支撐:熱點數據快取,計數器,流量削峰等。
- 應用流程
- 封裝操作方法
@Service public class RedisServiceImpl implements RedisService { @Resource private RedisTemplate<Object,Object> redisTemplate ; @Override public boolean set(Object key, Object value) { boolean redisFlag = true ; try { redisTemplate.opsForValue().set(key,value); } catch (Exception e){ redisFlag = false ; e.printStackTrace(); } return redisFlag ; } @Override public boolean set(Object key,Object value, long expire) { boolean redisFlag = true ; try { redisTemplate.opsForValue().set(key,value,expire,TimeUnit.SECONDS); } catch (Exception e){ redisFlag = false ; e.printStackTrace(); } return redisFlag ; } @Override public String get(Object key) { String value = null ; try { value = String.valueOf(redisTemplate.opsForValue().get(key)) ; } catch (Exception e){ e.printStackTrace(); } return value ; } }
- 提供
Feign
服務
@RestController public class RedisController implements RedisFeign { @Resource private RedisService redisService ; @Override public boolean set (String key, String value) { return redisService.set(key,value) ; } @Override public boolean setTimeOut (String key, String value,long expire){ return redisService.set(key,value,expire) ; } @Override public String get (String key) { return redisService.get(key) ; } }
3、搜素中間件
ES
搜索簡介
ElasticSearch
是一個基於Lucene的搜索伺服器。它提供了一個分散式多用戶能力的全文搜索引擎,基於RESTful
的 web介面。是當前流行的企業級搜索引擎。
- 應用流程
- 封裝操作方法
@Service public class BookInfoEsServiceImpl implements BookInfoEsService { @Resource private BookInfoRepository bookInfoRepository ; @Override public void batchSave(List<EsBookInfo> bookInfoList) { bookInfoRepository.saveAll(bookInfoList) ; } @Override public List<EsBookInfo> queryList() { Iterable<EsBookInfo> bookInfoIterable = bookInfoRepository.findAll() ; List<EsBookInfo> esBookInfoList = Lists.newArrayList(bookInfoIterable) ; if (esBookInfoList == null){ esBookInfoList = new ArrayList<>() ; } return esBookInfoList; } @Override public List<EsBookInfo> getByKeyWord(String keyWord) { QueryStringQueryBuilder builder = new QueryStringQueryBuilder(keyWord); Iterable<EsBookInfo> bookInfoIterable = bookInfoRepository.search(builder) ; List<EsBookInfo> esBookInfoList = Lists.newArrayList(bookInfoIterable) ; if (esBookInfoList == null){ esBookInfoList = new ArrayList<>() ; } return esBookInfoList ; } }
- 提供
Feign
服務
@RestController public class BookInfoEsController implements BookInfoEsFeign { @Resource private BookInfoEsService bookInfoEsService ; @Override public void batchSave(List<EsBookInfo> bookInfoList) { bookInfoEsService.batchSave(bookInfoList); } @Override public List<EsBookInfo> queryList() { return bookInfoEsService.queryList(); } @Override public List<EsBookInfo> getByKeyWord(String keyWord) { return bookInfoEsService.getByKeyWord(keyWord); } }
4、定時器中間件
Quartz
簡介
Quartz
是由Java
編寫的開源任務調度的框架,通過觸發器設置作業定時運行規則,控制任務的執行時間。其中quartz
集群通過故障切換和負載平衡的功能,能給調度器帶來高可用性和伸縮性。
- 應用流程
@Component("SendMsgJob") public class SendMsgJob implements TaskJobService { @Resource private SendEmailFeign sendEmailFeign ; @Override public void run(String param) { String nowDate = TimeUtil.formatDate(new Date(),TimeUtil.FORMAT_01) ; LOGGER.info("SendMsgJob Execute Time:{}",nowDate); sendEmailFeign.sendEmail("","定時郵件通知",""+nowDate); } }
四、公共服務管理
1、Token服務
Token
服務簡介
通過一個公共的Token
管理服務,對訪問系統的用戶身份做管理:身份令牌創建,校驗,刷新等。
- 應用流程
- 封裝操作方法
@Service public class UserTokenServiceImpl implements UserTokenService { @Resource private UserBaseMapper userBaseMapper ; @Resource private RedisFeign redisFeign ; @Override public String getToken(String userName, String passWord) throws Exception { UserBaseExample example = new UserBaseExample() ; example.createCriteria().andUserNameEqualTo(userName) ; UserBase userBase = selectByExample(example) ; if (userBase != null){ String secrete = userBase.getPassWord() ; if (secrete.equals(passWord)) { // 返回 Token String value = userBase.getId().toString() ; String publicKeyStr = RsaCryptUtil.getKey(RsaCryptUtil.PUB_KEY) ; String token = RsaCryptUtil.encrypt(RsaCryptUtil.createPublicKey(publicKeyStr),value.getBytes()) ; String key = RedisUtil.formatUserTokenKey(userBase.getId()) ; redisFeign.setTimeOut(key,token, Constant.USER_TOKEN_EXPIRE) ; return token ; } } return null; } @Override public Integer verifyToken(String token) throws Exception { String privateKeyStr = RsaCryptUtil.getKey(RsaCryptUtil.PRI_KEY) ; String userId = RsaCryptUtil.decrypt(RsaCryptUtil.createPrivateKey(privateKeyStr), RsaCryptUtil.parseBase64Binary(token)); return Integer.parseInt(userId) ; } @Override public boolean refreshToken(String token) throws Exception { Integer userId = verifyToken(token) ; if (userId > 0 ){ String key = RedisUtil.formatUserTokenKey(userId) ; // 判斷Token 是否過期 String cacheToken = redisFeign.get(key) ; if (StringUtils.isEmpty(cacheToken)){ return false ; } redisFeign.setTimeOut(key,token, Constant.USER_TOKEN_EXPIRE) ; return true ; } return false ; } }
- 提供
Feign
服務
@FeignClient("MOPSZ-BASIS-TOKEN") public interface UserTokenFeign { /** * 獲取 TOKEN */ @PostMapping("/token/getToken") RespObject getToken (@RequestParam("userName") String userName, @RequestParam("passWord") String passWord) ; /** * 驗證 TOKEN */ @PostMapping("/token/verifyToken") RespObject verifyToken (@RequestParam("token") String token) ; /** * 刷新 TOKEN */ @PostMapping("/token/refreshToken") boolean refreshToken (@RequestParam("token") String token) ; }
2、消息服務
Msg
服務簡介
在一個複雜的系統中,消息通知是一個必備模組,一般封裝方式主要從下面兩個方式入手,消息類型:用戶消息,系統消息等,消息接收方式:郵件,簡訊,應用端等。
- 應用流程
- 封裝郵件發送
@Service public class SendEmailServiceImpl implements SendEmailService { @Override public void sendEmail(String receive, String title, String msg) { try { EmailUtil.sendEmail01(receive,title,msg); } catch (Exception e){ e.printStackTrace() ; LOGGER.info("郵件發送失敗:{}",e.getMessage()); } } }
- 提供
Feign
服務
@FeignClient("MOPSZ-BASIS-MSGBOX") public interface SendEmailFeign { /** * 發送Email */ @PostMapping("/msgBox/sendEmail") void sendEmail (@RequestParam("receive") String receive, @RequestParam("title") String title, @RequestParam("msg") String msg) ; }
五、源程式碼地址
GitHub·地址 https://github.com/cicadasmile/husky-spring-cloud GitEE·地址 https://gitee.com/cicadasmile/husky-spring-cloud