微服务笔记
文章目录
Nacos(注册中心/配置中心)
介绍
- Nacos 是阿里巴巴推出来的一个新开源项目,这是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
- Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。
- Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。 Nacos 是构建以“服务”为中心的现代应用架构 (例如微服务范式、云原生范式) 的服务基础设施
功能
- 服务发现和服务健康监测(使服务更容易注册,并通过DNS或HTTP接口发现其他服务,还提供服务的实时健康检查,以防 止向不健康的主机或服务实例发送请求。 )
- 支持基于DNS和基于RPC的服务发现。服务提供者使用原生SDK、OpenAPI、或一个独立的Agent TODO注册 Service 后,服务消费者可以使用DNS TODO 或HTTP&API查找和发现服务。
- Nacos提供对服务的实时的健康检查,阻止向不健康的主机或服务实例发送请求。Nacos 支持传输层 (PING 或 TCP)和应用层 (如 HTTP、MySQL、用户自定义)的健康检查。 对于复杂的云环境和网络拓扑环境中(如 VPC、边缘网络等)服务的健康检查,Nacos 提供了 agent 上报模式和服务端主动检测2种健康检查模式。Nacos 还提供了统一的健康检查仪表盘,帮助您根据健康状态管理服务的可用性及流量。
- 动态配置服务
- 以中心化、外部化和动态化的方式管理所有环境的应用配置和服务配置。
- 消除了配置变更时重新部署应用和服务的需要,让配置管理变得更加高效和敏捷。
- 配置中心化管理让实现无状态服务变得更简单,让服务按需弹性扩展变得更容易。
- 提供了一个简洁易用的UI (控制台样例 Demo) 帮助管理所有的服务和应用的配置。
- Nacos 还提供包括配置版本跟踪、金丝雀发布、一键回滚配置以及客户端配置更新状态跟踪在内的一系列开箱即用的配置管理特性,能更安全地在生产环境中管理配置变更和降低配置变更带来的风险。
- 动态 DNS 服务
- 动态 DNS 服务支持权重路由,更容易地实现中间层负载均衡、更灵活的路由策略、流量控制以及数据中心内网的简单DNS解析服务。动态DNS服务还能更容易地实现以 DNS 协议为基础的服务发现,消除耦合到厂商私有服务发现 API 上的风险。
- Nacos 提供了一些简单的 DNS APIs TODO ,管理服务的关联域名和可用的 IP:PORT 列表
- 服务及其元数据管理
- 从微服务平台建设的视角管理数据中心的所有服务及元数据,包括管理服务的描述、生命周期、服务的静态依赖分析、服务的健康状态、服务的流量管理、路由及安全策略、服务的 SLA 以及最首要的 metrics 统计数据。
Nacos使用
conf目录下配置文件application.properties ,Nacos默认端口是8848
编辑startup.cmd文件,选择集群还是单机模式
修改完保存双击启动
Nacos启动的时候如果闪退,查看一下是集群模式还是单机模式
启动失败,报"Error creating bean with name ‘user’: Unsatisfied dependency expressed through field 'jwtTokenManager’“这种错的时候
打开conf文件夹下的application.properties文件,找到"nacos.core.auth.plugin.nacos.token.secret.key=”
把这一段复制到后面
SecretKey012345678901234567890123456789012345678901234567890123456789
Nacos 服务器启动后,进入 http://ip:8848 查看控制台(默认账号名/密码为 nacos/nacos)
配置
yml
server:
port: 8080
spring:
application:
name: test-service
cloud:
nacos:
server-addr: 127.0.0.1:8848
discovery:
# enabled: true
namespace: public
username: nacos
password: nacos
xml
<!--nacos服务注册发现-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2021.0.4.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
官网:pom的配置.xml以下是pom.xml的完整示例
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>open.source.test</groupId>
<artifactId>nacos-discovery-test</artifactId>
<version>1.0-SNAPSHOT</version>
<name>nacos-discovery-test</name>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>${spring.boot.version}</version>
<relativePath/>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.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.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Ribbon(负载均衡器)
介绍
在某个时间里面,微服务A的访问请求非常多,导致微服务A无法处理过来,从而导致微服务A宕机,对于这种情况,我们应该专门避免呢???
对于上面这种情况,一台微服务A无法处理那么多的请求,那如果是多台微服务A应用程序呢,可以将请求平均分配到不同的微服务A机器上面,从而减少某一台微服务A的请求处理量,避免服务宕机情况的出现,这里介绍的思想就是:负载均衡
nacos-discovery依赖了ribbon,可以不用再引入ribbon依赖
常见的负载均衡策略
-
随机策略(Random):采用随机的方式选择服务调用地址。
-
轮询策略(RoundRobin):按照从头到尾循环选择的方式调用服务地址,默认策略。
-
加权轮询策略(WeightedResponseTime):一开始某一个服务地址都没有权重,所以默认采用轮询方式调用,当调用每一个服务地址时候,都会记录服务响应时间,Ribbon会根据响应时间给这个服务地址设置一个权重,下一次调用时候会根据权重来选择需要调用哪个地址,响应时间越短,说明这个服务机器性能越好,处理的效率越高,下一次被选中的概率就越大。
-
重试轮询策略(Retry):默认采用轮询策略,如果选择的服务地址可以则直接调用,服务地址不可用,则循环重试选择可以服务地址。
-
最小并发策略(BestAvailable):首先将所有不可用的服务去掉,然后从可用服务列表里面获取并发数量最小的地址进行调用,由于一开始还每月并发数量,所以默认先采用轮询策略,一段时间之后,改用最小并发策略。
-
过滤不可用策略(AvailabilityFiltering):首先将不可用的服务、并发连接数超过阈值的服务全部过滤掉,然后采用轮询的策略选择调用地址。
Ribbon使用
全局配置
@Configuration
public class RibbonConfig {
/**
*全局配置
*指定负载均衡策略
* @return
*/
@Bean
public IRule() [
// 指定使用Nacos提供的负载均衡衰略(优先调用同一集群的实例,基于随机权重)
return new NacosRule();
}
局部配置
调用指定微服务提供的服务时,使用对应的负载均衡算法
# ribbon 配置
RIBBON-PROVIDER: # 这里就是单独给 RIBBON-PROVIDER 微服务设置 Ribbon 相关配置属性
ribbon:
# 指定 Ribbon 的负载均衡策略
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule # 这里采用轮询策略
自定义负载均衡策略
实现【IRule】接口,重写其中的方法完成负载均衡算法即可。
如果直接实现【IRule】接口,那就需要重写接口中的所有方法,所以Ribbon给我们提供了一个抽象类【AbstractLoadBalancerRule】,我们继承这个类,只需要重写【initWithNiwsConfig()】方法和【choose()】方法即可完成自定义负载均衡策略。
package com.gitcode.ribbon.config;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import java.util.List;
import java.util.Random;
/**
* @version 1.0.0
* @Date: 2023/10/25 22:36
* @Description:
*/
public class CustomRibbonRule extends AbstractLoadBalancerRule {
@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {
}
@Override
public Server choose(Object o) {
// 从父类中获取负载均衡器对象
ILoadBalancer lb = getLoadBalancer();
// 获取所有可以服务
List<Server> reachableServers = lb.getReachableServers();
// 随机选择一个服务地址
Random random = new Random();
int index = random.nextInt(reachableServers.size() - 1);
// 返回这个选择服务地址
return reachableServers.get(index);
}
}
Feigin(服务调用)
介绍
- Feign 的英文表意为“假装,伪装,变形”, 是一个 Http 请求调用的轻量级框架,可以以 Java 接口注解的方式调用 Http
请求,而不用像 Java 中通过封装 HTTP 请求报文的方式直接调用。- Feign 通过处理注解,将请求模板化,当实际调用的时候,传入参数,根据参数再应用到请求上,进而转化成真正的请求,这种请求相对而言比较直观。
- Feign 被广泛应用在 Spring Cloud 的解决方案中,是学习基于 Spring Cloud 微服务架构不可或缺的重要组件。
Feigin使用
添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
创建fegin接口
/*
*name 指定调用rest接口所对应的服务名
*path 指定调用rest接口所在的stockController指定的@RequestMapping
*/
@FeignClient(name="stock-service",path ="/stock")
public interface StockFeignService {
// 声明需要调用的rest接口对应的方法,对应controller
@RequestMapping("/reduct")
String reduct();
}
调用fegin接口
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
StockFeignService stockFeignService;
@RequestMapping("/add")
public String add(){
System.out.println("下单成功!");
String msg = stockFeignService.reduct();
return msg;
}
}
启动类加注解
@EnableFeignClients
扩展配置
调用超时配置
"default"可以更改为其他服务名
feign:
client:
config:
default:
# 建立链接的超时时长
connectTimeout: 5000
# 读取超时时长
readTimeout: 5000
日志打印配置
"default"可以更改为其他服务名
- none:不记录任何日志(默认值)
- basic:仅记录请求方法、URL、响应状态代码以及执行时间
- headers:在basic级别的基础上,还记录了请求和响应的header
- full:记录请求和响应的header、body和元数据。
feign:
client:
config:
default:
# 设置日志级别
loggerLevel: basic
logging:
level:
# Feign接口所在的包
com.test.feign.wage: debug
指定配置文件
我们可以自定义一个配置类,并且将Feign指向自定义的配置类,比如:我们可以在类中配置header传递,也可以进行其他操作
@Configuration
public class FeignConfiguration implements RequestInterceptor {
/**
* 设置header传递,将当前服务的header信息传递到下游服务
*
* @param template
*/
@Override
public void apply(RequestTemplate template) {
ServletRequestAttributes attributes = (ServletRequestAttributes)
RequestContextHolder.getRequestAttributes();
if (attributes != null) {
HttpServletRequest request = attributes.getRequest();
Enumeration<String> headerNames = request.getHeaderNames();
if (headerNames != null) {
while (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
String values = request.getHeader(name);
// 跳过 content-length
if ("content-length".equals(name)) {
continue;
}
template.header(name, values).header("test", "test");
}
}
}
}
}
修改@FeignClient
@FeignClient(value = "user-service",configuration = {FeignConfiguration.class})
public interface TestFeign {
....
}
整合Sentinel降级容错
<!--sentinel客户端-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
# feign启用sentinel
feign:
sentinel:
enabled: true
Fallback容错类
@Component
public class TestServiceFallbackFactory implements FallbackFactory<TestServiceFeign> {
@Override
public WageServiceFeign create(Throwable throwable) {
// 可以将异常继续抛出,也可以实现自定义的内容
throw new RuntimeException(throwable.getMessage());
}
}
修改@FeignClient
//加入fallbackFactory指向容错类
@FeignClient(value = "user-service",fallbackFactory = TestServiceFallbackFactory .class),configuration = {FeignConfiguration.class})
public interface TestFeign {
....
}
异常处理器
我们可以实现异常拦截,在feign抛出异常后进行一些自定义处理
定义FeignErrorDecoder:
@Configuration
public class FeignErrorDecoder extends ErrorDecoder.Default {
private final Logger log = LoggerFactory.getLogger(FeignErrorDecoder.class);
@Override
public Exception decode(String methodKey, Response response) {
Exception exception = super.decode(methodKey, response);
// 如果是RetryableException,则返回继续重试
if (exception instanceof RetryableException) {
return exception;
} else {
// 可以自定义一些逻辑,比如抛出一个其他的(统一个异常)
exception = new RuntimeException("Feign调用出错了");
}
return exception;
}
}
修改@FeignClient
在configuration中加入FeignErrorDecoder 类
@FeignClient(value = "user-service",configuration = {FeignConfiguration.class, FeignErrorDecoder.class})
public interface TestFeign {
....
}
与Fallback容错类关系:
如果同时配置FeignErrorDecoder与FallbackFactory,那么异常会先进入到FeignErrorDecoder 然后再FallbackFactory中。
Sentinel(服务保护)
介绍
Sentinel是阿里开源的项目,提供了流量控制、熔断降级、系统负载保护等多个维度来保障服务之间的稳定性。
2012年,Sentinel诞生于阿里巴巴,其主要目标是流量控制。2013-2017年,Sentinel迅速发展,并成为阿里巴巴所有微服务的基本组成部分。它已在6000多个应用程序中使用,涵盖了几乎所有核心电子商务场景。2018年,Sentinel演变为一个开源项目。2020年,Sentinel Golang发布。
Sentinel 具有以下特征
- 丰富的应用场景 :Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
- 完备的实时监控 :Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
- 广泛的开源生态 :Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入Sentinel。
- 完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
Sentinel使用
引入 Sentinel 依赖
<!--Sentinel 依赖-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>1.8.6</version>
</dependency>
<!--客户端需要引入 Transport 模块来与 Sentinel 控制台进行通信-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-transport-simple-http</artifactId>
<version>1.8.6</version>
</dependency>
<!--@SentinelResource 注解用于定义资源-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-annotation-aspectj</artifactId>
<version>1.8.0</version>
</dependency>
下载完,打开cmd窗口,运行 java -jar 文件名
有的会因为版本问题启动失败,比如上图报的*Initializing Spring DispatcherServlet
‘dispatcherServlet’*等错,修改下命令java --add-opens java.base/java.lang=ALL-UNNAMED -jar 文件名即可
运行成功就可以进入Sentinel登录界面了,用户名密码默认为sentinel
以下依次修改Sentinel端口号,用户名,密码,Spring Boot 服务端 session 的过期时间,详细参考官方文档
java -Dserver.port=8086 -Dsentinel.dashboard.auth.username=sentinel -Dsentinel.dashboard.auth.password=123456 -Dserver.servlet.session.timeout=7200 -jar 文件名
启动时加入 JVM 参数 -Dcsp.sentinel.dashboard.server=consoleIp:port 指定控制台地址和端口
添加bean(SentinelResourceAspect),可放在启动类中
@Bean
public SentinelResourceAspect sentinelResourceAspect() {
return new SentinelResourceAspect();
}
/**
* spring 初始化方法
* @PostConstruct是Spring带的,当前这个bean在被spring容器创建的时候,就会自动的调用
* 被@PostConstruct这个注解修饰的方法进行初始化
*/
@PostConstruct
private static void initFlowRules() {
// 流控规则
List<FlowRule> rules = new ArrayList<>();
// 流控
FlowRule rule = new FlowRule();
// 为哪个资源进行流控
rule.setResource(REOURCE_NAME);
// 设置流控规则类型 sQPS
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
// Set limit QPS to 20.设置受保护的资源阙值
rule.setCount(1);
// rule.setMaxQueueingTimeMs(10);
rules.add(rule);
// 加载配置好的规则
FlowRuleManager.loadRules(rules);
}
private static final String REOURCE_NAME = "user";
/**
* @SentinelResource 改善接口中资源定义和被流控降级后的处理方法
* value 是定义资源
* blockHandler 设置被流控降级后的处理方法(默认该方法必须声明在同一个类中)
* fallback 当接口中出现了异常,就可以交给fallback指定的方法进行处理
*/
@RequestMapping("/user")
@SentinelResource(value = REOURCE_NAME, blockHandler = "blockHandlerForUser")
public String getUser() throws BlockException {
return "lijie";
}
/**
* 注意:
* 1、一定要是public
* 2、返回值一定要和源方法保证一致,包含源方法的参数
* 3、可以在参数的最后添加BlockException 可以区分是什么规则的处理方法
* @param ex 异常处理
*/
public String blockHandlerForUser(BlockException ex) {
ex.printStackTrace();
return "流控";
}
配置完成,调用接口,就可以看到了
Gateway服务网关
介绍
Spring Cloud Gateway是Spring官方基于Spring 5.0,Spring Boot 2.0和Project Reactor等技术开发的网关,Spring Cloud Gateway旨在为微服务架构提供一种简单而有效的统一的API路由管理方式。Spring Cloud Gateway作为Spring Cloud生态系中的网关,目标是替代ZUUL,其不仅提供统一的路由方式,并且基于Filter链的方式提供了网关基本的功能,例如:安全,监控/埋点,和限流等。
三大核心
- Route(路由):路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如果断言为true则匹配该路由。
- Predicate(断言):参考的是Java8的java.util.function.Predicate,开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由。
- Filter(过滤):指的是Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前或者之后对请求进行修改。
使用
导入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<version>3.1.8</version>
</dependency>
配置yml,
server:
port: 8080 # 网关端口
spring:
application:
name: gylxt-service # 服务的名称
cloud:
nacos:
server-addr: 127.0.0.1:8848
discovery:
# enabled: true
namespace: public
username: nacos
password: nacos
group: gylxt_AEATA_GROUP
# sea
gateway:
routes:
- id: gylxt-route # 路由的ID,没有固定规则但要求唯一,建议配合服务名
uri: lb://gylxt-service # 匹配后提供服务的路由地址; lb指的是从nacos中按照名称获取微服务,并遵循负载均衡策略
predicates:
- Path=/product-server/** # 断言,路径相匹配的进行路由
filters:
- StripPrefix=1 # 转发之前去掉一层路径
# discovery:
# locator:
# enabled: true # 是否开启自动识别nacos服务
持续更新中…