最全SpringCloud学习笔记

SpringCloud

一、服务注册与发现
Eureka(AP)
  • 服务注册相对要快,因为不需要等注册信息replicate到其他节点,也不保证注册信息是否replicate成功;
  • 当数据出现不一致时,虽然A,B上的注册信息不完全相同,但每个Eureka节点依然能够正常对外提供服务,这回出现查询服务信息时如果请求A查不到,但请求B就能查到。如此保证了可用性但牺牲了一致性。
Nacos
/**
 * 1-如何使用Nacos作为配置中心统一管理配置
 *      1.依赖
 *          <dependency> cloud</grouId>
 *              <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
 *          </dependency>
 *      2.创建一个bootstrap.properties:
 *          spring.application.name=gulimall-coupon
 *          spring.cloud.nacos.config.server-addr=127.0.0.1:8848
 *      3.需要给配置中心默认添加一个(Data Id/数据集)gulimall-coupon.properties(应用名.properties)
 *      4.动态获取配置
 *              @RefreshScope:动态获取并刷新配置
 *              @Value("${配置项的名}"):获取到配置
 *              如果nacos配置中心和本地的配置文件中都配置了相同的项--优先使用nacos配置中心的配置
 * 2-细节
 *      1.命名空间:配置隔离:
 *          默认:public(保留空间):默认新增的所有配置都在public空间;
 *          1.开发、测试、生产:利用命名空间实现环境隔离
 *            注意:在bootstrap.properties配置上 需要在哪个命名空间下的配置
 *            spring.cloud.nacos.config.namespace=XXXXXXXXXXXXXXXXXXXXXXXXXXX(配置管理ID)
 *          2.每一个微服务之间互相隔离配置,每一个微服务都创建自己的命名空间,只加载自己命名空间下的所有配置
 *      2.配置集:所有的配置的集合
 *      3.配置集ID:
 *          Data ID--类似文件名
 *      4.配置分组:
 *          默认所有的配置集都属于:DEFAULT_GROUP;----1111,618,1212
 *          配置配置分组:spring.cloud.nacos.config.group=1111---配置分组
 *
 * 每个微服务创建自己的命名空间,使用配置分组区分环境-dev\test\prod
 * 3-同时加载多个配置集
 *  1.微服务任何配置信息,在任何配置文件都可以放在配置中心中
 *  2.只需要在bootstrap.properties说明加载配置中心中哪些配置文件即可
 *  3.@Value、@ConfigurationProperties
 *  以前SpringBoot任何方法从配置文件中获取值,都能使用
 *  配置中心有的优先使用配置中心的配置
 */
Conul(CP)
  • 服务注册相比Eureka会稍慢一些,因为Consul的reft协议要求必须过半数的节点都写入成功才认为注册成功;
  • Leader过掉时,重新选举期间整个Consul不可用。保证了强一致性但牺牲可可用性。
##以开发者模式启动consul/localhost:8500
consul agent -dev -client=0.0.0.0
<!--springcloud提供的集于consul的服务发现-->
<dependency>
	<groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-consul</artifactId>
</dependency>
<!--actuator的健康检查-->
<dependency>
	<groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-actuator</artifactId>
</dependency>
#consul服务注册配置
cloud:
	consul:
        host: 127.0.0.1 #consul的主机地址
        post: 8500	#服务器的端口号
        discovery:
            #是否需要注册
            register: true
            #注册的实例ID(唯一标志)
            instance-id: ${spring.application.name}-1
            #服务的名称
            service-name: ${spring.application.name}
            #服务的请求端口
            port: ${server.port}
            #指定开启ip地址注册
            prefer-ip-address: true
            #当前服务的请求ip
            ip-address: ${spring.cloud.client.ip-addrss}
Zookeeper

二、远程调用
RestTemplate
Feign
  • Feign是Netfix开发的声明式,模板化的HTTP客户端
  • Feign自带Ribbon负载均衡(可以Ribbon的方式修改负载均衡策略)
1.导入依赖
<!--SpringCloud整合的openFeign-->
<dependency>
	<groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2.配置调用接口
/**
 * Created with IntelliJ IDEA
 * User: GHYANG
 * Date: 2020-06-13
 * Description:声明需要调用的微服务名称
 */
