一次自己搭建springCloud 应用以及思考

前言

本次搭建springCloud的根本目偏向于应用,因为之前公司主流框架是SpringBoot那套。基于拓宽视野,我准备搭建一个cloud框架,然后再搭建的过程中,我同时会去引用一些我所知道,但是没有实际引用过的中间件。

写下这篇文章的本意在于记录一下,在搭建的过程中,方才明了,知易行难。

过程中遇到了许许多多的坑,也做一些统计和记录,方便自己。当然如果有对别有帮助,那最好不过。

框架图

再聊具体框架之前,我们先思考一下,springCloud,springBoot,Spring之间的关系。(以下所有均个人思考)

Spring是一个集成框架,他最大的作用和目的在于简化开发,为企业提供快速解决方案。他诞生的背景是在当时J2EE横行的时代,因为当时代码框架的冗余和臃肿,他提出来轻量化的技术框架(虽然光用spring好像并不怎么轻量)。我觉得Spring最大的出彩点,并不是像很多博客说的那样,是AOP,IOC,DI,甚至也不应该是Bean,事物等等。而是他提供了一个代码编写框架,做到了很多和第三方之间的桥接,比如早期的Struts、Hibernate,Spring没有实现这些,但是Spring做到了引用他们,兼容他们,组成了一个连贯的框架体系。

当然优势就是在他的初衷之后,spring有着低代码侵入,成熟的现有可使用的其他框架(ORM 日志 Quartz等等),AOP的切面,web mvc的规范,IOC的控制反转等等。

SpringBoot是在Spring上的优化,它主要优化了Spring繁琐的配置,复杂的项目版本管理,分布式的支持,以及其他一些。

SpringBoot的最大的一个特点就是”约定大于配置“。

同时他有两个核心功能:起步依赖(我把他理解为Starter),自动配置

往往用SpringBoot搭建的框架,我们认为他是分布式的,一套SpringBoot的框架,往往会搭配上一个注册中心(Zookeeper,Nacos),一个RPC调度服务(dubbo),一些成熟的ORM框架(Mybatis,Mybatis plus),还有一些成熟的第三方中间件(redis,mq,es,kafaka等等)

SpringCloud是在SpringBoot上的集成优化,如果说boot是一个标准的分布式,那我认为cloud是一个真正的微服务架构体系。

他有两个主要的生态源 spring-cloud-starter-netflix 与 spring-cloud-alibaba

然后他们提供了更多的一站式的分布式微服务的解决框架,包括zuul,eureka,config,Hystrix,consul,stream等等

最后,由于本次我搭建的是cloud,我简单的画一下我所理解的架构图

项目工程概览

父工程

在一切开始的时候,我们需要一个父工程,用于描述pom,管理项目。

我有一个cloud的父工程,他没有任何实现,只是去做了pom.xml管理。需要说明的是,在这个pom.xml里面的dependency是不做引入实际的jar的,而自己的子module里面才会去做实现

具体他为何是申明依赖,而不去做实现是依赖于dependencyManagement

<dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring.boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-autoconfigure</artifactId>
                <version>${spring.boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>2021.0.1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <!--https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E -->
                <version>2.2.7.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
                <version>${eureka.version}</version>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${mysql.version}</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid-spring-boot-starter</artifactId>
                <version>${druid.version}</version>
            </dependency>
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>${mybatis.spring.boot.version}</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>${alibaba.fastjson}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
                <version>${ribbon.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-openfeign</artifactId>
                <version>${openfeign.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
                <version>${hystrix.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
                <version>${hystrix.version}</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-seata</artifactId>
                <version>${cloud.seata.version}</version>
                <exclusions>
                    <exclusion>
                        <groupId>io.seata</groupId>
                        <artifactId>seata-spring-boot-starter</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>io.seata</groupId>
                <artifactId>seata-spring-boot-starter</artifactId>
                <version>${io.seata.version}</version>
            </dependency>

        </dependencies>
    </dependencyManagement>

版本管理

其实我觉得最痛苦的就是这个

这是我的版本

<properties>
    <project.encoding>UTF-8</project.encoding>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <java.version>1.8</java.version>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <maven.compiler.compilerVersion>3.3</maven.compiler.compilerVersion>
    <junit.version>4.12</junit.version>
    <!-- 重要组件版本 -->
    <!--这些都是自定义的版本号 子工程做引用 -->
    <mysql.version>8.0.28</mysql.version>
    <!---https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core/2.17.2 -->
    <log4j.version>2.17.2</log4j.version>
    <!--https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter/2.2.2-->
    <mybatis.spring.boot.version>2.2.2</mybatis.spring.boot.version>
    <!-- https://mvnrepository.com/artifact/com.alibaba/druid/1.2.8 这里有涉及mysql的-->
    <druid.version>1.2.8</druid.version>
    <spring.boot.version>2.6.3</spring.boot.version>
    <spring.cloud.version>2021.0.1</spring.cloud.version>
    <alibaba.fastjson>1.2.53</alibaba.fastjson>
    <eureka.version>3.1.1</eureka.version>
    <ribbon.version>2.2.10.RELEASE</ribbon.version>
    <openfeign.version>3.1.1</openfeign.version>
    <hystrix.version>2.2.10.RELEASE</hystrix.version>
    <cloud.seata.version>2.2.0.RELEASE</cloud.seata.version>
    <io.seata.version>1.2.0</io.seata.version>
</properties>

然后这是官网的版本发布,我选择了最新的稳定发布版本(release)

https://spring.io/projects/spring-cloud

然后这个是对应的boot与cloud之间的版本依赖关系,他给出了一个范围

https://start.spring.io/actuator/info

而最终我敲定了这个

如果说上面的这些都有迹可循的话,那么其他的版本我都是靠这个

https://mvnrepository.com

去寻找依赖,稳定版本

项目结构分层

下图是我的整个项目图(我原本想给一个pom的依赖图的,然后一打开太乱了,没有具体研究怎么只看当前工程的依赖,所以就只给了项目图)。

然后我有一个framework的工程,这个工程我是准备集成第三方和业务无关的中间件,诸如redis,mq的适当封装,一些具体的工具类。

在然后就是我的business他会去做一些业务抽象,比如抽象的返回结果

最后就是各个实际的业务service,需要说明的是hystrix-databoard是hystrix的控制台。eurekaService是eureka的服务端

注册中心

任何一个分布式微服务架构,他都需要一个注册中心,需要做服务发现和注册(集群情况下,在宕机的时候,也要选举出一个可用服务),这里我用了eureka

具体的搭建服务 可以看一下这篇博客:SpringCloud(二)-手把手教你搭建Eureka Server和Eureka Client - 简书

那为什么我要用eureka呢?是因为我看到的springCloud大家用的不是nacos就是eureka,而我准备两个都用,所以eureka做注册中心,nacos做配置中心

选型比较

市面上我知道的注册中心,有Nacos,eureka,Zookeeper,consul 还有一个阿波罗(Apollo)。这里只说前四者,因为阿波罗我只限于听过,我记得他是携程研发的,总归感觉他和nacos很像

然后这是网上的一张图

服务通讯

在有了刚刚的注册中心之后,服务与服务之间需要做通讯,我所知道的主流的通讯方式有两种

一种是RPC 也就是远程过程调用,具体原理如下

1.建立tcp连接 2.寻址 3.发送方序列化二进制 4.接收方反序列化 5.发送方在反序列化接收方的传值

引用自 谁能用通俗的语言解释一下什么是 RPC 框架? - 知乎

一种是Rest

那么Rest本身只是一个架构风格,他是通过http做通讯的,他严格按照crud各个来进行区别。

然后这是网上看到的两者之间的区别

  • 对性能有着严格的要求:RPC
  • 考虑学习成本,团队成员的上手难度以及开发效率成本:REST
  • 对外开放的需求,rpc需要进一步转换,而rest可直接对外开放:REST
  • 代码耦合度要求松散耦合:REST
  • 与其他框架集成的难度低,微服务框架基本支持rest:REST
  • 异步需求,rest需要额外的实现手段,如通过中间件等:RPC

所以在对于微服务多主机的场景下,我觉得rest更加合适,这边具体选型就是Fegin

restTemplate

其实在最一开始的时候,我用的是这个restTemplate来进行http的调度,但是在用他的时候,我总觉得我在写原生的jdbc在对数据操作。

实在是过于丑陋,网址端口都要写死

OpenFegin

后来我就用了这个,相对来说,这个封装了http请求,直接操作的是对应的service的controller,从代码结构层次上看,优雅了很多。

然后这个是相关的底层流程:

SpringBoot 应用启动时, 由针对 @EnableFeignClient 这一注解的处理逻辑触发程序扫描 classPath中所有被@FeignClient 注解的类, 这里以 DemoService 为例, 将这些类解析为 BeanDefinition 注册到 Spring 容器中

Sping 容器在为某些用的 Feign 接口的 Bean 注入 DemoService 时, Spring 会尝试从容器中查找 DemoService 的实现类

由于我们从来没有编写过 DemoService 的实现类, 上面步骤获取到的 DemoService 的实现类必然是 feign 框架通过扩展 spring 的 Bean 处理逻辑, 为 DemoService 创建一个动态接口代理对象, 这里我们将其称为 DemoServiceProxy 注册到spring 容器中。

Spring 最终在使用到 DemoService 的 Bean 中注入了 DemoServiceProxy 这一实例。

当业务请求真实发生时, 对于 DemoService 的调用被统一转发到了由 Feign 框架实现的 InvocationHandler 中, InvocationHandler 负责将接口中的入参转换为 HTTP 的形式, 发到服务端, 最后再解析 HTTP 响应, 将结果转换为 Java 对象, 予以返回。

原文链接:Spring Cloud OpenFeign 工作原理解析_萧萧九宸的博客-CSDN博客_openfeign底层原理

在使用OpenFegin之前,记得在主启动类上增加一个@EnableFeignCleints的注解

然后以下是部分使用代码

@Autowired
GoodsService gdsService;

@ResponseBody()
@GetMapping(value = "/goods/get")
@HystrixCommand
public CommonResult getGoods(@RequestParam(name = "Id", required = true) Long id) {
    return gdsService.getGoods(id);
}

@ResponseBody()
@RequestMapping(value = "/goods/create", method = {RequestMethod.POST})
public CommonResult createGoods(@RequestBody(required = true) String goods) {
    return gdsService.createGoods(goods);
}

这里的FeginClient的name 就是我对应的服务提供方的applicationName,配置在yml文件中的那个

@Component
@FeignClient(name = "cloud-product-service", fallbackFactory = HystrixClientFallbackFactory.class)
public interface GoodsService {

    /**
     * 查询
     *
     * @return
     */
    @GetMapping("goods/get")
    public CommonResult getGoods(@RequestParam("Id") Long id);

    /***
     * create
     * @param goods
     * @return
     */
    @PostMapping(value = "/goods/create", consumes = "application/json")
    public CommonResult createGoods(@RequestBody(required = true) String goods);

}

负载均衡策略

一个微服务系统,既然要高可用,就必然会涉及到服务集群,既然涉及到了集群,那必然会说一下负载均衡。

负载均衡策略大致有以下几种:

  • 简单随机
  • 加权随机
  • 简单轮询
  • 简单加权轮询
  • 平滑加权轮询
  • 一致性哈希
  • 最少活跃数

我记得其实真正的最好轮询方法应该是最小活跃数。因为只有这种轮询方式,才是真正的,动态的轮询,能达到服务的最大可用。

版本迁移

Spring Cloud 2020版本以后,默认移除了对Netflix的依赖,其中就包括Ribbon,官方默认推荐使用Spring Cloud Loadbalancer正式替换Ribbon,并成为了Spring Cloud负载均衡器的唯一实现。

大概的pom如下:

当时我就是因为这个版本错了,半天服务调度都有问题,死活不行。

这里面其实不需要额外引入,它内部其实有loadbanlancer策略

ribbon

ribbon本质上是一个客户端的负载均衡,而我们也可以自定义负载均衡策略,但是他就是停更了。

这个gitHub官网 https://github.com/Netflix/ribbon

ribbon与nginx

那同样都是负载均衡,ribbon和nginx区别在哪里

 

其实他们区别特别大:

  • Nginx 是服务器负载均衡,客户端所有请求都会交给nginx, 然后 nginx 实现转发请求。即负载均衡是由服务端实现的。
  • Ribbon 本地负载均衡,在调用微服务接口的时候,会在注册中心上获取注册信息服务列表后缓存到JVM 本地,从而在本地实现RPC远程 服务调用技术

从实现方式看:

1、nginx集中式

即在服务的消费方和提供方之间使用独立的LB 设施(可以是硬件,如F5, 也可以是软件如 Nginx ), 由该设置负责把访问请求通过某种策略转发至服务的提供方

2、ribbon进程内 LB

将 LB 逻辑集成到消费方,消费方从服务注册中心获取有哪些地址可用,然后自己再从这些地址中选择一个适合的服务器。

Ribbon 就属于进程内LB,它只是一个类库,集成于消费方进程,消费方通过它来获取服务提供方的地址。

参考 springcloud----负载均衡--Ribbon与LoadBalance_千钧~的博客-CSDN博客_loadbalancer和ribbon

Spring Cloud Loadbalancer

其实对于这个loadbalancer,我本地根本就没有引入,他在eureka里面。

验证的话,也很好验证,我只要启动多个provider服务,大概像这样,指定好端口就可以启动多个服务,作为集群来看到了

当然配置yml改一下,就可以实现动态的端口注入了

最后的就是这样,对应的product服务,有多个

至于他的底层,我看见网上有说,调用的是ribbon的客户端。

限流 降级 熔断 

在聊这个话题之前,你可以看一下我在很久很久以前写的blog: 分布式降级,限流,熔断_TT_4419的博客-CSDN博客

对于一个分布式微服务架构来说,你必然会瞬时大量请求的业务场景,而这很有可能会导致服务堆积,而一旦堆积的请求处理不及时,服务调度链路复杂过长,你就是会服务扇出,最终大家一起都挂了。

针对于大量瞬时请求,第一步当然是缓存。

我不管是服务器内存缓存,还是redis的缓存,他始终是挡在并发的第一线。当然这里面会涉及到很多东西,比如缓存击穿,缓存穿透,缓存数据不一致,缓存雪崩,当然这些都是有成熟方案的,不管是缓存空对象,布隆过滤器,缓存预热,缓存降级,设置key的TTL随机值,延迟双删策略

那么对于服务的限流、降级、熔断,是有成熟的中间件来去实现这些的,那这里我用的是Hystrix。

而为什么用这个呢,一方面官网上有这个组件,一方面我之前写的博客主体就是sentinel

然后其实Hystrix值提供了降级和熔断

降级:

  • 服务降级是当服务器压力剧增的情况下,根据当前业务情况及流量对一些服务和页面有策略的降级,以此释放服务器资源以保证核心任务的正常运行。
  • 通俗的讲就是当服务器繁忙时,不让服务继续在这里耗着,而是返回一个友好的提示【一个Fallback方法】,释放该线程的资源,减轻压力。
  • 降级往往是针对于客户端的,往往是服务器级别的,基本上的应用场景属于下游服务不可用了,则内部调用一个FallBack

触发条件有以下几种:

  • 程序运行时异常 【即程序错误异常】
  • 超时
  • 服务熔断也会触发服务降级
  • 线程池的最大线程数已满,无可再用线程

关键代码如下:

我这里分为两种FallBack,一个是全局FallBack。他指的是我针对于所有服务的返回,一个是指定FallBack,他指的是我针对于当前这个服务返回的错误信息,需要做的特殊提示

 

@RestController
@Slf4j
public class GoodsControllerConsume extends ConsumeBaseController {

    @Autowired
    GoodsService gdsService;

    @ResponseBody()
    @GetMapping(value = "/goods/get")
    @HystrixCommand
    public CommonResult getGoods(@RequestParam(name = "Id", required = true) Long id) {
        return gdsService.getGoods(id);
    }

    @ResponseBody()
    @RequestMapping(value = "/goods/create", method = {RequestMethod.POST})
    public CommonResult createGoods(@RequestBody(required = true) String goods) {
        return gdsService.createGoods(goods);
    }


}
/**
 * @author :shengjie.tang
 * @date :Created in 2022/5/2 21:00
 * @description 抽象controller 实现全局降级、熔断方法
 * @modified By:
 * @version: 1$
 */
@DefaultProperties(defaultFallback = "consumerGlobalHandler")
public class ConsumeBaseController {

    /**
     * fallback 方法  当方法出现异常时调用该方法
     *
     * @return
     */
    public CommonResult consumerGlobalHandler() {
        return CommonResult.error("500", " GlobalHandler异常信息处理  系统繁忙,请稍后再试!");
    }


}

这里的注解使用的FallbackFactory 而不是FallBack方法

@Component
@FeignClient(name = "cloud-product-service", fallbackFactory = HystrixClientFallbackFactory.class)
public interface GoodsService {

    /**
     * 查询
     *
     * @return
     */
    @GetMapping("goods/get")
    public CommonResult getGoods(@RequestParam("Id") Long id);

    /***
     * create
     * @param goods
     * @return
     */
    @PostMapping(value = "/goods/create", consumes = "application/json")
    public CommonResult createGoods(@RequestBody(required = true) String goods);

}
@Component
public class HystrixClientFallbackFactory implements FallbackFactory<GoodsService> {

    private static final Logger logger = LoggerFactory.getLogger(HystrixClientFallbackFactory.class);

    @Override
    public GoodsService create(Throwable cause) {
        HystrixClientFallbackFactory.logger.info("fallback reason was: {} ", cause.getMessage());
        return new GoodsService() {

            @Override
            public CommonResult getGoods(Long id) {
                return CommonResult.error("500", "获取商品的时候,服务不可用,降级了");
            }

            @Override
            public CommonResult createGoods(String goods) {
                return CommonResult.error("500", "创建商品的时候,服务不可用,降级了");
            }
        };
    }
}

熔断:

服务熔断的作用类似于我们家用的保险丝,当某服务出现不可用或响应超时的情况时,为了防止整个系统出现雪崩,暂时停止对该服务的调用。

服务熔断也会触发服务降级,服务熔断会直接暂时拒绝请求,然后调用服务降级方法返回友好提示。

服务熔断机制:当出现故障时,在固定时间窗口内,接口调用超时比率达到一个阈值,会开启熔断。,熔断一旦开启,这时就会拒绝请求包括正确的请求,直接执行本地的默认方法,达到服务降级的效果,熔断不会一直开启,熔断后一段时间,会继续放一批请求,这是处于半熔断状态,当进去的请求得到成功执行时,这时会认为服务故障已经清除,熔断就会关闭,服务正常进行。

Hystrix设计了三种状态:

熔断关闭状态(Closed):服务正常进行,不受限制。

熔断开启状态(Open): 在固定时间内(Hystrix默认是10秒),接口调用出错比率达到一个阈值(Hystrix默认为50%),会进入熔断开启状态。这时就会拒绝所有服务,调用服务降级方法。

半熔断状态(Half-Open): 在进入熔断开启状态一段时间之后(Hystrix默认是5秒),熔断器会进入半熔断状态。该状态下会放一批请求,监控该批请求的正确率,如果成功率达到预约,就认为服务恢复正常,关闭熔断状态。

从使用上来看,熔断和降级很像,你可以把它理解为一个在客户端(降级),一个在服务端(熔断)

看一下我的熔断配置

/**
 * @author :shengjie.tang
 * @date :Created in 2022/5/2 21:00
 * @description 抽象controller 实现全局降级、熔断方法
 * @modified By:
 * @version: 1$
 */
@DefaultProperties(defaultFallback = "consumerFuseHandler")
public class ProviderBaseController {

    /**
     * fallback 方法  当方法出现异常时调用该方法
     *
     * @return
     */
    public CommonResult consumerFuseHandler() {
        return CommonResult.error("500", " GlobalFuseHandler 异常熔断处理。系统问题,请稍后再试!");
    }


}

其实我就是这个一个全局的Controller抽象类,我没有做任何处理,我看到了好多博客都说,要通过注解的方式配置

大概像这样,这个简直就是灾难,我不可能每个方法都这个去配置的,要配置我也是通过yml的方式去设置全局的,当然我也是说这种方式不好(好像sentinel就不可以这样)。

只是这种配置略显麻烦

然后就是一个控制台的配置

一定要注意中间有个actuator

然后这个搭建起来还要servlet

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
@EnableHystrix
public class ProductConsumeApplication {

    public static void main(String[] args) {
        SpringApplication.run(ProductConsumeApplication.class, args);
    }

    @Bean
    public ServletRegistrationBean getServlet() {
        HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
        ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
        registrationBean.setLoadOnStartup(1);
        registrationBean.addUrlMappings("/actuator/hystrix.stream");
        registrationBean.setName("HystrixMetricsStreamServlet");
        return registrationBean;
    }
}

Seata

作为一个分布式微服务架构,分布式事务是一个难点。

我们可以有很多种方式来处理这些,包括TCC,包括2PC,3PC提交,包括状态机,包括BASE最终一致性,当然还有之前一直用的事务MQ

这些可以看我之前的博客分布式事务解决方案与基本概念_TT_4419的博客-CSDN博客

然后Seata是业内已经算是成熟的分布式框架了

然后文档在这里:

Seata官网:Seata

Seata常见FAQ:Seata常见问题

Seata新人部署文档:Seata部署指南

文字版教程:七步带你集成Seata 1.2 高可用搭建

springcloud整合demo:spring-cloud-alibaba-seata-demo: springboot+seata+mybatisplus+springcloudalibaba整合demo,动动手给个star呗

dubbo整合demo:springboot-dubbo-mybatisplus-seata: springboot-dubbo-mybatsiplus-seata demo整合,欢迎大家尝试,目前seata的文档跟博客较少,后续我会根据这个项目发一篇博客,再过一阵子seata 正式版要出了,欢迎大家去seata的官网http://seata.io/zh-cn/ 学习

官方示例:https://github.com/seata/seata-samples

这是我加了官方群之后的拿到的一些引用

我用的是AP,然后准备用Eureka做注册中心(虽然他们的例子都是nacos)

我理解的逻辑是这样的,A提交事务,然后在业务表里面有个undo_log的日志表,如果A成功,但是B失败了,就去通过这个undoLog回滚

然后我失败了,失败的原因是,他们有个seata-service,然后我就看着视频里的大哥点了一个bat文件,也就是windos批处理文件,然而我的电脑是mac

然后我寻思着,这玩意不能docker部署吗?

这个等后面好了,我在更新一下(EMMM 到时候放到我的云服务器上去,挂在一下)。

Steam 

官方的描述是这样的:

使用Spring Cloud Stream,开发人员可以:

*隔离地构建,测试,迭代和部署以数据为中心的应用程序。

*应用现代微服务架构模式,包括通过消息传递进行组合。

*以事件为中心的思维将应用程序职责分离。事件可以表示及时发生的事件,下游消费者应用程序可以在不知道事件起源或生产者身份的情况下做出反应。

*将业务逻辑移植到消息代理(例如RabbitMQ,Apache Kafka,Amazon Kinesis)上。

*通过使用项目Reactor的Flux和Kafka Streams API,可以在基于通道的应用程序和基于非通道的应用程序绑定方案之间进行互操作,以支持无状态和有状态的计算。

*依靠框架对常见用例的自动内容类型支持。

可以扩展到不同的数据转换类型。

而我恰恰准备在我的项目里面集成rocketMq和kafaka,

前者主要用于项目解耦和异步,其实mq的主要作用有三者:异步,解耦,削峰。

我对前两者大有体会,解耦可以使得mq绕过pom的工程依赖,异步可以对非关键实时代码做调度,减少总体服务时间

关于削峰,emmm,我到现在都没有查出来,生产环境为何一会大批量数据mq消费不了,一会又可以(最终我妥协使用了RpcContext.ansyCall() )。

后者搭配mongoDB,elasticsearch去做日志分析,好怀念数据中台那套。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值