一:SpringCloud Alibaba介绍
Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。此项目包含开发分布式应用微服
务的必需组件,方便开发者通过 Spring Cloud 编程模型轻松使用这些组件来开发分布式应用服务。
依托 Spring Cloud Alibaba,您只需要添加一些注解和少量配置,就可以将 Spring Cloud 应用接
入阿里微服务解决方案,通过阿里中间件来迅速搭建分布式应用系统。
主要功能
服务限流降级:默认支持 WebServlet、WebFlux, OpenFeign、RestTemplate、Spring Cloud
Gateway, Zuul, Dubbo 和 RocketMQ 限流降级功能的接入,可以在运行时通过控制台实时修
改限流降级规则,还支持查看限流降级 Metrics 监控。
服务注册与发现:适配 Spring Cloud 服务注册与发现标准,默认集成了 Ribbon 的支持。
分布式配置管理:支持分布式系统中的外部化配置,配置更改时自动刷新。
消息驱动能力:基于 Spring Cloud Stream 为微服务应用构建消息驱动能力。
分布式事务:使用 @GlobalTransactional 注解, 高效并且对业务零侵入地解决分布式事务问题。
阿里云对象存储:阿里云提供的海量、安全、低成本、高可靠的云存储服务。支持在任何应用、任
何时间、任何地点存储和访问任意类型的数据。
分布式任务调度:提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。
同时提供分布式的任务执行模型,如网格任务。网格任务支持海量子任务均匀分配到所有
Worker(schedulerx-client)上执行。
阿里云短信服务:覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建
客户触达通道
二:微服务环境搭建
备注:将搭建好的环境进行备份,以便练习
2.1 数据库准备
创建数据库
2.3 创建工程
结构图
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 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.yc</groupId><artifactId>springcloud-alibaba-02</artifactId><packaging>pom</packaging><version>1.0-SNAPSHOT</version><modules><module>shop-common</module><module>shop-user</module><module>shop-product</module><module>shop-order</module></modules><!-- 父工程 --><parent><artifactId>spring-boot-starter-parent</artifactId><groupId>org.springframework.boot</groupId><version>2.1.3.RELEASE</version></parent><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target><!-- <spring-cloud.version>Greenwich.RELEA</spring-cloud.version>--><!-- <spring-cloud-alibaba.version>2.1.1.RELEASE</spring-cloud-alibaba.version>--><spring-cloud-alibaba.version>2.1.0.RELEASE</spring-cloud-alibaba.version></properties><dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>Greenwich.RELEASE</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>2.1.0.RELEASE</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement></project>
2.4 shop-common 模块
1.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 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>springcloud-alibaba-02</artifactId><groupId>com.yc</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>shop-common</artifactId><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.56</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.6</version></dependency></dependencies></project>
2.结构
3.实体类
Order
package com.yc.domain;import lombok.Data;import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;/*** @Author yc* @PackageName springcloud-alibaba-02* @Package com.yc.domain* @Date 2024/3/4 18:17*/
@Entity(name = "shop_order")
@Data
public class Order {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long oid;//订单idprivate Integer uid;//用户idprivate String username;//用户名private Integer pid;//商品idprivate String pname;//商品名称private Double pprice;//商品单价private Integer number;//购买数量
}
Product
package com.yc.domain;import lombok.Data;import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;/*** @Author yc* @PackageName springcloud-alibaba-02* @Package com.yc.domain* @Date 2024/3/4 18:18*/
@Entity(name = "shop_product")
@Data
public class Product {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Integer pid;//主键private String pname;//商品名称private Double pprice;//商品价格private Integer stock;//库存
}
** User**
package com.yc.domain;import lombok.Data;import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;/*** @Author yc* @PackageName springcloud-alibaba-02* @Package com.yc.domain* @Date 2024/3/4 18:17*/
@Entity(name = "shop_user")
@Data
public class User {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Integer uid;//主键private String username;//用户名private String password;//密码private String telephone;//手机号
}
2.5 shop-user 模块
1.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 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>springcloud-alibaba-02</artifactId><groupId>com.yc</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>shop-user</artifactId><dependencies><dependency><groupId>com.yc</groupId><artifactId>shop-common</artifactId><version>1.0-SNAPSHOT</version></dependency></dependencies>
</project>
2.结构
3.启动类
package com.yc;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;/*** @Author yc* @PackageName springcloud-alibaba-02* @Package com.yc* @Date 2024/3/4 18:22*/
@SpringBootApplication
public class UserApplication {public static void main(String[] args) {SpringApplication.run(UserApplication.class,args);}
}
4.配置文件 application.yml
server:port: 8071
spring:application:name: service-productdatasource:driver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql:///hmshop02?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=trueusername: ****password: ****jpa:## 自动生成数据库表配置properties:hibernate:hbm2ddl:auto: updatedialect: org.hibernate.dialect.MySQL5InnoDBDialect
2.6 shop_product 模块
1.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 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>springcloud-alibaba-02</artifactId><groupId>com.yc</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>shop-product</artifactId><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.yc</groupId><artifactId>shop-common</artifactId><version>1.0-SNAPSHOT</version></dependency></dependencies>
</project>
2.结构
3.启动类
package com.yc;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;/*** @Author yc* @PackageName springcloud-alibaba-02* @Package com.yc* @Date 2024/3/4 18:29*/
@SpringBootApplication
public class ProductApplication {public static void main(String[] args) {SpringApplication.run(ProductApplication.class,args);}
}
4.配置文件 application.yml
server:port: 18082
spring:application:name: service-productdatasource:driver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql:///hmshop02?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=trueusername: rootpassword: 123456jpa:properties:hibernate:hbm2ddl:auto: updatedialect: org.hibernate.dialect.MySQL5InnoDBDialect
5.ProductController
package com.yc.controller;import com.alibaba.fastjson.JSON;
import com.yc.domain.Product;
import com.yc.service.ProductService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;/*** @Author yc* @PackageName springcloud-alibaba-02* @Package com.yc.controller* @Date 2024/3/4 18:33*/
@RestController
@Slf4j
public class ProductController {@Autowiredprivate ProductService productService;@GetMapping("/product/{pid}")public Product product(@PathVariable("pid") Integer pid) {Product product = productService.findByPid(pid);log.info("查询到商品:" + JSON.toJSONString(product));return product;}}
6.ProductDao
package com.yc.dao;import com.yc.domain.Product;
import org.springframework.data.jpa.repository.JpaRepository;/*** @Author yc* @PackageName springcloud-alibaba-02* @Package com.yc.dao* @Date 2024/3/4 18:31*/
public interface ProductDao extends JpaRepository<Product,Integer> {
}
7.ProductService
package com.yc.service;import com.yc.domain.Product;/*** @Author yc* @PackageName springcloud-alibaba-02* @Package com.yc.service* @Date 2024/3/4 18:31*/
public interface ProductService {Product findByPid(Integer pid);
}
8.ProductServiceImpl
package com.yc.service.impl;import com.yc.dao.ProductDao;
import com.yc.domain.Product;
import com.yc.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;/*** @Author yc* @PackageName springcloud-alibaba-02* @Package com.yc.service.impl* @Date 2024/3/4 18:32*/
@Service
public class ProductServiceImpl implements ProductService {@Autowiredprivate ProductDao productDao;@Overridepublic Product findByPid(Integer pid) {return productDao.findById(pid).get();}
}
9.启动后查看数据库
10.写入测试数据
INSERT INTO shop_product VALUE(NULL,'小米','1000','5000');
INSERT INTO shop_product VALUE(NULL,'华为','2000','5000');
INSERT INTO shop_product VALUE(NULL,'苹果','3000','5000');
INSERT INTO shop_product VALUE(NULL,'OPPO','4000','5000');
11.通过浏览器访问服务
http://localhost:18082/product/1
2.7 shop-order 模块
1.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 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>springcloud-alibaba-02</artifactId><groupId>com.yc</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>shop-order</artifactId><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.yc</groupId><artifactId>shop-common</artifactId><version>1.0-SNAPSHOT</version></dependency></dependencies></project>
2.结构
3.启动类
package com.yc;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;/*** @Author yc* @PackageName springcloud-alibaba-02* @Package com.yc* @Date 2024/3/4 18:39*/
@SpringBootApplication
public class OrderApplication {public static void main(String[] args) {SpringApplication.run(OrderApplication.class, args);}@Beanpublic RestTemplate getRestTemplate() {return new RestTemplate();}
}
4.application.yml
server:port: 8091
spring:application:name: service-productdatasource:driver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql:///hmshop02?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=trueusername: rootpassword: 123456jpa:properties:hibernate:hbm2ddl:auto: updatedialect: org.hibernate.dialect.MySQL5InnoDBDialect
5.OrderController
package com.yc.controller;import com.alibaba.fastjson.JSON;
import com.yc.domain.Order;
import com.yc.domain.Product;
import com.yc.service.OrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;/*** @Author yc* @PackageName springcloud-alibaba-02* @Package com.yc.controller* @Date 2024/3/4 18:45*/
@RestController
@Slf4j
public class OrderController {@Autowiredprivate RestTemplate restTemplate;@Autowiredprivate OrderService orderService;//准备买1件商品@GetMapping("/order/prod/{pid}")public Order order(@PathVariable("pid") Integer pid) {log.info(">>客户下单,这时候要调用商品微服务查询商品信息");//通过restTemplate调用商品微服务Product product = restTemplate.getForObject("http://localhost:18082/product/" + pid, Product.class);log.info(">>商品信息,查询结果:" + JSON.toJSONString(product));Order order = new Order();order.setUid(1);order.setUsername("测试用户");order.setPid(product.getPid());order.setPname(product.getPname());order.setPprice(product.getPprice());order.setNumber(1);orderService.save(order);return order;}
}
6.OrderDao
package com.yc.dao;import com.yc.domain.Order;
import org.springframework.data.jpa.repository.JpaRepository;/*** @Author yc* @PackageName springcloud-alibaba-02* @Package com.yc.dao* @Date 2024/3/4 18:42*/
public interface OrderDao extends JpaRepository<Order,Long> {
}
7.OrderService
package com.yc.service;import com.yc.domain.Order;/*** @Author yc* @PackageName springcloud-alibaba-02* @Package com.yc.service* @Date 2024/3/4 18:42*/
public interface OrderService {void save(Order order);
}
8.OrderServiceImpl
package com.yc.service.impl;import com.yc.dao.OrderDao;
import com.yc.domain.Order;
import com.yc.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;/*** @Author yc* @PackageName springcloud-alibaba-02* @Package com.yc.service.impl* @Date 2024/3/4 18:43*/
@Service
public class OrderServiceImpl implements OrderService {@Autowiredprivate OrderDao orderDao;@Overridepublic void save(Order order) {orderDao.save(order);}}
9启动后浏览器访问
http://localhost:8091/order/prod/1
三 Nacos Discovery–服务治理
什么是服务治理
服务治理是微服务架构中最核心最基本的模块。用于实现各个微服务的自动化注册与发现。
- 服务注册:在服务治理框架中,都会构建一个注册中心,每个服务单元向注册中心登记自己提供服
务的详细信息。并在注册中心形成一张服务的清单,服务注册中心需要以心跳的方式去监测清单中
的服务是否可用,如果不可用,需要在服务清单中剔除不可用的服务。 - 服务发现:服务调用方向服务注册中心咨询服务,并获取所有服务的实例清单,实现对具体服务实
例的访问。
通过上面的调用图会发现,除了微服务,还有一个组件是服务注册中心,它是微服务架构非常重要
的一个组件,在微服务架构里主要起到了协调者的一个作用。注册中心一般包含如下几个功能:
- 服务发现:
服务注册:保存服务提供者和服务调用者的信息
服务订阅:服务调用者订阅服务提供者的信息,注册中心向订阅者推送提供者的信息 - 服务配置:
配置订阅:服务提供者和服务调用者订阅微服务相关的配置
配置下发:主动将配置推送给服务提供者和服务调用者 - 服务健康检测
检测服务提供者的健康情况,如果发现异常,执行服务剔除
3.1 nacos简介
Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您快速
实现动态服务发现、服务配置、服务元数据及流量管理。
从上面的介绍就可以看出,nacos的作用就是一个注册中心,用来管理注册上来的各个微服务。
3.2 ncaos实战入门
1.搭建nacos环境
第1步: 安装nacos
备注 :以windos系统为例子
下载地址: https://github.com/alibaba/nacos/releases
下载zip格式的安装包,然后进行解压缩操作
官方文档:https://nacos.io/zh-cn/docs/quick-start.html
第2步:修改配置
第3步: 启动nacos
第4步: 访问nacos
打开浏览器输入http://localhost:8848/nacos,即可访问服务, 默认密码是nacos/nacos
2.将所有模块注册到nacos
shop-order 模块 shop-product 模块 shop-user模块
1 在pom.xml中添加nacos的依赖
<!--nacos客户端-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
2 在主类上添加@EnableDiscoveryClient注解
3 在application.yml中添加nacos服务的地址
spring:cloud:nacos:discovery:server-addr: 127.0.0.1:8848
4 启动服务, 观察nacos的控制面板中是否有注册上来的微服务
3.修改OrderController, 实现微服务调用
修改OrderController, 实现微服务调用
DiscoveryClient是专门负责服务注册和发现的,我们可以通过它获取到注册到注册中心的所有服
务
OrderController中注入依赖
@Autowiredprivate DiscoveryClient discoveryClient;
将通过restTemplate直接调用商品微服务 ,改为通过 nacos中获取服务地址
//从nacos中获取服务地址ServiceInstance serviceInstance =discoveryClient.getInstances("service-product").get(0);String url = serviceInstance.getHost() + ":" +serviceInstance.getPort();log.info(">>从nacos中获取到的微服务地址为:" + url);
//通过restTemplate调用商品微服务Product product = restTemplate.getForObject("http://" + url + "/product/" + pid, Product.class);/*//通过restTemplate直接调用商品微服务Product product = restTemplate.getForObject("http://localhost:18082/product/" + pid, Product.class);*/
重新启动订单服务,
重启后 访问浏览器 : http://localhost:8091/order/prod/1
四:Ribbon实现负载均衡
Ribbon是Spring Cloud的一个组件, 它可以让我们使用一个注解就能轻松的搞定负载均衡
链接: Ribbon负载均衡
1.启动两个shop-product 微服务
-Dserver.port=18084
2:在RestTemplate 的生成方法上添加@LoadBalanced注解
package com.yc;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;/*** @Author yc* @PackageName springcloud-alibaba-02* @Package com.yc* @Date 2024/3/4 18:39*/
@SpringBootApplication
@EnableDiscoveryClient
public class OrderApplication {public static void main(String[] args) {SpringApplication.run(OrderApplication.class, args);}@Bean@LoadBalancedpublic RestTemplate getRestTemplate() {return new RestTemplate();}
}
3:修改服务调用的方法
package com.yc.controller;import com.alibaba.fastjson.JSON;
import com.yc.domain.Order;
import com.yc.domain.Product;
import com.yc.service.OrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;/*** @Author yc* @PackageName springcloud-alibaba-02* @Package com.yc.controller* @Date 2024/3/4 18:45*/
@RestController
@Slf4j
public class OrderController {@Autowiredprivate RestTemplate restTemplate;@Autowiredprivate OrderService orderService;@Autowiredprivate DiscoveryClient discoveryClient;//准备买1件商品@GetMapping("/order/prod/{pid}")public Order order(@PathVariable("pid") Integer pid) {log.info(">>客户下单,这时候要调用商品微服务查询商品信息");//直接使用微服务名字, 从nacos中获取服务地址String url = "service-product01";//通过restTemplate调用商品微服务Product product = restTemplate.getForObject("http://" + url + "/product/" + pid, Product.class);/*//从nacos中获取服务地址ServiceInstance serviceInstance = discoveryClient.getInstances("service-product01").get(0);String url = serviceInstance.getHost() + ":" + serviceInstance.getPort();log.info(">>从nacos中获取到的微服务地址为:" + url);//通过restTemplate调用商品微服务Product product = restTemplate.getForObject("http://" + url + "/product/" + pid, Product.class);*//*//通过restTemplate调用商品微服务Product product = restTemplate.getForObject("http://localhost:18082/product/" + pid, Product.class);*/log.info(">>商品信息,查询结果:" + JSON.toJSONString(product));Order order = new Order();order.setUid(1);order.setUsername("测试用户");order.setPid(product.getPid());order.setPname(product.getPname());order.setPprice(product.getPprice());order.setNumber(1);orderService.save(order);return order;}
}
4.通过修改配置来调整Ribbon的负载均衡策略
# ribbon 实现负载均衡
service-product: # 调用的提供者的名称ribbon:NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #随机进行负载
5.重启后 访问浏览器
多次范围跟: http://localhost:8091/order/prod/1
shop-order服务日志
shop-product01服务日志
shop-product02服务日志
五:Feign实现服务调用
Feign是Spring Cloud提供的一个声明式的伪Http客户端, 它使得调用远程服务就像调用本地服务
一样简单, 只需要创建一个接口并添加一个注解即可。
Nacos很好的兼容了Feign, Feign默认集成了 Ribbon, 所以在Nacos下使用Fegin默认就实现了负
载均衡的效果。
链接: Feign服务调用
1 加入Fegin的依赖
<!--fegin组件-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2 在主类上添加Fegin的注解
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients //开启fegin
public class OrderApplication {public static void main(String[] args) {SpringApplication.run(OrderApplication.class, args);}@Bean@LoadBalancedpublic RestTemplate getRestTemplate() {return new RestTemplate();}
}
3 创建一个service, 并使用Fegin实现微服务调用
package com.yc.service;import com.yc.domain.Product;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;/*** @Author yc* @PackageName springcloud-alibaba-02* @Package com.yc.service* @Date 2024/3/5 17:54*/
@FeignClient("service-product01")//声明调用的提供者的name
public interface ProudService {//指定调用提供者的哪个方法//@FeignClient+@GetMapping 就是一个完整的请求路径 http://serviceproduct/product/{pid}@GetMapping(value = "/product/{pid}")Product findByPid(@PathVariable("pid") Integer pid);}
4 修改controller代码,并启动验证
package com.yc.controller;import com.alibaba.fastjson.JSON;
import com.yc.domain.Order;
import com.yc.domain.Product;
import com.yc.service.OrderService;
import com.yc.service.ProudService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;/*** @Author yc* @PackageName springcloud-alibaba-02* @Package com.yc.controller* @Date 2024/3/4 18:45*/
@RestController
@Slf4j
public class OrderController {@Autowiredprivate RestTemplate restTemplate;@Autowiredprivate OrderService orderService;@Autowiredprivate DiscoveryClient discoveryClient;@Autowiredprivate ProudService productService;//准备买1件商品@GetMapping("/order/prod/{pid}")public Order order(@PathVariable("pid") Integer pid) {log.info(">>客户下单,这时候要调用商品微服务查询商品信息");//通过fegin调用商品微服务Product product = productService.findByPid(pid);/*//直接使用微服务名字, 从nacos中获取服务地址String url = "service-product01";//通过restTemplate调用商品微服务Product product = restTemplate.getForObject("http://" + url + "/product/" + pid, Product.class);*//*//从nacos中获取服务地址ServiceInstance serviceInstance = discoveryClient.getInstances("service-product01").get(0);String url = serviceInstance.getHost() + ":" + serviceInstance.getPort();log.info(">>从nacos中获取到的微服务地址为:" + url);//通过restTemplate调用商品微服务Product product = restTemplate.getForObject("http://" + url + "/product/" + pid, Product.class);*//*//通过restTemplate调用商品微服务Product product = restTemplate.getForObject("http://localhost:18082/product/" + pid, Product.class);*/log.info(">>商品信息,查询结果:" + JSON.toJSONString(product));Order order = new Order();order.setUid(1);order.setUsername("测试用户");order.setPid(product.getPid());order.setPname(product.getPname());order.setPprice(product.getPprice());order.setNumber(1);orderService.save(order);return order;}
}
5 重启order微服务, 浏览器访问查看效果
http://localhost:8091/order/prod/1
六:Sentinel–服务容错
什么是Sentinel
Sentinel (分布式系统的流量防卫兵) 是阿里开源的一套用于服务容错的综合性解决方案。它以流量
为切入点, 从流量控制、熔断降级、系统负载保护等多个维度来保护服务的稳定性。
Sentinel 具有以下特征:
丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景, 例如秒杀(即
突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用
应用等。
完备的实时监控:Sentinel 提供了实时的监控功能。通过控制台可以看到接入应用的单台机器秒
级数据, 甚至 500 台以下规模的集群的汇总运行情况。
广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块, 例如与 Spring
Cloud、Dubbo、gRPC 的整合。只需要引入相应的依赖并进行简单的配置即可快速地接入
Sentinel
完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快
速地定制逻辑。例如定制规则管理、适配动态数据源等
Sentinel 分为两个部分:
- 核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo /
Spring Cloud 等框架也有较好的支持。 - 控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等
应用容器。
链接: Sentinel 服务容错
6.1服务雪崩效应
在分布式系统中,由于网络原因或自身的原因,服务一般无法保证 100% 可用。如果一个服务出现了
问题,调用这个服务就会出现线程阻塞的情况,此时若有大量的请求涌入,就会出现多条线程阻塞等
待,进而导致服务瘫痪。
由于服务与服务之间的依赖性,故障会传播,会对整个微服务系统造成灾难性的严重后果,这就是
服务故障的 “雪崩效应” 。
雪崩发生的原因多种多样,有不合理的容量设计,或者是高并发下某一个方法响应变慢,亦或是某
台机器的资源耗尽。我们无法完全杜绝雪崩源头的发生,只有做好足够的容错,保证在一个服务发生问
题,不会影响到其它服务的正常运行。也就是"雪落而不雪崩"。
6.2 常见容错方案
要防止雪崩的扩散,我们就要做好服务的容错,容错说白了就是保护自己不被猪队友拖垮的一些措
施, 下面介绍常见的服务容错思路和组件。
常见的容错思路
常见的容错思路有隔离、超时、限流、熔断、降级这几种,下面分别介绍一下。
隔离
它是指将系统按照一定的原则划分为若干个服务模块,各个模块之间相对独立,无强依赖。当有故
障发生时,能将问题和影响隔离在某个模块内部,而不扩散风险,不波及其它模块,不影响整体的
系统服务。常见的隔离方式有:线程池隔离和信号量隔离.
超时
在上游服务调用下游服务的时候,设置一个最大响应时间,如果超过这个时间,下游未作出反应,
就断开请求,释放掉线程。
限流
限流就是限制系统的输入和输出流量已达到保护系统的目的。为了保证系统的稳固运行,一旦达到
的需要限制的阈值,就需要限制流量并采取少量措施以完成限制流量的目的。
熔断
在互联网系统中,当下游服务因访问压力过大而响应变慢或失败,上游服务为了保护系统整
体的可用性,可以暂时切断对下游服务的调用。这种牺牲局部,保全整体的措施就叫做熔断。
服务熔断一般有三种状态:
- 熔断关闭状态(Closed)
服务没有故障时,熔断器所处的状态,对调用方的调用不做任何限制 - 熔断开启状态(Open)
后续对该服务接口的调用不再经过网络,直接执行本地的fallback方法 - 半熔断状态(Half-Open)
尝试恢复服务调用,允许有限的流量调用该服务,并监控调用成功率。如果成功率达到预
期,则说明服务已恢复,进入熔断关闭状态;如果成功率仍旧很低,则重新进入熔断关闭状
态
降级
降级其实就是为服务提供一个托底方案,一旦服务无法正常调用,就使用托底方案。
6.3 集成Sentinel
1 在pom.xml中加入下面依赖
<!--Sentinel--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId></dependency>
2 编写一个Controller测试使用
package com.yc.controller;import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** @Author yc* @PackageName springcloud-alibaba-02* @Package com.yc.controller* @Date 2024/3/5 18:11*/
@RestController
@Slf4j
public class OrderControllerTest001 {@RequestMapping("/order/message1")public String message1() {return "message1";}@RequestMapping("/order/message2")public String message2() {return "message2";}}
6.4 安装Sentinel控制台
Sentinel 提供一个轻量级的控制台, 它提供机器发现、单机资源实时监控以及规则管理等功能。
1 下载jar包,解压到文件夹
https://github.com/alibaba/Sentinel/releases
2 启动控制台
监听端口 + sentinel 服务端口地址 + 项目名称 + 运行sentinel的jar包
# 直接使用jar命令启动项目(控制台本身是一个SpringBoot项目)
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -
Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.7.0.jar
3 修改 shop-order模块配置类 ,在里面加入有关控制台的配置
server:port: 8091
spring:application:name: service-order02cloud:nacos:discovery:server-addr: 192.168.93.1:8848sentinel:transport:port: 18081 #跟控制台交流的端口,随意指定一个未使用的端口即可dashboard: localhost:18080 # 指定控制台服务的地址datasource:driver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql:///hmshop02?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=trueusername: rootpassword: 123456jpa:properties:hibernate:hbm2ddl:auto: updatedialect: org.hibernate.dialect.MySQL5InnoDBDialect# ribbon 实现负载均衡
service-product: # 调用的提供者的名称ribbon:NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #随机进行负载
第4步: 通过浏览器访问 http://localhost:8091/order/message1
第5步: 通过浏览器访问localhost:18080 进入控制台 ( 默认用户名密码是 sentinel/sentinel )
补充:了解控制台的使用原理
Sentinel的控制台其实就是一个SpringBoot编写的程序。我们需要将我们的微服务程序注册到控制台上,
即在微服务中指定控制台的地址, 并且还要开启一个跟控制台传递数据的端口, 控制台也可以通过此端口
调用微服务中的监控程序获取微服务的各种信息
6.5 实现接口限流
1 通过控制台为message1添加一个流控规则
2 通过控制台快速频繁访问, 观察效果
6.6 Sentinel的概念和功能
1.基本概念
资源
资源就是Sentinel要保护的东西
资源是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容,可以是一个服务,也可以是
一个方法,甚至可以是一段代码。
我们入门案例中的message1方法就可以认为是一个资源
规则
规则就是用来定义如何进行保护资源的
作用在资源之上, 定义以什么样的方式保护资源,主要包括流量控制规则、熔断降级规则以及系统
保护规则。
我们入门案例中就是为message1资源设置了一种流控规则, 限制了进入message1的流量
2 重要功能
Sentinel的主要功能就是容错,主要体现为下面这三个:
流量控制
流量控制在网络传输中是一个常用的概念,它用于调整网络包的数据。任意时间到来的请求往往是
随机不可控的,而系统的处理能力是有限的。我们需要根据系统的处理能力对流量进行控制。
Sentinel 作为一个调配器,可以根据需要把随机的请求调整成合适的形状。
熔断降级
当检测到调用链路中某个资源出现不稳定的表现,例如请求响应时间长或异常比例升高的时候,则
对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联故障。
Sentinel 对这个问题采取了两种手段:
- 通过并发线程数进行限制
Sentinel 通过限制资源并发线程的数量,来减少不稳定资源对其它资源的影响。当某个资源
出现不稳定的情况下,例如响应时间变长,对资源的直接影响就是会造成线程数的逐步堆
积。当线程数在特定资源上堆积到一定的数量之后,对该资源的新请求就会被拒绝。堆积的
线程完成任务后才开始继续接收请求。 - 通过响应时间对资源进行降级
除了对并发线程数进行控制以外,Sentinel 还可以通过响应时间来快速降级不稳定的资源。
当依赖的资源出现响应时间过长后,所有对该资源的访问都会被直接拒绝,直到过了指定的
时间窗口之后才重新恢复。 - Sentinel 和 Hystrix 的区别
两者的原则是一致的, 都是当一个资源出现问题时, 让其快速失败, 不要波及到其它服务
但是在限制的手段上, 确采取了完全不一样的方法:
Hystrix 采用的是线程池隔离的方式, 优点是做到了资源之间的隔离, 缺点是增加了线程
切换的成本。
Sentinel 采用的是通过并发线程的数量和响应时间来对资源做限制。
系统负载保护
Sentinel 同时提供系统维度的自适应保护能力。当系统负载较高的时候,如果还持续让
请求进入可能会导致系统崩溃,无法响应。在集群环境下,会把本应这台机器承载的流量转发到其
它的机器上去。如果这个时候其它的机器也处在一个边缘状态的时候,Sentinel 提供了对应的保
护机制,让系统的入口流量和系统的负载达到一个平衡,保证系统在能力范围之内处理最多的请
求。
总之一句话: 我们需要做的事情,就是在Sentinel的资源上配置各种各样的规则,来实现各种容错的功
能
6.7 Sentinel规则
1 流控规则
流量控制,其原理是监控应用流量的QPS(每秒查询率) 或并发线程数等指标,当达到指定的阈值时
对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。
第1步: 点击簇点链路,我们就可以看到访问过的接口地址,然后点击对应的流控按钮,进入流控规则配
置页面。新增流控规则界面如下:
资源名:唯一名称,默认是请求路径,可自定义
针对来源:指定对哪个微服务进行限流,默认指default,意思是不区分来源,全部限制
阈值类型/单机阈值:
- QPS(每秒请求数量): 当调用该接口的QPS达到阈值的时候,进行限流
- 线程数:当调用该接口的线程数达到阈值的时候,进行限流
是否集群:暂不需要集群
接下来我们以QPS为例来研究限流规则的配置。
2 简单配置
我们先做一个简单配置,设置阈值类型为QPS,单机阈值为1。即每秒请求量大于3的时候开始限流。
接下来,在流控规则页面就可以看到这个配置
然后快速访问 /order/message1 接口,观察效果。此时发现,当QPS > 1的时候,服务就不能正常响
应,而是返回Blocked by Sentinel (flow limiting)结果。
3.配置流控模式
点击上面设置流控规则的编辑按钮,然后在编辑页面点击高级选项,会看到有流控模式一栏。
sentinel共有三种流控模式,分别是:
- 直接(默认):接口达到限流条件时,开启限流
- 关联:当关联的资源达到限流条件时,开启限流 [适合做应用让步]
- 链路:当从某个接口过来的资源达到限流条件时,开启限流
下面呢分别演示三种模式:
直接流控模式
直接流控模式是最简单的模式,当指定的接口达到限流条件时开启限流。上面案例使用的就是直接流控
模式。
关联流控模式
关联流控模式指的是,当指定接口关联的接口达到限流条件时,开启对指定接口开启限流。
链路流控模式
链路流控模式指的是,当从某个接口过来的资源达到限流条件时,开启限流。它的功能有点类似于针对
来源配置项,区别在于:针对来源是针对上级微服务,而链路流控是针对上级接口,也就是说它的粒度
更细
第1步: 编写一个service,在里面添加一个方法message
@Service
public class OrderServiceImplTest001 {@SentinelResource("message")public void message() {System.out.println("message");}
}
第2步: 在Controller中声明两个方法,分别调用service中的方法m
@RestController
@Slf4j
public class OrderControllerTest001 {@Autowiredprivate OrderServiceImplTest001 orderServiceImplTest001;@RequestMapping("/order/message1")public String message1() {orderServiceImplTest001.message();return "message1";}@RequestMapping("/order/message2")public String message2() {orderServiceImplTest001.message();return "message2";}}
第3步: 禁止收敛URL的入口 context
备注:如果使用的版本过低需要配置,如果是最新版本无需配置
从1.6.3 版本开始,Sentinel Web filter默认收敛所有URL的入口context,因此链路限流不生效。
1.7.0 版本开始(对应SCA的2.1.1.RELEASE),官方在CommonFilter 引入了
WEB_CONTEXT_UNIFY 参数,用于控制是否收敛context。将其配置为 false 即可根据不同的
URL 进行链路限流。
SCA 2.1.1.RELEASE之后的版本,可以通过配置spring.cloud.sentinel.web-context-unify=false即
可关闭收敛
我们当前使用的版本是SpringCloud Alibaba 2.1.0.RELEASE,无法实现链路限流。
目前官方还未发布SCA 2.1.2.RELEASE,所以我们只能使用2.1.1.RELEASE,需要写代码的形式实
现
配置文件中关闭sentinel的CommonFilter实例化
server:port: 8091
spring:application:name: service-order02cloud:nacos:discovery:server-addr: 192.168.93.1:8848sentinel:transport:port: 9999 # 跟控制台交流的端口,随意指定一个未使用的端口即可dashboard: localhost:18080 #指定控制台服务的地址filter:enabled: false
添加一个配置类,自己构建CommonFilter实例
package com.itheima.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;
@Configuration
public class FilterContextConfig {
@Bean
public FilterRegistrationBean sentinelFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new CommonFilter());
registration.addUrlPatterns("/*");
// 入口资源关闭聚合
registration.addInitParameter(CommonFilter.WEB_CONTEXT_UNIFY, "false");
registration.setName("sentinelFilter");
registration.setOrder(1);
return registration;
}
}
第4步:配置完重启服务。浏览器分别访问
http://localhost:8091/order/message1
http://localhost:8091/order/message2
第5步: 控制台配置限流规则
第6步: 分别通过 /order/message1 和 /order/message2 访问, 发现关联的接口资源被限流了
4 配置流控效果
快速失败(默认): 直接失败,抛出异常,不做任何额外的处理,是最简单的效果
Warm Up:它从开始阈值到最大QPS阈值会有一个缓冲阶段,一开始的阈值是最大QPS阈值的
1/3,然后慢慢增长,直到最大阈值,适用于将突然增大的流量转换为缓步增长的场景。
排队等待:让请求以均匀的速度通过,单机阈值为每秒通过数量,其余的排队等待; 它还会让设
置一个超时时间,当请求超过超时间时间还未处理,则会被丢弃。