@FeignClient(name="service-product")
public interface ProductFeignClient{
    /**
    *配置需要调用的微服务接口(直接复制controller头部信息)
    */
    @RequestMapping(value="/product/{id}",method=RequestMethod.GET)
    public Product findById(@PathVariable("id") Long id);
}
3.在启动类上激活Feign

@EnableFeignClients

4.通过自动的接口调用远程微服务
//注入feign实例
@Autowired
ProductFeignClient productFeignClient;
//在方法体里调用
productFeignClient.findById();
5.其它配置
#配置fegin日志的输出  
#NONE: 不输出日志
#BASIC: 适用于生产环境追踪问题
#HEADERS: 在BASIC的基础上,记录请求和相应头的信息
#FULL: 记录所有
feign:
	client:
		config:
			service-product: #需要调用的服务名称
				loggerLevel: FULL

三、缓存机制
Redis+Reidsson
  • 缓存穿透: 空结果缓存

  • 缓存雪崩:设置随机过期时间

  • 缓存击穿:添加锁

    //只要是同一把锁,就能锁住需要这个锁的所有线程
    //1-synchroized(this) :SpringBoot所有的组件在容器中都是单例的
    // 1–得到锁以后,我们应该再去缓存中确定一次,如果没有才需要继续查询
    // 2–本地锁:synchaonized,JUC(Lock),在分布式情况下,想要锁住所有,必须使用分布锁,本地锁每个服务都会执行一次业务;

//使用map模拟本地缓存
private Map<String,Object> cache = new HashMap<>();
//模拟方式
Map<String, List<Catelog2Vo>> catalogJson = (Map<String, List<Catelog2Vo>>) cache.get("catalogJson");
if(cache.get("catalogJson") == null){
    //缓存为空
   	//调用业务,并存入缓存
    cache.put("catalogJson",parent_cid);
}
	return catalogJson;
//使用Redisson
	@ResponseBody
    @GetMapping("/hello")
    public String hello(){
        //1-获取一把锁 只要锁的名字相同 就是同一把锁 从而实现分布式
        RLock my_lock = redisson.getLock("my_lock");
        //2-加锁
        my_lock.lock();//阻塞式等待,默认加的锁都是30s时间
        //2--锁的自动续期,如果业务超长,运行期间自动给锁续上新的30s,无需担心业务时间长,锁自动过期被删掉
        //3--加锁的业务只要运行完成,就不会给当前锁续期,及时不手动解锁,锁默认在30s以后自动删除
//        my_lock.lock(10, TimeUnit.SECONDS); //10秒自动解锁,自动解锁时间需大于业务的执行时间
        //问题:my_lock.lock(10, TimeUnit.SECONDS:在锁时间到了以后,不会自动续期
        /**
         * 1-如果我们传递了锁的超时时间,就发送给redis执行脚本,进行占锁,默认超时就是我们指定的时间
         * 2-如果我们未指定锁的超时时间,就使用30 * 1000{lockWatchdogTimeout看门狗的默认时间}
         *  只要占锁成功,就会启动一个定时任务{重新给锁设置过期时间,新的过期时间就是看门狗的默认时间}
         *  internalLockLeaseTime{看门狗时间}/3--->10s 没隔10s自动再次续期,续成30s
         *
         *  最佳实战:
         *  使用my_lock.lock(10, TimeUnit.SECONDS);省掉了整个续期操作,手动解锁,不使用看门狗机制
         */
        try {
            System.out.println("加锁成功-执行业务"+Thread.currentThread().getId());
            Thread.sleep(20000);
        }catch (Exception e){

        }finally {
            //3-解锁   假设解锁代码没有运行 redisson会不会出现死锁
            System.out.println("释放锁"+Thread.currentThread().getId());
            my_lock.unlock();
        }
        return "hello";
    }
  • redis读写锁:

  • 读写锁–保证一定读到最新数据,修改期间,写锁是一个排他锁(互斥锁、独享锁),读锁是一个共享锁

  • 写锁没释放就必须等待
  • 读锁 + 读锁:相当于无锁,并发读,只会在redis中记录好,所有当前的读锁,他们都会同时加锁成功
  • 写锁 + 读锁:等待写锁释放
  • 写锁 + 写锁:阻塞方式
  • 读锁 + 写锁:有读锁。写也必须等待
  • 只要有写的存在,都必须等待

