分布式 微服务架构

  • 微服务发展
  • 1.1微服务架构演变
  • 1.1.1微服务架构
  • 1.1.2微服务架构常见概念
  • 1.1.2.1服务治理
  • 1.1.2.2服务调用
  • 1.1.2.3服务网关
  • 1.1.2.4服务容错
  • 1.1.2.5链路追踪
  • 1.2微服务架构解决方案
  • 1.2.1springcloud Alibaba介绍
  • 1.2.2主要功能
  • 1.2.3核心组件
  • 微服务项目搭建
  • 2.1项目环境搭建
  • 2.1.1父工程
  • 2.1.2shop-common公共模块
  • 2.1.3shop-order订单模块
  • 2.2nacos服务注册
  • 2.2.1 步骤
  • 引入依赖包:
  • 主类
  • yml配置
  • 2.3ribbon负载均衡
  • 2.3.1步骤
  • 配置
  • 策略
  • 2.4feign服务调用
  • 2.4.1步骤
  • 引入依赖
  • 主类
  • 接口
  • 2.5sentinel服务容错
  • 导入依赖
  • 下载jar包,运行sentinel环境
  • 资源名配置@SentinelResource
  • sentinel整合feign实现远程调用容错
  • sentinel持久化
  • 2.6gateway服务网关
  • gateway介绍
  • 创建api-gateway模块
  • gateway从nacos获取微服务信息
  • 2.7Sleuth链路跟踪
  • 名词
  • 导入依赖
  • 2.8zipkin集成
  • 介绍
  • 导入依赖
  • 配置
  • 启动
  • 2.9nacos配置中心
  • 添加依赖
  • 在nacos中加入配置
  • 新建bootstrap.yml文件
  • 自动刷新
  • 配置方式
  • 2.10Seata分布式事务
  • 修改nacos-config.txt文件
  • 配置到nacos
  • 启动
  • 添加数据log表
  • 项目中添加配置


微服务发展

1.1微服务架构演变

分布式微服务 分布式微服务框架_java

1.1.1微服务架构

提倡服务的原子化拆分,是服务架构soa的进一步发展。
问题:1、服务微小且繁多,如何管理 2、他们之间如何调用? 3、客户端如何访问?4、如果其中一个出现问题,服务自身如何自处理? 5、中间环节出问题了,程序员如何排查?

1.1.2微服务架构常见概念

1.1.2.1服务治理

服务治理就是服务自动化管理,核心是服务自动注册与发现
1、服务注册:服务实例将自身服务信息注册到服务注册中心
2、服务发现:服务实例通过注册中心,获取注册到其中的服务实例信息,通过这些信息去请求他们提供的服务
3、服务剔除:服务注册中心将出问题的服务自动剔除到可用列表外,使其不会被调用

1.1.2.2服务调用

微服务架构中,通常存在多个服务间的远程调用需求,目前主流的远程调用技术有基于HTTP的Restful接口以及TCP的RPC协议

1、REST(Representational State Transfer)是一种Http调用的格式,标准通用的。

2、RPC是一种进程间通信方式,允许像调用本地服务一样调用远程服务,RPC框架的主要目标就是让远程服务调用更简单透明。RPC会帮助屏蔽底层的传输方式、序列化方式和通信细节。

分布式微服务 分布式微服务框架_maven_02

1.1.2.3服务网关

网关的基本功能:1、统一接入 2、安全防护 3、协议适配 4、流量管控 5、长短连接支持 6、容错能力

1.1.2.4服务容错

三个核心思想:1、不被外界影响 2、不被上游请求压垮 3、不被下游响应拖垮

分布式微服务 分布式微服务框架_微服务_03

1.1.2.5链路追踪

提供一个链路跟随标志,方便追踪服务错误。

1.2微服务架构解决方案

1、servicecomb
2、springcloud
3、springCloud Alibaba

1.2.1springcloud Alibaba介绍

只需要添加少量注解和配置,即可将springcloud应用接入阿里微服务解决方案。

1.2.2主要功能

1、服务限流降级
2、服务注册与发现
3、分布式配置管理
4、消息驱动能力
5、分布式事务
6、阿里云对象存储

1.2.3核心组件

1、Sentinel:把流量作为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性
2、Nacos:动态服务发现、配置管理和服务管理平台
3、RocketMQ:分布式消息系统,基于高可用集群技术,提供低延时、高可靠的消息发布与订阅服务。
4、Dubbo:高性能java RPC框架
5、Seata:分布式事务解决方案

