SpringCloud-Alibaba组件使用
SpringCloud-Alibaba组件使用
1、Nacos
1)服务注册与发现
-
pom文件: spring-cloud-starter-alibaba-nacos-discovery
-
在主启动类上添加: @EnableDiscoveryClient 注解
-
在配置文件中
-
需要给每个微服务一个 application.name
-
配置注册地址和端口: !!!ip要和port写在一起! 大坑
nacos: discovery: server-addr: ip:port
-
-
配合 openFeign 远程调用
- 在公共pom文件加入:spring-cloud-starter-openfeign
- 在消费者服务中创建 openFeign接口,添加 @FeignClient(value = "被调用服务名"),接口中的方法需要与消费者中的controller中的方法签名一致
- 在消费者的服务的主启动加上: @EnableFeignClients(basePackages = "feign接口包名")
2)配置中心
一、基本配置
-
公共pom文件: spring-cloud-starter-alibaba-nacos-config
-
配置文件中创建bootstrap.yml:
-
应用名和配置中心地址:
spring: application: name: 应用名 cloud: nacos: config: server-addr: ip:port
-
-
在配置中心中创建配置文件:${应用名}-${spring.profile.active}.${后缀名}
-
在controller方法上:@RefreshScope
-
在方法中取配置中心中的对应配置文件中的k-v;当配置中心中的配置文件名和项目中的配置文件名相同时,优先使用配置中心里面的
-
在控制台看到:Located property source: [BootstrapPropertySource {name='bootstrapProperties-gulimall-coupon.properties'}]这句话时才成功!
二、细节
-
命名空间--配置隔离
-
默认:public(保留空间);默认新添加的所有配置都在这里面
-
基于环境:可以自己添加命名空间,写上配置,然后在 bootstrap.yaml 中声明使用哪个命名空间
nacos.config.namespace: 这里写的是命名空间的唯一ID
-
基于微服务名:每一个微服务之间相互隔离,可以给每一个微服务都创建自己的命名空间
-
-
配置集--所有的配置集合
-
配置集ID--类似于配置文件名
- Data ID:文件名
-
配置分组:
- 默认所有的配置集都属于:DEFAULT_GROUP
- 创建配置文件时可以自定义分组
- 在bootstrap.yaml中:nacos.config.group: xxxx分组名
每个微服务创建自己的命名空间,使用配置分组区分环境,dev、test、prod
可以将application.yml中的各个配置文件抽取出来放在配置中心中,如:
- 数据源配置:datasource.yaml
- mybatis配置:mybatis.yaml
- 其他的配置:other.yaml
之后在 bootstrap.yaml 中,使用命名空间 namespace 和 group 来指定配置文件,并用 ext-config 来聚合分散的配置文件,如:
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848
namespace: b20ea011-09da-4e00-b72f-65ce52f792ab # 指定命名空间
group: prod # 指定工程中需要用的配置文件的分组
ext-config: # 是一个 list
- data-id: datasource.yaml # 指定配置文件
group: dev # 分组
refresh: true #动态刷新
- data-id: mybatis.yaml
group: dev
refresh: true
- data-id: other.yaml
group: dev
refresh: true
1、微服务的任何配置信息,任何配置文件都可以将放在配置中心里面
2、只需要在 bootstrap.yaml中说明加载配置中心中哪些配置文件即可
3、@Value @Configuration 等注解,优先会从配置文件中读取
2、Gateway
服务网关,所有的请求都从这里过滤,并转发到相应的服务中
1)、使用:
- 导入pom: spring-cloud-starter-gateway
- 添加到nacos--在启动类上添加: @EnableDiscoveryClient,注意将数据源配置排除
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class) - 在bootstrap.yml中配置 nacos地址、项目名称、命名空间等
- 在 application.yml 中配置端口和gateway
server:
port: 88
spring:
cloud:
### 网关过滤拦截配置
gateway:
routes: # 是一个数组,可以配置多个路由规则
- id: baidu_route # 网关的唯一 id
uri: https://www.baidu.com # 断言为 true 后跳转哪个地址
predicates: # 是一个数组,可以配置多个断言规则
- Query=url,baidu # 如果请求路径上带了 url ,且其值为 baidu 就断言成功,跳转指定路径
- id: qq_route
uri: https://www.qq.com
predicates:
- Query=url,qq
# 前端请求配置网关
- id: admin_route
# lb://uri 负载均衡
uri: lb://renren-fast
# 所有的带/api/ 的路径都会经过网关
predicates:
- Path=/api/**
# 将其根据规则转换成对应的地址
filters:
- RewritePath=/api/(?<segment>.*),/renren-fast/$\{segment}
#前端项目中的请求地址发送不到对应的服务器,需要转换地址
## 前端项目 : /api
## 原本访问路径:http://localhost:88/api/captcha.jpg
## 需要转成: http://localhost:8080/renren-fast/captcha.jpg
## 需要使用路径重写
2)、跨域
-
是什么
跨域:指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对 javascript施加的安全限制。
同源策略:是指协议,域名,端口都要相同,其中有一个不同都会产生跨域
例如:
![image-20200517133832754]()
-
跨域流程
![image-20200517134028017]()
-
解决
方法一、使用 nginx部署为同一域
![image-20200517134341175]()
方法二、配置当次请求允许跨域
添加响应头
· Access- Control--Alow- Origin:支持哪些来源的请求跨域
· Access- Control- Allow- Methods:支持哪些方法跨域
·Access- Control-Allw- Credentials:跨域请求默认不包含 cookie,设置为true可以包含 cookie
·Access- Control- Expose- Headers:跨堿请求暴露的字段
·CORS请求时, XmlhTtp Request对象的 getResponseHeader()方法只能拿到6个基本字段: Cache- Control、 Content- Language、 Content-Type、 Expires、Last- Modified、 Pragma。如果想拿到其他字段,就必须在 Access- Control- Expose- Headers里面指定。
·Access- Contro-Max-Age:表明该响应的有效时间为多少秒。在有效时间内,浏览器无须为同一请
求再次发起预检请求。请注意,浏览器自身维护了一个最大有效时间,如果该首部字段的值超过了最
大有效时间,将不会生效。在gateway微服务中添加相应的config配置类
/** * @ClassName GulimallCorsConfiguration * @Description: 跨域配置 **/ @Configuration public class GulimallCorsConfiguration { @Bean public CorsWebFilter corsWebFilter() { // 基于URL路径跨域配置 UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); CorsConfiguration corsConfiguration = new CorsConfiguration(); // 1、配置跨域 // 允许那些头进行跨域 corsConfiguration.addAllowedHeader("*"); // 允许那些请求方式进行跨域 corsConfiguration.addAllowedMethod("*"); // 允许那些请求来源进行跨域 corsConfiguration.addAllowedOrigin("*"); // 允许携带cookie进行跨域 corsConfiguration.setAllowCredentials(true); source.registerCorsConfiguration("/**",corsConfiguration); // reactive包下的 return new CorsWebFilter(source); } }
3、Alibaba-OSS
1)、简介
对象存储服务( Object Storage Service,oss)是一种海量、安全、低成本、高可靠的云存储服务,适合存放任意类型的这件。容量和处理能力弹性扩展,多种存储类型供选择,全面优化存储成本。
2)、原生OSS服务使用
-
开通阿里云存储
![image-20200518180948164]()
-
服务端签名后直传