四、负载均衡
Ribbon
1.服务调用
  • eureka内部集成了ribbon(SpirngCloud同样为Consul内部集成了ribbon 用法相同)
  • 在创建RestTemplate的时候,声明@LoadBalanced
  • 使用restTemplate调用远程服务:不需要拼接微服务的URL,以待请求的服务名替换IP地址
2.负载均衡

负载均衡策略:轮询(默认)、随机、重试、权重

3.重试机制

引入spring的重试依赖

<dependency>
	<groupId>org.springframework.retry</groupId>
    <artfactId>spring-retry</artfactId>
</dependency>

对ribbon进行重试配置

service-product:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
    ConnectTimeout: 250 # Ribbon的连接超时时间
    ReadTimeout: 1000 # Ribbon的数据读取超时时间
    OkToRetryOnAllOperations: true # 是否对所有操作都进行重试
    MaxAutoRetriesNextServer: 1 # 切换实例的重试次数
    MaxAutoRetries: 1 # 对当前实例的重试次数
Nginx F5
  • nginx是一个高性能的HTTP和反向代理服务器,被很多网站应用为HTTP软负载均衡器,nginx同样可以作为静态资源服务器
  • 负载均衡算法:轮询、权重、ip绑定

#轮询配置
upstream product{
	service 127.0.0.1:8002; #服务器一
	service 127.0.0.1:8081; #服务器二
}

#扩展:以下为静态资源配置
server{
	listen 80; #监听的端口号
	server_name gulimall.com #监听的域名
	
	location /static/{ #匹配的url
		root /usr/share/nginx/html; #匹配成功访问的实际资源路径
	}
}

#扩展:以下为反向代理
server{
	listen 80; #监听8080端口
	server_name gulimall.com #监听的域名
	
	location /{ #拦截监听到的请求
		proxy_pass http://gulimall; #请求转发到系统网关 其中gulimall与下方upstream中的server相同
	}
}
#upstream xxx:表示负载均衡服务器 即为上游服务器	
upstream gulimall{
	server 192.168.252.1:88 #实际请求的路径
}
#权重配置
#upstream xxx:表示负载均衡服务器 即为上游服务器	
upstream gulimall{
	server 192.168.252.1:88 weight=2; #实际网关及权重值
	server 192.168.252.1:89 weight=3; #实际网关2及权重值
}
#ip绑定配置
#upstream xxx:表示负载均衡服务器 即为上游服务器	
#匹配原理:根据获取客户端的ip地址,通过哈希函数计算得到一个数值,用该数值对服务器列表的大小进行取模运算,得到的结果便是客服端要访问服务器的序号,采用源地址哈希法进行负载均衡,同一IP地址的客户端,当后端服务器列表不变时,其每次都会映射到用一台后端服务器进行访问。
upstream gulimall{
	server 192.168.252.1:88; #实际网关
	server 192.168.252.1:89; #实际网关2
	ip_hash;
}
  • F5负载均衡器是应用交付网络的全球领导者

五、服务熔断
  • 雪崩效应:服务与服务之间依赖性强,故障会传播,造成连锁反应,会对整个微服务系统造成灾难性的严重后果。

  • 服务隔离:指将系统按照一定的原则划分为若干个服务模块,各个模块之间相对独立,无强依赖性。

  • 熔断降级:当下游服务因访问压力过大而相应变慢或失败,上游服务为了保护系统的可用性,可用暂时切断下游服务的调用。

  • 服务限流:限流可以认为服务降级的一种,限流就是限制系统的输入和输出流量以达到保护系统的目的。如:推迟解决、拒绝解决、部分拒绝解决。

Hystrix
1.简介

其是由Netfix开源的一个推迟和容错库,用于隔离访问远程系统、服务或者第三方库,防止级联失败,从而提升系统的可用性与容错性。

2.实现延迟和容错

包裹请求、跳闸机制、资源隔离、监控、回退机制、自我修复

