因为之前学过Eureka 所以很多基础的设置就不记录了
几个仓库供学习参考
写在前面(相关更新)
2021.1.15
提交了一个根据vue-admin-template修改的一个基本的前端架子
然后跟本项目做了联调 实现了登录注销等、用户信息的获取是在consumer模块中 把信息写死了 admin角色
前端仓库-VueAdmin
1.版本选择
2.Nacos作为注册中心和配置中心
注册中心
下载自行百度 顺便可以把sentinal-dashboard也下载了 跟Eureka不同 nacos可以直接使用jar包启动就行了
然后访问localhost:8848/nacos 就可以进入页面了 账号密码都是nacos (sentinal的dashboard的登录账号密码都是sentinal)
只需要下面三步 启动就可以注册上nacos里了
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
server:
port: 8881
spring:
application:
name: cloudstudy-provider
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
配置中心
导入依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
server:
port: 9101
spring:
#配置环境
#profiles:
#active: dev
application:
name: nacos-config-client
cloud:
nacos:
discovery:
server-addr: localhost:8848 #Nacos地址
config:
server-addr: localhost:8848 #Nacos地址
file-extension: yml #这里我们获取的yaml格式的配置
项目的配置文件要使用bootstrap.yml 配置信息如上
nacos中的配置文件的dataid的组成格式及与SpringBoot配置文件中的属性对应关系
spring.profiles.active为配置的环境
假设我当前项目用的是dev环境 项目名为consumer 后缀是yml 那么组合起来就是 consumer-dev.yml 就会根据这个去配置中心找对应的文件 读取
${
spring.application.name}-${
spring.profiles.active}.${
spring.cloud.nacos.config.file-extension}
对应在nacos上创建配置文件 然后测试一下是否将配置文件记载进来 使用@Value注入
然后调用接口返回值 是否对应配置的值即可验证
添加其他的配置文件 共享一些公共的配置 例如mysql redis之类的
#共享配置文件
shared-configs[0]:
data-id: commom.yml
group: DEFAULT_GROUP
refresh: true #要配置刷新
# 这样也可以共享配置文件
# extension-configs[0]:
# - data-id: shareconfig3.yml
# group: SHARE3_GROUP
# refresh: true
看启动的控制台可以发现加载到了对应的两个配置文件
3.服务消费的方式
使用RestTemplate
在之前的例子中,已经使用过RestTemplate
来向服务的某个具体实例发起HTTP请求,但是具体的请求路径是通过拼接完成的,对于开发体验并不好。但是,实际上,在Spring Cloud中对RestTemplate做了增强,只需要稍加配置,就能简化之前的调用方式。
比如:
@EnableDiscoveryClient
@SpringBootApplication
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
@Slf4j
@RestController
static class TestController {
@Autowired
RestTemplate restTemplate;
@GetMapping("/test")
public String test() {
String result = restTemplate.getForObject("http://alibaba-nacos-discovery-server/hello?name=didi", String.class);
return "Return : " + result;
}
}
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
可以看到,在定义RestTemplate的时候,增加了@LoadBalanced
注解,而在真正调用服务接口的时候,原来host部分是通过手工拼接ip和端口的,直接采用服务名的时候来写请求路径即可。在真正调用的时候,Spring Cloud会将请求拦截下来,然后通过负载均衡器选出节点,并替换服务名部分为具体的ip和端口,从而实现基于服务名的负载均衡调用。
使用WebClient(可以不看)
WebClient是Spring 5中最新引入的,可以将其理解为reactive版的RestTemplate。下面举个具体的例子,它将实现与上面RestTemplate一样的请求调用:
@EnableDiscoveryClient
@SpringBootApplication
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
@Slf4j
@RestController
static class TestController {
@Autowired
private WebClient.Builder webClientBuilder;
@GetMapping("/test")
public Mono<String> test() {
Mono<String> result = webClientBuilder.build()
.get()
.uri("http://alibaba-nacos-discovery-server/hello?name=didi")
.retrieve()
.bodyToMono(String.class);
return result;
}
}
@Bean
@LoadBalanced
public WebClient.Builder loadBalancedWebClientBuilder() {
return WebClient.builder();
}
}
可以看到,在定义WebClient.Builder的时候,也增加了@LoadBalanced
注解,其原理与之前的RestTemplate时一样的。关于WebClient的完整例子也可以通过在文末的仓库中查看。
使用Feign(建议使用 与平常的SpringBoot项目类似 Controller调用Service层 只不过service层使用feign去调用对应的服务提供者的接口)
上面介绍的RestTemplate和WebClient都是Spring自己封装的工具,下面介绍一个Netflix OSS中的成员,通过它可以更方便的定义和使用服务消费客户端。下面也举一个具体的例子,其实现内容与上面两种方式结果一致:
第一步:在pom.xml
中增加openfeign的依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
第二步:定义Feign客户端和使用Feign客户端:
@EnableDiscoveryClient
@SpringBootApplication
@EnableFeignClients
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
@Slf4j
@RestController
static class TestController {
@Autowired
Client client;
@GetMapping("/test")
public String test() {
String result = client.hello("didi");
return "Return : " + result;
}
}
@FeignClient("alibaba-nacos-discovery-server")
interface Client {
@GetMapping("/hello")
String hello(@RequestParam(name = "name") String name);
}
}
这里主要先通过@EnableFeignClients
注解开启扫描Spring Cloud Feign客户端的功能;然后又创建一个Feign的客户端接口定义。使用@FeignClient
注解来指定这个接口所要调用的服务名称,接口中定义的各个函数使用Spring MVC的注解就可以来绑定服务提供方的REST接口,比如下面就是绑定alibaba-nacos-discovery-server
服务的/hello
接口的例子。最后,在Controller中,注入了Client接口的实现,并调用hello方法来触发对服务提供方的调用。
feign相关配置
cloudstudy-provider: #对应FeignClient注解上的服务名
ribbon:
# NFLoadBalancerRuleClassName: com.zyfgoup.config.MyRule #自己写的只取第一个服务
# NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #配置规则 随机
# NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule #配置规则 轮询
# NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RetryRule #配置规则 重试
# NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule #配置规则 响应时间权重
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.BestAvailableRule #配置规则 最空闲连接策略
ConnectTimeout: 500 #请求连接超时时间
ReadTimeout: 1000 #请求处理的超时时间
OkToRetryOnAllOperations: true #对所有请求都进行重试
MaxAutoRetriesNextServer: 2 #切换实例的重试次数
MaxAutoRetries: 1 #对当前实例的重试次数
深入思考
不论我用的是RestTempalte
也好、还是用的WebClient
也好,还是用的Feign
也好,似乎跟我用不用Nacos没啥关系?我们在之前介绍Eureka和Consul的时候,也都是用同样的方法来实现服务调用的,不是吗?
确实是这样,对于Spring Cloud老手来说,就算我们更换了Nacos作为新的服务注册中心,其实对于我们应用层面的代码是没有影响的。那么为什么Spring Cloud可以带给我们这样的完美编码体验呢?实际上,这完全归功于Spring Cloud Common的封装,由于在服务注册与发现、客户端负载均衡等方面都做了很好的抽象,而上层应用方面依赖的都是这些抽象接口,而非针对某个具体中间件的实现。所以,在Spring Cloud中,我们可以很方便的去切换服务治理方面的中间件。
4.sentinel 流量监控、降级、熔断
下载对应的sentinel-dashboard的jar文件 启动即可 端口号为8080 登录账号密码都是sentinel
导入依赖和相关的配置
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel</artifactId>
</dependency>
server:
port: 8882
spring:
application:
name: cloudstudy-cunsumer
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
sentinel:
transport:
dashboard: localhost:8080
port: 8719 #这是sentinel会创建一个HttpServer的端口号 默认8719 被占用则+1+1.... 当设置了一些规则后 传到这个server然后再注册到sentinel上
@ResourceSentinel
value 指定资源名 可自定义 一般与请求路径一直即可
fallback:失败调用,若本接口出现未知异常,则调用fallback指定的接口。
blockHandler:sentinel定义的失败调用或限制调用,若本次访问被限流或服务降级,则调用blockHandler指定的接口。
要注意指定的blockHandler方法与原本的方法 返回的参数要一致
(假设一个接口里抛出了异常 则会去执行对应的fallback方法 如果又在sentinal设置了限流等 则调用了几次后 返回blockhandler对应的方法)
blockHandlerClass 指定自定义全局的处理服务降级熔断后的返回方法 这样就不用一个方法又对应一个熔断方法
@GetMapping("/get/{i}")
@SentinelResource(value = "getI",blockHandler = "getI_Handler")
public String getI(@PathVariable("i") Integer i) throws InterruptedException {
//地址 返回值类型 参数在地址后面用数字表示 对应后面的可变长参数
return String.valueOf(restTemplate.getForObject(PROVIDERSERVICEURL+"/get/{1}",Integer.class,i));
}
public String getI_Handler(Integer i,BlockException e){
return "使用Sentinel实现服务降级";
}
控制台页面如上所示
当启动项目后 需要先访问一次端口号 才会在控制台有显示
可以看到有流控、降级、热点
流控是可以直接指定一个qps阈值 超过的话 则会去调用blockhandler对应的方法进行返回
降级是定义一个规则 例如RT(响应时间)超过多久就会进行降级 然后进行熔断一段时间(如下图中的时间窗口) 调用blockhandler
热点就是可以设立某个资源当传入某些某个参数时会进行降级 参数下标从0开始
每次重启服务时,配置的各种规则都是消失,所以需要做规则的持久化
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>