微服务-架构案例

微服务架构案例(01):项目技术选型简介,架构图解说明

一、单体架构

单体架构在中等偏小的业务中比较常见,场景模式就是单个应用、单个数据库。一个程序包(例如war格式或者Jar格式)包含所有业务需求功能,这是一种比较传统的架构风格。

单体架构的缺陷

  1. 复杂性高,整个项目包含的模块多,依赖模糊,修改程序容易触发不可知问题。
  2. 扩展能力受限,单体应用只能整体进行扩展,无法针对业务模块的特性进行伸缩。
  3. 稳定性差,任何微小的问题,都可能导致整个应用服务直接挂掉。

二、微服务架构

微服务架构是一种架构概念,核心思想在于通过将业务功能和需求分解到各个不同的服务中进行管理,实现对业务整体解耦。围绕业务模式创建应用服务,应用服务可独立地进行开发、迭代、部署。使项目的架构更加清晰明确。

微服务优劣势

  1. 单个服务对应单个业务功能,方便理解,开发,维护;
  2. 服务独立部署,可以根据每个服务的请求量来部署满足需求的规模;
  3. 数据库,服务,架构,业务拆分等难度增大,对技术能力要求较高;

三、项目简介

微服务架构案例核心内容,基于SpringCloud框架几个核心组件,Eureka服务注册与发现组件,Feign声明式的WebService客户端组件,Zuul动态路由网关组件。进行多个数据管理,多个服务管理搭建,多个中间件集成,多业务拆分等模式,搭建SpringCloud微服务框架的综合应用案例。

核心模块

  1. 业务拆分架构设计
  2. 多个中间件服务集成
  3. 微服务下代码分块管理
  4. 多个MySQL数据源管理

四、技术选型

1、核心框架组件

1. 基础层框架:Spring5+,SpringBoot2+,SpringCloud2+
2. 持久层框架:mybatis,mybatis-plus
3. 开发组件:Druid,Log4j,FastJson,JodaTime,JavaMail
4. 中间件集成:RocketMQ,Redis,Quart,ElasticSearch
5. 数据存储:MySQL、Redis、ElasticSearch

2、代码分层结构

五、项目架构

1、架构图解

2、架构说明

  • 客户端接口服务
1. 用户端服务接口;
2. 管理端服务接口;
3. 数据入库服务接口;
4. 数据分析服务接口
  • 通用服务接口
1. RocketMQ消息队列服务 ;
2. ElasticSearch搜索引擎服务 ;
3. Quart定时器服务 ;
4. Redis缓存服务 ;
5. 基础业务:Token认证服务 ;
6. 基础业务:MsgBox消息中心服务 ;
MARKDOWN 复制 全屏
  • 微服务组件
1. 路由网关:Zuul组件;
2. 服务注册与发现:Eureka组件;
3. 服务间调用组件:Feign组件;
MARKDOWN 复制 全屏
  • 数据存储容器
MySQL、Redis、ElasticSearch

微服务架构案例(02):业务架构设计,系统分层管理

一、业务架构设计

1、基础概念

服务的架构设计决定软件的业务支撑能力,清晰的业务设计可以帮助开发人员理解系统。在业务架构设计过程中,需要根据用户需求作为核心方向,根据用户需求确定产品设计、框架搭建、服务划分、数据库规划。如果需求比较单一,单个应用服务可以支撑,则不需要设计复杂的微服务系统,如果根据对业务的判断,会在一段时间内出现业务并发,则最好开始的时候就考虑业务的扩展性,架构的支撑能力。

2、案例架构图

基于该项目的架构设计,下面逐个描述一下业务设计。

二、业务流程

1、数据入库服务

  • 流程图解

  • 流程描述
1. 请求入库服务接口;
2. 搜索数据,通过搜索服务Feign接口写入ES服务器;
3. 相同的搜索数据,写入用户数据库;
  • 程序入口

所属代码包:mopsz-data-form

@RestController
@RequestMapping("/search/data")
public class SearchDataController {
    // 调用搜索服务Feign接口
    @Resource
    private BookInfoEsFeign bookInfoEsFeign ;
    @Resource
    private BookInfoService bookInfoService ;
    @RequestMapping("/batchSave")
    public String batchSave (){
        // 省略业务代码
    }
}

