Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >微服务的架构下,如何根据业务抽象出适合自己系统的组件?

微服务的架构下,如何根据业务抽象出适合自己系统的组件?

作者头像
码农架构
发布于 2021-09-18 04:06:14
发布于 2021-09-18 04:06:14
52700
代码可运行
举报
文章被收录于专栏:码农架构码农架构
运行总次数:0
代码可运行

导读:基于SpringBoot/SpringCloud微服务的架构下,我们或多或少会根据业务抽象出适合自己系统的组件或SDK,来应对对内、对外的拓展。

在SpringBoot/SpringCloud先前介绍了一些,如:

@Conditional 来指定指定条件的时候才将某个 bean 加载到应用上下文中。

@FunctionalInterface 函数式接口申明

@JsonTypeInfo 在Java类继承的情况下如何实现父类及子类的JSON序列化与反序列化。等等其他的注解标识,极大简化了业务逻辑和代码。

当然不这么实现是不是没有其他方式实现了,其实也不是唯一的方式!简单暴力的方式也是可以的,写业务逻辑时,if-else 可能是最容易想到的逻辑方式了。然而大量堆砌的 if-else 毫无疑问将给代码维护带来巨大的困难。如果想用if-else 来完善你的业务组件,尽量优化你的代码,避免后期业务拓展棘手。

如何优化你的if-else?来试试“责任链模式+策略模式”

如果在同一JVM中上述的方式没有多大问题,但是分布在不同的JVM中(微服务集群),上述的方案估计要丢弃了。

在阅读下文时,考虑几个问题:

  • 自定义的组件规则/SDK包,什么时候扫描才合理?
  • 组件元数据怎样采集?

案例场景


目前存在三个服务,引擎层服务A,业务服务B、业务服务C。A|B|C在同一注册集群中,A需提供组件于B\C服务使用,依赖关系如下图所示。

方案选型


业务服务启动后主动上报服务引擎A所需要的元数据信息和提供服务的实现服务引擎A指定的数据上报接口

方案一 | 业务服务主动上报
优点:

服务器引擎只集成对外开放的业务,业务服务模块需自行完善数据上报,对于服务引擎A来讲。业务简单化,不需要关注业务服务太多场景。

只需要持久化或者缓存业务服务上报的元数据即可!

缺点:

缺点就是对于服务引擎A中集成SDK的升级调整,需要既要考虑自身服务的升级改造,同时锁涉及的业务模块也需要随之调整升级。对于业务模块或者第三方服务是非常不友好的。

▐ 方案二 | 引擎服务主动采集

引擎服务在完善业务模块元数据持久化或者缓存的同时,并且提供业务模块采集的元数据SDK或客户端,便于业务模块快速集成,完善整体架构。

方案实现


定义基础元注解

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * Meta-annotation definition
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MetaProperty{
    // Custom properties
}

再此基础上可对于基础元注解再进行内部拓展

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * Service engine metadata extension
 */
