一、背景簡介

分布式系統(tǒng)中存在很多拆分的服務(wù),在不斷迭代升級的過程中,會出現(xiàn)如下常見的棘手情況:

某個(gè)技術(shù)組件版本升級,依賴包升級導(dǎo)致部分語法或者API過期,或者組件修復(fù)緊急的問題,從而會導(dǎo)致分布式系統(tǒng)下各個(gè)服務(wù)被動(dòng)的升級迭代,很容易引發(fā)意外的問題;不同的服務(wù)中對組件的依賴和版本各不相同,從而導(dǎo)致不兼容問題的出現(xiàn),很難對版本做統(tǒng)一的管理和維護(hù),一旦出現(xiàn)問題很容易手忙腳亂,引發(fā)蝴蝶效應(yīng);

所以在復(fù)雜的系統(tǒng)中,對于依賴的框架和組件進(jìn)行統(tǒng)一管理和二次淺封裝,可以較大程度降低上述問題的處理成本與風(fēng)險(xiǎn),同時(shí)可以更好的管理和控制技術(shù)棧。

二、框架淺封裝

1、淺封裝作用

為什么淺封裝,核心目的在于統(tǒng)一管理和協(xié)調(diào)組件的依賴與升級,并對常用方法做一層包裝,實(shí)際上很多組件使用到的功能點(diǎn)并不多,只是在業(yè)務(wù)中的使用點(diǎn)很多,這樣給組件本身的迭代升級帶來了一定的難度:

例如某個(gè)組件常用的API中存在巨大風(fēng)險(xiǎn),或者替換掉過期的用法,需要對整個(gè)系統(tǒng)中涉及的地方做升級,這種操作的成本是非常高的;

如果是對這種常用的組件方法進(jìn)行二次包裝,作為處理業(yè)務(wù)的工具方法,那么解決上面的問題就相對輕松許多,只要對封裝的工具方法升級,服務(wù)的依賴升級即可,降低時(shí)間成本和風(fēng)險(xiǎn)。

通過淺封裝的手段,可以實(shí)現(xiàn)兩個(gè)方面的解耦:

業(yè)務(wù)與技術(shù)

技術(shù)棧中常用的方法進(jìn)行二次淺封裝,這樣可以較大程度的降低業(yè)務(wù)與技術(shù)的耦合,如此可以獨(dú)立的升級技術(shù)棧,擴(kuò)展功能而不影響業(yè)務(wù)服務(wù)的迭代。

框架與組件

不同的框架與組件都需要一定程度的自定義配置,同時(shí)分模塊管理,在不同的服務(wù)中引入特定的依賴,也可以在基礎(chǔ)包中做統(tǒng)一依賴,以此實(shí)現(xiàn)技術(shù)棧的快速組合搭配。

這里說的淺封裝,是指包裝常規(guī)常用的語法,組件本身就是技術(shù)層面的深度封裝,所以也不可能完全隔開技術(shù)棧原生用法。

2、統(tǒng)一版本控制

例如微服務(wù)架構(gòu)下,不同的研發(fā)組負(fù)責(zé)不同的業(yè)務(wù)模塊,然而受到開發(fā)人員的經(jīng)驗(yàn)和能力影響,很容易出現(xiàn)不同的服務(wù)組件選型不一致,或者相同的組件依賴版本不同,這樣很難對系統(tǒng)架構(gòu)做標(biāo)準(zhǔn)的統(tǒng)一管理。

對于二次封裝的方式,可以嚴(yán)格的控制技術(shù)棧的迭代擴(kuò)展,以及版本沖突的問題,通過對二次封裝層的統(tǒng)一升級,可以快速實(shí)現(xiàn)業(yè)務(wù)服務(wù)的升級,解決不同服務(wù)的依賴差異問題。

三、實(shí)踐案例

1、案例簡介

Java分布式系統(tǒng)中,微服務(wù)基礎(chǔ)組件(Nacos、Feign、Gateway、Seata)等,系統(tǒng)中間件(Quartz、Redis、Kafka、ElasticSearch,Logstash)等,對常用功能、配置、API等,進(jìn)行二次淺封裝并統(tǒng)一集成管理,以滿足日常開發(fā)中基礎(chǔ)環(huán)境搭建與臨時(shí)工具的快速實(shí)現(xiàn)。

  • butte-flyer 組件封裝的應(yīng)用案例;
  • butte-frame 常用技術(shù)組件二次封裝;