2、用户API服务

  • 流程图解

  • 流程描述
1. 用户请求进入,网关服务拦截;
2. 调用Token管理服务,验证用户的身份令牌,使用Redis存储Token;
3. 如果身份验证通过,则网关放行用户请求;
4. 执行用户搜索请求处理;
5、根据搜索条件,调用ES搜索服务,返回结果;
6、将用户的搜索动作进行封装,请求MQ服务;
7、MQ服务请消息转发到数据分析服务 ;
8、数据分析服务安装策略分析用户请求,存储分析结果;
  • 程序入口

(1)、网关拦截

所属代码包:mopsz-cloud-gateway

@Component
public class FilterConfig extends ZuulFilter {
    public static final Logger LOGGER = LoggerFactory.getLogger(FilterConfig.class) ;
    private static final String GET_TOKEN = "/token/getToken";
    private static final String VERIFY_TOKEN = "/token/verifyToken";
    private static final String REFRESH_TOKEN = "/token/refreshToken";
    /**
     * 拦截处理
     */
    @Override
    public Object run() throws ZuulException {
        RequestContext requestContext = RequestContext.getCurrentContext() ;
        try {
            doTokenProcess (requestContext);
        } catch (Exception e){
            LOGGER.info("异常:{}",e.getMessage());
            throw new ZuulException(e.getMessage(), 403, e.getMessage());
        }
        return null ;
    }
    public void doTokenProcess (RequestContext requestContext) throws Exception {
        HttpServletRequest request = requestContext.getRequest() ;
        String reqUri = request.getRequestURI() ;
        if (!reqUri.contains(GET_TOKEN)) {
            String token = request.getHeader("token") ;
            boolean flag = userTokenFeign.refreshToken(token) ;
            if (!flag){
                throw new ServiceException("Token 校验失败") ;
            }
            LOGGER.info("Token 校验通过");
        }
    }
}

(2)、Token管理

所属代码包:mopsz-basis-token

@RestController
@RequestMapping("/token")
public class UserTokenController implements UserTokenFeign {
    @Resource
    private UserTokenService tokenService ;
    /**
     * 获取Token
     */
    @Override
    @RequestMapping("/getToken")
    public RespObject getToken (@RequestParam("userName") String userName,
                                @RequestParam("passWord") String passWord){
        try {
            String token = tokenService.getToken(userName,passWord) ;
            return RespObject.ok().put(Constant.MAP_KEY,token) ;
        } catch (Exception e){
            e.printStackTrace();
            return RespObject.error() ;
        }
    }
    /**
     * 校验Token
     */
    @Override
    @RequestMapping("/verifyToken")
    public RespObject verifyToken(String token) {
        try {
            Integer userId = tokenService.verifyToken(token) ;
            return RespObject.ok().put(Constant.MAP_KEY,userId) ;
        } catch (Exception e){
            e.printStackTrace();
            return RespObject.error() ;
        }
    }
    /**
     * 刷新Token
     */
    @Override
    @RequestMapping("/refreshToken")
    public boolean refreshToken(String token) {
        try {
            return tokenService.refreshToken(token) ;
        } catch (Exception e){
            e.printStackTrace();
            return false ;
        }
    }
}
折叠

(3)、搜索接口

所属代码包:mopsz-user-client

@RestController
@RequestMapping("/search/book/")
public class BookSearchController {
    @Resource
    private BookInfoEsFeign bookInfoEsFeign ;
    @Resource
    private UserSearchFeign userSearchFeign ;
    /**
     * 关键字全文搜索
     */
    @RequestMapping("/getByKeyWord")
    public List<EsBookInfo> getByKeyWord (@RequestParam("keyWord") String keyWord,
                                          @RequestParam("userId") Integer userId){
        // 搜索引擎执行
        List<EsBookInfo> esBookInfoList = bookInfoEsFeign.getByKeyWord(keyWord) ;
        // 执行异步分析
        if (StringUtils.isNotEmpty(keyWord) && esBookInfoList != null){
            KeySearchModel keySearchModel = new KeySearchModel() ;
            keySearchModel.setUserId(userId);
            keySearchModel.setKeyWord(keyWord);
            keySearchModel.setSearchResult(esBookInfoList);
            userSearchFeign.sendBookSearch(JsonUtil.objToJson(keySearchModel));
        }
        return esBookInfoList ;
    }
}

