SpringCloud

x33g5p2x  于2021-10-07 转载在 Spring  
字(12.2k)|赞(0)|评价(0)|浏览(394)

一、SpringCloud是什么?

Spring最擅长的就是集成,把世界上最好的框架拿过来,集成到自己的项目中。
Spring Cloud也是一样,它将现在非常流行的一些技术整合到一起,实现了诸如:配置管理,服务发现,智能路
由,负载均衡,熔断器,控制总线,集群状态等等功能。其主要涉及的组件包括:
Netflix
Eureka:注册中心
Zuul:服务网关
Ribbon:负载均衡
Feign:服务调用
Hystrix:熔断器

1.1 Spring Cloud与Spring Boot版本对应关系

二、服务拆分

2.1 服务拆分案例

例子
	1. 把以往单体架构的的项目里面有N个模块
	2. 我们单独把中间的几个业务拿出来分为N个小模块

3. 两个模块分别是 用户模块和订单模块
	4. 现在调用两个模块中间是没有任何关联的

5. 当我们想用端口号为8080的订单服务调用端口号为8081的查询用户API时,我们把两个模块分为两个项目来写,我们如何来实现远程调用呢?

2.2 服务拆分-服务远程调用

微服务调用方式

1. 注册RestTemplate
			@MapperScan("cn.itcast.order.mapper")
			@SpringBootApplication
			public class OrderApplication {
			
			    public static void main(String[] args) {
			        SpringApplication.run(OrderApplication.class, args);
			    }
			
			    /** * 创建RestTemplate并注入Spring容器 * @return */
			    @Bean
			    public RestTemplate restTemplate(){
			        return new RestTemplate();
			    }
			}
	2. 服务远程调用RestTemplate
		@Service
		public class OrderService {
		
		    @Autowired
		    private OrderMapper orderMapper;
		    @Autowired
		    private RestTemplate restTemplate;
		    public Order queryOrderById(Long orderId) {
		        // 1.查询订单
		        Order order = orderMapper.findById(orderId);
		        // 2.利用RestTemplate发起HTTP请求,查询用户
		        String url="http://localhost:8081/user/"+order.getUserId();
		        // 2.1发送HTTP请求,实现远程调用,
		        // 第二个参数 告诉RestTemplate我们返回的是User对象,他会自动帮我们把JSON反序列化成User类型
		        User user = restTemplate.getForObject(url, User.class);
		        // 3. 将我们远程调用得到的数据封装到Order中
		        order.setUser(user);
		        // 4.返回
		        return order;
		    }
		}

2.3 提供者与消费者

  • 服务提供者:一次业务中,被其他微服务调用的服务。(提供接口给其他微服务)
  • 服务消费者:一次业务中,调用其他微服务的服务。(调用其他微服务提供的接口 )

2.4 Eureka注册中心

2.5 搭建Eureka服务

1. 引入Eureka依赖
			<dependency>
	            <!--eureka服务端依赖            -->
	            <groupId>org.springframework.cloud</groupId>
	            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
	        </dependency>
	2. 编写启动类,添加 @EnableEurekaServer //开启Eureka服务
	3. 添加application.yml添加配置文件
		spring:
		  application:
		    name: eurekaserver #Eureka的服务名称
		eureka:
		  client:
		    service-url:  #eureka地址信息
		      defaultZone: http://127.0.0.1:10086/eureka
	4. 启动成功,打开我们application.yml中eureka的defaultZone:定义的地址就能看见服务

2.6 Eureka服务注册

1. 引入Eureka客户端依赖
			<!--引入 eureka客户端依赖        -->
	        <dependency>
	            <groupId>org.springframework.cloud</groupId>
	            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
	        </dependency>
	2. 添加application.yml添加配置文件
			spring:
			  application:
			    name: userService #User服务端名称
			eureka:
			  client:
			    service-url:  #eureka地址信息
			      defaultZone: http://127.0.0.1:10086/eureka

2.7 Eureka服务发现/服务拉取

  • 服务拉取:方法一