2、分層架構(gòu)

整體劃分五層:網(wǎng)關(guān)層、應(yīng)用層、業(yè)務(wù)層、中間件層、基礎(chǔ)層,組合成一套分布式系統(tǒng)。

服務(wù)總覽

服務(wù)名 分層 端口 緩存庫 數(shù)據(jù)庫 描述
flyer-gateway 網(wǎng)關(guān)層 8010 db1 nacos 路由控制
flyer-facade 應(yīng)用層 8082 db2 facade 門面服務(wù)
flyer-admin 應(yīng)用層 8083 db3 admin 后端管理
flyer-account 業(yè)務(wù)層 8084 db4 account 賬戶管理
flyer-quartz 業(yè)務(wù)層 8085 db5 quartz 定時(shí)任務(wù)
kafka 中間件 9092 --- ------ 消息隊(duì)列
elasticsearch 中間件 9200 --- ------ 搜索引擎
redis 中間件 6379 --- ------ 緩存中心
logstash 中間件 5044 --- es6.8.6 日志采集
nacos 基礎(chǔ)層 8848 --- nacos 注冊配置
seata 基礎(chǔ)層 8091 --- seata 分布事務(wù)
mysql 基礎(chǔ)層 3306 --- ------ 數(shù)據(jù)存儲

3、目錄結(jié)構(gòu)

butte-frame中對各個(gè)技術(shù)棧進(jìn)行二次封裝管理,在butte-flyer中進(jìn)行依賴引用。

butte-frame├── frame-base          基礎(chǔ)代碼塊├── frame-jdbc          數(shù)據(jù)庫組件├── frame-core          服務(wù)基礎(chǔ)依賴├── frame-gateway       路由網(wǎng)關(guān)├── frame-nacos         注冊與配置中心├── frame-seata         分布式事務(wù)├── frame-feign         服務(wù)間調(diào)用├── frame-security      安全管理├── frame-search        搜索引擎├── frame-redis         緩存管理├── frame-kafka         消息中間件├── frame-quartz        定時(shí)任務(wù)├── frame-swagger       接口文檔└── frame-sleuth        鏈路日志butte-flyer├── flyer-gateway       網(wǎng)關(guān)服務(wù):路由控制├── flyer-facade        門面服務(wù):功能協(xié)作接口├── flyer-account       賬戶服務(wù):用戶賬戶├── flyer-quartz        任務(wù)服務(wù):定時(shí)任務(wù)└── flyer-admin         管理服務(wù):后端管理

4、技術(shù)棧組件

系統(tǒng)常用的技術(shù)棧:基礎(chǔ)框架、微服務(wù)組件、緩存、安全管理、數(shù)據(jù)庫、定時(shí)任務(wù)、工具依賴等。

名稱 版本 說明
spring-cloud 2.2.5.RELEASE 微服務(wù)框架基礎(chǔ)
spring-boot 2.2.5.RELEASE 服務(wù)基礎(chǔ)依賴
gateway 2.2.5.RELEASE 路由網(wǎng)關(guān)
nacos 2.2.5.RELEASE 注冊中心與配置管理
seata 2.2.5.RELEASE 分布式事務(wù)管理
feign 2.2.5.RELEASE 微服務(wù)間請求調(diào)用
security 2.2.5.RELEASE 安全管理
sleuth 2.2.5.RELEASE 請求軌跡鏈路
security-jwt 1.0.10.RELEASE JWT加密組件
hikari 3.4.2 數(shù)據(jù)庫連接池,默認(rèn)
mybatis-plus 3.4.2 ORM持久層框架
kafka 2.0.1 MQ消息隊(duì)列
elasticsearch 6.8.6 搜索引擎
logstash 5.2 日志采集
redis 2.2.5.RELEASE 緩存管理與加鎖控制
quartz 2.3.2 定時(shí)任務(wù)管理
swagger 2.6.1 接口文檔
apache-common 2.7.0 基礎(chǔ)依賴包
hutool 5.3.1 基礎(chǔ)工具包

四、微服務(wù)組件

1、Nacos

