spring cloud
Spring Cloud是微服务架构思想的一个具体实现,它为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理、服务发、断路器,智能路由、微代理、控制总线等)。
Spring Cloud 基于 Spring Boot 框架,它不重复造轮子,而是将第三方实现的微服务应用的一些模块集成进去。准确的说,Spring Cloud 是一个容器。
最简单的 Spring Cloud 微服务架构包括服务发现和服务提供者(即一个大型系统拆分出来的子模块),最极端的微服务可以做到一个方法就是一个服务,一个方法就是一个项目。在一个系统中,服务怎么拆分,要具体问题具体分析,也取决于系统的并发性、高可用性等因素。
微服务间消息传递
微服务是一种软件开发架构,它将一个大型应用程序拆分为一系列小型、独立的服务。每个服务都可以独立开发、部署和扩展,并通过轻量级的通信机制进行交互。
应用开发
- common模块中包含服务提供者和服务消费者共享的内容
- provider模块是服务的提供者,用于通过SpringMVC的控制器提供访问接口
服务提供者
@RestController
@RequestMapping("/users")
public class HelloController {
@GetMapping("/hello")
public String sayHello(@RequestParam String username) {
if (username == null || username.trim().length() < 1)
username = "MicroService";
return "Provider: hello " + username + "!";
}
}
服务消费者
服务消费者通过http协议访问服务提供者,可以使用JDK的URL或者使用HttpClient之类的工具,但是直接使用工具比较繁琐,所以使用SpringBoot提供的RestTemplate进行访问。
在主类或者当前应用的配置类上声明RestTemplate。
@SpringBootApplication
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
核心配置,主要修改一下访问端口号,因为应用的默认端口都是8080,会有端口号冲突的问题
server.port=7081
定义控制器实现访问服务提供者
@RestController
@RequestMapping("/consumer")
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/{name}")
public String test(@PathVariable String name){
//RestTemplate针对RESTful中的get/post/delete/put分别提供了对应的方法
String res =restTemplate.getForObject("http://localhost:7080/users/hello?username="+name, String.class);
return res;
}
}
测试验证
Nacos服务治理
Nacos即Naming and Configuration Service是一个用于动态服务发现、配置管理和服务元数据的开源平台。
应用开发步骤
服务提供者
需要添加额外的依赖,也就是Nacos的客户端
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacosdiscovery</artifactId>
</dependency>
添加配置application.properties
# 配置当前应用名称,默认情况下向nacos注册的服务名称就是这个名称
spring.application.name=service-provider
# Nacos 服务发现与注册配置,其中子属性 server-addr 指定 Nacos 服务器主机和端口
spring.cloud.nacos.discovery.server-addr=localhost:8848
# 注册到 nacos 的指定 namespace,默认为 public
spring.cloud.nacos.discovery.namespace=public
# 应用服务 WEB 访问端口
server.port=6080
添加JavaConfig配置或者在主类上添加注解以启用Nacos客户端
@EnableDiscoveryClient
@Configuration
public class NacosDiscoveryConfiguration {
}
定义服务提供者
@RestController
@RequestMapping("/users")
public class HelloController {
@GetMapping("/hello")
public String sayHello(@RequestParam String username) {
if (username == null || username.trim().length() < 1)
username = "MicroService";
return "Provider: hello " + username + "!";
}
}
服务消费者
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacosdiscovery</artifactId>
</dependency>
添加核心配置application.properties
server.port=6081
spring.application.name=service-consumer
# Nacos 服务发现与注册配置,其中子属性 server-addr 指定 Nacos 服务器主机和端口
spring.cloud.nacos.discovery.server-addr=localhost:8848
# 注册到 nacos 的指定 namespace,默认为 public
spring.cloud.nacos.discovery.namespace=public
添加配置类或者在主类上添加注解启用Nacos客户端
@EnableDiscoveryClient
@SpringBootApplication
public class Consumer1Application {
public static void main(String[] args) {
SpringApplication.run(Consumer1Application.class, args);
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
定义服务消费者
@RestController
@RequestMapping("/consumer")
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/{name}")
public String test(@PathVariable String name){
//服务的名称对应的是服务提供者的application.properties中的配置
spring.application.name=service-provider
List<ServiceInstance> instances =
discoveryClient.getInstances("service-provider"); //因为一个服务可以运行在多个节点上或者使用多个进程
//自定义负载均衡策略实现从多个服务提供者实例中选择一个
Random r=new Random();
ServiceInstance instance =
instances.get(r.nextInt(instances.size()));
String res =restTemplate.getForObject(instance.getUri()+"/users/hello?username=" +name,String.class);
return res;
}
}
验证测试
引入负载均衡
在消费方引入负载均衡机制,同时简化获取服务提供者信息的流程
Spring Cloud引入组件LoadBalance实现负载均衡
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacosdiscovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
properties配置
spring.application.name=service-consumer
# Nacos 服务发现与注册配置,其中子属性 server-addr 指定 Nacos 服务器主机和端口
spring.cloud.nacos.discovery.server-addr=localhost:8848
# 注册到 nacos 的指定 namespace,默认为 public
spring.cloud.nacos.discovery.namespace=public
server.port=6082
主类上添加对应的注解
@EnableDiscoveryClient
@SpringBootApplication
public class Consumer2Application {
public static void main(String[] args) {
SpringApplication.run(Consumer2Application.class, args);
}
@LoadBalanced
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
定义对应的控制器,需要访问服务提供者
@RestController
@RequestMapping("/consumer")
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/{name}")
public String test(@PathVariable String name){
//使用LB后具体的URL地址中使用服务提供者的名称来替代原始的主机名和端口号
String res = restTemplate.getForObject("http://serviceprovider/users/hello?username=" + name, String.class);
return res;
}
}
验证测试
缓存配置
使用LB从Nacos获取服务的注册信息,可以在本地进行缓存。添加配置即可
spring.cloud.loadbalancer.cache.enabled=true #启用本地缓存,可以根据实际情况权衡
spring.cloud.loadbalancer.cache.capacity=1000 #设置缓存空间大小
spring.cloud.loadbalancer.cache.ttl=20 #缓存的存活时间,单位为s
订阅更新
缓存有可能和远程的注册信息不一致,所以引入长连接的订阅实现Nacos的主动通知更改
spring.cloud.nacos.discovery.watch.enabled=true
引入OpenFeign
添加依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacosdiscovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
定义配置:
server.port=6083
# Nacos帮助文档: https://nacos.io/zh-cn/docs/concepts.html
spring.application.name=service-consumer
# Nacos 服务发现与注册配置,其中子属性 server-addr 指定 Nacos 服务器主机和端口
spring.cloud.nacos.discovery.server-addr=localhost:8848
# 注册到 nacos 的指定 namespace,默认为 public
spring.cloud.nacos.discovery.namespace=public
在主类或者配置类上添加注解以支持OpenClient应用
@EnableFeignClients // 激活 @FeignClient
@EnableDiscoveryClient
@SpringBootApplication
public class Consumer3Application {
public static void main(String[] args) {
SpringApplication.run(Consumer3Application.class, args);
}
}
定义http伪客户端接口
@FeignClient("service-provider") // 指向服务提供者应用
public interface ProviderClient {
@GetMapping("/users/hello")
public String sayHello(@RequestParam("username") String username);
}
定义控制器,通过feign接口调用远程的服务提供者
@RestController
@RequestMapping("/consumer")
public class ConsumerController {
@Autowired
private ProviderClient providerClient;
@GetMapping("/{name}")
public String test(@PathVariable String name){
String res = providerClient.sayHello(name);
return res;
}
}
测试
负载均衡策略配置
LB中提供了三种负载均衡策略,同时提供接口允许用户自定义扩展
1、定义配置类
public class FeignClientConfiguration {
@Bean
public ReactorLoadBalancer
reactorServiceInstanceLoadBalancer(ObjectProvider
serviceInstanceListSuppliers, Environment environment) {
String name =
environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new RandomLoadBalancer(serviceInstanceListSuppliers, name);
}
}
2、可以全局或者局部配置使用设置的负载均衡策略
@EnableFeignClients(defaultConfiguration = FeignClientConfiguration.class)
@EnableDiscoveryClient
@SpringBootApplication
public class Consumer3Application {
public static void main(String[] args) {
SpringApplication.run(Consumer3Application.class, args);
}
}
局部配置
@FeignClient(value="service-provider",configuration =
FeignClientConfiguration.class)
public interface ProviderClient {
@GetMapping("/users/hello")
public String sayHello(@RequestParam("username") String username);
}
失败重试机制配置
如果通过使用OpenFeign访问远程的服务提供者,则可以配置由于网络、连接、读取等问题出现访问失败时,自动执行重试处理
1、首先定义配置
public class FeignClientConfiguration {
@Bean
public Retryer retryer(){
return new Retryer.Default(100, 1000, 2);
//表示每间隔100ms,最大间隔1000ms重试一次,最大重试次数是1,因为第三个参数包含了第一次请求
}
}
2、可以在注解中进行全局配置和局部配置
全局配置
@EnableFeignClients(defaultConfiguration = FeignClientConfiguration.class)
@EnableDiscoveryClient
@SpringBootApplication
public class Consumer3Application {
public static void main(String[] args) {
SpringApplication.run(Consumer3Application.class, args);
}
}
局部配置
@FeignClient(value="service-provider",configuration =
FeignClientConfiguration.class)
public interface ProviderClient {
@GetMapping("/users/hello")
public String sayHello(@RequestParam("username") String username);
}
服务降级配置
在使用注册中心时,OpenFeign作为服务间通信的组件,它本身集成了负载均衡能力、错误重试、日志、服务熔断等机制,同时也能够支持点对点的通信方式,让开发者感觉更像是调用本地接口,而不是发起HTTP请求
具体的服务降级是依赖Sentinel组件实现的,所以需要添加Sentinel依赖
1、添加依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
2、开启服务熔断配置application.properties
feign.circuitbreaker.enabled=true
3、定义对应的Fallback Factory实现
@Component
public class ProviderClientFallbackFactory implements
FallbackFactory<ProviderClient> {
@Override
public ProviderClient create(Throwable cause) {
return new ProviderClient() {
@Override
public String sayHello(String username) {
return cause.getMessage();
}
};
}
}
4、配置使用Fallback降级处理
@FeignClient(value="service-provider",fallbackFactory =
ProviderClientFallbackFactory.class)
public interface ProviderClient {
@GetMapping("/users/hello")
public String sayHello(@RequestParam("username") String username);
}
Nacos配置管理
将应用中的所有配置信息进行统一管理,同时当修改配置时会自动通知对应的微服务进行热加载。
Nacos在微服务集群中充当了配置管理中心的用途。
需求例如有个配置 yan.date.format=yyyy-MM-dd
配置中心的思路是:
- 首先把项目中各种配置全部都放到一个集中的地方进行统一管理,并提供一套标准的接口。
- 当各个服务需要获取配置的时候,就来配置中心的接口拉取自己的配置。
- 当配置中心中的各种参数有更新的时候,也能通知到各个服务实时的过来同步最新的信息,使之动态更新。
1、添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacosconfig</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
<version>3.1.5</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacosdiscovery</artifactId>
</dependency>
2、新增一个配置文件bootstrap.properties或者yaml格式都可以
spring.cloud.nacos.config.server-addr=localhost:8848 服务配置中心的配置
spring.cloud.nacos.config.file-extension=properties 配置使用的后缀名,一般只使用
properties和yaml两种格式
spring.cloud.nacos.config.prefix=nacos-service 配置DataId名称,默认就是服务名称。
可以人为指定在Nacos中创建的DataID值
spring.cloud.nacos.config.group=DEFAULT_GROUP 默认分组名称
spring.cloud.nacos.config.namespace=public所使用的名空间
# 设置配置中心服务端地址
spring.cloud.nacos.config.server-addr=localhost:8848
# Nacos 配置中心的namespace。需要注意,如果使用 public 的 namcespace ,请不要填写这
个值,直接留空即可
spring.cloud.nacos.config.namespace=public
# 配置自动刷新 对应的格式为nacos:服务名称.properties
spring.config.import=nacos:nacos-config-example.properties?refresh=true
3、核心配置文件application.properties
# Nacos帮助文档: https://nacos.io/zh-cn/docs/concepts.html
spring.application.name=nacos-service
# Nacos 服务发现与注册配置,其中子属性 server-addr 指定 Nacos 服务器主机和端口
spring.cloud.nacos.discovery.server-addr=localhost:8848
# 注册到 nacos 的指定 namespace,默认为 public
spring.cloud.nacos.discovery.namespace=public
# 应用服务 WEB 访问端口
server.port=8080
4、定义控制器类可以读取配置信息 yan.date.format
@RefreshScope
@RestController
public class HelloController {
@Value("${yan.date.format}") //SpEL
private String dateFormat;
@GetMapping("/hello")
public String hello(String name) {
return new SimpleDateFormat(dateFormat).format(new Date()) +"said:'hello " + name + "!'";
}
}
5、在Nacos的webUI中使用图形化界面工具的方式创建一组配置
一般规则【最佳软件开发实践】
- 命名空间Namespace:不同的项目可以分为不同的命名空间。
- 配置分组Group:根据项目的不同环境可以一个分组。
- 配置集Data ID:服务不同环境的不同配置,就是一个配置集
在webUI中可以查看所有的配置信息,并允许编辑修改
查看历史配置版本