3.引入Hystrix依赖(feign中已经继承了Hystrix)
<dependency>
	<groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netfix-hystrix
    </artifactId>
</dependency>
4.配置开启Hystrix
feign:
	hystrix:
		enabled: true
5.自定义一个接口降级逻辑
/**
 * Created with IntelliJ IDEA
 * User: GHYANG
 * Date: 2020-06-13
 * Description:
 */
@Component
public class ProductFeignClientCallBack implements PeoductFeignClient{
    /**
     * 熔断降级方法
     * @param id
     * @return
     */
    @Override
    public Product findById(Long id) {
        System.out.println("feign调用触发熔断降级方法");
        return null;
    }
}
6.在接口添加降级方法支持
@FeignClient(name = "service-product",fallback = ProductFeignClientCallBack.class)
7.配置超时时间
hystrix:
	command:
		default:
			execution:
				isolation:
					thread:
						timeoutInmilliseconds: 3000
#默认的连接超时时间1秒,若1秒没有返回数据,自动触发降级逻辑
8.使用监控功能
<!--引入hystrix的监控依赖-->
<dependency>
	<groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netfix-hystrix</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netfix-hystrix-dashboard</artifactId>
</dependency>
#1--暴露所有actuator监控的端点
management:
	endpoints:
		web:
			exposure:
				include: '*'
#2--在页面上访问localhost:端口/actuator/hystrix.stream
#3--在启动类上激活web监控平台@EnableHystrixDashboard
Alibaba Sentinel
  • 丰富的应用场景、完备的实时监控、广泛的开源生态、完善的SPI扩展点
1.管理控制台
 java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar xxxxxx.jar 
#localhost:8080 sentiel--sentiel
2.将服务交给控制台管理
<!--父工程引入Alibaba实现的SpringCloud-->
<dependency>
	<grupId>com.alibaba.cloud</grupId>
    <artifactId>spring-cloud-alibaba-dependecies</artifactId>
    <version>2.1.0.RELEASE</ version>
    <type>pom</type>
    <scope>pom</scope>
</dependency>

<!--子工程引入sentinel-->
<dependency>
	<grupId>com.alibaba.cloud</grupId>
    <artifactId>spring-cloud-starter-sentinel</artifactId>
</dependency>
spring:
	cloud:
		sentinel:
			transport:
				dashboard: localhost:8080 
#sentinel控制台的请求地址
3.定义降级逻辑
/**
*	blockHandler:声明熔断时调用的降级方法
*	fallback:声明异常执行时执行的降级方法
*	value:自定义的资源名称(默认为当前全类名.方法名)
*	具体熔断配置需要在sentunel控制台上进行实时配置
*/
@SentinelResource(blockHandler = "orderBlockHandler",fallback = "orderFallback")


4.加载本地配置
spring:
	cloud:
		sentinel:
			datasource:
				ds1:
					file:
						file: classpath: XXXX.json #本地的xxx.json文件
						data-type:	json
						rule-type:	flow
//本地json文件配置
{
    {
    	"resource":"orderFindById",
    	"controlBahavior":0,
    	"count":1,
    	"grade":1,
    	"limitApp":"default",
    	"strategy":0
	}
}

六、服务网关
Zuul 1.0 网关
1.介绍
2.搭建Zuul网关服务器
<dependency>
	<groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netfix-eureka-client</artifactId>
