Nacos
一、下载安装
1、下载
linux:https://github.com/alibaba/nacos/releases/download/2.0.4/nacos-server-2.0.4.tar.gz
执行下载命令
wget https://github.com/alibaba/nacos/releases/download/2.0.4/nacos-server-2.0.4.tar.gz
解压安装包
tar -zxxvf nacos-server-2.0.4.tar.gz nacos/
2、linux部署
(1)创建数据库nacos,执行conf目录下的nacos-mysql.sql脚本,初始化数据库
(2)进入conf目录编辑application.properties
(4)在bin目录下启动
./startup.sh -m standalone //standalone代表着单机模式运行,非集群模式
看到最后一行的successfully启动成功
(5)将nacos配置为系统服务
①添加文件nacos.service vim /lib/systemd/system/nacos.service 并且增加如下信息 (nacos路径替换成自己的路径即可)
[Unit]
Description=nacos
After=network.target
[Service]
Type=forking
ExecStart=/usr/nacos-server-1.1.3/nacos/bin/startup.sh -m standalone
ExecReload=/usr/nacos-server-1.1.3/nacos/bin/shutdown.sh
ExecStop=/usr/nacos-server-1.1.3/nacos/bin/shutdown.sh
PrivateTmp=true
[Install]
WantedBy=multi-user.target
②编辑nacos/bin目录下的startup.sh, 修改JAVA_HOME的路径,注释其他三项
③执行以下命令
1. 重新加载所有service服务
systemctl daemon-reload
2. 开机启动nacos.service
systemctl enable nacos.service
3. 查看该service是否开机启用
systemctl is-enabled nacos.service
4. 启动该服务
systemctl start nacos.service
可能报错: Job for nacos.service failed because the control process exited with error code. See "systemctl status nacos.service" and "journalctl -xe" for details.
5. 查看该服务状态
systemctl status nacos.service
3、访问nacos:http://localhost:8848/nacos
初始账号密码:nacos/nacos.
二、nacos注册中心
1、依赖导入
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
2、服务注册
spring:
application:
name: sca-provider #服务注册的服务名 ,注意服务名的横线
cloud:
nacos:
discovery:
server-addr: localhost:8848 #向nacos控制台注册
3、启动该工程查看是否注册成功
客户端定时(5秒)向nacos发送请求(心跳包),中断15秒,nacos客户端显示不健康,30秒认为中断死亡
三、服务远端调用
1、restTemplate远端调用
什么是restTemplate:传统情况下在java代码里访问restful服务,一般使用Apache的HttpClient。不过此种方法使用起来太过繁琐。spring提供了一种简单便捷的模板类来进行操作,这就是RestTemplate。
在SpringCloud的项目中,我们使用了自动配置的OAuth2RestTemplate,RestTemplate,但是在使用这些restTemplate的时候,url必须是服务的名称,如果要调用真实的域名或者ip的url,会有错误
@Bean
public RestTemplate restTemplate(){ //基于此对象实现远端服务调用
return new RestTemplate();
}
restTemplate实现远端调用,此方法是将该类交给spring管理
@Autowired
private RestTemplate restTemplate; // 实现远端调用服务
restTemplate.getForObject(url, String.class);//参数一调用远端的url,参数二为该方法返回值类型的class对象
2、loadBalancerClient远端调用负载均衡客户端
LoadBalancerClient: 是 SpringCloud 提供的一种负载均衡客户端,Ribbon 负载均衡组件内部也是集成了 LoadBalancerClient 来实现负载均衡。
@Autowired
private LoadBalancerClient loadBalancerClient; //此对象负责从nacos服务中发现和获取服务实例
// /ˈbælənsə(r)/ /ˈklaɪənt/
@GetMapping("/controller/doRestEcho02")
public String doRestEcho02() {
ServiceInstance serviceInstance = loadBalancerClient.choose("sca-provider");
String url = String.format("http://%s:%s/provider/echo/%s",
serviceInstance.getHost(),
serviceInstance.getPort(), appName);
//从nacos获取provider服务注册信息
System.out.println("url :" + url);
return restTemplate.getForObject(url, String.class);
}
3、loadBalancer注解简化服务获取过程
在restTemplate交给spring管理的方法上加上@loadBalancer,在该步骤请求发送时,底层对该请求拦截,然后底层执行choose等一系列方法,获取host和port进行请求拼接
@Autowired
private RestTemplate loadBalancedRestTemplate;
@GetMapping("/controller/doRestEcho03")
public String oRestEcho03() {
String serviceId = "sca-provider";
String url = String.format("http://%s/provider/echo/%s", serviceId, appName);
return loadBalancedRestTemplate.getForObject(url, String.class);//在该步骤请求发送时,底层对该请求拦截,
// 然后执行方法二的choose等一系列方法,获取host和port进行请求拼接
}
4、OpenFeign实现远程服务调用,实现代码结构的优化
什么是OpenFeign
OpenFeign是一种声明式、模板化的HTTP客户端(仅在Application Client中使用)(称OpenFeign作用:声明式服务调用)。声明式调用是指,就像调用本地方法一样调用远程方法,无需感知操作远程http请求。学习完OpenFeign后可以不使用RestTemplate进行调用。
- OpenFeign是Spring Cloud的子项目,而Feign是Netflix公司研发的;
- Feign不支持SpringMVC的注解,另外Feign已不再维护,OpenFeign正常维护;
(1)添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
(2)在启动类上添加@EnableFeignClients,作用类似于@MapperScan
(3)service层创建接口
@Autowired
private RemoteProviderService remoteProviderService;
@GetMapping("/controller/doFeignEcho/{msg}")
public String doFeignEcho(@PathVariable String msg) {
return remoteProviderService.echoMassage(msg);
}
5、Ribbon负载均衡策略
- Ribbon是Netflix开源的负载均衡组件,可以用来做客户端负载均衡,调用注册中心的服务;
- OpenFeign与Feign都内置了Ribbon,用于实现负载均衡;
Nacos中的负载均衡底层是通过Ribbon实现,Ribbon中定义了一些负载均衡算法,然后基于这些算法从服务实例中获取一个实例为消费方法提供服务。
随机策略 轮询策略 重试策略 最低并发策略 可用过滤策略 响应时间加权重策略 区域权重策略
四、Nacos服务配置中心
配置中心的作用:动态管理发布配置,无需重启服务配置经常变化的配置信息,例如连接池,日志、线程池、限流熔断规则等。
(1)添加服务配置依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
(2)将项目中application.yml的名字修改为bootstrap.yml(启动优先级最高),并添加配置中心配置
cloud:
nacos:
discovery: #配置服务注册中心地址
server-addr: 127.0.0.1:8848
config: #配置服务配置中心地址
server-addr: 127.0.0.1:8848
file-extension: yml
五、Nacos配置管理模型
1、Namespace:命名空间
Nacos中的命名空间一般用于配置隔离,这种命名空间的定义一般会按照环 境(开发,生 产等环境)进行设计和实现.我们默认创建的配置都存储到了 public命名空间
更换命名空间:
在xml文件config下新增namespace: (命名空间的id)
2、Group:分组
当我们在指定命名空间下,按环境或服务做好了配置以后,有时还需要基于 服务做分组配置, 例如,一个服务在不同时间节点(节假日,活动等)切换 不同的配置
更换分组:
在xml文件config下新增group: 分组名称
3、Service/DataId:共享配置
在xml文件config下新增
shared-configs[0]: #该命名空间下共享配置的引用,共享配置不止一个,使用下标依次引用。
data-id: app-public.yml #该命名空间下共享配置的 Data id的值
refresh: true #支持配置中心共享配置改变,动态刷新该共享配置。 默认fales
#group: DEFAULT_GROUP #该命名空间下分组名称。#DEFAULT_GROUP,作为共享配置,创建时默认分组
名。
4、具体配置
六、实时nacos中的配置参数
写一个Controller试试看,这里代码中需要加上 @RefreshScope 注解,注解作用就是当nacos中的配置文件更新的时候,同步更新到代码中!
@RestController
@RequestMapping("/TestController")
@RefreshScope
public class TestController {
@Value("${pattern.envSharedValue}")
private String myName;
@GetMapping("/test1")
public String test1() {
return myName;
}
}
Sentinel
作用:分布式系统的流量防卫兵
下载网址:https://github.com/alibaba/Sentinel/releases
知识补充:限流算法
计数器、令牌桶、漏斗算法,滑动窗口算法
Sentinel 默认的限流算法是滑动窗口算法
一、Sentinel环境搭建
(1)添加依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
(2)配置bootstrap.yml
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8180 # 指定sentinel控制台地址。
(3)找到目录执行运行命令
java-Dserver.port=8180-Dcsp.sentinel.dashboard.server=localhost:8180-Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.1.jar
idea启动Sentinel
-Dserver.port=8180
-Dcsp.sentinel.dashboard.server=localhost:8180
-Dproject.name=sentinel-dashboard
-jar
D:\javaXM\sentinel-1.8.1\sentinel-dashboard-1.8.1.jar
二、Sentinel限流
QPS(代表每秒请求次数)单机阈值(设置请求次数)为1,代表每秒请求不超过1次,超过做限流处理,处理方式直接调用失败。
流控模式:
直接,关联和链路
Sentinel的限流效果:快速失败,预热,排队等待
链路模式实例:
/**
@SentinelResource使用此注解描述的方法,在此方法被访问时,会sentinel的簇点链路中显示,此注解中指定的
名字就是资源名。此注解中的blockHandlerClass用于指定,出现限流异常时的异常处理类,blockHandler属性
用于指定异常处理类中的方法(此方法的返回类型,参数要与 @SentinelResource注解描述的方法参数一致,可
以BlockException异常类型参数,而且方法必须是静态.)fallbackClass 用于指定业务异常处理类,fallback
用于指向业务处理类中的异常处理方法(此方法的返回类型,参数要与@SentinelResource注解描述的方法参
数一致,可以加Throwable异常类型参数)
*/
@SentinelResource(value="doGetResource",
blockHandlerClass = ResourceBlockHandler.class,
blockHandler = "call",
fallback = "call",
fallbackClass = ResourceFallbackHandler.class)
public String doGetResource(){
return "do get resource";
}
说明:流控模式为链路模式时,Sentinel Web过滤器默认聚合所有URL的入口为sentinel_spring_web_context,在application.yml添加如下语句来关闭URL PATH聚合,入口名就不为默认
sentinel:
web-context-unify: false
三、Sentinel熔断降级
作用:对调用链路中不稳定的资源进行熔断降级
熔断策略:
①慢调用比例:请求数超过3时,平均响应时间超过200毫秒的有30%,则对请求进行熔断,熔断时长为10秒钟,10秒以后恢复正常。
②异常比例
③异常数
四、Sentinel 异常处理
系统提供了默认的异常处理机制,如果默认处理机制不满足我们需求,我们可以自己进行定义
实现方式:定义方式上可以直接或间接实现BlockExceptionHandler接口,并将对象交给spring管理。
@Slf4j
@Component
public class ServiceBlockExceptionHandler implements BlockExceptionHandler {
@Override
public void handle(HttpServletRequest request,
HttpServletResponse response,
BlockException e) throws Exception {
//设置响应数据编码
response.setCharacterEncoding("utf-8");
//告诉客户端响应数据的类型,以及客户端显示内容的编码
response.setContentType("text/html;charset=utf-8");
//向客户端响应一个json格式的字符串
//String str="{\"status\":429,\"message\":\"访问太频繁了\"}";
Map<String,Object> map=new HashMap<>();
map.put("status", 429);
map.put("message","访问太频繁了");
//将map集合变为json格式的字符串
String jsonStr=new ObjectMapper().writeValueAsString(map);
PrintWriter out = response.getWriter();
out.print(jsonStr);
out.flush();
out.close();
}
}
五、Sentinel 热点规则
作用:对经常使用的数据做配置
①写方法
@GetMapping("/sentinel/findById")
@SentinelResource("resource")
public String doFindById(@RequestParam("id") Integer id){
return "resource id is "+id;
}
②设置限流的热点
资源名:@SentinelResource注解的值
参数索引:@SentinelResource注解的方法参数下标,0代表第一个参数,1代表第二个参数。
单机阈值以及统计窗口时长表示在此窗口时间超过阈值就限流。
参数例外项:表示参数为5时阈值为100,其它参数值阈值为1.
六、Sentinel 系统规则
作用:监控服务器的状态,保证服务器正常的运行
Sentinel的常用系统规则:
RT,QPS,CPU,线程,Load-linux,unix
七、Sentinel授权规则
作用:根据黑白名单规则,限制请求是否通过,只有请求来源位于不在黑名单或则在白名单内时才可通过;若配置黑名单则请求来源位于黑名单时不通过。
基于参数授权规则的实现:
①定义请求解析器:
用于对请求进行解析(RequestOriginParser接口的实现类),并返回解析结果,sentinel底层在
拦截到用户请求以后,会对请求数据基于此对象进行解析,判定是否符合黑白名单规则
@Component
public class DefaultRequestOriginParser implements RequestOriginParser {
@Override
public String parseOrigin(HttpServletRequest request) {
String origin = request.getParameter("origin");//这里的参数名会与请求中的参数名一致
return origin;
}
}
②定义流控规则
资源名:即限流规则的作用对象
流控应用:对应的黑名单/白名单中设置的规则值,多个值用逗号隔开.
③执行资源访问
基于请求ip等方式进行黑白名单的规则设计:
①定义请求解析器:
@Component
public class DefaultRequestOriginParser implements RequestOriginParser {
//解析请求源数据
@Override
public String parseOrigin(HttpServletRequest request) {
//获取访问请求中的ip地址,基于ip地址进行黑白名单设计(例如在流控应用栏写ip地址)
String ip= request.getRemoteAddr();
System.out.println("ip="+ip);
return ip;
}//授权规则中的黑白名单的值,来自此方法的返回值
}
②定义流控规则
Gateway网关
作用:一个各种服务访问的入口,在项目中简化前端的调用逻辑,同时也简化内部服务之间互相调用的复杂度
特点:
(1)性能强劲:是第一代网关Zuul的1.6倍。
(2)依赖Netty与WebFlux(Spring5.0),不是传统的Servlet编程模型 (Spring MVC就是基于此模型实现),学习成本高。
(3)需要Spring Boot 2.0及以上的版本,才支持
一、配置流程
(1)导入依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactI
</dependency>
(2)application.yml(假如已有则无须创建)中添加相关配置,设定uri实现负载均衡
gateway: #和nacos同一级
routes: #配置网关路由规则
- id: route01 #路由id,自己指定一个唯一值即可
uri: lb://sca-provider
#uri: http://localhost:8082/ #网关帮我们转发的url(url是uri 的子集,uri是统一资源标
识符)
predicates: #断言(谓此):匹配请求规则
- Path=/nacos/provider/echo/**
- Before=2021-01-30T00:00:00.000+08:00
- Method=GET
#请求路径定义,此路径对应uri中的资源,**代表传入的参数
filters: #网关过滤器,用于对谓词中的内容进行判断分析以及处理
- StripPrefix=1 #转发之前去掉path中第一层路径
Predicate(断言)又称谓词,用于条件判断,只有断言结果都为真,才会真正的执行路由
二、断言的类型
(1)基于Datetime类型的断言工厂,此类型的断言根据时间做判断,主要有三个:
AfterRoutePredicateFactory:判断请求日期是否晚于指定日 期
BeforeRoutePredicateFactory:判断请求日期是否早于指定 日期
BetweenRoutePredicateFactory:判断请求日期是否在指定 时间段内
例:-After=2020-12-31T23:59:59.789+08:00[Asia/Shanghai]:判断请求日期是否晚于指定日期
(2)基于header的断言工厂HeaderRoutePredicateFactory,判断请求Header是否具有给定名称且值与正则表达式匹配
例:-Header=X-Request-Id, \d+
(3)基于Method请求方法的断言工厂,MethodRoutePredicateFactory
接收一个参数,判断请求类型是否跟指定的类型匹配
例:-Method=GET
(4)基于Query请求参数的断言工厂,QueryRoutePredicateFactory
接收两个参数,请求param和正则表达式, 判断请求参数是否具 有给定名称且值与正则表达式匹配.
例:-Query=pageSize,\d+
三、网关限流
(1)添加依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
(2)添加Sentinel 控制台地址
sentinel:
transport:
dashboard: localhost:8180 #Sentinel 控制台地址
eager: true #取消Sentinel控制台懒加载,即项目启动即连接
(3)添加sentinel的jvm参数,通过此菜单可以让网关服务在sentinel控制台显示不一样的菜单,代码如下:-Dcsp.sentinel.app.type=1
四、特殊菜单中自定义API维度限流
作用:自定义API分组,是一种更细粒度的限流规则定义,它允许我们利用sentinel提供的API,将请求路径进行分组,然后在组上设置限流规则
第一步:新建API分组
第二步:新建分组流控规则
第三步:定制流控网关返回值
@Configuration
public class GatewayConfig {
public GatewayConfig(){
GatewayCallbackManager.setBlockHandler( new BlockRequestHandler() {
@Override
public Mono<ServerResponse> handleRequest(
ServerWebExchangeserverWebExchange,
Throwable throwable) {
Map<String,Object> map=new HashMap<>();
map.put("state",429);
map.put("message","two many request");
String jsonStr=JSON.toJSONString(map);
return ServerResponse.ok().body(Mono.just(jsonStr),String.class);
}
});
}
}
五、定制流控网关返回值
1、设置拦截条件,返回true放行,返回false表示拦截
/*MVC中的拦截器对象*/
@Slf4j
public class TimeAccessInterceptor implements HandlerInterceptor {
/*此方法在执行目标controller方法之前执行,返回值为true表示放行该请求*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
LocalTime now= LocalTime.now();//JDK8中的时间对象(推荐使用)
int hour=now.getHour();//获取当前时间对应小时
//System.out.println("hour="+hour);
log.info("hour {}",hour);
/*在规定时间范围内(6点到22点)允许访问,将请求放行*/
if(hour<=6||hour>=22)
throw new RuntimeException("请在6~10点进行访问");
return true;
}
}
被限流时定制返回错误信息
@Configuration
public class GatewayConfig {
public GatewayConfig(){
GatewayCallbackManager.setBlockHandler( new BlockRequestHandler(){
@Override
public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
Map<String,Object> map=new HashMap<>();
map.put("state",429);
map.put("message","two many request");
String jsonStr=JSON.toJSONString(map);
return ServerResponse.ok().body(Mono.just(jsonStr),String.class);
}
});
}
}