微服务概念
why
因为现在是互联网项目,超高并发的访问,传统的方式无法解决问题。
单体
Tomcat和Mysql等软件部署在一台服务器上面,比如项目一和项目二。
只能用于小项目中,访问量小的项目中,如果访问量大了,所有的请求到在一个服务器就绷不住了。
分布式
分布式(部署角度):项目中包含Tomcat和Mysql等软件分别部署在不同的服务器中,比如Tocat放在一个服务器,Mysql放在一个服务器。但是假如Tomcat的服务器崩了,整个系统也崩了。
能够解决单体的问题,但是高并发上来了,有一个服务器绷不住了,这个项目也崩了
集群
集群:通过了负载均衡器搭配分布式项目,通过负载均衡器来解决高并发对单个服务器的压力过大的问题,分担服务器的一个压力。但是对于项目来说还是没有分离,仅仅是多个服务器部署一个项目。
能够解决高并发的问题,负载均衡器能够承受超强的高并发问题。
面试问题
1、分布式架构或者微服务架构相当于单体架构有什么好处
2、你的项目用户量比较少,为什么使用微服务架构
3、你在微服务架构当中遇到最大的难题是什么?
4、你们项目的服务划分原则是怎么进行划分的
在前公司没有接触到,但是会根据以下原则考虑。
先是根据业务来分,按照职责的范围来区分,
5、你们项目一个服务多少个人负责,或则你负责的这个服务一共多少个人
理想情况,一个服务三个人负责
真实情况,一个人负责一个或者多个服务
系统架构演变
1、单体应用架构
就是想我们学习开发,所有的软件启动部署都在自己的服务器中。
优点:开发简单,容易上手,容易维护,成本低
缺点:扩展性差,项目模块之间耦合度高
2、垂直应用架构
将一个项目拆分成几个系统(比如电商系统、后台系统、CMS系统)。一个系统可以部署在多个服务器上,需求大的可以安排几个服务器,需求小的可以安排少量服务器即可。
优点:能够解决高并发问题、系统分离、一个系统倒了不会影响其他系统,只会影响部分项目功能。
缺点:存在代码重复,毕竟还是一个服务器部署一个小系统。各个系统之间不能交互
3、分布式架构(部署角度)
将一个项目拆成几个系统(比如电商系统、后台系统、CMS系统),再将一个系统中的模块分别放在不同的服务器的Tomcat内。
优点:减少了代码重复问题,提高代码复用性
缺点:耦合度很高,调用复杂,难以维护
4、微服务架构(业务拆分角度)
一个项目中有很多个功能模块,根据功能模块来进行划分,可以根据功能模块的访问量来进行动态扩展,可以针对访问量大的和访问量小的功能来设计集群的大小等等。
优点:独立性强,扩展性强,每个服务之间清晰的任务划分
缺点:一个服务一个服务器,一个数据库一个服务器,成本高。
分布式架构和微服务的区别:
1、微服务的服务器是一个服务器对应一个数据库
2、微服务是按照业务拆分代码,而分布式会有重复的代码,只是将服务独立放在服务器中。
3、拆分的角度不同,微服务是根据业务需求来拆分,一个业务服务就是一个项目,一个服务器,专用一个数据库服务器
Nacos Discovery服务治理
WHY
微服务中,每个服务器之间的沟通是一个问题。比如需要调用服的时候,需要通过接口路径传送参数获取数据,但是在多个服务器的时候,我们无法准备获取某个服务器的路径。但是路径又不能够写死,写死就不能够换服务器分担了。
HOW
1、服务在启动的时候,会把信息注册到到注册中心,并且会拉取一份注册中心服务列表信息
2、每个一段时间就给注册中心发送一个心跳包,确保服务器还活着,并且会拉取一份最新的服务列表信息
3、如果连续几次没有给注册中心发送心跳包,以为服务已经挂了,注册中心就会把节点信息删除
实战入门
环境
1、安装Nacos
下载地址: https://github.com/alibaba/nacos/releases
下载zip格式的安装包,然后进行解压缩操作,上课使用的Nacos Server版本是1.3.2
2、启动Nacos
#切换目录 (或者在目录中代开cmd)
cd nacos/bin
#命令启动
startup.cmd -m standalone
3、访问Nacos
打开浏览器输入http://localhost:8848/nacos,即可访问服务, 默认密码是nacos/nacos
将服务注册到Nacos中
1、在pom.xml中添加Nacos的依赖
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
2、在主类上(SpringBootApplication类)添加@EnableDiscoveryClient注解
让配置中心发现
3、在配置文件中添加 Nacos服务的地址
spring:
cloud:
nacos:
discovery:
server-addr: localhost:8848
4、启动服务、观察Nacos的控制面板中是否有注册上来的微服务
5、实现微服务调用
//从nacos中获取服务地址
ServiceInstance instance = discoveryClient.
getInstances("product-service").get(0);
String url = instance.getHost()+":"+instance.getPort();
//远程调用商品微服务,查询商品信息
Product product = restTemplate.getForObject(
"http://"+url+"/product/"+productId,Product.class);
log.info("查询到{}号商品的信息,内容是:{}", productId, JSON.toJSONString(product));
WHAT
服务治理是微服务架构中最核心最基本的模块。用于实现各个微服务的自动化注册与发现。
自动化注册:在每个微服务中,会将信息发送到注册中心,进行自动化注册。相当于形成一张动态的服务器状态登记表,每隔一段时间会记录服务器的情况。也可以说是实时监控微服务的服务器们。
Ribbon 远程调用负载均衡
WHY
微服务架构,服务器特别多,但是对于服务器的调用也是问题。如果通自己写逻辑去对请求进行分流也很麻烦。所以使用 Ribbon 组件来解决请求分配到服务器的问题。
WHAT
将请求通过策略,将服务器的使用最大化
HOW
1、通过反射接收到传递的 url 参数,将url的信息提出切割处理
2、上本地中去找product-service 对应服务信息, localhost:8081 localhost:8082
3、然后根据你配置的负载均衡策略,选择下一个节点 localhost:8081,吧 url中服务名字替换 ip:port
4、使用 RestTemplate 对象去发送请求
配置策略需要加载到服务调用方!
自定义负载均衡
1、通过idea启动多个微服务(两种方式,不懂百度)
2、通过Nacos查看微服务的启动情况
3、通过负载均来实现
@Autowired
private DiscoveryClient discoveryClient;
List instances = discoveryCient.
getInstances("product-service");
int index = new Random().nextInt(instances.size());
ServiceInstance instance = instances.get(index);
String url = instance.getHost()+":"+instance.getPort();
log.info(">>从nacos中获取到的微服务地址为:" + url);
//远程调用商品微服务,查询商品信息
Product product = restTemplate.getForObject(
"http://"+url+"/product/"+productId,Product.class);
log.info("查询到{}号商品的信息,内容是:{}", productId, JSON.toJSONString(product));
4、测试有没有调用两个服务器来处理请求
基于Ribbon实现负载均衡
1、在RestTemplate 的生成方法上添加@LoadBalanced注解
@Bean
@LoadBalanced //开启负载均衡的功能
public RestTemplate restTemplate(){
return new RestTemplate();
}
2、修改OrderServiceImpl服务调用的方法
Product product = restTemplate.getForObject(
"http://product-service/product?productId="+productId,Product.class);
log.info("查询到{}号商品的信息,内容是:{}", productId, JSON.toJSONString(product));
3、被调用的微服务(Controller中)查看
获取被调用服务器的端口
@Value("${server.port}")
private String port;
修改均衡策略
Ribbon内置了多种负载均衡策略,内部负载均衡的顶级接口为
com.netflix.loadbalancer.IRule , 具体的负载策略如下图所示:
策略名 | 策略描述 | 实现说明 |
---|---|---|
BestAvailableRule | 选择一个最小的并发请求的server | 逐个考察Server,如果Server被tripped了,则忽略,在选择其中ActiveRequestsCount最小的server |
AvailabilityFilteringRule | 先过滤掉故障实例,再选择并发较小的实例; | 使用一个AvailabilityPredicate来包含过滤server的逻辑,其实就就是检查status里记录的各个server的运行状态 |
WeightedResponseTimeRule | 根据相应时间分配一个weight,相应时间越长,weight越小,被选中的可能性越低。 | 一个后台线程定期的从status里面读取评价响应时间,为每个server计算一个weight。Weight的计算也比较简单responsetime 减去每个server自己平均的responsetime是server的权 |
RetryRule | 对选定的负载均衡策略机上重试机制。 | 在一个配置时间段内当选择server不成功,则一直尝试使用subRule的方式选择一个可用的server |
RoundRobinRule | 轮询方式轮询选择server | 轮询index,选择index对应位置的server |
RandomRule | 随机选择一个server | 在index上随机,选择index对应位置的server |
ZoneAvoidanceRule(默认) | 复合判断server所在区域的性能和server的可用性选择server | 使用ZoneAvoidancePredicate和AvailabilityPredicate来判断是否选择某个server,前一个判断判定一个zone的运行性能是否可用,剔除不可用的zone(的所有server),AvailabilityPredicate用于过滤掉连接数过多的Server。 |
product-service: # 调用的提供者的名称
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
Feign 远程调用
WHY
在微服务中调用其他的服务还是麻烦,只需要创建一个接口和一个注解即可
HOW
1、通过反射拿到代理类实现接口
2、拿到接口上的注解 @FeignClient() ,并且拿到注解的值, product-server
3、拿到接口中方法,取方法上的 @RequestMapping注解,并且拿到注解中的 / Xxx 方法
4、拿到接中方法,去方法参数中注解@RequestParam(), 并且拿到注解中 id=xx
5、拼接路径, http://product-server/Xxx?id=xx
6、根据服务名字去本地区找 product-server 节点信息: localhost:8081, localhost:8082
7、根据 Ribbon 负载均衡策略选择一个节点 : localhost:8081
8、把服务名去替换成真实的 ip和端口 , http://localhost:8081/Xxx?id=xx
9、使用 RestTemplate 对象去发送 Http 请求
WHAT
声明式的伪Http客户端,它使得调用远程服务就像调用本地服务一样简单。
Nacos兼容了Feigh,Feign默认集成了Ribbin,可以在配置:超时配置和重试次数配置
Feign入门
1、在shop-order-server项目的pom文件加入Fegin的依赖
org.springframework.cloud
spring-cloud-starter-openfeign
2、在调用服务类启动类上添加Fegin的扫描注解,注意扫描路径(默认扫描当前包及其子包)
@SpringBootApplication
@EnableDiscoveryClient // 开启治理中心管理
@EnableFeignClients
public class OrderServer {
public static void main(String[] args) {
SpringApplication.run(OrderServer.class,args);
}
}
3、在调用服务的项目中新增接口 被调用类FeignApi
//name的名称一定要和订单服务的服务名保持一致
@FeignClient(name = "product-service")
public interface ProductFeignApi {
// 这里直接复制被调用接口的代码
@RequestMapping("/product")
public Product findByPid(@RequestParam("pid") Long pid);
}
4、修改 远程调用方法(通过创建的 XxxFeignApi )
@Service
@Slf4j
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderDao orderDao;
@Autowired
private ProductFeignApi productFeignApi;
@Override
public Order createOrder(Long productId,Long userId) {
log.info("接收到{}号商品的下单请求,接下来调用商品微服务查询此商品信息", productId);
//远程调用商品微服务,查询商品信息
Product product = productFeignApi.findByPid(productId);
log.info("查询到{}号商品的信息,内容是:{}", productId, JSON.toJSONString(product));
//创建订单并保存
Order order = new Order();
order.setUid(userId);
order.setUsername("叩丁狼教育");
order.setPid(productId);
order.setPname(product.getPname());
order.setPprice(product.getPprice());
order.setNumber(1);
orderDao.save(order);
log.info("创建订单成功,订单信息为{}", JSON.toJSONString(order));
return order;
}
}
超时配置
默认配置是1秒,重试次数1次
在服务调用方
feign:
client:
config:
default:
connectTimeout: 5000 # 连接等待时间
readTimeout: 5000 # 连接通话时间
重试次数配置
要考虑你的业务情况,如果是插入数据的请求,重试会造成数据库的多次操作
https://github.com/Netflix/ri...
product-service: # 调用的提供者的名称
ribbon:
MaxAutoRetries: 0 # 最大重试次数
MaxAutoRetriesNextServer: 0 # 最大重试节点个数
Sentinel(分布式系统的流量防卫兵)服务熔断降级
WHY
服务器雪崩效应
在分布式的系统中,由于网络等等原因,造成某个服务阻塞.由于微服务是相互调用的关系(相互依赖),所以当一个服务受到影响,调用这个服务的服务器也会受到影响,从而影响整个系统.
解决思路
隔离机制(资源隔离)、超时机制(超时就脱离)、限流机制、熔断机制、降级机制(弄个备选方案)
HOW
通过网络来监控每个微服务的情况,再根据所设置的方案来进行调控。
WHAT
Sentinel (分布式系统的流量防卫兵) 是阿里开源的一套用于服务容错的综合性解决方案。它以流量
为切入点, 从流量控制、熔断降级、系统负载保护等多个维度来保护服务的稳定性
Sentinel 具有以下特征:
- 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景, 例如秒杀(即
突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用
应用等。
- 完备的实时监控:Sentinel 提供了实时的监控功能。通过控制台可以看到接入应用的单台机器秒
级数据, 甚至 500 台以下规模的集群的汇总运行情况。
- 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块, 例如与 Spring
Cloud、Dubbo、gRPC 的整合。只需要引入相应的依赖并进行简单的配置即可快速地接入
Sentinel。
Sentinel入门
1、导入依赖
com.alibaba.cloud
spring-cloud-starter-alibaba-sentinel
2、安装Sentinel 控制台
- 下载jar包 https://github.com/alibaba/Se...
启动控制台
# 直接使用jar命令启动项目(控制台本身是一个SpringBoot项目) java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.0.jar
修改shop-order-server项目中的配置文件application.yml,新增如下配置:
spring: cloud: sentinel: transport: port: 9999 #跟控制台交流的端口,随意指定一个未使用的端口即可 dashboard: localhost:8080 # 指定控制台服务的地址
通过浏览器访问localhost:8080 进入控制台 ( 默认用户名密码是 sentinel/sentinel )
注意: 默认是没显示order-service的,需要访问几次接口,然后再刷新sentinel管控台才可以看到.
Sentinel规则
流控
监控流量的QPS或者并发线程数等指标,达到阔值进行限流,避免冲破服务器
PS:峰值QPS,同一个时刻的并发量,一秒内能够处理的请求
资源名:唯一名称,默认是请求路径,可自定义
针对来源:指定对哪个微服务进行限流,默认指default,意思是不区分来源,全部限制
阈值类型/单机阈值:
- QPS(每秒请求数量): 当调用该接口的QPS达到阈值的时候,进行限流
- 线程数:当调用该接口的线程数达到阈值的时候,进行限流
是否集群:暂不需要集群
高级选项:
sentinel共有三种流控模式,分别是:
- 直接(默认):接口达到限流条件时,开启限流
- 关联:当关联的资源达到限流条件时,开启限流 [适合做应用让步]
- 链路:当从某个接口过来的资源达到限流条件时,开启限流
降级
降级规则就是设置当满足什么条件的时候,对服务进行降级。Sentinel提供了三个衡量条件:
- 慢调用比例: 选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
- 异常比例: 当单位统计时长内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是
[0.0, 1.0]
,代表 0% - 100%。 - 异常数:当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
热点(细化的流控)
1、添加注解@SentinelResource
需要在请求方法上天注解,否则热点规则无效
2、控制台新增热点
授权
很多时候,我们需要根据调用来源来判断该次请求是否允许放行,这时候可以使用 Sentinel 的来源访问控制(黑白名单控制)的功能。来源访问控制根据资源的请求来源(origin
)限制资源是否通过,若配置白名单则只有请求来源位于白名单内时才可通过;若配置黑名单则请求来源位于黑名单时不通过,其余的请求通过。
Sentinel自定义异常返回
当前面设定的规则没有满足是,我们可以自定义异常返回.
- FlowException 限流异常
- DegradeException 降级异常
- ParamFlowException 参数限流异常
- AuthorityException 授权异常
- SystemBlockException 系统负载异常
在shop-order-server项目中定义异常返回处理类
@Component
public class ExceptionHandlerPage implements BlockExceptionHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
response.setContentType("application/json;charset=utf-8");
ResultData data = null;
if (e instanceof FlowException) {
data = new ResultData(-1, "接口被限流了");
} else if (e instanceof DegradeException) {
data = new ResultData(-2, "接口被降级了");
}else if (e instanceof ParamFlowException) {
data = new ResultData(-3, "参数限流异常");
}else if (e instanceof AuthorityException) {
data = new ResultData(-4, "授权异常");
}else if (e instanceof SystemBlockException) {
data = new ResultData(-5, "接口被降级了...");
}
response.getWriter().write(JSON.toJSONString(data));
}
}
@Data
@AllArgsConstructor//全参构造
@NoArgsConstructor//无参构造
class ResultData {
private int code;
private String message;
}
Feign 整合 Sentinel
在shop-order-server项目的配置文件中开启feign对Sentinel的支持
feign: sentinel: enabled: true
创建容错类
@Component public class ProductFeignFallBack implements ProductFeignApi { @Override public Product findByPid(Long pid) { Product product = new Product(); product.setPid(-1L); product.setPname("兜底数据"); product.setPprice(0.0); return product; } }
在feign接口中定义容错类
@FeignClient(name = "product-service",fallback = ProductFeignFallBack.class) public interface ProductFeignApi { @RequestMapping("/product") public Product findByPid(@RequestParam("pid") Long pid); }
- 停止所有 商品服务,重启 shop-order 服务,访问请求,观察容错效果
Gateway 服务网关
WHY
在微服务的项目中,往往是多个服务器,也就意味着有多个微服务地址,那我们需要通过微服务地址去寻找调用。但是地址很多,没有统一化管理, 逻辑比较复杂。而且如果添加了鉴权需求,需要去较多的代码去实现。
HOW
1、当请求到网关,会有请求处理器会接受请求。然后转发处理器映射器,返回执行链
2、交给 Gateway web 处理器进行处理,根据发来的 url 跟本地配置进行匹配,如果匹配到了对应服务信息, product-service 服务器名对应 id 信息,比如 localhost:8081, localhost:8082
3、根据你配置 ribbon 的负载均衡策略选择一个节点 localhost:8081
4、最终他会进行拼接路径和请求,然后通过 template 来发送 HTTP 请求
WHAT
1、网关的作用作为统一入口
2、一些通用的功能放到网关这里进行处理
3、网络隔离
相当于以前项目的拦截器
Gateway快速入门
1、创建一个 api-gateway 的模块,导入相关依赖
shop-parent
cn.wolfcode
1.0.0
4.0.0
api-gateway
org.springframework.cloud
spring-cloud-starter-gateway
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
org.projectlombok
lombok
2、编写启动类
需要 @EnableDiscoveryClient,因为需要去注册中心拉取微服务的服务信息
@SpringBootApplication
@EnableDiscoveryClient
public class ApiGatewayServer {
public static void main(String[] args) {
SpringApplication.run(ApiGatewayServer.class,args);
}
}
3、编写配置文件
server:
port: 9000
spring:
application:
name: api-gateway
cloud:
nacos:
discovery:
server-addr: localhost:8848
gateway:
discovery:
locator:
enabled: true # 让gateway可以发现nacos中的微服务
4、通过网关的端口去访问微服务
localhost:9000/服务名/接口?参数
自定义路由接口
1、在 application/yml 中添加新的路由规则
- **id**,路由标识符,区别于其他 Route。
- **uri**,路由指向的目的地 uri,即客户端请求最终被转发到的微服务。
- **order**,用于多个 Route 之间的排序,数值越小排序越靠前,匹配优先级越高。
- **predicate**,断言的作用是进行条件判断,只有断言都返回真,才会真正的执行路由。
- **filter**,过滤器用于修改请求和响应信息。
spring:
application:
gateway:
routes:
- id: product_route
uri: lb://product-service
predicates:
- Path=/product-serv/**
filters:
- StripPrefix=1
- id: order_route
uri: lb://order-service
predicates:
- Path=/order-serv/**
filters:
- StripPrefix=1
Filter 过滤器
过滤器就是在请求的传递过程中,对请求和响应做一些手脚.
在Gateway中, Filter的生命周期只有两个:“pre” 和 “post”。
- PRE: 这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择
请求的微服务、记录调试信息等。
- POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTP
Header、收集统计信息和指标、将响应从微服务发送给客户端等。
在Gateway中,Filter的作用范围两种:
- GatewayFilter:应用到单个路由或者一个分组的路由上。
- GlobalFilter:应用到所有的路由上
局部过滤
1、编写Filter 类,注意名称是有固定格式 xxxGatewayFilterFactory (Xxx与添加的路由配置相同)
@Component
public class TimeGatewayFilterFactory extends AbstractGatewayFilterFactory {
/**
* Parts key.
*/
public static final String PARTS_KEY = "parts";
public TimeGatewayFilterFactory() {
super(TimeGatewayFilterFactory.Config.class);
}
@Override
public List shortcutFieldOrder() {
return Arrays.asList(PARTS_KEY);
}
public GatewayFilter apply(TimeGatewayFilterFactory.Config config) {
return new GatewayFilter() {
@Override
public Mono filter(ServerWebExchange exchange,
GatewayFilterChain chain) {
// System.out.println("前置逻辑");
if(!config.isParts()){
return chain.filter(exchange);
}
/**
* 前置这里去记录一下时间
*/
exchange.getAttributes().put("beginTime",new Date().getTime());
return chain.filter(exchange).then(Mono.fromRunnable(new Runnable() {
@Override
public void run() {
// 记录一下时间, 然后相减
System.out.println("后置逻辑");
long endTime = new Date().getTime();
long beginTime = (long) exchange.getAttributes().get("beginTime");
System.out.println("最终耗时"+(endTime-beginTime) +"ms");
}
}));
}
};
}
@Getter@Setter
public static class Config {
private boolean parts;
}
}
2、添加路由规则
routes:
- id: order_route
uri: lb://order-service
predicates:
- Path=/order-serv/**
filters:
- StripPrefix=1
- Time=true #这里与创建的Filter类名前缀相同
全局过滤
全局过滤器作用于所有路由, 无需配置。通过全局过滤器可以实现对权限的统一校验,安全性验证等功
能。
需求: 实现统一鉴权的功能,我们需要在网关判断请求中是否包含token且,如果没有则不转发路由,有则执行正常逻辑。
1、编写全局过滤类
@Component
public class AuthGlobalFilter implements GlobalFilter {
@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String token = exchange.getRequest().getQueryParams().getFirst("token");
if (StringUtils.isBlank(token)) {
System.out.println("鉴权失败");
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
}
2、测试携带 Token 是否能够进入方法
Sleuth&Zipkin 链路追踪
WHY
在微服务框架中,每天产生的日志也会很多,而且每个服务器之间调用频繁,需要查看不同的服务器的日志,错误寻找困难。
HOW
Sleuth在日志中产生日志的唯一标记,能够通过标记来寻找某一次的请求所产生的日志,再通过 Zipkin 来创建可视化的链路追踪。
日志格式:
[order-server,c323c72e7009c077,fba72d9c65745e60,false]
1、第一个值,spring.application.name的值
2、第二个值,c323c72e7009c077 ,sleuth生成的一个ID,叫Trace ID,用来标识一条请求链路,一条请求链路中包含一个Trace ID,多个Span ID
3、第三个值,fba72d9c65745e60、spanID 基本的工作单元,获取元数据,如发送一个http
4、第四个值:true,是否要将该信息输出到zipkin服务中来收集和展示。
WHAT
分布式链路追踪(Distributed Tracing),就是将一次分布式请求还原成调用链路,进行日志记录,性能监控并将一次分布式请求的调用情况集中展示。比如各个服务节点上的耗时、请求具体到达哪台机器上、每个服务节点的请求状态等等。
Zipkin + Sleuth 整合
1、下载 Zipkin 的jar包:官网:https://zipkin.io/
2、通过 java -jar 启动
3、通过 浏览器访问 http://localhost:9411访问
4、添加依赖(添加了此依赖,Sleuth的依赖可以删除,里面整合了)
org.springframework.cloud
spring-cloud-starter-zipkin
5、配置
spring:
zipkin:
base-url: http://127.0.0.1:9411/ #zipkin server的请求地址
discoveryClientEnabled: false #让nacos把它当成一个URL,而不要当做服务名
sleuth:
sampler:
probability: 1.0 #采样的百分比
6、重启服务的服务器
7、访问zipkin的UI界面,观察效果
Nacos Config 配置中心
WHY
1、配置重复问题。当我们在多个服务中,需要配置相同的信息,在多个服务中配置量相同的配置,比如多个服务使用 Redis ,那服务都需要进行Redis的配置。
2、配置没有历史版本,修改完之后没有记录,想恢复之前的版本就无法退回。
3、切换环境麻烦。开发中会有开发环境、有测试环境还有生成环境,每个配置都需要修改
4、配置没有办法动态刷新
HOW
把项目的各种配置全部放到一个集合中的地方进行统一管理,并提供一套标准的接口
当哥哥服务需要获取配置的时候,就去配置中心拉取自己的配置
因为服务每次启动都会拉取,所以配置有更新的时候,服务这边也会同步更新
WHAT
使用nacos作为配置中心,其实就是将nacos当做一个服务端,将各个微服务看成是客户端,我们
将各个微服务的配置文件统一存放在nacos上,然后各个微服务从nacos上拉取配置即可。
Nacos Config入门
1、搭建好Nacos 的环境(Nacos Discovery服务治理中)
2、在商品微服务中引入 Nacos 配置的依赖
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-config
3、在微服务中添加 Nacos config 的配置
配置文件优先级(由高到低):
bootstrap.properties -> bootstrap.yml -> application.properties -> application.yml
Boostrap.yml文件
spring:
application:
name: product-service
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848 #nacos中心地址
file-extension: yaml # 配置文件格式
profiles:
active: dev # 环境标识
4、在 Nacos 中添加配置,然后把商品微服务 application.yml 配置复制到配置内容中
5、注释本地的application.yaml中的内容,启动程序进行测试
配置动态刷新
在nacos中的product-service-dev.yaml配置项中添加下面配置:
appConfig: name: product2020
在商品微服务中新增NacosConfigControlller.java
@RestController @RefreshScope public class NacosConfigController { @Value("${appConfig.name}") private String appConfigName; @RequestMapping("/nacosConfig1") public String nacosConfig(){ return "远程信息:"+appConfigName; } }
配置共享
同一个微服务的不同环境之间(开发、测试、部署等环境)共享配置
创建一个公共配置,名称不带环境,如 product-service.yaml
不同微服务中间共享配置
不同微服务之间实现配置共享的原理类似于文件引入,就是定义一个公共配置,然后在当前配置中引
入。
在nacos中定义一个Data ID为global-config.yaml的配置,用于所有微服务共享
globalConfig: global