</dependency>
3.路由
##方法一:路由配置--直接配置
zuul:
	routes:
		##配置商品服务映射
		product-service: XXX #路由id,随便写
			path: /product-service/** #映射路径
			url: http://127.0.0.1:9001 #映射路径对应的实际微服务url地址
##方法二:路由配置--根据服务进行配置
#该方法需要配合eureka注册中心配置
#其中serviceId是注册到注册中心的服务名称
zuul:
	routes:
		##配置商品服务映射
		product-service: XXX #路由id,随便写
			path: /product-service/** #映射路径
			serviceId: service-product
##方法三:路由配置--若路由id和对应的微服务serviceId一致(该方法配合Eureka使用)
zuul:
	routes:
		service-product: /product-service/**
##方法四:路由默认配置--若当前的微服务名称service-product 可直接使用默认路径 /service-product/** (该方法配合Eureka使用)
4.过滤器
//自定义过滤器--以身份认证为例
@Component
public class LoginFilter entends ZuulFilter{
    /**
    * 定义过滤器类型
    * pre: 转发到微服务之前执行的过滤器 -- 多用于身份认证
    * routing: 在路由请求时执行的过滤器
    * post: 执行微服务获取返回值之后执行的过滤器
    * error: 在整个阶段抛出异常时执行的过滤器
    */
    public String filterType(){
        return "pre";
    }
    
    /**
    *	指定过滤器的执行顺序
    *		返回值越小 执行顺序越高
    */
   	public int filterOrder(){
        return 1;
    }
    
    /**
    *	当前过滤器是否生效*/
    public boolean showFilter(){
        return true;
    }
    
    //执行过滤器中的业务逻辑
    /**
    *	案例身份认证:
    *	1-所有的请求需要携带一个参数:access-token
    *	2-获取request请求
    *	3-通过request获取参数access-token
    *	4-判断token是否为空
    */
    public Object run() throws ZuulException{
        System.out.println("Run Filter");
        //1-获取zuul提供的上下文对象RequestContext
        RequestContext ctx = RequestContext.getCurrentContext();
        //2-从RequestContext中获取request
        HttpServletRequest request = ctx.getRequest();
        //3-获取请求参数access-token
        String token = request.getParameter("access-token");
        //4-判断
        if(token == null){
            //拦截请求返回认证失败
            ctx.setSendZuulResponse(false);//拦截请求
            ctx.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
        }
        //token不为空 则return null 继续操作
        return null;
    }
}
SpringCloud Gateway
1.路由配置
<!--springcloud Gateway内部是通过netty + webflux实现
webflux实现和springmvc存在冲突-->
<dependency>
	<groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
#方法一:直接配置
spring:
	cloud:
		gateway:
			routes:
				#配置路由:路由id/url/断言
				- id: product-service
				  url: http://127.0.0.1:9001 #目标微服务请求地址
				  predicates:
				  	-Path=/product/**    
#方法一:根据微服务配置
spring:
	cloud:
		gateway:
			routes:
				#配置路由:路由id/url/断言
				- id: product-service
				  url: lb://service-product #lb://根据微服务名称从注册中心拉取请求路径
				  predicates:
				  	-Path=/product/**    
spring:
	cloud:
		gateway:
			routes:
				#配置路由:路由id/url/断言
				- id: product-service
				  url: lb://service-product #lb://根据微服务名称从注册中心拉取请求路径
				  predicates:
				  -Path=/product/**   
				  filters:
				  - RewritePath=/product-service/(?<segment>.*),/$\{segment} #路径重写的过滤器,在yaml中$字符需要转义
			 discovery:
				locator:
					enabled: true #开启根据服务名称自动转发
					lower-case-service-id: true #微服务名称以小写形式呈现
2.过滤器
/**
 * Created with IntelliJ IDEA
 * User: GHYANG
 * Date: 2020-06-14
 * Description:自定义一个全局过滤器 实现GlobalFilter ordered接口
 */
@Component
public class LoginFilter implements GlobalFilter, Ordered {