具体操作:https://help.aliyun.com/document_detail/84781.html?spm=a2c4g.11186623.6.792.4d8e6328IcApUx
-
在阿里云中使用子账户创建 AccessKey (鼠标移动到人头像,可以看到AccessKey管理)
-
给创建的用户分配权限:AliyunOSSFullAccess完全权限
-
导入maven依赖
<dependency> <groupId>com.aliyun.oss</groupId> <artifactId>aliyun-sdk-oss</artifactId> <version>3.8.0</version> </dependency> -
上传文件流
// Endpoint以杭州为例,其它Region请按实际情况填写。 String endpoint = "http://oss-cn-hangzhou.aliyuncs.com"; // 云账号AccessKey有所有API访问权限,建议遵循阿里云安全最佳实践,创建并使用RAM子账号进行API访问或日常运维,请登录 https://ram.console.aliyun.com 创建。 String accessKeyId = "<yourAccessKeyId>"; String accessKeySecret = "<yourAccessKeySecret>"; // 创建OSSClient实例。 OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret); // 上传文件流。 InputStream inputStream = new FileInputStream("<yourlocalFile>"); ossClient.putObject("<yourBucketName>", "<yourObjectName>", inputStream); // 关闭OSSClient。 ossClient.shutdown();
3)、使用Alibaba-OSS
-
导入maven
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alicloud-oss</artifactId> <version>2.2.0.RELEASE</version> </dependency> -
配置文件中配置
alicloud: access-key: accessKeyId secret-key: accessKeySecret oss: endpoint: oss-cn-hangzhou.aliyuncs.com bucket: bucketName -
使用OSS相关操作
@Resource OSSClient ossClient; @Test void testUpload() throws FileNotFoundException { // 上传文件流。 InputStream inputStream = new FileInputStream("E:\\笔记\\学习笔记\\studyNotes\\springcloud\\upload\\image-20200515142518168.png"); ossClient.putObject("gulimall-plusjun", "image-20200515142518168.png", inputStream); System.out.println("上传完成"); // 关闭OSSClient。 ossClient.shutdown(); } -
跨域设置,将post请求都设置能跨域
-
controller
@RestController public class OssController { @Resource private OSSClient ossClient; @Value("${oss.config.name}") String name; @Value("${spring.cloud.alicloud.oss.endpoint}") private String endpoint; @Value("${spring.cloud.alicloud.oss.bucket}") private String bucket; @Value("${spring.cloud.alicloud.access-key}") private String accessId; @RequestMapping("/oss/policy") public R policy() { //https://gulimall-hello.oss-cn-beijing.aliyuncs.com/hahaha.jpg String host = "https://" + bucket + "." + endpoint; // host的格式为 bucketname.endpoint // callbackUrl为 上传回调服务器的URL,请将下面的IP和Port配置为您自己的真实信息。 // String callbackUrl = "http://88.88.88.88:8888"; String format = new SimpleDateFormat("yyyy-MM-dd").format(new Date()); String dir = format + "/"; // 用户上传文件时指定的前缀。 Map<String, String> respMap = null; try { long expireTime = 30; long expireEndTime = System.currentTimeMillis() + expireTime * 1000; Date expiration = new Date(expireEndTime); PolicyConditions policyConds = new PolicyConditions(); policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000); policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir); String postPolicy = ossClient.generatePostPolicy(expiration, policyConds); byte[] binaryData = postPolicy.getBytes("utf-8"); String encodedPolicy = BinaryUtil.toBase64String(binaryData); String postSignature = ossClient.calculatePostSignature(postPolicy); respMap = new LinkedHashMap<String, String>(); respMap.put("accessid", accessId); respMap.put("policy", encodedPolicy); respMap.put("signature", postSignature); respMap.put("dir", dir); respMap.put("host", host); respMap.put("expire", String.valueOf(expireEndTime / 1000)); // respMap.put("expire", formatISO8601Date(expiration)); } catch (Exception e) { // Assert.fail(e.getMessage()); System.out.println(e.getMessage()); } return R.ok().put("data",respMap); } } -
前端
修改上传地址为自己的 oss 外网访问地址
4、OpenFeign
1、使用
-
pon.xml
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> -
在启动类上添加注解:@EnableFeignClients(basePackages = "feign接口包名")
-
创建接口
@FeignClient("远程服务名") public interface XxxFeignService {}
2、Feign远程调用丢失请求头
-
问题
feign构造请求发送给远程服务时,没有带上请求头,就没有cookie和ssion
![image-20200703195712206]()
-
解决
在Feign创建请求后,加入feign的拦截器,
![image-20200704151409483]()
@Configuration public class GuliFeignConfig { @Bean("requestInterceptor") public RequestInterceptor requestInterceptor(){ return new RequestInterceptor() { @Override public void apply(RequestTemplate requestTemplate) { // 1、RequestContextHolder拿到刚进来的这个请求 ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); if(attributes != null) { // 获取到老请求中的Cookie HttpServletRequest request = attributes.getRequest(); String cookie = request.getHeader("Cookie"); // 2、同步请求头信息:cookie // 为新请求设置新cookie,同步老请求cookie requestTemplate.header("Cookie", cookie); } } }; } }
3、Feign异步情况丢失上下文
-
问题
开启异步调用之后,线程中的请求上下文不一致
![image-20200704151443528]()
-
解决
将主线程的上下文设置到异步线程
1、在主线程中获取 requestAttributes:
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
2、在异步线程分别中设置 requestAttributes
RequestContextHolder.setRequestAttributes(requestAttributes);
5、Seata
1、使用
-
安装seata
- 配置
- conf/regisry.conf
- regisry -> type="nacos"
- conf/file.conf
- nacos -> serverAddr = "101.200.53.195"
- conf/regisry.conf
- 配置
-
在数据库中建立undo_log表
CREATE TABLE `undo_log` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `branch_id` bigint(20) NOT NULL, `xid` varchar(100) NOT NULL, `context` varchar(128) NOT NULL, `rollback_info` longblob NOT NULL, `log_status` int(11) NOT NULL, `log_created` datetime NOT NULL, `log_modified` datetime NOT NULL, `ext` varchar(100) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -
导入依赖
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-seata</artifactId> </dependency> -
在需要分布式事务的业务方法上标注注解
- @GlobalTransactional
-
所有想要用到分布式事务的微服务,都需要使用seata DataSourceProxy 来代理自己的数据源
@Configuration public class MySeataConfig { @Resource DataSourceProperties dataSourceProperties; @Bean public DataSource dataSource(DataSourceProperties properties) { HikariDataSource dataSource = dataSourceProperties.initializeDataSourceBuilder() .type(HikariDataSource.class) .build(); if (StringUtils.hasText(properties.getName())) { dataSource.setPoolName(properties.getName()); } return new DataSourceProxy(dataSource); } } -
每个微服务都需要导入 seata中的registry.conf file.conf
- file.conf:
- vgroup_mapping.微服务应用名-fescar-service-group = "default"
- file.conf:
6、Sentinel
1、简介
一、熔断、降级、限流
-
熔断
- A服务调用B服务的某个功能,由于网络不稳定问题,或者B服务卡机,导致功能时间超长。如果这样子的次数太多。我们就可以直接将B断路了(A不再请求B接口),凡是调用B的直接返回降级数据,不必等待B的超长执行。这样B的故障问题,就不会级联影响到A。
-
降级
- 整个网站处于流量高峰期,服务器压力剧增,根据当前业务情况及流量,对一些服务和页面进行有策略的降级[停止服务,所有的调用直接返回降级数据]。以此缓解服务器资源的的压力,以保证核心业务的正常运行,同时也保持了客户和大部分客户的得到正确的相应。
-
熔断降级的异同
- 相同点
- 为了保证集群大部分服务的可用性和可靠性,防止崩溃,牺牲小我
- 用户最终都是体验到某个功能不可用
- 不同点
- 熔断是被调用方故障,触发的系统主动规则
- 降级是基于全局考虑,停止一些正常服务,释放资源
- 相同点
-
限流
- 对打入服务的请求流量进行控制,是服务能够承担不超过自己能力的流量压力
二、Sentinel简介
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
2、Hystrix 与 Sentinel比较
| 功能 | Sentinel | Hystrix |
|---|---|---|
| 隔离策略 | 信号量隔离(并发线程数限流) | 线程池隔离/信号量隔离 |
| 启断降级策略 | 基于响应时间、异常比率、异常数 | 基于异常比率 |
| 实时统计实现 | 滑动窗口(LeapArray) | 滑动窗口(基于RxJava) |
| 动态规则配置 | 支持多种数据源 | 支持多种数据源 |
| 扩展性 | 多个扩展点 | 插件形式 |
| 基于注解的支持 | 支持 | 支持 |
| 限流 | 基于QPS,支持基于调用关系的限流 | 有限的支持 |
| 流量整形 | 支持预热模式、匀速器模式、预热排队模式 | 不支持 |
| 系统自适应保护 | 支持 | 不支持 |
| 控制台 | 可配置规则、查看秒级监控、机器发现等 | 简单的监控查看 |
3、整合Feign + Sentinel
-
pom.xml
-
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> <version>2.1.1.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-actuator</artifactId> </dependency>
-
-
下载 com.alibaba.csp 对应的dashboard 这里对应 1.7的dashboard
-
配置控制台信息
-
management.endpoints.exposure.include=* spring.cloud.sentinel.transport.dashboard=localhost:8080
-
1、自定义流控返回数据
-
@Configuration public class SeckillSentinelConfig { public SeckillSentinelConfig() { /** * 自定义错误页面 */ WebCallbackManager.setUrlBlockHandler(new UrlBlockHandler() { @Override public void blocked(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws IOException { httpServletResponse.setCharacterEncoding("utf-8"); R r = R.error(BizCodeEnum.TOO_MANY_REQYEST.getCode(), BizCodeEnum.TOO_MANY_REQYEST.getMsg()); httpServletResponse.getWriter().write(JSON.toJSONString(r)); } }); } }
2、熔断保护
-
调用方开启熔断保护:feign.sentinel.enabled=true
-
再feignService中指定熔断保护:
- @FeignClient(value = "gulimall-product",fallback = ProductFeignServiceFallBack.class)
-
熔断保护方法:(需要实现feignService接口)
-
@Component public class ProductFeignServiceFallBack implements ProductFeignService { @Override public R getSkuInfos(List<Long> skuIds) { return R.error(BizCodeEnum.TOO_MANY_REQYEST.getCode(),BizCodeEnum.TOO_MANY_REQYEST.getMsg()); } } -
调用方手动指定远程服务的降级策略:远程服务被降级处理。触发熔断回调方法
-
超大浏览的时候,必须牺牲一些远程服务。在服务的提供方(远程方)指定降级策略;提供方是在运行。但是不运行自己的业务逻辑。返回的是默认的降级数据(限流的数据)
-
3、自定义受保护的资源
-
基于代码
-
try(Entry entry = SphU.entry("资源名")) { // 业务逻辑 } catch(Exception e) { // ... } -
在控制页面自定义流控规则,资源名需要与代码中的一致
-
-
基于注解
- 在任何想要控制的方法上标注注解 : @SentinelResource(value = "资源名",blockHandler="方法名")
- 指定的阻塞方法后,在本类中写一个与原方法一样的 阻塞调用方法,参数可以接收 BlockException
4、网关限流
-
pom:
-
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId> <version>2.1.1.RELEASE</version> </dependency>
-
-
网关流控的资源名可以为网关中配置的路由名字
4、整合Sentinel测试限流
7、Sleuth + Zipkin
一、为什么用
- 微服务架构是一个分布式架构,它按业务划分服务单元,一个分布式系统往往有很多个服务单元。由于服务单元数量众多,业务的复杂性,如果出现了错误和异常,很难去定位。主要体现在,一个请求可能需要调用很多个服务,而内部服务的调用复杂性,决定了问题难以定位。所以微服务架构中,必须实现分布式链路追踪,去跟进一个请求到底有哪些服务参与,参与的顺序又是怎样的,从而达到每个请求的步骤清晰可见,出了问题,很快定位。链路追踪组件有Google的Dapper,Twitter的Zipkin,以及阿里的Eagleeye(鹰眼)等,它们都是非常优秀的链路追踪开源组件。
- 链路追踪组件有Google的Dapper,Twitter的Zipkin,以及阿里的Eagleeye(鹰眼)等,它们都是非常优秀的链路追踪开源组件。
二、基本术语
- Span(跨度):基本工作单元,发送一个远程调度任务就会产生一个Span,Span是一个64位TD唯一标识的,Trace是用另一个64位ID唯一标识的,Span还有其他数据信息,比如摘要、时间截事件、Span的ID、以及进度ID。
- Trace(跟踪):一系列Span组成的一个树状结构。请求一个微服务系统的API接口,这个API接口,需要调用多个微服务,调用每个微服务都会产生一个新的Span,所有由这个请求产生的Span组成了这个Trace。
- Annotation(标注):用来及时记录一个事件的,一些核心注解用来定义一个请求的开始和结束。这些注解包括以下:
cs-Client Sent客户端发送一个请求,这个注解描述了这个Span的开始- sr - Server Received-服务端获得请求并准备开始处理它,如果将其sr减去cs时间戳便可得到网络传输的时间。
- ss - Server Sent(服务端发送响应)-该注解表明请求处理的完成(当请求返回客户端),如果ss的时间戳减去sr时间戳,就可以得到服务器请求的时间。
- cr - Client Received(客户端接收响应)-此时Span的结束,如果cr的时间戳减去cs时间戳便可以得到整个请求所消耗的时间。
三、整合 Sleuth
-
服务提供者导入依赖
-
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-sleuth</artifactId> </dependency>
-
-
打开debug日志
-
logging: level: org.springframework.cloud.openfeign: debug org.springframework.cloud.sleuth: debug
-
-
发起一次远程调用,观察控制台








浙公网安备 33010602011771号