Nacos在整個(gè)組件體系中,提供兩個(gè)核心能力,注冊發(fā)現(xiàn):適配微服務(wù)注冊與發(fā)現(xiàn)標(biāo)準(zhǔn),快速實(shí)現(xiàn)動(dòng)態(tài)服務(wù)注冊發(fā)現(xiàn)、元數(shù)據(jù)管理等,提供微服務(wù)組件中最基礎(chǔ)的能力;配置中心:統(tǒng)一管理各個(gè)服務(wù)配置,集中在Nacos中存儲管理,隔離多環(huán)境的不同配置,并且可以規(guī)避線上配置放開的風(fēng)險(xiǎn);

連接管理

spring:  cloud:    nacos:      # 配置讀取      config:        prefix: application        server-addr: 127.0.0.1:8848        file-extension: yml        refresh-enabled: true      # 注冊中心      discovery:        server-addr: 127.0.0.1:8848

配置管理

  • bootstrap.yml :服務(wù)中文件,連接和讀取Nacos中配置信息;
  • application.yml :公共基礎(chǔ)配置,這里配置mybatis組件;
  • application-dev.yml :中間件連接配置,用作環(huán)境標(biāo)識隔離;
  • application-def.yml :各個(gè)服務(wù)的自定義配置,參數(shù)加載;

2、Gateway

Gateway網(wǎng)關(guān)核心能力,提供統(tǒng)一的API路由管理,作為微服務(wù)架構(gòu)體系下請求唯一入口,還可以在網(wǎng)關(guān)層處理所有的非業(yè)務(wù)功能,例如:安全控制,流量監(jiān)控限流,等等。

路由控制:各個(gè)服務(wù)的發(fā)現(xiàn)和路由;

@Componentpublic class RouteFactory implements RouteDefinitionRepository {    @Resource    private RouteService routeService ;    /**     * 加載全部路由     * @since 2021-11-14 18:08     */    @Override    public Flux getRouteDefinitions() {        return Flux.fromIterable(routeService.getRouteDefinitions());    }    /**     * 添加路由     * @since 2021-11-14 18:08     */    @Override    public Mono save(Mono routeMono) {        return routeMono.flatMap(routeDefinition -> {            routeService.saveRouter(routeDefinition);            return Mono.empty();        });    }}

全局過濾:作為網(wǎng)關(guān)的基礎(chǔ)能力;

@Componentpublic class GatewayFilter implements GlobalFilter {    private static final Logger logger = LoggerFactory.getLogger(GatewayFilter.class);    @Override    public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {        ServerHttpRequest request = exchange.getRequest();        String uri = request.getURI().getPath() ;        String host = String.valueOf(request.getHeaders().getHost()) ;        logger.info("request host : {} , uri : {}",host,uri);        return chain.filter(exchange);    }}

3、Feign

Feign組件是聲明式的WebService客戶端,使微服務(wù)之間的調(diào)用變得更簡單,F(xiàn)eign通過注解手段,將請求進(jìn)行模板化和接口化管理,可以更加標(biāo)準(zhǔn)的管理各個(gè)服務(wù)間的通信交互。

響應(yīng)解碼:定義Feign接口響應(yīng)時(shí)解碼邏輯,校驗(yàn)和控制統(tǒng)一的接口風(fēng)格;

public class FeignDecode extends ResponseEntityDecoder {    public FeignDecode(Decoder decoder) {        super(decoder);    }    @Override    public Object decode(Response response, Type type) {        if (!type.getTypeName().startsWith(Rep.class.getName())) {            throw new RuntimeException("響應(yīng)格式異常");        }        try {            return super.decode(response, type);        } catch (IOException e) {            e.printStackTrace();            throw new RuntimeException(e.getMessage());        }    }}

4、Seata

Seata組件是開源的分布式事務(wù)解決方案,致力于提供高性能和簡單易用的分布式事務(wù)服務(wù),實(shí)現(xiàn)AT、TCC、SAGA、XA事務(wù)模式,支持一站式的分布式解決方案。

事務(wù)配置:基于nacos管理Seata組件的參數(shù)定義;

服務(wù)注冊:在需要管理分布式事務(wù)的服務(wù)中連接和使用Seata服務(wù);

seata:  enabled: true  application-id: ${spring.application.name}  tx-service-group: butte-seata-group  config:    type: nacos    nacos:      server-addr: ${spring.cloud.nacos.config.server-addr}      group: DEFAULT_GROUP  registry:    type: nacos    nacos:      server-addr: ${spring.cloud.nacos.config.server-addr}      application: seata-server      group: DEFAULT_GROUP

五、中間件集成

1、Kafka