微服务项目搭建

2.1项目环境搭建

2.1.1父工程

创建父工程,父工程只需要保存pom文件,其他文件可以删除,注意加入所有模块

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.lv</groupId>
    <artifactId>studyspringcloud</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>pom</packaging>

    <modules>
        <module>shop-user</module>
        <module>shop-product</module>
        <module>shop-common</module>
        <module>shop-order</module>
    </modules>

    <!--父工程-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.13.RELEASE</version>
    </parent>

    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-cloud.version>Greenwich.RELEASE</spring-cloud.version>
        <spring-cloud-alibaba.version>2.1.0.RELEASE</spring-cloud-alibaba.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud-alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

2.1.2shop-common公共模块

.创建shop-common公共模块,父工程依赖上一pom,加入mysql驱动,jpa,fast-json等公共依赖。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>com.lv</groupId>
        <artifactId>studyspringcloud</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <artifactId>shop-common</artifactId>

    <dependencies>
        <!--springboot-web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
            <version>2.1.13.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.56</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
            <version>2021.1</version>
        </dependency>

    </dependencies>

</project>

2.1.3shop-order订单模块

创建order模块,依赖父工程,加入公共模块依赖,加入nacos依赖开启nacos服务注册,yml文件中也做相应修改

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.lv</groupId>
       <artifactId>studyspringcloud</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <artifactId>shop-order</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>com.lv</groupId>
            <artifactId>shop-common</artifactId>
            <version>0.0.1-SNAPSHOT</version>
            <scope>compile</scope>
        </dependency>

        <!--  nacos 注册中心-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--        feign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

yml文件

server:
  port: 8091
spring:
  application:
    name: service-order //nacos服务注册名
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/websystem?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
    username: root
    password: ******
  jpa:
    properties:
      hibernate:
        hbm2ddl:
          auto: update
        dialect: org.hibernate.dialect.MySQL5InnoDBDialect
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 //此处就是开启nacos服务注册代码,nacos会自动监听并将服务自动注册
service-product:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule //开启ribbon策略均衡

其余模块都大同小异,只有服务名改一下和端口号改一下

2.2nacos服务注册

nacos是alibaba开发的一款微服务注册于管理配置中心,可以看作监控微服务的容器。
在springcloud项目开发中,引入nacos的步骤:

2.2.1 步骤

引入依赖包:

<!--  nacos 注册中心-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

主类

在启动类上加如@EnableDiscoveryClient注解,开启nacos客户端,将此服务作为nacos的一个客户端。

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients //开启feign客户端
public class ShopOrderApplication {

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

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

}

yml配置

此配置是开启注册中心,注册中心地址为localhost:8848,注意:此配置写在需要被注册的微服务模块上,nacos会为我们自动发现有此配置的服务,并注册到注册中心管理。,当然是先开启了nacos客户端和服务才会被发现和注册。进入bin目录,启动命令:startup.cmd -m standalone

spring:
 	cloud:
    	nacos:
      		discovery:
        		server-addr: localhost:8848 //此处就是开启nacos服务注册代码,nacos会自动监听并将服务自动注册

2.3ribbon负载均衡

ribbon是springcloud提供的实现负载均衡的组件。使用ribbon可以配置负载均衡策略,使我们调用服务更加安全和规律。

2.3.1步骤

配置

因为我们在父工程中已经配置了依赖管理器,且加入了springcloud依赖,所以我们可以直接配置使用ribbon。
yml中写:

service-product:
	ribbon:
    	NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule //开启ribbon策略均衡

同时,在调用远程服务的Bean对象添加@LoadBalanced注解来进行负载均衡策略的管理。注意:不加@LoadBalanced时,服务调用是使用默认的随机策略,加了@LoadBalanced,我们可以通过配置来修改负载均衡的策略,在使用feign代替ribbon后,就不需要加此注解了。

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients //开启feign客户端
public class ShopOrderApplication {

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

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

}

策略

1,随机规则:RandomRule

2,最可用规则:BestAvailableRule

3,轮询规则:RoundRobinRule

4,重试实现:RetryRule

5,客户端配置:ClientAonfigEnabledRoundRobinRule

6,可用性过滤规则:AvailabilityFilteringRule

7,RT权重规则:WeigthedResponseTimeRule

8,规避区域规则:ZoneAvoidanceRule

2.4feign服务调用

Feign是Netflix开发的声明式、模板化的伪HTTP客户端。简单来说,feign的作用就是做服务调用(继承了ribbon负载均衡的多种策略),使远程服务调用可以像本地方法调用一样简单。