    /**
     * 指定过滤器的执行顺序,返回值越小,执行优先级越高
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }

    /**
     * 执行过滤器中的业务逻辑
     * @param exchange
     * @param chain
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("执行了自定义的全局过滤器");
        //过滤器链执行下一个
        return chain.filter(exchange);
    }
}
3.统一鉴权
/**
     * 执行过滤器中的业务逻辑
     *  对请求参数的access-token进行判断
     *      如果存在此参数:代表已经认证成功
     *      如果不存在此参数:认证失败
     *   ServerWebExchange:相当于请求和相应的上下文(类似zuul中的RequestContext)
     * @param exchange
     * @param chain
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("执行了自定义的全局过滤器");
        //获取请求参数
        String token = exchange.getRequest().getQueryParams().getFirst("access-token");
        //判断
        if(token == null){
            System.out.println("Fail");
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete(); //拦截请求
        }
        //过滤器链执行下一个
        return chain.filter(exchange);
    }
4.网关限流
  • 集于filter的限流
    • 结合redis的限流
<!--监控依赖-->
<dependency>
	<groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--redis依赖-->
<dependency>
	<groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
spring:
	redis:
		host: localhost
		pool: 6379
		database: 0
	cloud:
		gateway:
			routes:
				#配置路由:路由id/url/断言
				- id: product-service
				  url: lb://service-product #lb://根据微服务名称从注册中心拉取请求路径
				  predicates:
				  -Path=/product-service/**   
				  filters:
				  - name: RequestRateLimiter
				  	args:
				  	#使用SpEL从容器中获取对象
				  	key-resolver: '#{@pathKeyResolver}'
				  	#令牌桶每秒填充平均速率
				  	redis-rate-limiter.replenishRate: 1
				  	#令牌桶的上限
				  	redis-rate-limiter.bursCamacity: 3
				  - RewritePath=/product-service/(?<segment>.*),/$\{segment}
				  
#RequestRateLimiter:使用限流过滤器 其是springcloud gateway提供的
	#参数 replenishRate:向令牌桶中填充的速率
	#参数 burstCapacity:令牌中的容量
@Configuration
public class KeyResolverConfiguration {
	/**
	 * 编写基于请求路径的限流规则
	 * 方法名和配置文件中的key-resolver: '#{@pathKeyResolver}'相同
	 */
	 @Bean
	public KeyResolver pathKeyResolver() {
		//自定义的KeyResolver
		return new KeyResolver() {
			/**
			 * ServerWebExchange :
			 *      上下文参数
			 */
			public Mono<String> resolve(ServerWebExchange exchange) {
				return Mono.just( exchange.getRequest().getPath().toString());
			}
		};
	}

	/**
	 * 基于请求参数的限流
	 *  请求 abc ? userId=1
	 * 方法名和配置文件中的key-resolver: '#{@pathKeyResolver}'相同
	 */
	@Bean
	public KeyResolver userKeyResolver() {
		return exchange -> Mono.just(
				exchange.getRequest().getQueryParams().getFirst("userId")
				//exchange.getRequest().getHeaders().getFirst("X-Forwarded-For") 基于请求ip的限流
		);
	}
}
  • 集于Sentinel的限流
<!--sentinel限流-->
<dependency>
	<groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-spring-cloud-gatewat-adapter</artifactId>
</dependency>
/**
 * sentinel限流的配置
 */
//@Configuration
public class GatewayConfiguration {