(4)、请求分析

所属代码包:mopsz-data-analy

@RestController
public class BookEsDataController implements BookEsAnalyFeign {
    @Resource
    private BookEsDataService bookEsDataService ;
    @Override
    public void sendBookEsMsg(String msgBody) {
        bookEsDataService.saveBookEsData(msgBody);
    }
}

3、系统管理服务

  • 流程图解

  • 流程描述

整合流程基础SpringSecurity,JWT等组件开发。所属代码包:mopsz-admin-client。

1. 系统用户登录,安全配置:SecurityConfig ;
2. 登录成功处理:LoginSuccessHandler ;
3. 登录失败处理:LoginFailHandler ;
4. 系统服务具备管理:业务数据库,缓存数据,ES服务等功能 ;

4、数据分析服务

  • 流程图解

  • 流程描述

所属代码包:mopsz-data-analy

1. 接收MQ服务转发数据分析请求 ;
2. 按照指定策略,分析数据,分析结果存储 ;

三、系统分层管理

1、分层示意图

2、结构层次描述

  • 公共代码块层

管理系统工具类、数据表结构实体类、Mapper层、Service服务层等,按照数据库服务规划划分,作为整个系统的公共依赖代码块。

  • 数据管理层

管理数据入库服务API,数据分析服务API两个模块。

  • 客户端接口层

管理用户服务API,后台系统服务API。

  • 微服务接口层

微服务之间的调用采用Feign接口的方式,按照不同服务提供的Feign接口进行封装,在该层统一管理,进行服务间的请求调用。

  • 通用业务服务层

管理通用Token服务,消息发送服务,作为系统共用的业务服务层。

  • 中间件服务层

管理RocketMQ消息队列服务、Redis缓存服务、Quart定时器服务、ES搜索服务,提供统一的Feign服务接口。

  • 微服务组件层

管理整个系统的网关拦截服务、Eureka服务注册和发现 等微服务基础组件。

微服务架构案例(03):数据库选型简介,业务数据规划设计

一、数据库选择

1、数据库分类

数据库类型常见数据库
关系型 MySQL、Oracle、DB2、SQLServer等。
非关系型 Hbase、Redis、MongodDB等。
行式存储 MySQL、Oracle、DB2、SQLServer等。
列式存储 Hbase、ClickHouse等。
分布式存储 Cassandra、Hbase、MongodDB等。
键值存储 Memcached、Redis、MemcacheDB等。
图形存储 Neo4J、TigerGraph等。
文档存储 MongoDB、CouchDB等。

2、数据库选择

基于特定的应用环境,选择最适合的数据库,建立数据存储模式,使之能够有效地存储数据,满足各种用户的应用需求。例如:普通的业务库,数据量不大情况下选择MySQL;有频繁的搜索操作,可以使用ElasticSearch;系统存在大量热点数据,可以使用常见的缓存数据库等。

3、微服务数据库

微服务架构的一个关键点是数据库设计规划,基本原则是每个服务都有自己单独的数据库,而且只有微服务本身可以访问这个数据库。其他的服务要是想访问,只能通过调用该服务对外提供的接口进行操作,这样可以压缩数据库操作的接口,在问题排查和性能优化上都可以提供支持,这样也使系统的框架更具有条理。该模式图解如下:

微服务C通过微服务A操作数据库A,或者通过微服务B操作数据库B。

二、规划业务数据库

1、总体划分

主要使用三种数据存储:MySQL(划分三个业务库),ElasticSearch(单台),Redis(单台)。

2、用户库

(user-data)存储用户相关的数据结构,比如User信息,Token,操作日志等。