2.4.1步骤

引入依赖

<!--        feign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

主类

在主类上加@EnableFeignClients //开启feign客户端 注解

接口

我们需要通过order服务调用product服务,最好是创建一个接口来进行服务调用。

import com.lv.pojo.Product;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

@FeignClient(value = "service-product") //value用于指定调用nacos的微服务名称
public interface ProductService {

    @RequestMapping("/product/{pid}") //指定请求的URI
    Product findByPid(@PathVariable Integer pid);
}

然后调用本地接口一样,使用feign进行服务调用。将接口注入到需要调用的地方,然后使用接口调用配置了相同请求路径的方法。

//调用者
   @Autowired
    private ProductService productService;
    
	@GetMapping("/product/{pid}")
    public Order order3(@PathVariable("pid") Integer pid) {
        log.info(">>>客户下单,调用商品微服务查询商品信息<<<");
        // 采用restTemplate调用
		// Product product = restTemplate.getForObject("http://service-product/product/" + pid, Product.class);
		
        // 通过feign接口调用远程微服务
        Product product = productService.findByPid(pid);

        log.info(">>商品信息,查询结果:" + JSON.toJSONString(product));

        Order order = new Order();
        order.setUid(1);
        order.setUsername("测试用户1");
        order.setPid(product.getPid());
        order.setPname(product.getPname());
        order.setPprice(product.getPprice());
        order.setNumber(1);

        orderService.save(order);
        return order;
    }
    
//接口
@FeignClient(value = "service-product") //value用于调用nacos的微服务名称
public interface ProductService {

    @RequestMapping("/prod/{pid}")
    Product findByPid(@PathVariable Integer pid);
}

//被调用者
@RestController
@Slf4j
public class ProductController {

    @Autowired
    private ProductService productService;

    //商品信息查询
    @RequestMapping("/prod/{pid}")
    public Product product(@PathVariable("pid") Integer pid) {
        log.info("接下来要进行{}号商品信息的查询", pid);
        Product product = productService.findByPid(pid);
        log.info("商品信息查询成功,内容为{}" + JSON.toJSONString(product));
        return product;
    }

}

注意:此处的接口上的请求一定要和被调用服务上的请求一致。重点是:接口上的请求,一定是和被调用服务的相同,不是调用者,而是被调用者,注意是被调用者
如果不一致,会出现异常:status 404 reading ProductService#findByPid(Integer) 是说ProductService中服务调用失败,因为请求不匹配。
可以直接请求接口的路径,但请求了没用,因为findByPid方法没有实现,所以没有结果。
只有二者请求相同,才能通过服务名与请求URI进行目标服务调用。

2.5sentinel服务容错

导入依赖

我们需要在服务上游加入依赖,调用其他服务的那个服务就是上游。

<!-- sentinel服务容错 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>

下载jar包,运行sentinel环境

https://github.com/alibaba/Sentinel/releases 下载
进入jar包目录,运行命令:java -Dserver.port=8081 -Dcsp.sentinel.dashboard.server=127.0.0.1:8081 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.2.jar
运行完后访问http://localhost:8081就可以登录,下面就是将我们自己的微服务放到sentinel管理。
配置文件中下以下sentinel下的部分。

cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    sentinel:
      transport:
        port: 9999 #跟控制台交流的端口,随意指定一个未使用的就行
        dashboard: localhost:8081 #指定控制台服务的地址 我的是http://localhost:8080/#/dashboard

sentinel是懒加载的,需要访问一次服务,才能被发现并监控。

分布式微服务 分布式微服务框架_分布式微服务_04

资源名配置@SentinelResource

通过@SentinelResource注解配置资源名字。
sentinel有多种规则:1、流控规则 2、降级规则 3、热点规则 4、授权规则 5、系统规则 五个规则分别对应五种异常返回
我们可以自定义规则异常的返回信息,例如:

@Component
public class ExceptionHandlerPage implements UrlBlockHandler {
    @Override
    public void blocked(HttpServletRequest request, HttpServletResponse response, BlockException e) throws IOException {
        response.setContentType("application/json;charset=utf-8");
        ResponseData responseData = null;
        if (e instanceof FlowException) {
            responseData = new ResponseData(-1, "接口被限流了");
        } else if (e instanceof DegradeException) {
            responseData = new ResponseData(-2, "接口被降级了");
        }
        response.getWriter().write(JSON.toJSONString(responseData));
    }
}