1. 修改访问的URL路径,用服务名代替IP、端口
			String url="http://userservice:8081/user/"+order.getUserId();
	2. 在启动类中的RestTemplate添加负载均衡注解  @LoadBalanced //开启负载均衡
			/** * 创建RestTemplate并注入Spring容器 * @return */
		    @Bean
		    @LoadBalanced //开启负载均衡
		    public RestTemplate restTemplate(){
		        return new RestTemplate();
		    }
  • 服务拉取:方法二
1. 使用发现客户端
		@Autowired
	    private DiscoveryClient discoveryClient;
	2. 拼接url
		//拉取服务
        List<ServiceInstance> userService = discoveryClient.getInstances("userService");
        ServiceInstance serviceInstance = userService.get(0);
        //拼接url
        String url="http://"+serviceInstance.getHost()+":"+serviceInstance.getPort()+"/user/"+id;
	3. 在启动类上开启 发现服务注解
		@EnableDiscoveryClient //开启eureka客户端发现功能

2.8 Ribbon负载均衡执行流程

2.9 Ribbon负载均衡执行流程

2.10 Ribbon饥饿加载

Ribbon默认采用懒加载,即第一次访问时才回去创建LoadBalanceClient,请求时间会很长
	而饥饿加载则会在项目启动时就创建,降低第一次访问的耗时
1.配置饥饿加载
		ribbon:
		  eager-load:
		    enabled: true #开启饥饿加载
		    clients: userservice #指定饥饿加载的服务名称

2.11 Hystrix

  • Hystrix为每个依赖服务调用分配一个小的线程池,如果线程池已满调用将被立即拒绝,默认不采用排队,加速失败判定时间。
  • 用户的请求将不再直接访问服务,而是通过线程池中的空闲线程来访问服务,如果线程池已满,或者请求超时,则会进行降级处理,什么是服务降级?
  • 服务降级:可以优先保证核心服务。
用户的请求故障时,不会被阻塞,更不会无休止的等待或者看到系统崩溃,至少可以看到一个执行结果(例如返回
友好的提示信息) 。
  • 服务降级虽然会导致请求失败,但是不会导致阻塞,而且最多会影响这个依赖服务对应的线程池中的资源,对其它服务没有响应。 触发Hystrix服务降级的情况:
  • 1.线程池已满
  • 2.请求超时

2.11.1 服务降级

  • 引入依赖
<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
  • 开启熔断:在启动类上添加注解
@EnableCircuitBreaker 开启熔断
  • 编写服务降级 当该请求超时,会触发服务降级方法给用户友好提示
@RestController
@RequestMapping("/consumer")
@Slf4j
public class ConsumerController {![在这里插入图片描述](http://img.saoniuhuo.com/images/202110/45201633617197417.jpg)

    @Autowired
    public RestTemplate restTemplate;
    @GetMapping("{id}")
    //当请求超时,执行指定的服务降级方法
    @HystrixCommand(fallbackMethod = "fallback") 
    public String getUser(@PathVariable Long id){
        return restTemplate.getForObject("http://userservice/user/"+id,String.class);
    }
    public String fallback(Long id){
        return "网络拥挤,请稍后再试!!!";
    }
}

要注意;因为熔断的降级逻辑方法必须跟正常逻辑方法保证:相同的参数列表和返回值声明。

  • 正常测试请求

  • 关闭调用方法的微服务

2.11.2 默认fallback

