分布式 微服务架构
- 微服务发展
- 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微服务架构演变
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会帮助屏蔽底层的传输方式、序列化方式和通信细节。
1.1.2.3服务网关
网关的基本功能:1、统一接入 2、安全防护 3、协议适配 4、流量管控 5、长短连接支持 6、容错能力
1.1.2.4服务容错
三个核心思想:1、不被外界影响 2、不被上游请求压垮 3、不被下游响应拖垮
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是懒加载的,需要访问一次服务,才能被发现并监控。
资源名配置@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中加入配置
新建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,下面参数就是可以获取的,如图:
2.10Seata分布式事务
registry.conf中修改两个参数:
修改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
启动
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 //全局事务控制注解,即可实现全局事务控制