@Data
@AllArgsConstructor
@NoArgsConstructor
class ResponseData {
    private int code;
    private String massage;
}

sentinel整合feign实现远程调用容错

在feign客户端中的注解@FeignClient上添加fallback属性,指定容错类。

//value用于调用nacos的微服务名称,fallback用于指定容错类,fallbackFactory 用于指定容错工厂,工厂中可以抛出容错类的执行异常
@FeignClient(value = "service-product", fallbackFactory = ProductServiceFallback.class) 
public interface ProductService {

    @RequestMapping("/prod/{pid}")
    Product findByPid(@PathVariable Integer pid);
}

创建容错类,集成feign客户端的接口,并实现所有方法,写容错逻辑。

//这是一个容错类,实现feign所在的接口和方法,feign远程调用出现问题就走当前类的同名方法,加入了异常抛出的方法
@Service
public class ProductServiceFallback implements FallbackFactory<ProductService> {
    
    @Override
    public ProductService create(Throwable throwable) {
     	System.out.println("{}" + throwable);
        return pid -> {
            Product product = new Product();
            product.setPid(-100);
            product.setPname("远程调用商品微服务出现异常,进入容错逻辑");
            return product;
        };
    }
}

sentinel持久化

本地文件持久化方法:
1、新建配置类:

import com.alibaba.csp.sentinel.command.handler.ModifyParamFlowRulesCommandHandler;
import com.alibaba.csp.sentinel.datasource.*;
import com.alibaba.csp.sentinel.init.InitFunc;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRuleManager;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;
import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
import com.alibaba.csp.sentinel.transport.util.WritableDataSourceRegistry;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import org.springframework.beans.factory.annotation.Value;

import java.io.File;
import java.io.IOException;
import java.util.List;

public class FilePersistence implements InitFunc {
    @Value("spring.application.name")
    private String applicationName;
    @Override
    public void init() throws Exception {
        // TIPS: 持久化在本地的目录,如果你对这个路径不喜欢,可修改为你喜欢的路径
        String ruleDir = System.getProperty("user.home") + "/sentinel-rules/rules/" + applicationName;
        String flowRulePath = ruleDir + "/flow-rule.json";
        String degradeRulePath = ruleDir + "/degrade-rule.json";
        String systemRulePath = ruleDir + "/system-rule.json";
        String authorityRulePath = ruleDir + "/authority-rule.json";
        String paramFlowRulePath = ruleDir + "/param-flow-rule.json";

        this.mkdirIfNotExits(ruleDir);
        this.createFileIfNotExits(flowRulePath);
        this.createFileIfNotExits(degradeRulePath);
        this.createFileIfNotExits(systemRulePath);
        this.createFileIfNotExits(authorityRulePath);
        this.createFileIfNotExits(paramFlowRulePath);

        // 流控规则
        ReadableDataSource<String, List<FlowRule>> flowRuleRDS = new FileRefreshableDataSource<>(
                flowRulePath,
                flowRuleListParser
        );
        // 将可读数据源注册至FlowRuleManager
        // 这样当规则文件发生变化时,就会更新规则到内存
        FlowRuleManager.register2Property(flowRuleRDS.getProperty());
        WritableDataSource<List<FlowRule>> flowRuleWDS = new FileWritableDataSource<>(
                flowRulePath,
                this::encodeJson
        );
        // 将可写数据源注册至transport模块的WritableDataSourceRegistry中
        // 这样收到控制台推送的规则时,Sentinel会先更新到内存,然后将规则写入到文件中
        WritableDataSourceRegistry.registerFlowDataSource(flowRuleWDS);

        // 降级规则
        ReadableDataSource<String, List<DegradeRule>> degradeRuleRDS = new FileRefreshableDataSource<>(
                degradeRulePath,
                degradeRuleListParser
        );
        DegradeRuleManager.register2Property(degradeRuleRDS.getProperty());
        WritableDataSource<List<DegradeRule>> degradeRuleWDS = new FileWritableDataSource<>(
                degradeRulePath,
                this::encodeJson
        );
        WritableDataSourceRegistry.registerDegradeDataSource(degradeRuleWDS);

        // 系统规则
        ReadableDataSource<String, List<SystemRule>> systemRuleRDS = new FileRefreshableDataSource<>(
                systemRulePath,
                systemRuleListParser
        );
        SystemRuleManager.register2Property(systemRuleRDS.getProperty());
        WritableDataSource<List<SystemRule>> systemRuleWDS = new FileWritableDataSource<>(
                systemRulePath,
                this::encodeJson
        );
        WritableDataSourceRegistry.registerSystemDataSource(systemRuleWDS);

        // 授权规则
        ReadableDataSource<String, List<AuthorityRule>> authorityRuleRDS = new FileRefreshableDataSource<>(
                flowRulePath,
                authorityRuleListParser
        );
        AuthorityRuleManager.register2Property(authorityRuleRDS.getProperty());
        WritableDataSource<List<AuthorityRule>> authorityRuleWDS = new FileWritableDataSource<>(
                authorityRulePath,
                this::encodeJson
        );
        WritableDataSourceRegistry.registerAuthorityDataSource(authorityRuleWDS);

        // 热点参数规则
        ReadableDataSource<String, List<ParamFlowRule>> paramFlowRuleRDS = new FileRefreshableDataSource<>(
                paramFlowRulePath,
                paramFlowRuleListParser
        );
        ParamFlowRuleManager.register2Property(paramFlowRuleRDS.getProperty());
        WritableDataSource<List<ParamFlowRule>> paramFlowRuleWDS = new FileWritableDataSource<>(
                paramFlowRulePath,
                this::encodeJson
        );
        ModifyParamFlowRulesCommandHandler.setWritableDataSource(paramFlowRuleWDS);
    }