Kafka是由Apache開源,具有分布式、分區(qū)的、多副本的、多訂閱者,基于Zookeeper協(xié)調(diào)的分布式消息處理平臺,由Scala和Java語言編寫。還常用于搜集用戶在應(yīng)用服務(wù)中產(chǎn)生的日志數(shù)據(jù)。

消息發(fā)送:封裝消息發(fā)送的基礎(chǔ)能力;

@Componentpublic class KafkaSendOperate {    @Resource    private KafkaTemplate kafkaTemplate ;    public void send (SendMsgVO entry) {        kafkaTemplate.send(entry.getTopic(),entry.getKey(),entry.getMsgBody()) ;    }}

消息消費(fèi):消費(fèi)監(jiān)聽時(shí)有兩種策略;

  • 消息生產(chǎn)方自己消費(fèi),通過Feign接口去執(zhí)行具體消費(fèi)服務(wù)的邏輯,這樣有利于流程跟蹤排查;
  • 消息消費(fèi)方直接監(jiān)聽,減少消息處理的流程節(jié)點(diǎn),當(dāng)然也可以打造統(tǒng)一的MQ總線服務(wù)(文尾);
public class KafkaListen {    private static final Logger logger = LoggerFactory.getLogger(KafkaListen.class);    /**     * Kafka消息監(jiān)聽     * @since 2021-11-06 16:47     */    @KafkaListener(topics = KafkaTopic.USER_TOPIC)    public void listenUser (ConsumerRecord record, Acknowledgment acknowledgment) {        try {            String key =  String.valueOf(record.key());            String body = record.value();            switch (key){ }        } catch (Exception e){            e.printStackTrace();        } finally {            acknowledgment.acknowledge();        }    }}

2、Redis

Redis是一款開源組件,基于內(nèi)存的高性能的key-value數(shù)據(jù)結(jié)構(gòu)存儲系統(tǒng),它可以用作數(shù)據(jù)庫、緩存和消息中間件,支持多種類型的數(shù)據(jù)結(jié)構(gòu),如字符串、集合等。在實(shí)際應(yīng)用中,通常用來做變動(dòng)頻率低的熱點(diǎn)數(shù)據(jù)緩存和加鎖機(jī)制。

KV數(shù)據(jù)緩存:作為Redis最常用的功能,即緩存一個(gè)指定有效期的鍵和值,在使用時(shí)直接獲取;

@Componentpublic class RedisKvOperate {    @Resource    private StringRedisTemplate stringRedisTemplate ;    /**     * 創(chuàng)建緩存,必須帶緩存時(shí)長     * @param key 緩存Key     * @param value 緩存Value     * @param expire 單位秒     * @return boolean     * @since 2021-08-07 21:12     */    public boolean set (String key, String value, long expire) {        try {            stringRedisTemplate.opsForValue().set(key,value,expire, TimeUnit.SECONDS);        } catch (Exception e){            e.printStackTrace();            return Boolean.FALSE ;        }        return Boolean.TRUE ;    }}

Lock加鎖機(jī)制:基于spring-integration-redisRedisLockRegistry,實(shí)現(xiàn)分布式鎖;

@Componentpublic class RedisLockOperate {    @Resource    protected RedisLockRegistry redisLockRegistry;    /**     * 嘗試一次加鎖,采用默認(rèn)時(shí)間     * @param lockKey 加鎖Key     * @return java.lang.Boolean     * @since 2021-09-12 13:14     */    @SneakyThrows    public  Boolean tryLock(T lockKey) {        return redisLockRegistry.obtain(lockKey).tryLock(time, TimeUnit.MILLISECONDS);    }    /**     * 釋放鎖     * @param lockKey 解鎖Key     * @since 2021-09-12 13:32     */    public  void unlock(T lockKey) {        redisLockRegistry.obtain(lockKey).unlock();    }}

3、ElasticSearch

ElasticSearch是一個(gè)基于Lucene的搜索服務(wù)器,它提供了一個(gè)分布式多用戶能力的全文搜索引擎,基于RESTful web接口,Elasticsearch是用Java開發(fā)的,是當(dāng)前流行的企業(yè)級搜索引擎。

索引管理:索引的創(chuàng)建和刪除,結(jié)構(gòu)添加和查詢;

基于ElasticsearchRestTemplate的模板方法操作;