  • 刚才把fallback写在了某个业务方法上,如果这样的方法很多,那岂不是要写很多。所以可以Fallback配置加在类上,实现默认fallback;
  • 在业务类上添加注解 @DefaultProperties(defaultFallback = “默认服务降级方法”)
  • 编写服务降级方法
public String defaultFallback() {
		return "默认提示:对不起,网络太拥挤了!";
	}
  • 在业务方法上添加注解 @HystrixCommand

2.12 服务熔断

在服务熔断中,使用的熔断器,也叫断路器,其英文单词为:Circuit Breaker 熔断机制与家里使用的电路熔断原理
类似;当如果电路发生短路的时候能立刻熔断电路,避免发生灾难。在分布式系统中应用服务熔断后;服务调用方
可以自己进行判断哪些服务反应慢或存在大量超时,可以针对这些服务进行主动熔断,防止整个系统被拖垮。
Hystrix的服务熔断机制,可以实现弹性容错;当服务请求情况好转之后,可以自动重连。通过断路的方式,将后续
请求直接拒绝,一段时间(默认5秒)之后允许部分请求通过,如果调用成功则回到断路器关闭状态,否则继续打
开,拒绝请求的服务。

三、Nacos

3.1 Nacos下载-Windows版

Nacos官网

1. 在 Nacos的bin目录下cmd命令行输入 单击模式命令行
			startup.cmd -m standalone
	2. 启动完毕后

3. 在网页打开
		http://本机ip:8848/nacos/index.html 登陆账号密码 都是 nacos

3.2 Nacos注册中心

1.在父工程中添加Nacos管理依赖
		<dependency>
		    <groupId>com.alibaba.cloud</groupId>
		    <artifactId>spring-cloud-alibaba-dependencies</artifactId>
		    <version>2.2.5.RELEASE</version>
		    <type>pom</type>
		    <scope>import</scope>
		</dependency>
	2. 在子模块中导入 nacos客户端依赖
		<!-- nacos客户端依赖包 -->
		<dependency>
		    <groupId>com.alibaba.cloud</groupId>
		    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
		</dependency>
	3. 在application核心文件中配置nacos地址
	   spring:
	   	 cloud:
		   nacos:
		     server-addr: localhost:8848 #nacos服务地址

3.3 添加集群

1. 在application中添加集群名称
		spring: 
		  discovery:
            cluster-name: HZ #集群名称 这里代表杭州
	当A模块向B模块发送请求时,会优先查看A模块和B模块是否在同一个集群中,若在同一个集群中发送数据延迟会大大降低

3.4 Nacos加权负载均衡

实例的权重控制:
		1. Nacos控制台可以设置实例的权重值,0~1之间
		2. 同集群内的多个实例,权重越高访问的频率越高
		3. 权重为0则完全不会访问

3.5 Nacos环境隔离

1. namespace用来做环境隔离
	2. 每个namespace都有唯一的ID
	3. 不同namespace下的服务不可见,无法跨namespace调用服务

3.5.1 Nacos新建隔离环境

4. 在application核心配置文件写入当前业务模块所在的命名空间
		spring:
		  cloud:
            nacos:
              discovery:
        	    namespace:  #命名空间的ID

3.6 Nacos配置管理

3.6.1 实现Nacos统一配置管理

3.6.2 Nacos微服务配置拉取

拉取Nacos上的配置中心的设置 具体配置看3.6.1的图2

@Value("${pattern.dateformat}") //${"获取Nacos配置中心的值"}
     private String format;
     @RequestMapping("now")
    public void now(){
     System.out.println(LocalDateTime.now().format(DateTimeFormatter.ofPattern(format)));
    }

3.6.3 Nacos配置中心实现配置热更新

  • 什么是热更新呢?就是当我们修改了Nacos配置文件后不需要重新启动微服务,我们的代码就可以自动监测到配置,如果发现了修改会自动更新配置
    方式一:
在@Value("${Nacos配置中心的属性}")注解的类上添加@RefreshScope

方式二:推荐

@Data
	@Component
	@ConfigurationProperties(prefix = "pattern")//获取配置文件前缀
	public class PatternProperties {
	    //如果前缀和当前属性拼接上在配置中心存在的话,就会自动注入到该属性
	    private String format;
	}

3.6.4 Nacos多服务共享配置

@Data
	@Component
	@ConfigurationProperties(prefix = "pattern")//获取配置文件前缀
	public class PatternProperties {
	    //如果前缀和当前属性拼接上在配置中心存在的话,就会自动注入到该属性
	    private String format;
	    private String envSharedValue;
	}

@GetMapping("/env")
    private Object Share(){
        return patternProperties;
    }

3.7 Nacos集群搭建

四、Feign

4.1 Feign远程调用

1. 导入依赖
		<!--        openFeign依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
	2. 启动类上添加Feign的开启注解
		@EnableFeignClients //开启openFeign
	3. 定义openFeign接口
		@FeignClient("userService")//要求与Nacos中的服务名一致
		public interface UserClient {
		    @GetMapping("/user/{id}")//暴露userService微服务的请求方法
		    User findById(@PathVariable("id") Long id);
		}
	4. 调用
		@Autowired
	    public UserClient userClient;
	    public Order openFeign(Long orderId) {
	        // 1.查询订单
	        Order order = orderMapper.findById(orderId);
	        //2.利用Feign实现远程调用
	        User user = userClient.findById(order.getUserId());
	        order.setUser(user);
	        // 4.返回
	        return order;
	    }

4.2 Feign自定义配置

方式一: 配置文件方式

feign:
	  client:
	    config:
	      default: # 这里用default就是全局配置,如果是写服务名称,则是针对某个微服务的配置
	        loggerLevel: FULL #日志级别

方式二: java代码方式,需要先声明一个Bean:

public class FeignClientConfiguration {
	    @Bean
	    public Logger.Level feignLoggerLevel(){
	        return Logger.Level.FULL;
	    }
	}
	