@Target({ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MetaPropertyExtension{
    // Custom properties
}

定义好元数据注解之后,面临的问题如开篇所提到的:自定义的组件规则/SDK包,什么时候扫描才合理?

思考一个问题,如果系统启动后再运行时业务初始化的时候发现元数据还未采集业务就阻塞住了,所以对于元数据的采集处理自然在系统启动时候就开应该处理。

对于SpringCloud服务启动后可以监听处理,简化代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Slf4j
@Component
@Order(ReadyEventOrder.FRAMEWORK + 10)
public class InitMetaAwareDefinition extends AbstractFormFactory implements NamingConstant, MetaConstant,
        ApplicationContextAware,
        ApplicationListener<ApplicationReadyEvent> {

    private AnnotatedMetaDefinition metaDefinition;

    /** 监听器是否已执行标示,该监听器只需要执行一次 */
    public static volatile AtomicBoolean executed = new AtomicBoolean(false);

    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        initMetaDefinition();
    }

    @Bean
    @Override
    public AnnotatedMetaDefinition setAnnotatedMetaDefinition() {
        metaDefinition = new ScannedAnnotatedMetaDefinition();
        return metaDefinition;
    }

    /** 注册元数据定义 */
    @Override
    public void initMetaDefinition() throws KmssRuntimeException {
        try {
            CompletableFuture.runAsync(() -> {
                // 扫描注册处理
            });
        } catch (Exception e) {
            executed.set(false);
            log.debug("引擎元数据注册失败!", e);
        }
        log.debug("引擎元数据注册成功!");
    }

    /** 初始化应用上下文 */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    protected ApplicationContext applicationContext;
}

确定触发时间后,要扫描注册处理

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Set<BeanDefinition> beanDefinitionSet = scanByAnnotation(BASE_PACKAGE
                + NamingConstant.DOT
                + "**"
                + NamingConstant.PATH_PREFIX_ENTITY, MetaProperty.class);
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * 扫描包路径下所有使用注解的类定义
 */
private Set<BeanDefinition> scanByAnnotation(String basePackage,
                                             Class<MetaProperty> annotationClass) {
    ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(
        false);
    provider.addIncludeFilter(new AnnotationTypeFilter(annotationClass, true, true));
    return provider.findCandidateComponents(basePackage);
}

ClassPathScanningCandidateComponentProvider 从基础包中提供候选组件的组件提供者。

如果可用,可以使用the index ,否则扫描类路径。通过应用排除和包含过滤器来识别候选组件。

AnnotationTypeFilter , AssignableTypeFilter支持使用Indexed注释的注释/超类上的包含过滤器:如果指定了任何其他包含过滤器,则忽略索引并改为使用类路径扫描。此类实现基于 Spring 的MetadataReader工具,由 ASM ClassReader支持。后续针对源码再做详细讲解

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@SneakyThrows
public void registerMetaDefinition(BeanDefinition beanDefinition) {
        try {
            // 元数据格式化
            // 保存拓展元数据(远程调用引擎服务A接口)
        } catch (Exception e) {
            throw new RuntimeException("初始化元数据失败异常!", e);
        }
    }

总结


针对服务集成无非两种方案,要么定义规则,乙方根据规则来完善业务,另外一种就是甲方定义好规则并且完善好业务,乙方集成使用即可。

目前市面上最多的还是第二种方案,毕竟后期迭代维护效率更好,版本更好把控。如果后期需要针对基础升级改造可完全替代集成SDK和业务模块的SDK依赖即可

- END -

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-09-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码农架构 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
30个类手写Spring核心原理之Ioc顶层架构设计(2)
Annotation的代码实现我们还是沿用Mini版本的,保持不变,复制过来便可。
Tom弹架构
2021/12/10
4840
实战|如何优雅地自定义Prometheus监控指标
大家好!我是"无敌码农",今天要和大家分享的是在实际工作中“如何优雅地自定义Prometheus监控指标”!目前大部分使用Spring Boot构建微服务体系的公司,大都在使用Prometheus来构建微服务的度量指标(Metrics)类监控系统。而一般做法是通过在微服务应用中集成Prometheus指标采集SDK,从而使得Spring Boot暴露相关Metrics采集端点来实现。
用户5927304
2021/04/14
2.1K0
源码学习系列之SpringBoot自动配置(篇一)
ok,本博客尝试跟一下Springboot的自动配置源码,做一下笔记记录,自动配置是Springboot的一个很关键的特性,也容易被忽略的属性,因为这个属性被包括在@SpringBootApplication注解里,所以不去跟一下源码都不知道还有这个属性,ps:本博客源码基于SpringBoot1.5.7版本
SmileNicky
2019/11/04
4820
Spring高手之路10——解锁Spring组件扫描的新视角
首先,我们将探讨一些Spring框架中IOC(Inversion of Control)的高级特性,特别是组件扫描的相关知识。组件扫描是Spring框架中一个重要的特性,它可以自动检测并实例化带有特定注解(如@Component, @Service, @Controller等)的类,并将它们注册为Spring上下文中的bean。这里,我们会通过一些详细的例子来阐明这些概念,并且展示如何在实际的代码中使用这些特性。
砖业洋__
2023/07/28
9820
Spring高手之路10——解锁Spring组件扫描的新视角
微服务架构学习Day01-SpringBoot入门
激活指定Profile: 1.在主配置文件application.properties中指定激活:
攻城狮Chova
2022/01/22
2980
【Spring注解驱动开发】使用@ComponentScan自动扫描组件并指定扫描规则
作者个人研发的在高并发场景下,提供的简单、稳定、可扩展的延迟消息队列框架,具有精准的定时任务和延迟队列处理功能。自开源半年多以来,已成功为十几家中小型企业提供了精准定时调度方案,经受住了生产环境的考验。为使更多童鞋受益,现给出开源框架地址:
冰河
2020/10/29
6460
微服务架构之服务框架Dubbo-注解配置剖析
我们再进入到DubboComponentScan.class去探索,发现还是个注解,真正的实现是DubboComponentScanRegistrar.class,而它是实现了ImportBeanDefinitionRegistrar接口的
公众号_松华说
2019/07/16
7500
Java 注解
JDK 5.0 开始, Java 增加了对元数据的支持, 也就是 Annotation(注解) 安闹太湿~
Java_慈祥
2024/08/06
1390
Spring Boot 3系列之-启动类详解
Spring Boot是一个功能强大、灵活且易于使用的框架,它极大地简化了Spring应用程序的开发和部署流程,使得开发人员能够更专注于业务逻辑的实现。在我们的Spring Boot 3系列之一(初始化项目)文章中,我们使用了Spring官方网站生成的Spring Boot项目作为示例。在该项目中,我们可以找到一个名为XjdocApplication的启动类,它是Spring Boot应用程序的入口点。本文将详细解释这个启动类的作用和功能。
修己xj
2023/11/06
4650
Spring Boot 3系列之-启动类详解
【一起学源码-微服务】Feign 源码一:源码初探,通过Demo Debug Feign源码
上一讲深入的讲解了Ribbon的初始化过程及Ribbon与Eureka的整合代码,与Eureka整合的类就是DiscoveryEnableNIWSServerList,同时在DynamicServerListLoadBalancer中会调用PollingServerListUpdater 进行定时更新Eureka注册表信息到BaseLoadBalancer中,默认30s调度一次。
一枝花算不算浪漫
2020/01/13
9530
【一起学源码-微服务】Feign 源码一:源码初探,通过Demo Debug Feign源码
@ComponentScan原理分析
这是@ComponentScan的官方介绍,大致意思就是扫描注册bean的一个注解,会扫描对应路径下被@Component标注的类,和xml方式的<context:component-scan>作用相似,常用的方式是basePackages方式。
叔牙
2022/01/04
8890
@ComponentScan原理分析
就想搞明白,component-scan 是怎么把Bean都注册到Spring容器的!
你经历过618和双11吗?你加入过大促时候那么多复杂的营销活动赚几毛钱吗?你开发过连读明白玩法都需要一周但只使用3天的大促需求吗?有时候对于有些产品的需求真的是太复杂了,复杂到开发、测试都需要在整个过程中不断的学习最后才可能读懂产品为啥这样的玩,要是一个长期的活动可能也就算了,培养用户心智吗!但这一整套拉新、助力、激活、下单、投保、领券、消费、开红包等等一连串的骚操作下来,如果在线上只用3天呢,或者是只用1天,那TM连参与的用户都没弄明白呢,活动就结束了,最后能打来什么样好的数据呢?对于这样流程复杂,估计连羊毛当都看不上!!!
小傅哥
2021/07/28
6870
就想搞明白,component-scan 是怎么把Bean都注册到Spring容器的!
将Bean交给Spring容器管理的几种方式
所谓IoC,对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系。
JanYork_简昀
2022/10/28
9200
将Bean交给Spring容器管理的几种方式
Spring Boot - 自动装配中的不可忽视的@Import
在使用 Spring Boot 时,@Import 也是一个非常常见的注解,可以用来动态创建 Bean。
小小工匠
2021/08/17
1.7K0
Spring Boot - 自动装配中的不可忽视的@Import
分布式系列接口调用openfeign小试牛刀---解读源码告诉你为什么接口可以直接注册到spring容器中
关于分布式我们之前主要集中讨论了服务治理。eureka、consul、zookeeper我们分别从三个角度不同程度的学习了这三个框架的原理及区别。这些作为前期springcloud的重要组成部分是我们学习分布式不容忽视的章节。至于现在springcloud alibaba我们这里重头菜要留到最后。对springcloud alibaba感兴趣还请关注我后续会更新相关内容
啵啵肠
2023/11/29
3700
Spring5参考指南:组件扫描
上一篇文章我们讲到了annotation-config配置,它主要用于bean内部的属性注入。而bean本身则需要通过配置的方式来定义。如果想使用配置的方式来定义bean,则可以使用component-scan,如下:
程序那些事
2020/07/07
6780
Spring源码学习笔记(7)——使用@Import导入组件
@Import注解的作用是导入其他的配置类或者组件,等同于在applicationContext.xml文件中添加如下配置
张申傲
2020/09/03
7220
思尐-ChatGPT:你不懂可以来问我啊!
Hello,大家好,我是java小面。不知道大家有没有自己实现过一个注解来替换Spring原有注解的经历,有人说Spring不是有注解给你用吗?干嘛还要特地的去实现一个来替换呢?
灬沙师弟
2023/03/07
1990
思尐-ChatGPT:你不懂可以来问我啊!
《Spring 手撸专栏》第 15 章:万人之敌,通过注解给属性注入配置和Bean对象
你听过扰动函数吗?你写过斐波那契(Fibonacci)散列吗?你实现过梅森旋转算法吗?怎么 没听过这些写不了代码吗!不会的,即使没听过你一样可以写的了代码,比如你实现的数据库路由数据总是落在1库1表它不散列分布、你实现的抽奖系统总是把运营配置的最大红包发出去提高了运营成本、你开发的秒杀系统总是在开始后的1秒就挂了货品根本给不出去。
小傅哥
2021/08/20
6230
《Spring 手撸专栏》第 15 章:万人之敌,通过注解给属性注入配置和Bean对象
Spring中Enable*功能的使用
@Enable** 注解,一般用于开启某一类功能。类似于一种开关,只有加了这个注解,才能使用某些功能。
zeody
2019/05/05
1.7K0
Spring中Enable*功能的使用
推荐阅读
相关推荐
30个类手写Spring核心原理之Ioc顶层架构设计(2)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验