微服务学习

目录

微服务框架的五大组件(阿里巴巴)

一.下载与使用Nacos(spring cloud 注册中心)

1.nacos下载地址

2.配置nacos 默认端口号8848  (http://localhost:8848/nacos)可以访问到nacos

二、Dubbo的配置学习

1.服务之间的相互调用

三. 分布式事务

除AT模式外的其他模式简介

下载Seata地址

四.Sentinel

下载地址

Sentinel特征

基本配置和限流效果

五.Spring Gateway 网关

内置断言

时间相关

要求指定参数

Gateway过滤器

动态路由

创建gateway网关项目

SpringMvc和SpringGateway冲突问题

奈非框架简介


微服务框架的五大组件(阿里巴巴)

Sentinel:把流量作为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
Nacos:一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
RocketMQ:一款开源的分布式消息系统,基于高可用分布式集群技术,提供低延时的、高可靠的消息发布与订阅服务。
Dubbo:Apache Dubbo™ 是一款高性能 Java RPC 框架。
Seata:阿里巴巴开源产品,一个易于使用的高性能微服务分布式事务解决方案。

一.下载与使用Nacos(spring cloud 注册中心)

1.nacos下载地址

​​​​​​https://github.com/alibaba/nacos/releases/download/1.4.3/nacos-server-1.4.3.zip

         下载解压到本地文件夹即可

启动nacos服务:

        启动Nacos不能直接双击startup.cmd

        而需要打开dos窗口来执行

        在安装路径下nacos的ben文件夹下,输入下面的代码启动 

startup.cmd -m standalone

 -m standalone 是单体使用,没有配置集群需要加上去

{在idea中启动nacos 需要的配置}

2.配置nacos 默认端口号8848  (http://localhost:8848/nacos)可以访问到nacos

(1)在pom文件中添加nacos配置

 (2)在yml文件中添加配置

以上配置都是将nacos配置到各服务中

二、Dubbo的配置学习

1.服务之间的相互调用

Dubbo    是一套RPC框架  默认的通信协议是Dubbo自己写的协议,序列化协议使用的就是json

                /**通信协议和序列化协议是可以通过配置文件修改的*/

注:RPC是Remote Procedure Call 翻译为:远程过程调用

 RPC主要包含了2个部分的内容

  • 序列化协议
  • 通信协议

通信协议

当老婆在外面时,就需要借助通讯工具通知老公来完成什么操作

在这个流程中通信修协议就指老婆使用什么方式通知老公要洗碗

可以是手机,也可以写信,可以飞鸽传书

序列化协议     指传输信息的内容是什么格式,双方都要能够理解这个格式

例如老婆说中文,老公要理解中文,其他语言也一样

发送信息是序列化的过程,接收信息是反序列化的过程

这样他们才能明确调用的目的

这样的流程就是我们生活中的RPC使用的场景

 因为 Dubbo内置了序列化协议和通信协议,所以会有一下特性:

        采用NIO单一长连接

        优秀的高并发处理性能

        编写简单,提升开发效率

配置:

        服务之间的相互调用 ,是分为服务的提供者和消费者

      1.  提供者项目需要,将原项目中的service接口分离出来,形成一个新的子项目,父子相认(非必要,但是这是行业规范)

       2. 将原项目中的实现代码也形成一个新的子项目(webapi),父子相认,在pom文件中添加dubbo依赖

3.将原项目的application.yml复制到webapi项目中,然后在application.yml中添加dubbo配置

4.需要在webapi子项目的service层中添加@DubboService注解,该注解的作用与@service相同

5.需要在webapi子项目的启动类中添加@EnableDubbo注解

6.还有就是业务逻辑层的实现有修改

之前order模块只是单纯的添加订单信息到数据库

现在我们要在添加订单到数据库之前先删除库存和购物车中的信息

@DubboReference 注解的作用与@Autowired注解作用相同 

消费者项目,

  1.只需要在pom文件中添加依赖

  2.在application.yml文件中添加配置(依照上述即可)

  3. 在消费者模块的业务逻辑层调用提供者的业务的方法(仅消费是不用添加@DubboService注解的)

例:

4.最后需要在启动类添加@EnableDubbo注解

(服务的调用dubbo完)

三. 分布式事务

不是一个项目的数据库操作,所以不能简单的通过一个数据库事务就完成

每个项目都有自己的数据库事务,那么要想在多个事务的提交过程中再添加支持他们事务就需要Seata

Seata将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。

Seata构成

  • 事务协调器TC
  • 事务管理器TM
  • 资源管理器RM

AT模式运行过程

1.事务的发起方(TM)会向事务协调器(TC)申请一个全局事务id,并保存

2.Seata会管理事务中所有相关的参与方的数据源,将数据操作之前和之后的镜像都保存在undo_log表中,这个表是seata框架规定的,方便提交(commit)或回滚(roll back)

3.事务的发起方(TM)会连同全局id一起通过远程调用运行资源管理器(RM)中的方法

4.资源管理器(RM)接收到全局id,并运行指定的方法,将运行的状态同步到事务协调器(TC)

5.如果运行整体没有发生异常,发起方(TM)会通过事务协调器通知所有分支,将本次事务所有对数据库的影响真正生效,

如果任何一个参与者发生异常,那么都会通知事务协调器,再由事务协调器通知有分支,根据undo_log表中保存的信息,撤销(回滚)即将正式影响数据库的数据

除AT模式外的其他模式简介

Seata将为用户提供了 AT、TCC、SAGA 和 XA 事务模式

AT模式只能用于数据库操作事务

如果事务中有的参与者操作的不是关系型数据库(例如操作Redis)

那么AT模式就不能生效了

TCC模式

这个模式可以实现对数据库之外的信息存储媒介进行回滚操作

只不过这个回滚需要我们自己编写代码

需要为每个业务编写Prepare\Commit\Rollback方法

Prepare编写常规准备,如果整个业务运行无异常运行Commit,如果有异常会自动运行Rollback

缺点是每个业务都需要编写3个对应的方法,代码有冗余,而且业务入侵量大

SAGA模式

一般用于修改老版本代码

不用编写像TCC模式那么多的方法

但是需要手动编写每个参与者的方向回滚的业务逻辑层代码类

开发量大

XA 模式

XA是适用于支持XA协议的数据库,使用的比较少

下载Seata地址

https://github.com/seata/seata/releases

https://github.com/seata/seata/releases/download/v1.4.2/seata-server-1.4.2.zip

配置 seata:

        1.在参与者模块的pom文件中添加seata的依赖 

<!--   Seata和SpringBoot整合依赖     -->
<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-spring-boot-starter</artifactId>
</dependency>
<!--  Seata 完成分布式事务的两个相关依赖(Seata会自动使用其中的资源)  -->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
</dependency>

        2. 修改参与者模块yml文件

seata:
  tx-service-group: csmall_group # 分组
  service:
    vgroup-mapping:
      csmall_group: default # 默认at模式
    grouplist: 
      default: localhost:8091

配置中,同一个事务的多个参与者必须在同一个名称的分组下

同时制定相同的seata-server的ip和端口

3.在发起者模块的pom文件中添加依赖

<!--   Seata和SpringBoot整合依赖     -->
<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-spring-boot-starter</artifactId>
</dependency>

在发起者模块的pom文件中只需要添加一个依赖

4.application-dev.yaml和之前的一样

seata:
  tx-service-group: csmall_group # 分组
  service:
    vgroup-mapping:
      csmall_group: default # 默认at模式
    grouplist:
      default: localhost:8091

5.消费者模块加入注解

发起者组织事务开始

必须由特定的注解标记业务逻辑层方法(@GlobalTrancsational)

在这个方法运行时会激活Seata的分布式事务管理流程

 4.启动seata服务

进入seata\seata-server-1.4.2\bin目录

打开cmd窗口

输入seata-server.bat -h 127.0.0.1 -m file 命令

在windows系统中运行seata可能出现不稳定的情况,重启seata即可解决

四.Sentinel

随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。

下载地址

https://github.com/alibaba/Sentinel/releases

Sentinel特征

  • 丰富的应用场景

    双11,秒杀,12306抢火车票

  • 完备的实时状态监控

    可以支持显示当前项目各个服务的运行和压力状态,分析出每台服务器处理的秒级别的数据

  • 广泛的开源生态

    很多技术可以和Sentinel进行整合,SpringCloud,Dubbo,而且依赖少配置简单

  • 完善的SPI扩展

    Sentinel支持程序设置各种自定义的规则

基本配置和限流效果

限流:

1.在pom文件中添加依赖

<!--   Sentinel整合SpringCloud     -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

2.在application-dev.yml配置Sentinel支持

spring:
  application:
    name: nacos-stock # 定义当前服务名称
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 # 指定正在运行的Nacos服务器的位置
    sentinel:
      transport:
        dashboard: localhost:8080  # Sentinel仪表台的ip:端口
        port: 8721  # 是localhost的8721 这个端口真正参与当前项目的限流措施

注意:每个项目模块的 port:8721 端口号都不能相同

3.下面要使用注解标记限流的控制层方法

运行控制器方法之后

Sentinel的仪表台就能设置这个控制方法的限流规则了

QPS是每秒请求数

并发线程数是同时方法这个方法的线程数量

超出的部分都会被Sentinel限流,快速失败

此外,sentinel还支持降级

@RestController
@RequestMapping("/base/business")
@Api(tags = "购买业务开始模块")
public class BusinessController {

    @Autowired
    private IBusinessService businessService;

    @PostMapping("/buy") //    localhost:20000/base/business/buy
    @ApiOperation("发起购买")
    // 标记当前控制层方法被Sentinel管理限流
    // blockHandler配置当请求被限流时 运行哪个方法
    // fallback配置当控制器方法运行异常时,运行哪个方法
    @SentinelResource(value = "buy",
                blockHandler = "blockError",fallback = "fallbackError")
    public JsonResult buy(){
        //  调用业务逻辑层方法,业务逻辑层方法没有返回值
        businessService.buy();
        return JsonResult.ok("购买完成");
    }
//  Sentinel规定的限流相应的方法有如下要求
    //  1.必须是public的方法
    //  2.返回值类型必须和控制器方法一致
    //  3.参数必须在控制方法的一样的前提下,在参数列表最后添加一个BlockException
    //  4.方法名必须和上面blockHandler指定的一致
    public JsonResult blockError(BlockException e){
        return JsonResult.failed(ResponseCode.BAD_REQUEST,"服务器忙!");
    }
// 当控制器方法运行发送异常时,Sentinel支持服务降级
    // 我们可以在这个方法中对用户的请求进行一些补救
    // 方法格式和上面限流方法一致,只是不必须添加BlockException参数
    public JsonResult fallbackError(){
        return JsonResult.failed(ResponseCode.BAD_REQUEST,"服务降级");
    }


}

blockHandler

  • 属性值是流控后调用的方法名称
  • 流控处理方法返回值和资源方法返回值一致
  • 流控处理方法参数和资源方法一致的同时,可以接收额外参数BlockException
  • 流控处理方法一定要在资源方法同一个类内,如果不在同一个类,使用blockHandlerClass属性

fallback

  • 属性值是降级处理方法名称
  • 降级处理方法返回值和资源方法返回值一致
  • 降级处理方法参数和资源方法一致的同时,可以接收额外参数BlockException
  • 降级处理方法一定要在资源方法同一个类内,如果不在同一个类,使用fallbackClass属性

blockHandler和fallback的区别

两者都是不能正常调用资源返回值的顶替处理逻辑.

blockHander只能处理BlockException 流控限制之后的逻辑.

fallback处理的是资源调用异常的降级逻辑.

五.Spring Gateway 网关

所谓网关,就是请求到当前微服务项目,当前项目为访问者提供的统一入口

网关可以将多个项目模块微服务程序混乱的调用关系变得简单

我们使用SpringGateway作为当前项目的网关组件

Spring Gateway是Spring自己开发的,也是SpringCloud中的组件之一

gateway的yml配置

server:
  port: 9000
spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    gateway:
      routes:   # gateway配置路由信息开始
        # spring.cloud.gateway.routes[0].predicates[0]
        - id: gateway-shanghai
          uri: lb://shanghai
          predicates:
            - Path=/sh/**
        - id: gateway-beijing # 设置路由的id,和其他任何名称没有关联,只是不能和其他路由id重名即可
          # uri表示设置路由的目标
          # lb是LoadBalance(负载均衡)的缩写 beijing是注册到nacos的服务名称
          uri: lb://beijing
          # predicates是断言 所谓断言就是判断一个条件,如果满足的话就做某些指定的操作
          predicates:
            # Path表示判断是否满足路径条件,如果访问当前网关项目的路径是/bj/**,
            # name本次访问网关的路由目标就是uri指定的路径
            - Path=/bj/**
            # http://localhost:9001/bj/show

 可以动态路由,以上配置是底层,参考即可

内置断言

所谓断言就是判断一个条件,如果满足的话就做某些指定的操作

predicates的意思就是断言

我们前面章节中使用的Path是内置断言的一种,指定满足某些路径特征时访问某些资源

predicates参数也是支持数组的,Path只是数组的一个元素而已,我们可以添加更多其他含义的内置断言

内置断言就是SpringGateway框架提供的断言规则,他们有

  • after
  • before
  • between
  • cookie
  • header
  • host
  • method
  • path
  • query
  • remoteaddr

时间相关

after,before,between

在指定时间之前,之后,之间访问,满足这个时间条件能够访问指定资源

为了方便获得一个时间格式我们可以使用这个方法


ZonedDateTime.now()// 获得系统当前时间

判断时间必须在xxx之后  After

- id: gateway-shanghai
  uri: lb://shanghai
  predicates:
    - Path=/sh/**
    - After=2022-05-07T15:32:03.138+08:00[Asia/Shanghai]

判断时间必须在xxx之前 Before

- id: gateway-shanghai
  uri: lb://shanghai
  predicates:
    - Path=/sh/**
    - Before=2022-05-07T15:32:03.138+08:00[Asia/Shanghai]

判断时间必须在xxx与xxx之间 Between

- id: gateway-shanghai
  uri: lb://shanghai
  predicates:
    - Path=/sh/**
    - Between=2022-05-07T15:32:03.138+08:00[Asia/Shanghai],2022-05-07T15:42:03.138+08:00[Asia/Shanghai]

要求指定参数

Query断言,要求必须有指定参数才能访问指定资源

- id: gateway-shanghai
  uri: lb://shanghai
  predicates:
    - Path=/sh/**
    - Query=name

Gateway过滤器

SpringGateway除了断言选项之外还提供了网关专业过滤器

在请求经过网关是,对请求进行一些操作

filters是添加过滤器的属性,也是支持数组赋值的

我们下面尝试一个将请求中添加参数信息的过滤器

- id: gateway-shanghai
  uri: lb://shanghai
  predicates:
    - Path=/sh/**
  filters:
    - AddRequestParameter=age,18

在shanghai项目的Controller中获得这个参数

@GetMapping("/show")
public String show(Integer age){

    return "这里是上海!"+age;
}

动态路由

如果一个微服务项目有很多模块

每个模块都手动配置路由规则的话,随着模块的增加,工作量也会越来越大

今后对这个路由配置文件的维护会非常的麻烦

我们希望SpringGateway能够自动的,动态的从Nacos中获得当前注册到微服务项目的模块

无需配置文件,给定固定格式就可以路由到这个模块

这就是动态路由配置

它的配置非常简单

server:
  port: 9000
spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    gateway:
      discovery:
        locator:
          # 开启动态路由,网关会自动从Nacos注册列表中寻找指定的服务器名称,路由到该项目
         
 enabled: true

创建gateway网关项目

先父子相认

删除test文件夹

pom文件需要的依赖

 <dependencies>
        <!-- web实例 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--  Nacos注册依赖 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-spring-boot-starter</artifactId>
        </dependency>

yml需要的配置

server:
  port: 10000
spring:
  application:
    name: gateway-server
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 //指定nacos服务器的地址
    gateway:
      discovery:
        locator:
          # 网关开启动态路由
          enabled: true
  main:
    web-application-type: reactive //解决SpringMvc和SpringGateway冲突问题

cn.tedu.gateway.config

SwaggerProvider类

@Component
public class SwaggerProvider implements SwaggerResourcesProvider {
    /**
     * 接口地址
     */
    public static final String API_URI = "/v2/api-docs";
    /**
     * 路由加载器
     */
    @Autowired
    private RouteLocator routeLocator;
    /**
     * 网关应用名称
     */
    @Value("${spring.application.name}")
    private String applicationName;

    @Override
    public List<SwaggerResource> get() {
        //接口资源列表
        List<SwaggerResource> resources = new ArrayList<>();
        //服务名称列表
        List<String> routeHosts = new ArrayList<>();
        // 获取所有可用的应用名称
        routeLocator.getRoutes().filter(route -> route.getUri().getHost() != null)
                .filter(route -> !applicationName.equals(route.getUri().getHost()))
                .subscribe(route -> routeHosts.add(route.getUri().getHost()));
        // 去重,多负载服务只添加一次
        Set<String> existsServer = new HashSet<>();
        routeHosts.forEach(host -> {
            // 拼接url
            String url = "/" + host + API_URI;
            //不存在则添加
            if (!existsServer.contains(url)) {
                existsServer.add(url);
                SwaggerResource swaggerResource = new SwaggerResource();
                swaggerResource.setUrl(url);
                swaggerResource.setName(host);
                resources.add(swaggerResource);
            }
        });
        return resources;
    }
}
 

cn.tedu.gateway.controller

SwaggerController类

@RestController
@RequestMapping("/swagger-resources")
public class SwaggerController {
    @Autowired(required = false)
    private SecurityConfiguration securityConfiguration;
    @Autowired(required = false)
    private UiConfiguration uiConfiguration;
    private final SwaggerResourcesProvider swaggerResources;
    @Autowired
    public SwaggerController(SwaggerResourcesProvider swaggerResources) {
        this.swaggerResources = swaggerResources;
    }
    @GetMapping("/configuration/security")
    public Mono<ResponseEntity<SecurityConfiguration>> securityConfiguration() {
        return Mono.just(new ResponseEntity<>(
                Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK));
    }
    @GetMapping("/configuration/ui")
    public Mono<ResponseEntity<UiConfiguration>> uiConfiguration() {
        return Mono.just(new ResponseEntity<>(
                Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK));
    }
    @GetMapping("")
    public Mono<ResponseEntity> swaggerResources() {
        return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
    }
}

cn.tedu.gateway.filter

SwaggerHeaderFilter类

@Component
public class SwaggerHeaderFilter extends AbstractGatewayFilterFactory {
    private static final String HEADER_NAME = "X-Forwarded-Prefix";

    private static final String URI = "/v2/api-docs";

    @Override
    public GatewayFilter apply(Object config) {
        return (exchange, chain) -> {
            ServerHttpRequest request = exchange.getRequest();
            String path = request.getURI().getPath();
            if (!StringUtils.endsWithIgnoreCase(path,URI )) {
                return chain.filter(exchange);
            }
            String basePath = path.substring(0, path.lastIndexOf(URI));
            ServerHttpRequest newRequest = request.mutate().header(HEADER_NAME, basePath).build();
            ServerWebExchange newExchange = exchange.mutate().request(newRequest).build();
            return chain.filter(newExchange);
        };
    }
}

网关项目创建完毕

启动所有服务

应该最后启动网关项目

SpringMvc和SpringGateway冲突问题

<!-- web实例 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

依赖会起冲突

原因是SpringMvc依赖自带了Tomcat服务器

而SpringGateway依赖自带了Netty服务器

当项目启动时,会两个服务器软件就冲突了

导致错误

我们可以在yml文件中添加下面配置解决

spring:
  main:
    web-application-type: reactive

至此五大组件介绍完毕

奈非框架简介

早期奈非公司提供的框架受到了很多微服务开发者的欢迎

它提供的组件几乎包含了所有微服务架构搭建的需要

eureka注册中心(Nacos)

ribbon+feign微服务间调用(Dubbo)

hystrix流控(Sentinel)

zuul网关(Gateway)

近几年奈非公司项目更新慢,跟不上需求,所以很多开发结构转向阿里

(同类的微服务框架,眼熟即可)

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值