CREATE TABLE `hc_user_base` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `user_name` varchar(50) DEFAULT NULL COMMENT '用户名',
  `pass_word` varchar(300) DEFAULT NULL COMMENT '加密密码',
  `phone` varchar(30) DEFAULT NULL COMMENT '手机号',
  `email` varchar(50) DEFAULT NULL COMMENT '邮箱',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  `state` int(1) DEFAULT '0' COMMENT '状态:0可用,1禁用',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户表';

3、管理系统库

(admin-data)存储后台微服务管理系统的支撑数据库,例如定时器,管理员权限,配置字典等。

-- 管理员列表
CREATE TABLE `hc_admin_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `user_name` varchar(50) DEFAULT NULL COMMENT '用户名',
  `pass_word` varchar(300) DEFAULT NULL COMMENT '加密密码',
  `phone` varchar(30) DEFAULT NULL COMMENT '手机号',
  `email` varchar(50) DEFAULT NULL COMMENT '邮箱',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  `state` int(1) DEFAULT '0' COMMENT '状态:0可用,1禁用',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='管理用户表';

-- 角色和权限列表
CREATE TABLE `hc_role_info` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `role_name` varchar(64) DEFAULT NULL COMMENT '角色名称',
  `role_auth` varchar(64) DEFAULT NULL COMMENT '角色权限',
  `create_time` datetime DEFAULT NULL COMMENT '添加时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='系统角色表';

-- 定时器列表
CREATE TABLE `schedule_job` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '任务id',
  `bean_name` varchar(200) DEFAULT NULL COMMENT 'spring bean名称',
  `params` varchar(2000) DEFAULT NULL COMMENT '参数',
  `cron_expression` varchar(100) DEFAULT NULL COMMENT 'cron表达式',
  `status` tinyint(4) DEFAULT NULL COMMENT '任务状态  0:正常  1:暂停',
  `remark` varchar(255) DEFAULT NULL COMMENT '备注',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='定时任务';

4、数据分析库

(report-data)存储数据归档的报表,分析结果等,案例主要演示把用户的搜索行为进行分析,存储到报表库。

-- 书籍搜索记录
CREATE TABLE `hc_search_book` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `user_id` int(11) DEFAULT NULL COMMENT '用户ID',
  `book_id` int(11) DEFAULT NULL COMMENT '书籍ID',
  `book_name` varchar(100) DEFAULT NULL COMMENT '书籍名称',
  `search_time` datetime DEFAULT NULL COMMENT '搜索时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COMMENT='书籍被搜索记录';

-- 关键词搜索记录
CREATE TABLE `hc_search_key_word` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `user_id` int(11) DEFAULT NULL COMMENT '用户ID',
  `key_word` varchar(50) DEFAULT NULL COMMENT '关键词',
  `search_num` int(11) DEFAULT NULL COMMENT '搜索次数',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='关键词搜索记录';

5、搜索引擎库

(es-data)存储用户的搜索数据,可以基于MySQL库动态实时的导入到ES服务。

-- 书籍搜索信息表
CREATE TABLE `hc_book_info` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `book_name` varchar(100) DEFAULT NULL COMMENT '书籍名称',
  `book_author` varchar(100) NOT NULL DEFAULT '0' COMMENT '作者',
  `book_desc` varchar(200) DEFAULT NULL COMMENT '简介',
  `book_press` varchar(100) NOT NULL DEFAULT '0' COMMENT '出版社',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  `state` int(1) DEFAULT '0' COMMENT '状态:0可用,1删除',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='书籍信息表';
SQL 复制 全屏

三、数据库设计总结

数据库设计是微服务设计的一个核心点,基本原则是每个微服务都有自己单独的数据库,而且只有微服务本身可以访问这个数据库。在微服务架构中,数据库设计首先要满足用户的需求,便于维护和扩展,具有很好的读写性能,还可以帮助开发人员理解和管理系统。

微服务架构案例(04):中间件集成,公共服务封装 

一、中间件简介

中间件是基础软件的一类, 属于复用性极高的软件。处于操作系统软件与应用程序的之间。是一种独立的系统软件,也可以是公共的服务程序,分布式架构系统借助中间件,可以在不同的技术之间共享资源,或者不同的服务直接传递信息。中间件位操作系统之上,管理计算机资源和网络通讯。是连接两个独立应用程序或独立系统的软件,例如:

  1. 消息队列中间件,在两个服务之间进行异步的消息传递;
  2. 数据缓存中间件,缓存整合系统的热点数据,提高程序的响应速度;
  3. Nginx中间件,提供负载均衡,服务代理,等功能;

二、公共服务简介

公共服务,顾名思义就是系统内通用的服务,例如用户身份验证,消息发送,监控预警,网关服务等。

该案例的中间件和公共服务,都是基于Feign接口统一的方式提供服务。

三、中间件集成

1、消息中间件

  • RocketMq简介

RocketMq 是一款分布式、队列模型的消息中间件,有两个核心角色:消息生产者和消息消费者。作为高并发系统的核心组件之一,能够帮助业务系统解构提高系统稳定性。

  • 应用流程
  1. 消息生产者
@Component
public class MsgSendService {
    @Resource
    private ProducerConfig producerConfig ;
    public void sendMsg (MsgWrap msgWrap) {
        producerConfig.sendMsg(msgWrap.getGroup(),msgWrap.getTopic(),
                               msgWrap.getTag(),msgWrap.getContent());
    }
}
  1. 消息消费者
@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);
    }
}
  1. 提供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数据库。对高并发系统提供各种场景的支撑:热点数据缓存,计数器,流量削峰等。

  • 应用流程
  1. 封装操作方法
@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 ;
    }
}
  1. 提供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接口。是当前流行的企业级搜索引擎。

  • 应用流程
  1. 封装操作方法
@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 ;
    }
}
  1. 提供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管理服务,对访问系统的用户身份做管理:身份令牌创建,校验,刷新等。

  • 应用流程
  1. 封装操作方法
@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 ;
    }
}
折叠
  1. 提供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服务简介

在一个复杂的系统中,消息通知是一个必备模块,一般封装方式主要从下面两个方式入手,消息类型:用户消息,系统消息等,消息接收方式:邮件,短信,应用端等。

  • 应用流程
  1. 封装邮件发送
@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());
        }
    }
}
  1. 提供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) ;
}

微服务架构案例(05):SpringCloud 基础组件应用设计

一、组件应用规划

1、注册中心

Eureka 组件是 Netflix 开发的,用于定位服务,基于RESTFul方式实现服务注册与发现。包括一个EurekaServer服务端(通常是指微服务中的注册中心) 和EurekaClient客户端(通常指微服务中的业务服务)。Eureka支持高可用的配置,当注册中心发现有故障的服务时,Eureka就会转入自动保护模式,故障时不影响服务的发现和注册,当故障恢复正常时,注册中心的服务会进行状态同步,继续提供服务。

2、服务调用

Feign 组件是一个声明式的 WebService客户端。在Eureka注册的服务中,可以使用Feign接口的形式来进行相关服务间的请求调用,并提供超时回退,快速熔断的相关特性。Feign是一个便利的RestFul框架,简化调用,通过ribbon在注册服务器中找到服务实例,然后对请求进行服务分配,实现了负载均衡的效果,默认采用轮询算法。

3、服务网关

GateWay网关,可以让所有客户端请求先通过微服务网关,跟网关进行交互,由网关进行各个微服务的请求转发,客户端(前端)对多服务架构无感知。降低客户端与微服务之间的交互耦合度;便于监控,可在网关中监控数据;在网关层统一处理认证,规划切面编程;也就是说与业务本身功能无关的公共逻辑可以在网关层统一封装实现,类似设计模式中的门面模式。

二、注册中心

1、服务端配置

eureka:
  server:
    enable-self-preservation: false
    eviction-interval-timer-in-ms: 15000
  instance:
    prefer-ip-address: true
  client:
      register-with-eureka: false
      fetch-registry: false
      service-url:
        defaultZone: http://127.0.0.1:5001/eureka/

2、客户端配置

eureka:
  instance:
    prefer-ip-address: true
  client:
      service-url:
        defaultZone: http://127.0.0.1:5001/eureka/

三、Feign组件

1、Feign接口

@FeignClient("MOPSZ-SOFT-ROCKET")
public interface UserSearchFeign {
    @GetMapping("/mq/user/search/sendBookSearch")
    void sendBookSearch (@RequestParam("msgContent") String msgContent) ;
}

2、Feign接口实现

@RestController
public class UserSearchController implements UserSearchFeign {
    @Override
    public void sendBookSearch(String msgContent) {
    }
}

3、接口配置

该配置文件在实现Feign接口的项目中配置就好,也可针对特定方法配置。

feign:
  hystrix:
    enabled: true
  client:
    config:
      feignName:
        connectTimeout: 60000
        readTimeout: 60000

4、Feign接口调用

@Resource
private UserSearchFeign userSearchFeign ;
userSearchFeign.sendBookSearch("");

四、服务网关

1、网关组件配置

zuul:
  # 前缀,可以用来做版本控制
  prefix: /v1
  # 禁用默认路由,执行配置的路由
  ignored-services: "*"
  routes:
    # 配置 Token 服务
    pro6601:
      serviceId: mopsz-basis-token
      path: /api/6601/**
    # 配置 UserClient 服务
    pro6102:
      serviceId: mopsz-user-client
      path: /api/6102/**
  • 配置FilterConfig进行用户Token认证拦截;
  • 配置FallBackConfig进行服务降级响应管理;

2、项目部署

在项目上线后,对外暴露一个网关服务端口,其他服务端口不对外暴露,所有的请求都必须通过网关拦截或者转发,这就是微服务提供服务的一种常见方式。

微服务架构案例(06):通过业务、应用、技术、存储方面,聊聊架构

一、架构的概念

架构分类可细化的分为业务架构、应用架构、技术选型、代码规划、部署环境架构等。业务架构是核心的驱动力,应用架构是实现的思路,技术选型落地是结果。根据用户需求,设计合理的业务架构,做出相应的应用架构流程,最后落地实施,完成项目。如何在架构的初期,预判业务发展的速度,保证架构可以稳定快速的扩展,支撑起业务发展,这个是软件开发者,特别是架构师,需要长期积累和修炼的核心能力。

二、业务架构

业务架构中包括业务规划、功能模块、流程设计,微服务架构模式中对整个系统的业务进行服务化拆分设计,把实际的业务抽象化,进而进行封装,优化服务结构。不需要最好的架构,只选则合适的架构,系统架构的原则都要以解决业务问题为核心目标,任何不基于业务做天马行空的架构都是对公司的不负责任。

三、应用架构

应用架构流程是基于业务架构来设计的,相辅相成的关系。每个应用可以作为独立的服务组件,把系统规划为一个个服务模块,进而进行开发,部署,运维等系列操作,所谓应用就是各个服务模块。首选要明确各个服务核心功能,其次确定各个服务间协调工作,保证服务的稳定性。微服务架构模式下,对系统进行水平拆分和垂直拆分,做到可以对任何高并发的服务模块进行独立扩展,灵活性非常好。

四、数据架构

数据架构是确定数据库模型的设计,不同业务场景下的数据可以选择不同种类的库来存储,例如搜索的数据可以使用ES,热点数据可以使用Redis,普通业务数据可以选择MySQL。

五、项目代码规划

1、分层结构设计

清晰的分层设计可以帮助快发人员快速的理解系统,缩短熟悉系统的时间。

2、模块依赖

给出模块依赖的明确顺序,比如接口(Web层)依赖服务层(Service层),服务实现(Impl)依赖持久层接口(Dao),持久层实现(DaoImpl)依赖表实体对象(Entity),避免系统陷入代码杂乱的坑中。

3、开发规范

每个项目开发必须要遵守统一的编码规范文档,统一命名格式,注释风格,这样可以方便其他人员快速阅读和理解代码逻辑。

六、技术选型落地

选择合适的基础框架,数据库,中间件,基于项目的开发规范,把应用架构完整的实现,这样项目就很难走偏。把握系统的高可用、高性能、扩展、伸缩、安全管理等方方面面的知识需要一个持久的积累和在实践中不断进步。

posted @ 2022-06-30 11:14  hanease  阅读(1316)  评论(0编辑  收藏  举报