1、Spring Cloud Alibaba 简介
Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。此项目包含开发分布式应用微服务的必需组件,方便开发者通过 Spring Cloud 编程模型轻松使用这些组件来开发分布式应用服务。
依托 Spring Cloud Alibaba,您只需要添加一些注解和少量配置,就可以将 Spring Cloud 应用接入阿里微服务解决方案,通过阿里中间件来迅速搭建分布式应用系统。
1.1 主要功能
-
服务限流降级:默认支持 WebServlet、WebFlux, OpenFeign、RestTemplate、Spring Cloud Gateway, Zuul, Dubbo 和 RocketMQ 限流降级功能的接入,可以在运行时通过控制台实时修改限流降级规则,还支持查看限流降级 Metrics 监控。
-
服务注册与发现:适配 Spring Cloud 服务注册与发现标准,默认集成了 Ribbon 的支持。
-
分布式配置管理:支持分布式系统中的外部化配置,配置更改时自动刷新。
-
消息驱动能力:基于 Spring Cloud Stream 为微服务应用构建消息驱动能力。
-
分布式事务:使用 @GlobalTransactional 注解, 高效并且对业务零侵入地解决分布式事务问题。。
-
阿里云对象存储:阿里云提供的海量、安全、低成本、高可靠的云存储服务。支持在任何应用、任何时间、任何地点存储和访问任意类型的数据。
-
分布式任务调度:提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。同时提供分布式的任务执行模型,如网格任务。网格任务支持海量子任务均匀分配到所有 Worker(schedulerx-client)上执行。
-
阿里云短信服务:覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。
1.2 组件
Sentinel:把流量作为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
Nacos:一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
RocketMQ:一款开源的分布式消息系统,基于高可用分布式集群技术,提供低延时的、高可靠的消息发布与订阅服务。
Dubbo:Apache Dubbo™ 是一款高性能 Java RPC 框架。
Seata:阿里巴巴开源产品,一个易于使用的高性能微服务分布式事务解决方案。
Alibaba Cloud OSS: 阿里云对象存储服务(Object Storage Service,简称 OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务。您可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。
Alibaba Cloud SchedulerX: 阿里中间件团队开发的一款分布式任务调度产品,提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。
Alibaba Cloud SMS: 覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。
为什么要用Spring Cloud Alibaba 呢?
1.我们需要知道Spring Cloud Alibaba在Spring Cloud家族中的地位,它是一个套件,与Netflix OSS一样,涵盖了非常多的实用组件,其中也有不少内容存在重叠。
2.我们需要知道Netflix OSS下的诸多重要组件先后宣布停止新功能开发的大背景,而Spring Cloud Alibaba是一个新生项目,正处于高速迭代中。对于未来,相信谁都会选。
3.对于中国用户来说,Spring Cloud Alibaba还有一个非常特殊的意义:它将曾经红极一时的Dubbo,以及阿里巴巴的强力消息中间件RocketMQ融入Spring Cloud体系。还在纠结于如何让这些共存的团队,你们所面临过的各种困难与问题,马上就会迎刃而解。不用再烦恼是不是要扩展Dubbo的注册中心,还是自己为RocketMQ实现一套的Spring Cloud Stream的Binder等等问题。
4.对于Spring Cloud Alibaba的上手学习成本如何呢?
如果您已经是Spring Cloud的用户,那么恭喜您,在Spring Cloud Common的抽象和Spring Cloud Alibaba团队的努力下,你会非常容易、甚至不需要改变多少编码模式,就能适应它。如果您第一次接触Spring Cloud,那么也恭喜您,因为这是有史以来,中文文档最全的一个Spring Cloud组件了,相信机制的您一定也能很快的上手使用它
创建父工程引入依赖
<packaging>pom</packaging>
<properties>
<!-- Environment Settings -->
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- Spring Settings -->
<spring-cloud.version>Hoxton.SR1</spring-cloud.version>
<spring-cloud-alibaba.version>2.2.0.RELEASE</spring-cloud-alibaba.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<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>
2、Nacos
Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。
Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。 Nacos 是构建以“服务”为中心的现代应用架构 (例如微服务范式、云原生范式) 的服务基础设施。
2.1 特性
-
服务发现和服务健康监测
Nacos 支持基于 DNS 和基于 RPC 的服务发现。服务提供者使用 原生SDK、OpenAPI、或一个独立的Agent TODO注册 Service 后,服务消费者可以使用DNS TODO 或HTTP&API查找和发现服务。
相当于dubbo的zookeeper和springcloud的eureka
-
动态配置服务
动态配置服务可以让您以中心化、外部化和动态化的方式管理所有环境的应用配置和服务配置。
动态配置消除了配置变更时重新部署应用和服务的需要,让配置管理变得更加高效和敏捷。
配置中心化管理让实现无状态服务变得更简单,让服务按需弹性扩展变得更容易。
相当于动态配置的zookeeper和springcloude-config
-
动态 DNS 服务
动态 DNS 服务支持权重路由,让您更容易地实现中间层负载均衡、更灵活的路由策略、流量控制以及数据中心内网的简单DNS解析服务。动态DNS服务还能让您更容易地实现以 DNS 协议为基础的服务发现,以帮助您消除耦合到厂商私有服务发现 API 上的风险。
-
服务及其元数据管理
Nacos 能让您从微服务平台建设的视角管理数据中心的所有服务及元数据,包括管理服务的描述、生命周期、服务的静态依赖分析、服务的健康状态、服务的流量管理、路由及安全策略、服务的 SLA 以及最首要的 metrics 统计数据。
2.2 安装
环境要求
-
64 bit OS,支持 Linux/Unix/Mac/Windows,推荐选用 Linux/Unix/Mac。
1.下载安装包
注意下载[nacos-server-1.2.0 以上版本会报错
2.解压并启动
linux
sh startup.sh -m standalone
window
cmd startup.cmd ##或者 双击startup.cmd
3.访问浏览器
http://localhost:8848/nacos #若需要访问密码为nacos/nacos
3.创建工程实例
3.1 创建父工程springcloudalibaba
引入以下依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.4.RELEASE</version>
</parent>
<packaging>pom</packaging>
<properties>
<!-- Environment Settings -->
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- Spring Settings -->
<spring-cloud.version>Hoxton.SR1</spring-cloud.version>
<spring-cloud-alibaba.version>2.2.0.RELEASE</spring-cloud-alibaba.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<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>
3.2 创建服务提供者nacos-provider
创建子工程nacos-provider,引入以下依赖
<parent>
<artifactId>springcloudalibaba</artifactId>
<groupId>com.wgz.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>nacos-provider</artifactId>
<dependencies>
<!--springBoot 相关 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- alibaba.cloud 相关-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
</dependencies>
创建配置文件application.properties
spring.application.name=nacos-provider
#nacos 的地址
spring.cloud.nacos.discovery.server-addr=localhost:8848
#配置应用端口
server.port=8081
#暴漏端点
management.endpoints.web.exposure.include=*
创建启动类
@SpringBootApplication
@EnableDiscoveryClient// //服务注册和服务发现 用在服务提供者或者服务消费者
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class,args);
}
}
创建测试类
@RestController
public class ProviderController {
@Value("${server.port}")
private String port;
@GetMapping("/getMessage/{message}")
public String getMessage( @PathVariable String message){
return "server port:"+ port +"----message:"+message;
}
@GetMapping("/say")
public String say(@RequestParam("message") String message){
return "server port:"+ port +"----message:"+message;
}
}
测试启动provider
访问nacos管理界面,可以看到
3.3 创建服务消费者
消费者通过nacos调用服务提供者的接口使用LoadBalanceClient 和 RestTemplate来完成
1.创建nacos-customer
引入以下pom和provider一致
<dependencies>
<!--springBoot 相关 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- alibaba.cloud 相关-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
</dependencies>
创建配置文件
spring.application.name=nacos-customer
#nacos 的地址
spring.cloud.nacos.discovery.server-addr=localhost:8848
#配置应用端口
server.port=8082
#暴漏端点
management.endpoints.web.exposure.include=*
创建启动类
@EnableDiscoveryClient
@SpringBootApplication
public class CustomerApplication {
public static void main(String[] args) {
SpringApplication.run(CustomerApplication.class,args);
}
}
创建配置类
@Configuration
public class NacosConfig {
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
创建控制类
@RestController
public class NacosCustomerController {
@Autowired
private LoadBalancerClient balancerClient;
@Autowired
private RestTemplate restTemplate;
@Value("${spring.application.name}")
private String applicationName;
@RequestMapping("/test")
public String test(){
// 通过balancerClient 可以得到 知识的服务的ip 和 端口
ServiceInstance serviceInstance = balancerClient.choose("nacos-provider");
String url = String.format("http://%s:%s/getMessage/%s",serviceInstance.getHost(),serviceInstance.getPort(),applicationName);
// 使用restTemplate 发起网络请求
return restTemplate.getForObject(url,String.class);
}
@RequestMapping("/test2")
public String test2(){
// 通过balancerClient 可以得到 知识的服务的ip 和 端口
ServiceInstance serviceInstance = balancerClient.choose("nacos-provider");
String url = String.format("http://%s:%s/say?message={message}",serviceInstance.getHost(),serviceInstance.getPort());
Map<String, Object> paramMap = new HashMap<>(16);
paramMap.put("message","hello world");
// 使用restTemplate 发起网络请求
return restTemplate.getForObject(url,String.class,paramMap);
}
}
启动消费者测试
查看nacos管理界面可以看到
浏览器访问:http://localhost:8082/test2
4.使用Ribbon
Ribbon概述
是一个客户端的负载均衡器,它提供对大量的HTTP和TCP客户端的访问控制
可以实现服务的远程调用和服务的负载均衡协调
Ribbon的负载均衡策略
示例
1.引入依赖
<!-- 引入负载均衡-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon
</artifactId>
</dependency>
2.配置负载均衡
@Configuration
public class TestConfig {
@LoadBalanced// 对RestTemplate 的发起网络请求 进行拦截,
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
@Bean
public IRule getIRule(){
// 创建一个随机的负载均衡策略
IRule rule = new RandomRule();
//2.权重响应时间分配规则 代替ResponseTimeRule 响应时间加权策略
// WeightedResponseTimeRule responseTimeRule=new WeightedResponseTimeRule();
// //3.最低并发策略 分配的时候选择目前并发量最小的
// BestAvailableRule bestAvailableRule=new BestAvailableRule();
// //4.轮训策略
// RoundRobinRule roundRobinRule=new RoundRobinRule();
// //5.重试策略 如果在配置时间内,无法选择服务,尝试选择一个服务 重试机制
// RetryRule retryRule=new RetryRule();
// //6.区域感知策略 就近访问
// ZoneAvoidanceRule zoneAvoidanceRule=new ZoneAvoidanceRule();
7.可用过滤策略 可用根据阈值进行服务过滤
// AvailabilityFilteringRule filteringRule=new AvailabilityFilteringRule();
return rule;
}
}
3.使用负载均衡器 请求
@RestController
public class CusomController {
@Autowired
private RestTemplate restTemplate;
/**
* loadBalancerClient 负载均衡的客户端 ,用来 更具服务名 从nacos 中获取对应的ip
*/
@Autowired
private LoadBalancerClient loadBalancerClient;
@RequestMapping("/test1")
public String test1(){
// 调用远程 provider 中的 @RequestMapping("/getMessage/{msg}")
// 现在是 写死 地址,也可以使用 nacos 找到 springcloud-alibaba-provider 对应的ip 端口
// String url = "http://localhost:8083/getMessage/hello";
// serviceInstance 就是远程 provider 实例的 ip 端口 的封装
// ServiceInstance serviceInstance = loadBalancerClient.choose("springcloud-alibaba-provider");
// 得到远程服务实例的ip
//serviceInstance.getHost();
// 得到端口
// serviceInstance.getPort();
// %s 就是需要替换的参数
String url = String.format("http://springcloud-alibaba-provider/test/%s","hello beijing");
System.out.println("url = " + url);
return restTemplate.getForObject(url,String.class);
}
@RequestMapping("/test2")
public String test2(){
// 远程调用 @RequestMapping("/say")
// public String say(@RequestParam("msg") String msg)
String url = String.format("http://springcloud-alibaba-provider/say?msg={msg}");
System.out.println("url = " + url);
// msg 值的初始化
Map map = new HashMap();
map.put("msg", "hello java2109");
return restTemplate.getForObject(url,String.class,map);
}
}
远程服务参数传递:
1.键值对传输 必须使用:@RequestParam注解进行修饰 不可省略
2.对象传输 必须使用:@RequestBody
下面为一个对象的例子
@GetMapping("/test23")
@ResponseBody
public Student test3(){
Student student = new Student();
student.setName("xiaoming");
return restTemplate.postForEntity("http://nacos-provider/addStudent", student,Student.class).getBody();
}
接收
@RequestMapping("/addStudent")
public Student addStudent (@RequestBody Student student){
System.out.println("*****");
return student;
}
注意:服务名不可以使用下划线连接 nacos_provider,必须是 nacos-provider
4.创建feign消费
什么是Feign?
Feign 的英文表意为“假装,伪装,变形”, 是一个http请求调用的轻量级框架,可以Java接口注解的方式调用Http请求,而不用像Java中通过封装HTTP请求报文的方式直接调用。Feign通过处理注解,将请求模板化,当实际调用的时候,传入参数,根据参数再应用到请求上,进而转化成真正的请求,这种请求相对而言比较直观。 Feign被广泛应用在Spring Cloud 的解决方案中,是学习基于Spring Cloud 微服务架构不可或缺的重要组件。
4.1 引入依赖
<dependencies>
<!--springBoot 相关 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- alibaba.cloud nacos 相关-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- opengin -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
4.2 创建配置文件(application.xml)
#设置端口号
server.port=8085
#应用名 模块名 注意应用名 不可以使用_ springcloud_alibaba_provider
spring.application.name=springcloud-alibaba-fegin-consumer
#设置nacos 服务地址
spring.cloud.nacos.server-addr=localhost:8848
#暴露所有应用断点,便于监控
management.endpoints.web.exposure.include=*
4.3 创建启动类
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients //激活feign相关功能
public class FeginApplication {
public static void main(String[] args) {
SpringApplication.run(FeginApplication.class,args);
}
}
4.4 创建feign服务
/**
* 伪装 接口
*
* 我们需要向那个一个 服务发起远程调用,就需要将远程的处理器 方法拷贝过来,只剩方法名,没有方法体
* 1.拷贝 provider 中的 处理器方法
* 2.删除方法体
*
* @FeignClient(value = "springcloud-alibaba-provider")
* //标记当前接口 是一个feign伪装接口,feign 更具接口配置的方法,生成对应的实现类
* 向远程服务发起请求 ----》 将改接口实现 完成 RestTemplate 请求
*
*/
@FeignClient(value = "springcloud-alibaba-provider")
public interface FeginService {
@RequestMapping("/say")
public String say(@RequestParam(value = "msg") String msg);
@RequestMapping("/test/{msg}")
public String test(@PathVariable(value = "msg") String msg);
}
4.5 容错实现类接口
注意只有在配置文件 才开启
在application.properties中
#开启feign 容错功能
feign.hystrix.enabled=true
@Component
public class FeignServiceImpl implements FeignService {
@Override
public String test(String message) {
return "FeignServiceImpl--触发熔断返回默认数据---message"+message;
}
@Override
public String say(String message) {
return "熔断:message:"+message;
}
}
feign=LoadBalancerClient+RestTemplate+容错=负载均衡+RestTemplate+容错
创建消费者controller
@RestController
public class FeignController {
@Autowired
private FeignService feignService;
@GetMapping("/test/{message}")
public String test(@PathVariable String message){
return feignService.test(message);
}
@RequestMapping("/test2")
public String test2(){
return feignService.say("test2");
}
}
启动CustomerFeignApplication测试
4.6 测试1
查看nacos管理界面
测试2查看负载均衡
1.启动多个provider
2修改端口的启动两个不同的provider进行测试
测试
访问 http://localhost:8083/test2,得到不同的服务provider提供的数据
接下来测试容错,我们可以关闭一个服务器在进行资源的访问,我这里将端口号为8077的关闭了
可以看到端口号为8077的服务器访问不到了,访问的话会输出触发熔断的信息
4.7 使用feign远程调用传递对象
首先在生产模块创建Student类
import java.io.Serializable;
/**
* 创建一个实体类
* 1.get set 方法
* 2.toString()
* 3.实现序列化 序列化作用:1.便于保存对象存入磁盘 2.便于网络间传输
*
* 4.如果创建了 有参构造 ,必须创建无参构造 必须注意
*/
public class Student implements Serializable {
public Student(){}
public Student(Integer id, String name, Integer age) {
this.id = id;
this.name = name;
this.age = age;
}
private Integer id;
private String name;
private Integer age;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
在control层加入修改对象名的方法
@RequestMapping("/updateStudent")
public Student updateStudent(@RequestBody Student student){
student.setName(student.getName()+"port:"+port);
return student;
}
接着在feign消费模块中也建一个相同的Student类
public class Student implements Serializable {
public Student(){}
public Student(Integer id, String name, Integer age) {
this.id = id;
this.name = name;
this.age = age;
}
private Integer id;
private String name;
private Integer age;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
在service以及实现类(兜底数据)中加入该方法
@RequestMapping("/updateStudent")
public Student updateStudent(@RequestBody Student student);
@Override
public Student updateStudent(Student student) {
student.setName(student.getName()+"触发熔断");
return student;
}
在control层新建一个对象并调用修改方法
@RequestMapping("test3")
public Student test3(){
return feginService.updateStudent(new Student(1,"张三",12));
}
运行两个模块
,可以在控制中心看到
访问这个资源可以看到输出的数据
关闭服务器再次访问测试熔断