@Componentpublic class TemplateOperate {    @Resource    private ElasticsearchRestTemplate template ;    /**     * 創(chuàng)建索引和結(jié)構(gòu)     * @param clazz 基于注解類實(shí)體     * @return java.lang.Boolean     * @since 2021-08-15 19:25     */    public  Boolean createPut (Class clazz){        boolean createIf = template.createIndex(clazz) ;        if (createIf){            return template.putMapping(clazz) ;        }        return Boolean.FALSE ;    }}

基于RestHighLevelClient原生API操作;

@Componentpublic class IndexOperate {    @Resource    private RestHighLevelClient client ;    /**     * 判斷索引是否存在     * @return boolean     * @since 2021-08-07 18:57     */    public boolean exists (IndexVO entry) {        GetIndexRequest getReq = new GetIndexRequest (entry.getIndexName()) ;        try {            return client.indices().exists(getReq, entry.getOptions());        } catch (Exception e) {            e.printStackTrace();        }        return Boolean.FALSE ;    }}

數(shù)據(jù)管理:數(shù)據(jù)新增、主鍵查詢、修改、批量操作,業(yè)務(wù)性質(zhì)的搜索封裝復(fù)雜度很高;

數(shù)據(jù)的增刪改方法;

@Componentpublic class DataOperate {    @Resource    private RestHighLevelClient client ;    /**     * 批量更新數(shù)據(jù)     * @param entry 對象主體     * @since 2021-08-07 18:16     */    public void bulkUpdate (DataVO entry){        if (CollUtil.isEmpty(entry.getDataList())){            return ;        }        // 請求條件        BulkRequest bulkUpdate = new BulkRequest(entry.getIndexName(),entry.getType()) ;        bulkUpdate.setRefreshPolicy(entry.getRefresh()) ;        entry.getDataList().forEach(dataMap -> {            UpdateRequest updateReq = new UpdateRequest() ;            updateReq.id(String.valueOf(dataMap.get("id"))) ;            updateReq.doc(dataMap) ;            bulkUpdate.add(updateReq) ;        });        try {            // 執(zhí)行請求            client.bulk(bulkUpdate, entry.getOptions());        } catch (IOException e) {            e.printStackTrace();        }    }}

索引主鍵查詢,分組查詢方法;

@Componentpublic class QueryOperate {    @Resource    private RestHighLevelClient client ;    /**     * 指定字段分組查詢     * @since 2021-10-07 19:00     */    public Map groupByField (QueryVO entry){        Map groupMap = new HashMap<>() ;        // 分組API        String groupName = entry.getGroupField()+"_group" ;        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();        sourceBuilder.size(0) ;        TermsAggregationBuilder termAgg = AggregationBuilders.terms(groupName)                                                             .field(entry.getGroupField()) ;        sourceBuilder.aggregation(termAgg);        // 查詢API        SearchRequest searchRequest = new SearchRequest(entry.getIndexName());        searchRequest.source(sourceBuilder) ;        try {            // 執(zhí)行API            SearchResponse response = client.search(searchRequest, entry.getOptions());            // 響應(yīng)結(jié)果            Terms groupTerm = response.getAggregations().get(groupName) ;            if (CollUtil.isNotEmpty(groupTerm.getBuckets())){                for (Terms.Bucket bucket:groupTerm.getBuckets()){                    groupMap.put(bucket.getKeyAsString(),bucket.getDocCount()) ;                }            }        } catch (IOException e) {            e.printStackTrace();        }        return groupMap ;    }}

4、Logstash

Logstash是一款開源的數(shù)據(jù)采集組件,具有實(shí)時(shí)管道功能。Logstash能夠動(dòng)態(tài)的從多個(gè)來源采集數(shù)據(jù),進(jìn)行標(biāo)準(zhǔn)化轉(zhuǎn)換數(shù)據(jù),并將數(shù)據(jù)傳輸?shù)剿x擇的存儲容器。

  • Sleuth:管理服務(wù)鏈路,提供核心TraceId和SpanId生成;
  • ElasticSearch:基于ES引擎做日志聚合存儲和查詢;
  • Logstash:提供日志采集服務(wù),和數(shù)據(jù)發(fā)送ES的能力;

logback.xml:服務(wù)連接Logstash地址,并加載核心配置;