	而后如果是全局配置,则把它放到@EnableFeignClients这个注解中:
	@EnableFeignClients(defaultConfiguration = FeignAutoConfiguration.class)
而后如果是全局配置,则把它放到@EnableFeignClients这个注解中:@EnableFeignClients(defaultConfiguration = FeignAutoConfiguration.class)
如果是局部配置,则把它放到@FeignClient这个注解中:@FeignClient(value = “userService”,configuration = FeignAutoConfiguration.class)

五 、 Gateway网关

5.1 网关的作用

  • 对用户做身份验证、权限校验
  • 将用户的请求路由到微服务,并实现负载均衡
  • 对用户请求做限流

5.2 网关的搭建

  • 要注意依赖版本冲突问题,否则网关启动失败
1. 添加网关依赖和nacos服务发现依赖
		<!--网关依赖        -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <!--nacos服务发现依赖        -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
	2. application核心文件设置网关配置
		server:
		  port: 10010 #网关端口
		spring:
		  application:
		    name: gateway #服务名称
		  cloud:
		    nacos:
		      server-addr: localhost:8848 #nacosnacos地址
		    gateway:
		      routes: #网关路由配置
		        - id: user-service #路由id,自定义,只要唯一即可
		          uri: lb://userService #路由的目标地址 lb就是负载均衡,后面跟服务名称
		          predicates:  #路由断言,也就是判断请求是否符合路由规则的条件
		            - Path=/user/** #这个是按照路径匹配,只要以/user/开头就符合要求 - id: order-service uri: lbb://orderService predicates: - Path=/order/**

5.3 过滤器Filter

官网:

  • 常见过滤器
过滤器名称说明
AddRequestHeader对匹配的上的请求添加请求头
AddRequestParameters对匹配上的路由添加请求参数
AddResponseHeader从网关返回的请求添加响应头
StripPrefix对匹配上的请求取出前缀

5.3.1 给单个路由添加过滤器

server:
  port: 10010 #网关端口
spring:
  application:
    name: gateway #服务名称
  cloud:
    nacos:
      server-addr: localhost:8848 #nacosnacos地址
    gateway:
      routes: #网关路由配置
        - id: user-service #路由id,自定义,只要唯一即可
          uri: lb://userService #路由的目标地址 lb就是负载均衡,后面跟服务名称
          predicates:  #路由断言,也就是判断请求是否符合路由规则的条件
            - Path=/user/** #这个是按照路径匹配,只要以/user/开头就符合要求
          filters:
            - AddRequestHeader=Truth,yyds
            - AddResponseHeader=Truth,yyds
        - id: order-service
          uri: lb://orderService
          predicates:
            - Path=/order/**

5.3.2 default-filters给所有路由定义过滤器

  • 在网关的yml配置文件中设置
spring:
		  application:
		    name: gateway #服务名称
		  cloud:
		    nacos:
		      server-addr: localhost:8848 #nacosnacos地址
		    gateway:
		      routes: #网关路由配置
		        - id: user-service #路由id,自定义,只要唯一即可
		          uri: lb://userService #路由的目标地址 lb就是负载均衡,后面跟服务名称
		          predicates:  #路由断言,也就是判断请求是否符合路由规则的条件
		            - Path=/user/** #这个是按照路径匹配,只要以/user/开头就符合要求
		        - id: order-service
		          uri: lb://orderService
		          predicates:
		            - Path=/order/**
		      default-filters: #默认过滤器,对所有路由都生效
		        - AddRequestHeader=Truth,yyds #添加请求头
		        - AddResponseHeader=Truth,yyds #添加响应头

5.3.3 GlobalFilter全局过滤器

  • GlobalFilter与default-filters的区别
  • 1 全局过滤器的作用也是处理一切进入网关的请求和微服务响应,与GatewayFilter作用一样
  • 2 区别在于GatewayFilter通过配置定义,处理逻辑是固定的。而GlobalFilter的逻辑需要自己写代码实现
1. 实现GlobalFilter接口的方法
	2. 标记当前的全局过滤器优先级 @Order
	@Order(-1)//决定了GlobalFilter全局过滤器的优先级,数字越小,优先级越高
	@Component
	public class AuthorizationFilter implements GlobalFilter {
	
	    @Override
	    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
	        //1.获取请求参数
	        ServerHttpRequest request = exchange.getRequest();
	        MultiValueMap<String, String> queryParams = request.getQueryParams();
	        //2.获取参数中的第一个为authorization的值
	        String authorization = queryParams.getFirst("authorization");
	        //3.判断是否相等
	        if("admin".equals(authorization)){
	            //4.相等 放行
	            return chain.filter(exchange);//若有下一个全局过滤器就执行下一个全局过滤器
	        }
	        //5.否 拦截
	        //5.1设置状态码
	        exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
	        //5.2拦截请求
	        return exchange.getResponse().setComplete();
	    }
	}

六、配置中心

6.1 Spring Cloud Config分布式配置中心

  • 说明
在分布式系统中,由于服务数量非常多,配置文件分散在不同的微服务项目中,管理不方便。为了方便配置文件集
中管理,需要分布式配置中心组件。在Spring Cloud中,提供了Spring Cloud Config,它支持配置文件放在配置服
务的本地,也支持放在远程Git仓库(GitHub、码云)。
  • 图解

  • 在GItee上创建好仓库后,创建配置文件
命名方式:配置文件的命名方式:{application}-{profile}.yml 或 {application}-{profile}.properties
	application为应用名称
	profile用于区分开发环境,测试环境、生产环境等
  • 在Git上创建好配置文件后创建一个新的模块当配置类
config模块导入依赖:
		<dependencies>
			<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
			</dependency>
			<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-config-server</artifactId>
			</dependency>
		</dependencies>
  • 并在config配置模块启动类上开启注解
@EnableConfigServer 开启配置模块
  • 在config模块中设置application.yml文件连接远程仓库
server:
 port: 12000
spring:
 application:
  name: config-server
 cloud:
  config:
   server:
    git:
     uri: 填写Git仓库的地址
eureka:
 client:
  service-url:
   defaultZone: http://127.0.0.1:10086/eureka

6.2 获取配置中心的配置

  • 先导入依赖
<dependency>
		<groupId>org.springframework.cloud</groupId>
		<artifactId>spring-cloud-starter-config</artifactId>
	</dependency>
  • 上面的操作已经把我们需要的yml配置文件设置到Git上,接下来我们将获取Git上面的yml配置文件
  • 把我们所需的yml删除,创建一个bootstrap.yml引导文件,优先级比application.yml高
spring:
 cloud:
  config:
   # 要与仓库中的配置文件的application保持一致
   name: user
   # 要与仓库中的配置文件的profile保持一致
   profile: dev
   # 要与仓库中的配置文件所属的版本(分支)一样
   label: master
   discovery:
    # 使用配置中心
    enabled: true
    # 配置中心服务名
    service-id: config-server
eureka:
 client:
  service-url:
   defaultZone: http://127.0.0.1:10086/eureka

相关文章