Nacos注册中心
服务端安装
官网下载地址:https://nacos.io/zh-cn/docs/quick-start.html
解压安装包
进入bin目录
单机模式启动 sh startup.sh -m standalone
访问 localhost:8848/nacos
默认账号密码 nacos/nacos
客户端注册与发现
1. 添加nacos的依赖 <!--添加nacos客户端--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> 2. 在配置文件指定注册中心地址 spring: cloud: nacos: discovery: server-addr: 127.0.0.1:8848 3. 在启动类用注解开启服务注册 @EnableDiscoveryClient
统一配置中心
1. 添加代码配置
1. 加入依赖 <!--统一配置中心--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency> 2. 配置bootstrap.yml (它的优先级比application.yml高) spring: application: name: order-service #在noas的名称 cloud: nacos: config: server-addr: 192.168.200.100:8848 #Nacos配置中心地址 file-extension: yaml #文件拓展格式 group: risk #分组,一般用来区分不同的服务 namespace: test #命名空间,用来区分不同环境(默认是public) #profiles: # active: dev
2. 先新建命名空间,命名空间id和namespace保持一致;配置文件名称和服务名称一致;grop也保持一致。
3. 通过url查看配置是否生效 http://192.168.200.100:8848/nacos/v1/cs/configs?dataId=order-service.yaml&group=risk&tenant=test
4. 代码里面通过 @RefreshScope 动态刷新Nacos配置
Feign远程调用
1. 引入依赖包 <!--引入feign--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> 2. 在主函数开启feign @EnableFeignClients 3. 编写远程调用接口(指定好其他服务的注册名称和路径) @FeignClient(value = "video-service",path = "/video") public interface VideoFeign { @RequestMapping("find_by_id") JsonResult findById(@RequestParam("videoId") int videoId); } 4. 不设置也可以(feign默认1s超时,我们这里设置为5s) ribbon: ReadTimeout: 5000 #超时时间 ConnectTimeout: 5000 #连接时间 MaxAutoRetries: 0 #同一台实例最大重试次数,不包括首次调用 MaxAutoRetriesNextServer: 0 #重试负载均衡其他的实例最大重试次数,不包括首次调用 OkToRetryOnAllOperations: false #是否所有操作都重试
熔断降级
1. 导入依赖 <!--引入sentinel--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency> 2. 开启Feign对Sentinel的支持 feign: sentinel: enabled: true 3. 配置feign容错类 @FeignClient(value = "video-service",path = "/video", fallback = VideoServiceFallback.class) public interface VideoFeign { 4. 创建容错类, 实现容错逻辑, 记得加注解 @Service @Service public class VideoServiceFallback implements VideoFeign { @Override public JsonResult findById(int videoId) { return JsonResult.error("feign的url错误,走了fallback"); } }
Sentinel流量哨兵
中文文档 https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D
-Dserver.port=8080 控制台端口,sentinel控制台是一个spring boot程序,指定启动端口。
-Dcsp.sentinel.dashboard.server=localhost:8080 指把控制台后当作一个客户端,然后自动向该地址发送心跳包。
-Dproject.name=sentinel-dashboard 指定Sentinel控制台程序的名称
启动
nohup java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.0.jar &
访问 localhost:8080
默认账号密码 sentinel/sentinel
客户端接入
1. 导入依赖 <!--引入sentinel--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency> 2. 添加注册地址 spring: cloud: sentinel: transport: port: 9999 #跟控制台交流的端口,随意指定一个未使用的端口即可,不同的服务用不同的端口 dashboard: localhost:8080 # 指定控制台服务的地址
默认是懒加载的,启动后,我们调用先服务接口,然后刷新 sentinel 面板就能看到了
流控规则
在簇点链路里面根据url配置,我们这是事根据qps来限流,然后快速刷新浏览器就会出现失败提示了。
熔断降级
慢调用比例(响应时间): 选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用
比例阈值:修改后不生效(这是一个bug,期待官方后续修复)
熔断时长:超过时间后会尝试恢复
最小请求数:熔断触发的最小请求数,请求数小于该值时即使异常比率超出阈值也不会熔断
异常比例:当单位统计时长内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断
比例阈值
熔断时长:超过时间后会尝试恢复
最小请求数:熔断触发的最小请求数,请求数小于该值时,即使异常比率超出阈值也不会熔断
异常数:当单位统计时长内的异常数目超过阈值之后会自动进行熔断
异常数:
熔断时长:超过时间后会尝试恢复
最小请求数:熔断触发的最小请求数,请求数小于该值时即使异常比率超出阈值也不会熔断
自定义异常降级
现在失败后,默认都是返回 Blocked by Sentinel (flow limiting) 字符串,比较不容易排查。我们可以自定义项目数据交互格式。AlibabCloud版本升级,自从v2.1.0到v2.2.0后就出现了不兼容问题。
- 【旧版】实现UrlBlockHandler并且重写blocked方法
@Component public class BaseUrlBlockHandler implements UrlBlockHandler { @Override public void blocked(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws IOException { //降级业务处理 } }
- 【新版】实现BlockExceptionHandler并且重写handle方法
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler; import com.alibaba.csp.sentinel.slots.block.BlockException; import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException; import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException; import com.alibaba.csp.sentinel.slots.block.flow.FlowException; import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException; import com.alibaba.csp.sentinel.slots.system.SystemBlockException; import com.alibaba.fastjson.JSON; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.HashMap; import java.util.Map; @Component public class BaseBlockExceptionHandler implements BlockExceptionHandler { @Override public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws Exception { Map<String,Object> info = new HashMap<>(); if(e instanceof FlowException){ info.put("code",-1); info.put("msg","限流异常"); } else if(e instanceof DegradeException){ info.put("code",-2); info.put("msg","降级异常"); } else if(e instanceof ParamFlowException){ info.put("code",-3); info.put("msg","热点参数异常"); } else if(e instanceof SystemBlockException){ info.put("code",-4); info.put("msg","系统异常"); } else if(e instanceof AuthorityException){ info.put("code",-5); info.put("msg","授权异常"); } //设置json返回 httpServletResponse.setStatus(200); httpServletResponse.setHeader("content-type","application/json;charset=UTF-8"); httpServletResponse.getWriter().write(JSON.toJSONString(info)); } }
gateway网关
动态路由
1. 添加依赖 <!--添加gateway--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> 2. 配置路由规则 server: port: 7100 spring: application: name: api-gateway cloud: gateway: discovery: locator: enabled: true #开启网关拉取nacos的服务 routes: #数组形式 - id: order-service #路由唯一标识 uri: lb://order-service #从nocas进行转发到指定服务 #order: 1 #优先级,数字越小优先级越高 predicates: #断言 配置路由规则 - Path=/order/** filters: #过滤器,请求在传递过程中通过过滤器修改 - StripPrefix=1 #转发到具体服务时候,自动去掉第一层前缀(predicates的第一层地址) - id: video-service uri: lb://video-service predicates: - Path=/video/** filters: - StripPrefix=1 nacos: discovery: server-addr: 192.168.200.100:8848 # nacos的地址 zipkin: base-url: http://192.168.200.100:7200/ #zipkin地址 discovery-client-enabled: false #不用开启服务发现 sleuth: sampler: probability: 1.0 #采样百分比
过滤器
import org.apache.commons.lang3.StringUtils; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; /** * 网关不要加太多业务逻辑,否则会影响性能 */ @Component public class TestFilter implements GlobalFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { System.out.println("TestFilter。。。。。。"); //写业务逻辑 String token = exchange.getRequest().getHeaders().getFirst("token"); if(StringUtils.isBlank(token)){ //停止请求 exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); return exchange.getResponse().setComplete(); } //继续往下执行 return chain.filter(exchange); } //数字越小,优先级越高 @Override public int getOrder() { return 0; } }
链路追踪
Sleuth链路追踪
1. 各个微服务添加依赖 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-sleuth</artifactId> </dependency>
参数解析:
[order-service,6f3487a81c89682e,7db08a9ad1ea24d6,false]
第一个值,spring.application.name的值
第二个值,6f3487a81c89682e,sleuth生成的一个ID,叫Trace ID,用来标识一条请求链路,一条请求链路中包含一个Trace ID,多个Span ID
第三个值,7db08a9ad1ea24d6、spanid 基本的工作单元,获取元数据,如发送一个http
第四个值:false,是否要将该信息输出到zipkin服务中来收集和展示。
然后进行链路调用的时候,就会出现如下日志了:
zipkin仪表盘
安装与持久化
1. 官网以及服务端下载 https://zipkin.io/pages/quickstart.html
2. 启动服务端 默认端口是9411 可以通过 http://127.0.0.1:9411/zipkin/ 进行访问
java -jar zipkin-server-2.12.9-exec.jar
3. 持久化
日志数据默认是存在内存中的,zipkin重启后数据就没了。我们可以持久化到mysql、es 中。
--STORAGE_TYPE 指定外部存储源 mysql/es
--MYSQL_HOST mysql地址
--MYSQL_TCP_PORT 数据库端口
--MYSQL_DB 数据库名称
--MYSQL_USER 账号
--MYSQL_PASS 密码
nohup 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_log --MYSQL_USER=root --MYSQL_PASS=root &
CREATE TABLE IF NOT EXISTS zipkin_spans ( `trace_id_high` BIGINT NOT NULL DEFAULT 0 COMMENT 'If non zero, this means the trace uses 128 bit traceIds instead of 64 bit', `trace_id` BIGINT NOT NULL, `id` BIGINT NOT NULL, `name` VARCHAR(255) NOT NULL, `remote_service_name` VARCHAR(255), `parent_id` BIGINT, `debug` BIT(1), `start_ts` BIGINT COMMENT 'Span.timestamp(): epoch micros used for endTs query and to implement TTL', `duration` BIGINT COMMENT 'Span.duration(): micros used for minDuration and maxDuration query', PRIMARY KEY (`trace_id_high`, `trace_id`, `id`) ) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci; ALTER TABLE zipkin_spans ADD INDEX(`trace_id_high`, `trace_id`) COMMENT 'for getTracesByIds'; ALTER TABLE zipkin_spans ADD INDEX(`name`) COMMENT 'for getTraces and getSpanNames'; ALTER TABLE zipkin_spans ADD INDEX(`remote_service_name`) COMMENT 'for getTraces and getRemoteServiceNames'; ALTER TABLE zipkin_spans ADD INDEX(`start_ts`) COMMENT 'for getTraces ordering and range'; CREATE TABLE IF NOT EXISTS zipkin_annotations ( `trace_id_high` BIGINT NOT NULL DEFAULT 0 COMMENT 'If non zero, this means the trace uses 128 bit traceIds instead of 64 bit', `trace_id` BIGINT NOT NULL COMMENT 'coincides with zipkin_spans.trace_id', `span_id` BIGINT NOT NULL COMMENT 'coincides with zipkin_spans.id', `a_key` VARCHAR(255) NOT NULL COMMENT 'BinaryAnnotation.key or Annotation.value if type == -1', `a_value` BLOB COMMENT 'BinaryAnnotation.value(), which must be smaller than 64KB', `a_type` INT NOT NULL COMMENT 'BinaryAnnotation.type() or -1 if Annotation', `a_timestamp` BIGINT COMMENT 'Used to implement TTL; Annotation.timestamp or zipkin_spans.timestamp', `endpoint_ipv4` INT COMMENT 'Null when Binary/Annotation.endpoint is null', `endpoint_ipv6` BINARY(16) COMMENT 'Null when Binary/Annotation.endpoint is null, or no IPv6 address', `endpoint_port` SMALLINT COMMENT 'Null when Binary/Annotation.endpoint is null', `endpoint_service_name` VARCHAR(255) COMMENT 'Null when Binary/Annotation.endpoint is null' ) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci; ALTER TABLE zipkin_annotations ADD UNIQUE KEY(`trace_id_high`, `trace_id`, `span_id`, `a_key`, `a_timestamp`) COMMENT 'Ignore insert on duplicate'; ALTER TABLE zipkin_annotations ADD INDEX(`trace_id_high`, `trace_id`, `span_id`) COMMENT 'for joining with zipkin_spans'; ALTER TABLE zipkin_annotations ADD INDEX(`trace_id_high`, `trace_id`) COMMENT 'for getTraces/ByIds'; ALTER TABLE zipkin_annotations ADD INDEX(`endpoint_service_name`) COMMENT 'for getTraces and getServiceNames'; ALTER TABLE zipkin_annotations ADD INDEX(`a_type`) COMMENT 'for getTraces and autocomplete values'; ALTER TABLE zipkin_annotations ADD INDEX(`a_key`) COMMENT 'for getTraces and autocomplete values'; ALTER TABLE zipkin_annotations ADD INDEX(`trace_id`, `span_id`, `a_key`) COMMENT 'for dependencies job'; CREATE TABLE IF NOT EXISTS zipkin_dependencies ( `day` DATE NOT NULL, `parent` VARCHAR(255) NOT NULL, `child` VARCHAR(255) NOT NULL, `call_count` BIGINT, `error_count` BIGINT, PRIMARY KEY (`day`, `parent`, `child`) ) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;
启动客户端
1. 加入依赖 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zipkin</artifactId> </dependency>
2. 配置地址和采样百分比配置
spring:
application:
name: api-gateway
zipkin:
base-url: http://127.0.0.1:9411/ #zipkin地址
discovery-client-enabled: false #不用开启服务发现
sleuth:
sampler:
probability: 1.0 #采样百分比
默认为0.1,即10%,这里配置1,是记录全部的sleuth信息,是为了收集到更多的数据(仅供测试用)。在分布式系统中,过于频繁的采样会影响系统性能,所以这里配置需要采用一个合适的值。
请求过后,直接可以在面板就能看到 链路详细信息 了。
。