                                ${DES_URI:- }:${DES_PORT:- }                                                        UTC                                                                            {                        "severity": "%level",                        "service": "${APP_NAME:-}",                        "trace": "%X{X-B3-TraceId:-}",                        "span": "%X{X-B3-SpanId:-}",                        "exportable": "%X{X-Span-Export:-}",                        "pid": "${PID:-}",                        "thread": "%thread",                        "class": "%logger{40}",                        "rest": "%message"                        }                                                            

5、Quartz

Quartz是一個(gè)完全由java編寫的開源作業(yè)調(diào)度框架,用來執(zhí)行各個(gè)服務(wù)中的定時(shí)調(diào)度任務(wù),在微服務(wù)體系架構(gòu)下,通常開發(fā)一個(gè)獨(dú)立的Quartz服務(wù),通過Feign接口去觸發(fā)各個(gè)服務(wù)的任務(wù)執(zhí)行。

配置參數(shù):定時(shí)任務(wù)基礎(chǔ)信息,數(shù)據(jù)庫表,線程池;

spring:  quartz:    job-store-type: jdbc    properties:      org:        quartz:          scheduler:            instanceName: ButteScheduler            instanceId: AUTO          jobStore:            class: org.quartz.impl.jdbcjobstore.JobStoreTX            driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate            tablePrefix: qrtz_            isClustered: true            clusterCheckinInterval: 15000            useProperties: false          threadPool:            class: org.quartz.simpl.SimpleThreadPool            threadPriority: 5            threadCount: 10            threadsInheritContextClassLoaderOfInitializingThread: true

6、Swagger

Swagger是常用的接口文檔管理組件,通過對API接口和對象的簡單注釋,快速生成接口描述信息,并且提供可視化界面可以快速對接口發(fā)送請求和調(diào)試,該組件在前后端聯(lián)調(diào)中,極大的提高效率。

配置基本的包掃描能力即可;

@Configurationpublic class SwaggerConfig {    @Bean    public Docket createRestApi() {        return new Docket(DocumentationType.SWAGGER_2)                .apiInfo(apiInfo())                .select()                .apis(RequestHandlerSelectors.basePackage("com.butte"))                .paths(PathSelectors.any())                .build();    }}

訪問:服務(wù):端口/swagger-ui.html即可打開接口文檔;

六、數(shù)據(jù)庫配置

1、MySQL

微服務(wù)架構(gòu)下,不同的服務(wù)對應(yīng)不同的MySQL庫,基于業(yè)務(wù)模塊做庫的劃分是當(dāng)前常用的方式,可以對各自業(yè)務(wù)下的服務(wù)做迭代升級,同時(shí)可以避免單點(diǎn)故障導(dǎo)致雪崩效應(yīng)。

2、HikariCP

HikariCP作為SpringBoot2版本推薦和默認(rèn)采用的數(shù)據(jù)庫連接池,具有速度極快、輕量簡單的特點(diǎn)。

spring:  datasource:    type: com.zaxxer.hikari.HikariDataSource    driver-class-name: com.mysql.cj.jdbc.Driver    url: jdbc:mysql://127.0.0.1:3306/${data.name.mysql}?${spring.datasource.db-param}    username: root    password: 123456    db-param: useUnicode=true&characterEncoding=UTF8&zeroDateTimeBehavior=convertToNull&useSSL=false    hikari:      minimumIdle: 5      maximumPoolSize: 10      idleTimeout: 300000      maxLifetime: 500000      connectionTimeout: 30000

連接池的配置根據(jù)業(yè)務(wù)的并發(fā)需求量,做適當(dāng)?shù)恼{(diào)優(yōu)即可。

3、Mybatis

Mybatis持久層的框架組件,支持定制化SQL、存儲過程以及高級映射,MyBatis-Plus是一個(gè)MyBatis的增強(qiáng)工具,在MyBatis的基礎(chǔ)上只做增強(qiáng)不做改變,可以簡化開發(fā)、提高效率。

mybatis-plus:  mapper-locations: classpath*:/mapper/**/*.xml  configuration:    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  • 同系列-架構(gòu):┃ 分布式 ┃ 消息中間件 ┃ 事務(wù)管理 ┃ 高并發(fā) ┃ 緩存管理 ┃
  • 同系列-組件:┃ Kafka消息 ┃ ElasticSearch搜索 ┃ Redis緩存 ┃ Quartz任務(wù) ┃ Swagger2接口 ┃

七、源代碼地址

應(yīng)用倉庫:https://gitee.com/cicadasmile/butte-flyer-parent組件封裝:https://gitee.com/cicadasmile/butte-frame-parent