	private final List<ViewResolver> viewResolvers;
	private final ServerCodecConfigurer serverCodecConfigurer;
	public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider,
	                            ServerCodecConfigurer serverCodecConfigurer) {
		this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
		this.serverCodecConfigurer = serverCodecConfigurer;
	}

	/**
	 * 配置限流的异常处理器:SentinelGatewayBlockExceptionHandler
	 */
	@Bean
	@Order(Ordered.HIGHEST_PRECEDENCE)
	public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
		return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
	}

	/**
	 * 配置限流过滤器
	 */
	@Bean
	@Order(Ordered.HIGHEST_PRECEDENCE)
	public GlobalFilter sentinelGatewayFilter() {
		return new SentinelGatewayFilter();
	}

	/**
	 * 配置初始化的限流参数
	 *  用于指定资源的限流规则.
	 *      1.资源名称 (路由id)--product-service
	 *      2.配置统计时间--setIntervalSec
	 *      3.配置限流阈值--setCount
	 */
	@PostConstruct
	public void initGatewayRules() {
		Set<GatewayFlowRule> rules = new HashSet<>();
//		rules.add(new GatewayFlowRule("product-service")
//				.setCount(1)
//				.setIntervalSec(1)
//		);
		rules.add(new GatewayFlowRule("product_api")
			.setCount(1).setIntervalSec(1)
		);
		GatewayRuleManager.loadRules(rules);
	}

	/**
	 * 自定义API限流分组
	 *      1.定义分组
	 *      2.对小组配置限流规则
	 */
	@PostConstruct
	private void initCustomizedApis() {
		Set<ApiDefinition> definitions = new HashSet<>();
		ApiDefinition api1 = new ApiDefinition("product_api")
				.setPredicateItems(new HashSet<ApiPredicateItem>() {{
					add(new ApiPathPredicateItem().setPattern("/product-service/product/**"). //以/product-service/product/开都的所有url
							setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
				}});
		ApiDefinition api2 = new ApiDefinition("order_api")
				.setPredicateItems(new HashSet<ApiPredicateItem>() {{
					add(new ApiPathPredicateItem().setPattern("/order-service/order")); //完全匹配/order-service/order 的url
				}});
		definitions.add(api1);
		definitions.add(api2);
		GatewayApiDefinitionManager.loadApiDefinitions(definitions);
	}

	/**
	 * 自定义限流处理器
	 * 触发限流时执行的处理器
	 */
	@PostConstruct
	public void initBlockHandlers() {
		BlockRequestHandler blockHandler = new BlockRequestHandler() {
			public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
				Map map = new HashMap();
				map.put("code",001);
				map.put("message","不好意思,限流啦");
				return ServerResponse.status(HttpStatus.OK)
						.contentType(MediaType.APPLICATION_JSON_UTF8)
						.body(BodyInserters.fromObject(map));
			}
		};
		GatewayCallbackManager.setBlockHandler(blockHandler);
	}
}
spring:
	cloud:
		gateway:
			routes:
				#配置路由:路由id/url/断言
				- id: product-service
				  url: lb://service-product #lb://根据微服务名称从注册中心拉取请求路径
				  predicates:
				  -Path=/product/**   
				  filters:
				  - RewritePath=/product-service/(?<segment>.*),/$\{segment} #路径重写的过滤器,在yaml中$字符需要转义
			 discovery:
				locator:
					enabled: true #开启根据服务名称自动转发
					lower-case-service-id: true #微服务名称以小写形式呈现
5.网关的高可用
  • 高可用HA是分布式系统架构设计中必须考虑的因素之一,其通常是指通过设计减少系统不能提供服务的时间。
  • 配合nginx搭建网关集群
Nginx模拟网关
<!--在nginx.conf配置反向代理-->
<!--路由到订单服务-->
location /api-product{
	proxy_pass http://127.0.0.1:9002;
}

<!--路由到商品服务-->
location /api-order{
	proxy_pass http://127.0.0.1:9002;
}

七、链路追踪
Sleuth
<!--sleuth链路追踪-->
<dependency>
	<groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
logging:
	level:
		root: INFO
		org.springframework.web.servlet.DispatcherServlet: DEBUG
		org.springframework.cloud.sleuth: DEBUG
Zipkin
  • Zipkin是一个开源项目,其致力于收集服务的定时数据,以解决微服务架构的延迟问题,包括数据的收集、存储、查找和展现。
  • 使用方式:
    • 下载Zipkin.jar包并打开

    • localhost:9411/Zipkin/

    • <dependency>
      <groupId>org.srpingframework.cloud</groupId>
      <artifactId>spring-cloud-starter-zipkin</artifactId>
      </dependency>
      
    • spring:
      	zipkin:
      		base-url: http://127.0.0.1:9411/ #server的请求地址
      		sender:
      			type: web #数据的传输方式,已http的形式向server端发送数据
      	sleuth:
      		sampler:
      			probability: 1 #采样比(1代表全采样)
      
  • 数据持久化
    • 官方提供的Zipkin数据库表

    • #数据库挂载
      java -jar xxxx.jar --STORAGE_TYPE:mysql --MYSQL_HOST:127.0.0.1 --MYSQL_TOP_PORT:3306 --MYSQL_USER:root --MYSQL_PASS=xxx --MYSQL_DB=zipkin
      
  • 使用消息中间件优化
  • 使用rabbitMQ进行数据异步收集

    • 准备rabbitMQ服务器
    • 修改zipkin客户端,将消息以rabbit的形式发送到mq服务器
    • 修改zipkin服务端,从rabbit中拉取消息

八、SpringCloud Stream
  • 在实际的企业开发中,消息中间件是至关重要的组件之一。消息中间件主要解决应用解耦,异步消息、流量削锋等问题,实现高性能,高可用,高伸缩和最终一致性架构,不同的中间件其实现方式、内部结构不相同,当项目前期的消息中间件与后期的中间件发生改变时,SpringCloud Stream可以提供一种解耦合的方式。
<dependency>
	<groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-stream</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-stream-bindeer-rabbit</artifactId>
</dependency>
#消息生产者配置
spring:
    cloud:
        stream:
            bindings:
                output:
                    destination: itcast-default #指定消息发送的目的地
            binders: #绑定器配置
                defaultRabbit:
                    type: rabbit #rabbit中间件

#消息消费者配置
spring:
	cloud:
		stream:
			bindings:
				input: #内置的获取消息的通道,从itcast-default中获取消息
					destination: itcast-default
			binders:
				defaultRabbit:
					type: rabbit

九、配置中心
SpringCloud config
  • SpringCloud Config项目是一个解决分布式系统的配置管理方案,其包含了Client和Server两个部分,server提供配置文件的存储、以接口的形式将配置文件的内容提供出去,client通过接口获取数据、并依据此数据初始化自己的应用。
  • 文件命名规则:
    • {application}-{profile}.yml
    • {application}-{profile}.properties
    • {application}为应用名称 profile指开发环境 (用于区分开发环境、测试环境、生产环境等)
<!--配置中心依赖-->
<dependency>
	<groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<!--子需拉取配置的服务-->
<dependency>
	<groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.cloud</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
    • 激活配置:@EnableConfigServer
 server:
 	port: 10000 #服务端口
 spring:
 	application:
 		name: config-server #指定服务名
 	cloud:
 		config:
 			server:
 				git:
 					uri: https://xxxxxxxx.git #git地址
    • 获取配置信息:localhost:10000/product-dev.yml —product-dev.yml是git上创建的yaml文件
#需要创建一个配置中心 在需要拉取配置的服务创建一个xxx.yaml文件 
spring:
	cloud:
		config:
			name: product #应用名称 需要对应git中配置文件名称的前半部分
			profile: dev #开发环境
			label: master #git中的分支
			uri: http://localhost:10000 #config-server的请求地址
			
#开启动态刷新的请求路径端点
managment:
	endpoints:
		web:
			exposure:
				include: refresh
    • 在需要开启动态刷新的类上@RefreshScope
    • 当远程配置发生改变时在postman中访问http://localhost:9002/actuator/bus-refresh 即可实现数据更新 从而避免重启服务器
  • 当微服务数量多时,远程配置发生改变每次都需要到postman发送更新访问,这样会增加工作负担,SpringCloud中也有对应的解决方案,SpringCloud Bus将分布式的节点用轻量的消息代理连接起来,可以很容易搭建消息总线,配合Spring CloudConfig实现微服务应用配置信息的动态更新
Apollo
  • Apollp是一个开源配置管理中心,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性。
  • 搭建路线:
    • 下载apollo相关jar包及sql文件
    • 在linux环境解压下载的zip包,并更改demo.sh里的数据库连接信息
<!--Apollo客户端依赖-->
<dependency>
	<groupId>com.ctrip.framework.apollp</groupId>
    <artifactId>apollo-client</artifactId>
</dependency>
apollo:
	bootstrap: #开启apllo
		enbaled: true
	meta: http://192.168.74.101:8080 
app:
	id: test01 #指定apollo配置中心的AppId

十、消息总线
  • 消息总线bus:在微服务架构中,通常会使用轻量级的消息代理来构建一个共用的消息主题来连接各个微服务实例,它广播的消息会被所有在注册中心的微服务实例监听和消费,也称消息总线
配置中心整合消息总线
<!--消息总线的依赖-->
<dependency>
	<groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-bus</artifactId>
</dependency>

<dependency>
	<groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-stream-binder-rabbit</artifactId>
</dependency>
#配置消息中间件rabbit
rabbit:
	host: 127.0.0.1
	port: 5672
	username: guest
	passwort: guest	
十一、补充
bootstrap.yml:用来在程序引导时执行,应用于更加早期配置信息读取,如可以使用bootstrap.yml配置application.yml中使用到的参数
application.yml:应用程序特有的配置信息,可以用来配置后续各个模块中需要使用的公共参数等
两者加载顺序:bootstrap.yml 先于 application.yml

以上为个人学习笔记 可能有些地方不严谨或者出错 本人纯属小白 欢迎指出

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值