    private Converter<String, List<FlowRule>> flowRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<FlowRule>>() {
            }
    );
    private Converter<String, List<DegradeRule>> degradeRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<DegradeRule>>() {
            }
    );
    private Converter<String, List<SystemRule>> systemRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<SystemRule>>() {
            }
    );

    private Converter<String, List<AuthorityRule>> authorityRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<AuthorityRule>>() {
            }
    );

    private Converter<String, List<ParamFlowRule>> paramFlowRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<ParamFlowRule>>() {
            }
    );

    private void mkdirIfNotExits(String filePath) throws IOException {
        File file = new File(filePath);
        if (!file.exists()) {
            file.mkdirs();
        }
    }

    private void createFileIfNotExits(String filePath) throws IOException {
        File file = new File(filePath);
        if (!file.exists()) {
            file.createNewFile();
        }
    }

    private <T> String encodeJson(T t) {
        return JSON.toJSONString(t);
    }

}

2、创建文件夹 META-INF.services
3、新建文件: com.alibaba.csp.sentinel.init.InitFunc 在文件中写FilePersistence 类路径,com开始到类名结束就可。

2.6gateway服务网关

gateway介绍

gateway是springcloud开发的同意api网关,是应用程序的统一入口,为客户端提供统一服务,与业务逻辑功能无关的公共逻辑就写在网关里。

创建api-gateway模块

pom.xml 注意:此模块不能引入starter-web

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.lv</groupId>
        <artifactId>studyspringcloud</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <artifactId>api-gateway</artifactId>

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

    </dependencies>

</project>

配置文件applicatin.yml

