了解微服务搭建
版本说明
版本兼容参考: alibaba/spring-cloud-alibaba
Spring Cloud: https://spring.io/projects/spring-cloud#overview
组件版本关系
Spring Cloud Alibaba Version | Sentinel Version | Nacos Version | RocketMQ Version | Dubbo Version | Seata Version |
---|---|---|---|---|---|
2.2.5.RELEASE | 1.8.0 | 1.4.1 | 4.4.0 | 2.7.8 | 1.3.0 |
2.2.3.RELEASE or 2.1.3.RELEASE or 2.0.3.RELEASE | 1.8.0 | 1.3.3 | 4.4.0 | 2.7.8 | 1.3.0 |
2.2.1.RELEASE or 2.1.2.RELEASE or 2.0.2.RELEASE | 1.7.1 | 1.2.1 | 4.4.0 | 2.7.6 | 1.2.0 |
2.2.0.RELEASE | 1.7.1 | 1.1.4 | 4.4.0 | 2.7.4.1 | 1.0.0 |
2.1.1.RELEASE or 2.0.1.RELEASE or 1.5.1.RELEASE | 1.7.0 | 1.1.4 | 4.4.0 | 2.7.3 | 0.9.0 |
2.1.0.RELEASE or 2.0.0.RELEASE or 1.5.0.RELEASE | 1.6.3 | 1.1.1 | 4.4.0 | 2.7.3 | 0.7.1 |
毕业版本依赖关系(推荐使用)
Spring Cloud Version | Spring Cloud Alibaba Version | Spring Boot Version |
---|---|---|
Spring Cloud Hoxton.SR8 | 2.2.5.RELEASE | 2.3.2.RELEASE |
Spring Cloud Greenwich.SR6 | 2.1.3.RELEASE | 2.1.13.RELEASE |
Spring Cloud Hoxton.SR3 | 2.2.1.RELEASE | 2.2.5.RELEASE |
Spring Cloud Hoxton.RELEASE | 2.2.0.RELEASE | 2.2.X.RELEASE |
Spring Cloud Greenwich | 2.1.2.RELEASE | 2.1.X.RELEASE |
Spring Cloud Finchley | 2.0.3.RELEASE | 2.0.X.RELEASE |
Spring Cloud Edgware | 1.5.1.RELEASE(停止维护,建议升级) | 1.5.X.RELEASE |
服务注册与发现-Nacos
服务治理概念
在RPC远程调用过程中,服务与服务之间依赖关系非常大,服务Url地址管理非常复杂,所以这时候需要对我们服务的url实现治理,通过服务治理可以实现服务注册与发现、负载均衡、容错等。
服务注册中心的概念
- 每次调用该服务如果地址直接写死的话,一旦接口发生变化的情况下,这时候需要重新发布版本才可以该接口调用地址,所以需要一个注册中心统一管理我们的服务注册与发现。
- 注册中心:我们的服务注册到我们注册中心,key为服务名称、value为该服务调用地址,该类型为集合类型。Eureka、consul、zookeeper、nacos等。
- 服务注册:我们生产者项目启动的时候,会将当前服务自己的信息地址注册到注册中心。
- 服务发现: 消费者从我们的注册中心上获取生产者调用的地址(集合),在使用负载均衡的策略获取集群中某个地址实现本地rpc远程调用。
微服务调用接口常用名词
- 生产者:提供接口被其他服务调用
- 消费者:调用生产者接口实现消费
- 服务注册:将当前服务地址注册到
服务发现
Nacos的基本的介绍
Nacos可以实现分布式服务注册与发现/分布式配置中心框架。
官网的介绍: https://nacos.io/zh-cn/docs/what-is-nacos.html
windows安装nacos
首先去NACOS官网 下载相关jar,这里使用的是最新版本1.4.1
- 下载最新版本后进行解压,目前最新版本1.4.1
- 进入解压后的目录:E:\SpringCloudAlibaba\nacos\bin
- cmd 打开命令窗口
- 运行: startup.cmd -m standalone
Nacos端口为8848
http://127.0.0.1:8848/nacos/#/login 账号密码nacos naocs
nacos可以支持分布式配置中心和服务注册与发现
搭建Spring Cloud Alibaba项目
首先要有一个SpringBoot项目,微服务项目是基于SpringBoot项目的,主要是由于SpringBoot项目可以不用写太多繁琐的配置文件,写过SSM的都体验过,那滋味。。。。
注意事项:必须先启动nacos,才能启动cloud的项目
搭建父工程
修改 pom 文件
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.2.RELEASE</version> <!-- Spring Boot 版本 -->
<relativePath/> <!-- lookup parent from repository -->
</parent>
<!-- 子模块 -->
<modules>
<module></module>
</modules>
<groupId>com.cp</groupId>
<artifactId>springcloud-father</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springcloud-father</name>
<description>Demo project for Spring Boot</description>
<!-- 统一版本号 -->
<properties>
<java.version>1.8</java.version>
<spring.cloud-version>Hoxton.SR8</spring.cloud-version>
<spring.cloud.alibaba-version>2.2.5.RELEASE</spring.cloud.alibaba-version>
</properties>
<!--统一依赖-->
<dependencyManagement>
<dependencies>
<!-- Spring Cloud -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud-version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Spring Cloud Alibaba -->
<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>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
创建生产者项目
这里我们现在Maven创建子模块,也可以使用SpringBoot创建子模块
使用SpringBoot 可以不用手动创建主启动类等一些文件
使用Maven 创建的子项目pom文件内容简短,方便建立父子模块关系
添加Nacos依赖
<!-- Nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
修改主启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient //添加注解,实现服务注册
public class SpringCloudProviderApplication {
public static void main(String [] args){
SpringApplication.run(SpringCloudProviderApplication.class,args);
}
}
修改 application.yml 配置
server:
port: 9000
spring:
application:
name: springcloud-provider #服务名称,微服务项目中必须设计服务名称
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 #Nacos的服务注册中心地址
创建一个controller进行测试
package com.cp.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author : CP
* @version: 1.0
* @program : springcloud-father
* @description :
* @date : 2021-03-06 10:08
**/
@RestController
public class ProviderController {
@GetMapping("/index")
public String index(){
return "Hello word";
}
}
添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
注意事项:必须先启动nacos,才能启动cloud的项目
访问地址:http://localhost:8848/nacos
启动完成后去nacos服务注册中心看看,发现provider生产者已经注册到了nacos,说明生产者已经搭建完成。
访问地址:http://localhost:9000/index ,如图所示,生产者已经搭建完成
创建消费者项目
创建消费者,步骤和创建生产者一样,修改一下端口号和服务名称
server:
port: 9001
spring:
application:
name: springcloud-consumer #服务名称,微服务项目中必须设计服务名称
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 #Nacos的服务注册中心地址
注意事项:必须先启动nacos,才能启动cloud的项目
访问地址:http://localhost:8848/nacos
启动完成后nacos注册服务中心可以看到消费者服务已经注册
服务发现
/**
* 服务发现
*/
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/instances")
public List<ServiceInstance> instances(){
List<ServiceInstance> instances = discoveryClient.getInstances("springcloud-consumer");
return instances;
}
注意事项:必须先启动nacos,才能启动cloud的项目
访问地址:http://localhost:9001/instances,如图所示:
消费者已经搭建完成并得到了注册信息
负载均衡-Ribbon
概述
LB(Load Balance,负载均衡)是一种集群技术,它将特定的业务(网络服务、网络流量等)分担给多台网络设备(包括服务器、防火墙等)或多条链路,从而提高了业务处理能力,保证了业务的高可靠性。
负载均衡简介
负载均衡是一种基础的网络服务,核心原理是按照指定的负载均衡算法,将请求分配到后端服务集群上,从而为系统提供并行处理和高可用的能力。
负载均衡一般分为以下两种:
- 集中式负载均衡,在消费者和服务提供方中间使用独立的代理方式进行负载,有硬件的负载均衡器,比如 F5,也有软件,比如 Nginx。
- F5,四层负载均衡通过虚拟 IP + 端口接收请求,然后再分配到真实的服务器
- Nginx,七层负载均衡通过虚拟的 URL 或主机名接收请求,然后再分配到真实的服务器。
- 客户端负载均衡,客户端根据自己的请求情况做负载,Ribbon 就属于客户端自己做负载均衡的框架。
集中式负载均衡
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F8AoPG0X-1616120955070)(https://images1.freesion.com/158/2a/2a380343a1444f63155c1499761c494e.png)]
客户端发送请求到负债均衡器,负载均衡器根据相关的负载均衡算法(随机、轮询、加权)选择其中一台服务器,将请求转发到服务器上。
客户端负载均衡
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b8Z9QJXe-1616120955070)(https://images2.freesion.com/499/b0/b06eaa2e8cf41870af8b798bcf6f48f3.png)]
如上图所示,和集中式负载均衡的不同是,客户端负载均衡器需要自己维护服务实例的信息,然后通过相关的负载均衡算法(随机、轮询、加权)从实例中选取一个实例,直接进行访问。
负载均衡策略
轮询策略
Ribbon 默认采用轮询策略
这里我们实现消费者(consumer)调用提供者(provider)
-
在提供者(provider)定义方法
import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; /** * @author : CP * @version: 1.0 * @program : springcloud-father * @description : * @date : 2021-03-06 10:08 **/ @RestController public class ProviderController { //获取端口号 @Value("${server.port}") private String port; @GetMapping("/echo") public String echo(){ //打印当前端口号 return "Hello word ------->"+port; } }
-
修改消费者(consumer)主启动类
添加@LoadBalanced注解
/** * @LoadBalanced * 实现赋值均衡 * 使用的是轮询策略 */ @Bean @LoadBalanced public RestTemplate restTemplate(){ return new RestTemplate(); }
-
在消费者(consumer)定义service层
/** * 调用provider模块中的方法 * @return String */ String echo();
@Autowired private RestTemplate restTemplate; @Override public String echo() { /** * 第一个参数获取路径 * 第二个参数是那边接口返回的类型 * springcloud-provider 是提供者模块yml配置的名称 * index 是提供者里有的提供方法 */ String s = restTemplate.getForObject("http://springcloud-provider/index", String.class); return s; }
-
在消费者(consumer)定义Controller层
@Autowired private CousmerService cousmerService; /** * 调用生产者提供的方法 * @return */ @GetMapping("/echo") public String echo(){ return cousmerService.echo(); }
注意事项:必须先启动nacos,才能启动cloud的项目
访问地址:http://localhost:9001/echo,如图所示
随机
修改消费者(consumer)模块中的配置
#springcloud-provider 是你提供者(provider)模块名称
springcloud-provider:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
权重
在消费者(consumer)添加NacosWeightLoadBalancerRule类
package com.cp.config;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.ribbon.NacosServer;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.DynamicServerListLoadBalancer;
import com.netflix.loadbalancer.Server;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
/**
* 基于Nacos权重的负载均衡
*
* @Author Administrator
*/
@Slf4j
public class NacosWeightLoadBalancerRule extends AbstractLoadBalancerRule {
@Autowired
private NacosDiscoveryProperties nacosDiscoveryProperties;
@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {
// 读取配置文件,并初始化NacosWeightLoadBalancerRule
}
@Override
public Server choose(Object o) {
DynamicServerListLoadBalancer loadBalancer = (DynamicServerListLoadBalancer) getLoadBalancer();
// 请求的微服务名称
String applicationName = loadBalancer.getName();
try {
// nacos 通过基于权重的负载均衡算法,算出一个健康的服务实例以供调用
Instance instance = nacosDiscoveryProperties.namingServiceInstance().selectOneHealthyInstance(applicationName);
return new NacosServer(instance);
} catch (NacosException e) {
log.error("获取服务实例异常:{}", e.getMessage());
}
return null;
}
}
添加依赖
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
配置文件
#springcloud-provider 是你提供者(provider)模块名称
springcloud-provider:
ribbon:
NFLoadBalancerRuleClassName: com.cp.config.NacosWeightLoadBalancerRule #刚刚创建类的路径
服务调用-OpenFeign
OpenFeign简介
OpenFeign是一个声明式的Web服务客户端、让编写Web服务客户端变得更加容易只需要创建一个接口然后添加上注解即可,同时,它集成了Ribbon,可以轻松实现负载均衡的效果。
Feign和OpenFeign的区别:
快速开始
这里我们对消费者(consumer)调用提供者(provider)进行优化
在消费者(consumer)模块中添加依赖
<!-- OpenFeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
在消费者(consumer)的主启动类添加注解
//支持 OpenFeign 组件的使用
@EnableFeignClients
修改消费者(consumer) service层
package com.cp.service;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
/**
* @FeignClient(value = "springcloud-provider") 声明要访问的服务
* value = 需要访问的服务名称
* @GetMapping("/echo") 声明要调用的方法
* /echo 必须是访问服务里拥有的方法接口
**/
@FeignClient(value = "springcloud-provider")
public interface ConsumerService {
@GetMapping("/echo")
String echo();
}
定义后可以不用编写实现类可直接通过Controller层(不需要改动)进行调用
注意事项:必须先启动nacos,才能启动cloud的项目
访问地址:http://localhost:9001/echo,如图所示
服务容错-Sentinel
服务雪崩效应
在分布式系统中,由于网络原因或者自身的原因,服务一般无法保证100%可用。如果一个服务出现了问题,调用这个服务就会出现线程阻塞的情况。此时若有大量的请求涌入,就会出现多条线程阻塞等待,进而导致服务瘫痪。
由于服务与服务之间的依赖性,故障会传播,会对整个微服务系统造成灾难性的严重后果,这就是服务故障的“雪崩效应”。
服务雪崩效应的定义
服务雪崩效应是一种因 服务提供者 的不可用导致 服务调用者 的不可用,并将不可用 逐渐放大 的过程.如果所示:
上图中, A为服务提供者, B为A的服务调用者, C和D是B的服务调用者. 当A的不可用,引起B的不可用,并将不可用逐渐放大C和D时, 服务雪崩就形成了
服务雪崩效应形成的原因
我把服务雪崩的参与者简化为 服务提供者 和 服务调用者, 并将服务雪崩产生的过程分为以下三个阶段来分析形成的原因:
- 服务提供者不可用
- 重试加大流量
- 服务调用者不可用
服务雪崩的每个阶段都可能由不同的原因造成, 比如造成 服务不可用 的原因有:
- 硬件故障
- 程序Bug
- 缓存击穿
- 用户大量请求
服务雪崩的应对策略
针对造成服务雪崩的不同原因, 可以使用不同的应对策略:
- 服务降级:释放服务器资源以保证核心任务的正常运行
- 服务限流:限制请求访问量
- 服务隔离:避免服务之间相互影响
- 服务容错:Sentinel、Hystrix、Resilience4j
Sentinel 服务容错
Sentinel 具有以下特征:
- 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
- 完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
- 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。
- 完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
Sentinel 的主要特性:
Sentinel 的开源生态:
Sentinel 分为两个部分:
- 核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
- 控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。
快速开始
在提供者(provider)添加依赖
<!-- sentinel 服务容错 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!-- 服务监控器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
在提供者(provider)添加YAML配置
spring:
cloud:
# 服务容错
sentinel:
transport:
dashboard: localhost:8080
management:
endpoints:
web:
exposure:
include: '*'
提供者(provider) YAML 完整配置
server:
port: 9000
spring:
application:
# 服务名称
name: springcloud-provider #服务名称,微服务项目中必须设计服务名称
cloud:
# 服务注册
nacos:
discovery:
server-addr: 127.0.0.1:8848 #Nacos的服务注册中心地址
# 服务容错
sentinel:
transport:
dashboard: localhost:8080
management:
endpoints:
web:
exposure:
include: '*'
注意:运行项目前先启动本地的sentinel-dashboard-1.8.0.jar
包
打开控制台启动命令:
java -jar sentinel-dashboard-1.8.0.jar
启动成功后访问: http://localhost:8080;如图所示
账号密码sentinel sentinel
进入控制台后需要先访问一下接口进行监控
Alibaba Sentinel 流控规则
流控是Sentinel核心功能之一,流控指的是流量控制。
- 资源名: 唯一名称,默认请求路径
- 针对来源: Sentinel可以针对调用者进行限流,填写微服务名,默认default(不区分来源)
- 阈值类型/单机阈值:
- QPS(每秒请求数量):当调用该api的QPS达到阈值的时候,进行限流
- 线程数:当调用该api的线程数达到阈值的时候,进行限流
- 是否集群: 不需要集群
- 流控模式:
- 直接:api达到限流条件时,直接限流
- 关联:当关联的资源达到限流阈值时,就限流自己
- 链路:只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到峰值,就进行限流)【api级别的针对来源】
- 流控效果:
- 快速失败:直接失败,抛异常
- Warm Up:根据coldFactor(冷加载因子,默认3)的值,从阈值/coldFactor,经过预热时长,才达到设置的QPS阈值
- 排队等待:匀速排队,让请求以匀速通过,阈值类型必须设置为QPS,否则无效
直接限流
点击簇点链路后进行操作
表示1秒内查询2次就是OK,若超过次数2,就直接失败-快速失败,报默认错误。
连续快速访问:
关联限流
当A接口访问到达指定的值,将B接口进行限流
在提供者(provider)定义方法
/**
* 测试关联限流
* @return
*/
@GetMapping("/show")
public String show(){
return "2333333";
}
目前提供者(provider)所拥有的方法
package com.cp.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author : CP
* @version: 1.0
* @program : springcloud-father
* @description :
* @date : 2021-03-06 10:08
**/
@RestController
public class ProviderController {
/**
* 获取端口号
*/
@Value("${server.port}")
private String port;
/**
* 被消费者进行调用并输出当前端口号
* @return
*/
@GetMapping("/echo")
public String echo(){
return "Hello word ------->"+port;
}
/**
* 测试关联限流
* @return
*/
@GetMapping("/show")
public String show(){
return "2333333";
}
}
编写测试类频繁访问提供者(provider)接口 来测试限流
package com.cp;
import org.springframework.web.client.RestTemplate;
/**
* @author : CP
* @version: 1.0
* @program : springcloud-father
* @description :
* @date : 2021-03-08 14:46
**/
public class ProviderTest {
public static void main(String[] args) {
RestTemplate restTemplate = new RestTemplate();
for (int i = 0; i < 500; i++) {
String forObject = restTemplate.getForObject("http://localhost:9010/show", String.class);
try {
//0.1秒延迟
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
通过关联/show接口;当接口每秒访问超过2时,/echo接口限流
添加限流规则后去确定测试类进行测试
访问结果:
链路限流
B和C都访问A接口,当B或C其中一个接口到达限流要求后将A接口限流
在提供者(provider)添加依赖
<!-- 链路限流所需以下三个依赖 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>1.8.0</version> <!-- 通过版本统一可以忽略版本号 -->
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-web-servlet</artifactId>
<version>1.8.0</version>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-transport-simple-http</artifactId>
<version>1.8.0</version>
</dependency>
在提供者(provider) YMAL 中配置添加
spring:
cloud:
sentinel:
filter:
enabled: false
提供者(provider) YAML 完整配置
server:
port: 9000
spring:
application:
# 服务名称
name: springcloud-provider #服务名称,微服务项目中必须设计服务名称
cloud:
# 服务注册
nacos:
discovery:
server-addr: 127.0.0.1:8848 #Nacos的服务注册中心地址
# 熔断器
sentinel:
transport:
dashboard: localhost:8080
filter:
enabled: false
management:
endpoints:
web:
exposure:
include: '*'
在提供者(provider)添加配置类
package com.cp.config;
import com.alibaba.csp.sentinel.adapter.servlet.CommonFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author : CP
* @version: 1.0
* @program : springcloud-father
* @description :
* @date : 2021-03-10 09:35
**/
@Configuration
public class FilterConfiguration {
@Bean
public FilterRegistrationBean registrationBean(){
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(new CommonFilter());
registrationBean.addUrlPatterns("/*");
registrationBean.addInitParameter(CommonFilter.WEB_CONTEXT_UNIFY,"false");
registrationBean.setName("sentinelFilter");
return registrationBean;
}
}
提供者(provider) 添加 ProviderServiceImpl 类
package com.cp.service.impl;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import org.springframework.stereotype.Service;
/**
* @author : CP
* @version: 1.0
* @program : springcloud-father
* @description :
* @date : 2021-03-10 07:56
**/
@Service
public class ProviderServiceImpl {
//必须添加 @SentinelResource 才有流控效果
@SentinelResource("/link")
public String link(){
return "链路访问结果";
}
}
在提供者(provider) ProviderController 添加方法
@Autowired
private ProviderServiceImpl providerService;
/**
* 链路限流测试
* @return
*/
@GetMapping("/link1")
public String link1(){
providerService.link();
return "link1";
}
@GetMapping("/link2")
public String link2(){
providerService.link();
return "link2";
}
打开Sentinel 控制台,查看相关链路
进行链路限流
选择好资源名后,流控模式选择链路
根据自己选择的资源名查看他的拥有哪些入口资源,添加一个入口资源,可添加多个用逗号隔开
当入口资源达到自己定义的阈值后选择的资源进行限流
修改之前编写过的测试类进行测试
package com.cp;
import org.springframework.web.client.RestTemplate;
/**
* @author : CP
* @version: 1.0
* @program : springcloud-father
* @description :
* @date : 2021-03-08 14:46
**/
public class ProviderTest {
public static void main(String[] args) {
RestTemplate restTemplate = new RestTemplate();
for (int i = 0; i < 500; i++) {
String forObject = restTemplate.getForObject("http://localhost:9000/link2", String.class);
try {
//0.5秒延迟
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
运行测试类后访问/link1得到结果
资源成功的被限流了
消息驱动-RocketMQ
消息队列概述
消息队列中间件是分布式系统中重要的组件,主要解决应用解耦,异步消息,流量削锋等问题,实现高性能,高可用,可伸缩和最终一致性架构。目前使用较多的消息队列有ActiveMQ,RabbitMQ,ZeroMQ,Kafka,MetaMQ,RocketMQ
核心概念
- Topic:消息主题,一级消息类型,生产者向其发送消息。
- 生产者:也称为消息发布者,负责生产并发送消息至Topic。
- 消费者:也称为消息订阅者,负责从Topic接收并消费消息。
- 消息:生产者向Topic发送并最终传送给消费者的数据和(可选)属性的组合。
- 消息属性:生产者可以为消息定义的属性,包含Message Key和Tag。
- Group:一类生产者或消费者,这类生产者或消费者通常生产或消费同一类消息,且消息发布或订阅的逻辑一致。
Linux 安装和部署RocketMQ
Linux 配置环境:传送门
JDK: jdk-8u271-linux-x64.tar.gz
RocketMQ: rocketmq-all-4.7.1-bin-release
RocketMQ控制台: rocketmq-externals
Spring Boot 整合 RocketMQ
创建 dao 模块统一公共的依赖
pom.xml 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
创建 Order 类
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Order {
private Integer id;
private String buyerName;
private String buyerTel;
private String address;
private Date createDate;
}
提供者(provider) 继承dao模块
提供者(provider)添加依赖
<dependency>
<groupId>com.cp</groupId>
<artifactId>dao</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!-- Spring Boot Rocketmq -->
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
<!-- Rocketmq 可视化连接 -->
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>4.7.1</version>
</dependency>
提供者(provider)目前依赖
<dependency>
<groupId>com.cp</groupId>
<artifactId>dao</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!-- Nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- sentinel 服务容错 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!-- 服务监控器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- 链路限流所需以下三个依赖 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>1.8.0</version>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-web-servlet</artifactId>
<version>1.8.0</version>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-transport-simple-http</artifactId>
<version>1.8.0</version>
</dependency>
<!-- Spring Boot Rocketmq -->
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
<!-- Rocketmq 可视化连接 -->
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>4.7.1</version>
</dependency>
提供者(provider) 添加 application.yml 配置
rocketmq:
name-server: 192.168.64.135:9876
producer:
group: myprovider
提供者(provider) 现在 YAML 配置
server:
port: 9000
spring:
application:
# 服务名称
name: springcloud-provider #服务名称,微服务项目中必须设计服务名称
cloud:
# 服务注册
nacos:
discovery:
server-addr: 127.0.0.1:8848 #Nacos的服务注册中心地址
# 熔断器
sentinel:
transport:
dashboard: localhost:8080
filter:
enabled: false
management:
endpoints:
web:
exposure:
include: '*'
rocketmq:
name-server: 192.168.64.135:9876
producer:
group: myprovider
在提供者(provider) 控制器中 定义方法
@Autowired
private RocketMQTemplate rocketMQTemplate;
@GetMapping("/create")
public Order create(){
Order order = new Order(
1,
"张三",
"123123",
"软件园",
new Date()
);
this.rocketMQTemplate.convertAndSend("orderTopic",order);
return order;
}
目前提供者(provider) 控制器所拥有的方法
package com.cp.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author : CP
* @version: 1.0
* @program : springcloud-father
* @description :
* @date : 2021-03-06 10:08
**/
@RestController
public class ProviderController {
/**
* 获取端口号
*/
@Value("${server.port}")
private String port;
/**
* 被消费者进行调用并输出当前端口号
* @return
*/
@GetMapping("/echo")
public String echo(){
return "Hello word ------->"+port;
}
/**
* 测试关联限流
* @return
*/
@GetMapping("/show")
public String show(){
return "2333333";
}
@Autowired
private RocketMQTemplate rocketMQTemplate;
/**
* 消息队列进行消息发送
*/
@GetMapping("/create")
public Order create(){
Order order = new Order(
1,
"张三",
"123123",
"软件园",
new Date()
);
this.rocketMQTemplate.convertAndSend("orderTopic",order);
return order;
}
}
启动前要保证nacos、ES、以及 [服务器/虚拟机] 端口号开放和打开
访问: http://localhost:9000/create 得到结果
消费者(consumer) 继承dao模块
消费者(consumer) 模块添加依赖和提供者(provider)添加依赖一致
<dependency>
<groupId>com.cp</groupId>
<artifactId>dao</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!-- Spring Boot Rocketmq -->
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
<!-- Rocketmq 可视化连接 -->
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>4.7.1</version>
</dependency>
消费者(consumer) 目前拥有的依赖
<dependency>
<groupId>com.cp</groupId>
<artifactId>dao</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!--nacos-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- Spring Boot Rocketmq -->
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
<!-- Rocketmq 可视化连接 -->
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>4.7.1</version>
</dependency>
<!-- OpenFeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
消费者(consumer) 添加 application.yml 配置
rocketmq:
name-server: 192.168.64.138:9876
消费者(consumer) 现在 YAML 配置
spring:
application:
name: springcloud-consumer
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
server:
port: 9001
rocketmq:
name-server: 192.168.64.135:9876
消费者(consumer) 创建 ConsumeService 类 进行消息消费
消费者(consumer)会实时监控提供者(provider)是否产生消息来通过消费
package com.cp.service;
import com.cp.enitiy.Order;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Service;
/**
* @author : CP
* @version: 1.0
* @program : spring-cloud-alibaba
* @description :
* @date : 2021-03-16 20:13
**/
@Slf4j
@Service
@RocketMQMessageListener(consumerGroup = "myConsumer",topic = "orderTopic")
public class ConsumeService implements RocketMQListener<Order> {
@Override
public void onMessage(Order order) {
log.info("新订单{},发短信",order);
}
}
无论先启动提供者(provider)还是消费者(consumer)都没有关系
提供者(provider) 用于提供消息
消费者(consumer)实时监控提供者(provider)
启动前要保证nacos、ES、以及 [服务器/虚拟机] 端口号开放和打开
每次刷新提供者提供消息的接口,消费者都会立刻进行消费
打开 RocketMQ控制台也可以查看消费记录
java -jar rocketmq-console-ng-1.0.0.jar 启动jar包 访问自己定义的端口号
API网关-Gateway
微服务网关概述
不同的微服务一般会有不同的网络地址,而外部客户端可能需要调用多个服务的接口才能完成一个业务需求,如果让客户端直接与各个微服务通信,会有以下的问题:
- 客户端会多次请求不同的微服务,增加了客户端的复杂性
- 存在跨域请求,在一定场景下处理相对复杂
- 认证复杂,每个服务都需要独立认证
- 难以重构,随着项目的迭代,可能需要重新划分微服务。例如,可能将多个服务合并成一个或者将一个服务拆分成多个。如果客户端直接与微服务通信,那么重构将会很难实施
- 某些微服务可能使用了防火墙 / 浏览器不友好的协议,直接访问会有一定的困难
以上这些问题可以借助网关解决。
Gateway特性
- 基于Spring Framework 5, Project Relactor 和 Spring Boot 2.0 进行构建;
- 动态路由:能够匹配任何请求属性;
- 可以对路由指定 Predicate (断言)和 Filter (过滤器) ;
- 集成 Hystrix 的断路器功能;
- 集成 Spring Cloud 服务发现功能;
- 易于编写的 Predicate (断言)和Filter (过滤器) ;
- 请求限流功能;
- 支持路径重写;
Gateway工作流程
客户端向Spring Cloud GateWay发出请求,然后在GateWay Handler Mapping中找到与请求相匹配的路由,将其发送到GateWay Web Handler;Handler再通过指定的过滤器链来将请求发送到我们实际的服务执⾏业务逻辑,然后返回。过滤器之
间⽤虚线分开是因为过滤器可能会在发送代理请求之前(pre)或者之后(post)执⾏业务逻辑。
Filter在“pre”类型过滤器中可以做参数校验、权限校验、流量监控、⽇志输出、协议转换等,在“post”类型的过滤器中可以做响应内容、响应头的修改、⽇志的输出、流量监控等。
快速开始
创建Gateway模块
项目名为 springcloud-gateway
添加依赖
<!-- Nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- OpenFeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- gateway -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
特别注意
Spring Cloud Gateway 不使用 Web 作为服务器,而是 使用 WebFlux 作为服务器,Gateway 项目已经依赖了 starter-webflux
,所以这里千万不要依赖starter-web
创建Gateway主启动类
package com.cp;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
/**
* @author : CP
* @version: 1.0
* @program : spring-cloud-alibaba
* @description :
* @date : 2021-03-19 09:35
**/
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class,args);
}
}
创建application.yml
在resources创建YAML文件
server:
port: 9010
spring:
application:
# 服务名称
name: springcloud-gateway #服务名称,微服务项目中必须设计服务名称
cloud:
gateway:
# 开启本地能使用服务访问路由
discovery:
locator:
enabled: true
routes:
#springcloud-provider 提供者模块
- id: SPRINGCLOUD-PROVIDER
uri: lb://springcloud-provider
predicates:
- Method=GET,POST #请求方式
#springcloud-consumer 消费者模块
- id: SPRINGCLOUD-CONSUMER
uri: lb://springcloud-consumer
predicates:
- Method=GET,POST
#其他服务可继续添加
传送门: 官方文档
启动 Gateway 模块、提供者(provider)模块、消费者(consumer)模块
访问提供者(provider)模块
访问消费者(consumer)模块
鉴权过滤器
添加后访问必须携带请求头token才能进行访问
package com.cp.filter;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Maps;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.Map;
/**
* 鉴权过滤器
*/
@Component
public class AuthFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String token = exchange.getRequest().getQueryParams().getFirst("token");
if (token == null || token.isEmpty()) {
ServerHttpResponse response = exchange.getResponse();
// 封装错误信息
Map<String, Object> responseData = Maps.newHashMap();
responseData.put("code", 401);
responseData.put("message", "非法请求");
responseData.put("cause", "Token is empty");
try {
// 将信息转换为 JSON
ObjectMapper objectMapper = new ObjectMapper();
byte[] data = objectMapper.writeValueAsBytes(responseData);
// 输出错误信息到页面
DataBuffer buffer = response.bufferFactory().wrap(data);
response.setStatusCode(HttpStatus.UNAUTHORIZED);
response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
return response.writeWith(Mono.just(buffer));
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
return chain.filter(exchange);
}
/**
* 设置过滤器的执行顺序
* @return
*/
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
}
配置管理-Nacos
什么是配置
我们应用程序在运行过程中需要读取一切配置信息,这些信息会伴随程序的整个生命周期。例如:数据库的配置
什么是配置中心
随着微服务的兴起,配置文件也随之增加并且分散冗余,配置中心就是将配置文件从应用中剥离出来进行统一的管理,不需要我们的服务自己去管理配置。
配置中心流程
- 用户在配置中心更新配置信息。
- 服务A和服务B得到配置更新的通知,从配置中心获取配置
主流配置中心对比
目前市面上用的比较多的配置中心有:Spring Cloud Config、Apollo(携程)和Nacos(阿里)等。
对比项目 | Spring Could Confi | Apollo | Nacos |
---|---|---|---|
配置实时推送 | 支持(Spring Cloud Bus) | 支持(HTTP长轮询1s内) | 支持(HTTP长轮询1s内) |
版本管理 | 支持(Git) | 支持 | 支持 |
配置回滚 | 支持(Git) | 支持 | 支持 |
灰度发布 | 支持 | 支持 | 不支持 |
权限管理 | 支持(依赖Git) | 支持 | 不支持 |
多集群 | 支持 | 支持 | 支持 |
多环境 | 支持 | 支持 | 支持 |
监听查询 | 支持 | 支持 | 支持 |
多语言 | 只支持Java | 主流语言,提供了Open API | 主流语言,提供了Open API |
配置格式校验 | 不支持 | 支持 | 支持 |
单机读(QPS) | 7(限流所致) | 9000 | 15000 |
单击写(QPS) | 5(限流所致) | 1100 | 1800 |
3节点读 (QPS) | 21(限流所致) | 27000 | 45000 |
3节点写 (QPS) | 5(限流所致) | 3300 | 5600 |
- 从配置中心角度来看,性能方面Nacos的读写性能最高,Apollo次之,Spring Cloud Config依赖Git场景不适合开 放的大规模自动化运维API。
- 功能方面Apollo最为完善,nacos具有Apollo大部分配置管理功能,而Spring Cloud Config不带运维管理界面,需要自行开发。Nacos的一大优势是整合了注册中心、配置中心功能,部署和操作相比 Apollo都要直观简单,因此它简化了架构复杂度,并减轻运维及部署工作。
快速开始
发布配置
1、需要在 Nacos Server 中创建配置文件,采用YAML的方式部署配置文件,操作流程如下:
浏览器打开 http://127.0.0.1:8848/nacos,访问 Nacos Server
2、修改提供者(provider) resources 目录下的配置文件
添加bootstrap.properties
# 这里的应用名对应 Nacos Config 中的 Data ID,实际应用名称以配置中心的配置为准
spring.application.name=springcloud-provider-config
# 指定查找名为 nacos-provider-config.yaml 的配置文件
spring.cloud.nacos.config.file-extension=yaml
# Nacos Server 的地址
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
为了避免出现问题将提供者(provider)application.yml删除或注释
3、修改提供者(provider) ProviderController 类
在类上添加@RefreshScope可以达到服务端修改配置后立刻得到反应
注入ConfigurableApplicationContext方式将要以applicationContext.getEnvironment().getProperty(“user.name”);来取值
package com.cp.controller;
import com.cp.service.impl.ProviderServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author : CP
* @version: 1.0
* @program : springcloud-father
* @description :
* @date : 2021-03-06 10:08
**/
@RefreshScope //范围刷新
@RestController
public class ProviderController {
@Autowired
private ProviderServiceImpl providerService;
/**
* 获取端口号
*/
@Value("${server.port}")
private String port;
/**
* 获取自定义名称
*/
@Value("${user.name}")
private String name;
/**
* 被消费者进行调用并输出当前端口号
* @return
*/
@GetMapping("/echo")
public String echo(){
return "Hello word ------->"+port;
}
/**
* 测试关联限流
* @return
*/
@GetMapping("/show")
public String show(){
return "2333333";
}
/**
* 链路限流测试
* @return
*/
@GetMapping("/link1")
public String link1(){
providerService.link();
return "link1";
}
@GetMapping("/link2")
public String link2(){
providerService.link();
return "link2";
}
// /**
// * 注入配置文件上下文
// */
// @Autowired
// private ConfigurableApplicationContext applicationContext;
@GetMapping("/getName")
public String getName(){
// return applicationContext.getEnvironment().getProperty("user.name");
return name;
}
}
ableApplicationContext方式将要以applicationContext.getEnvironment().getProperty(“user.name”);来取值
package com.cp.controller;
import com.cp.service.impl.ProviderServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author : CP
* @version: 1.0
* @program : springcloud-father
* @description :
* @date : 2021-03-06 10:08
**/
@RefreshScope //范围刷新
@RestController
public class ProviderController {
@Autowired
private ProviderServiceImpl providerService;
/**
* 获取端口号
*/
@Value("${server.port}")
private String port;
/**
* 获取自定义名称
*/
@Value("${user.name}")
private String name;
/**
* 被消费者进行调用并输出当前端口号
* @return
*/
@GetMapping("/echo")
public String echo(){
return "Hello word ------->"+port;
}
/**
* 测试关联限流
* @return
*/
@GetMapping("/show")
public String show(){
return "2333333";
}
/**
* 链路限流测试
* @return
*/
@GetMapping("/link1")
public String link1(){
providerService.link();
return "link1";
}
@GetMapping("/link2")
public String link2(){
providerService.link();
return "link2";
}
// /**
// * 注入配置文件上下文
// */
// @Autowired
// private ConfigurableApplicationContext applicationContext;
@GetMapping("/getName")
public String getName(){
// return applicationContext.getEnvironment().getProperty("user.name");
return name;
}
}