server:
  port: 7000
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes: #路由数组
        - id: product_route #当前路由发的标识,默认UUID
          uri: http://localhost:8081 #请求最终要被转发到的地址
          order: 1 #路由优先级,数字越小,优先级越高
          predicates: #断言,条件判断,返回boolean
            - Path=/product-serv/** #当请求路径满足path指定的规则,路由信息才会正常转发
          filters: #过滤器,请求传递过程中做一些手脚
            - StripPrefix=1 #在请求转发之前去掉一层路径

配置好后启动服务,访问 http://localhost:7000/product-serv/prod/1 就会判断条件并进行请求转发。

gateway从nacos获取微服务信息

1、加入nacos依赖

<dependencies>
        <!-- gateway -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <!--  nacos 注册中心-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
    </dependencies>

2、主类加注解

@SpringBootApplication
@EnableDiscoveryClient
public class ApiGatewayApplication {

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

}

3、修改配置文件yml

server:
  port: 7000
spring:
  application:
    name: api-gateway
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #将gateway注册到nacos
    gateway:
      discovery:
        locator:
          enabled: true #让gateway从nacos中获取服务信息
      routes: #路由数组
        - id: product_route #当前路由发的标识,默认UUID
#          uri: http://localhost:8081 #请求最终要被转发到的地址
          uri: lb://service-product #lb指负载均衡,后面跟具体微服务标识
          order: 1 #路由优先级,数字越小,优先级越高
          predicates: #断言,条件判断,返回boolean
            - Path=/product-serv/** #当请求路径满足path指定的规则,路由信息才会正常转发
          filters: #过滤器,请求传递过程中做一些手脚
            - StripPrefix=1 #在请求转发之前去掉一层路径

2.7Sleuth链路跟踪

在微服务中,为了能够定位服务请求的过程,引入了链路跟踪技术sleuth。sleuth在整个分布式系统中能跟踪一个用户请求的过程(包括数据采集,数据传输,数据存储,数据分析,数据可视化),捕获这些跟踪数据,就能构建微服务的整个调用链的视图,这是调试和监控微服务的关键工具。

名词

trace:请求的唯一标识
span:基本的工作单元,统计各个处理单元的开始、结束标记
annotation:记录一段时间内的事件

导入依赖

由于所有的微服务都要使用sleuth,所以在父工程的pom中加入此依赖

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

2.8zipkin集成

介绍

致力于收集服务的定时数据,以解决微服务中的延迟问题,包括数据的“收集、存储、查找和展现”。

导入依赖

父工程中加入,以便所有模块使用。

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

配置

zipkin:
    base-url: http://127.0.0.1:9411/ #zipkin server的地址
    discoveryClientEnabled: false  #让nacos把他当成一个URI,不要当成服务名
 sleuth:
   sampler:
     probability: 1.0 #采样的百分比

启动

配置完成后,进入zipkin所在目录,使用java -jar zipkin-server-2.12.9-exec.jar启动。
持久化启动:java -jar zipkin-server-2.12.9-exec.jar --STORAGE_TYPE=mysql --MYSQL_HOST=127.0.0.1 --MYSQL_TCP_PORT=3306 --MYSQL_DB=zipkin --MYSQL_USER=root --MYSQL_PASS=root
启动后访问:localhost:9411/zipkin/

2.9nacos配置中心

添加依赖

<!--  nacos config-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>

在nacos中加入配置

分布式微服务 分布式微服务框架_分布式微服务_05

新建bootstrap.yml文件

文件中加以下配置

spring:
  application:
    name: service-product
  cloud:
    nacos:
      config:
        server-addr: localhost:8848
        file-extension: yaml
  profiles:
    active: dev  #开发环境

注意dataId的规则是“服务名”-“环境标识”.ymal。

自动刷新

配置方式
@RestController
@RefreshScope //注解方式需要加上这个注解
public class NacosConfigController {
    @Autowired
    private ConfigurableApplicationContext applicationContext;  //配置方式

    @Value("${config.appName}")  //注解方式
    private String appName;

    @RequestMapping("/test-config1")  //配置方式
    public String testConfig1(){
        return applicationContext.getEnvironment().getProperty("config.appName");
    }

    @RequestMapping("/test-config2") //注解方式
    public String testConfig2(){
        return appName;
    }
}

需要在nacos上加入config,下面参数就是可以获取的,如图:

分布式微服务 分布式微服务框架_maven_06

2.10Seata分布式事务

registry.conf中修改两个参数:

分布式微服务 分布式微服务框架_分布式微服务_07

修改nacos-config.txt文件

将自己的服务写到文件中
service.vgroup_mapping.product-service=default
service.vgroup_mapping.order-service=default
注意:此项配置在0.9.0版本需要,后续版本不需要配置。

配置到nacos

在conf文件夹下执行nacos-config.sh 127.0.0.1

将配置刷新至nacos

分布式微服务 分布式微服务框架_spring_08

启动

bin目录下输入seata-server.bat -p 5000 -m file启动 port随便写

添加数据log表

CREATE TABLE `undo_log` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `branch_id` bigint(20) NOT NULL,
  `xid` varchar(100) NOT NULL,
  `context` varchar(128) NOT NULL,
  `rollback_info` longblob NOT NULL,
  `log_status` int(11) NOT NULL,
  `log_created` datetime NOT NULL,
  `log_modified` datetime NOT NULL,
  `ext` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8  COMMENT='AT transaction mode undo table';

项目中添加配置

在所有需要控制事务的项目中加入bootstrap.yml文件,文件内容:
tx-service-group的值是nacos-config.txt文件中配的service.vgroup_mapping.product-service=default

spring:
  application:
    name: service-product
  cloud:
    nacos:
      config:
        server-addr: localhost:8848
        namespace: public
        group: SEATA_GROUP
    alibaba:
      seata:
        tx-service-group: product-service

将registry.conf文件复制一份放在resources下
最后在开启事务的最上游方法上添加@GlobalTransactional //全局事务控制注解,即可实现全局事务控制