SpringCould初见(其一)

Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线负载均衡断路器数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。

个人理解就是对Springboot遇到的各种问题的解决,通过组件解决问题,有的组件可要可不要的,常见所需组件选择:

服务注册中心,优先选择Nacos和Zookeeper,Erueka已过时

服务负载均衡,考虑Ribben和LoadBalancer

服务熔断降级,优选Sentinel,次选Hystrix

服务调用,Open Feign 次选Feign

服务网关,GateWay, 次选Zuul

服务配置,Nacos,次选Config

服务总线,Nacos,次选Bus

Spring Cloud Alibaba,致力于提供微服务开发的一站式解决方案,只需添加一些注解和少量配置即可解决大量SB的问题,相比原生Spring Cloud简便搭建,学习曲线低,性能强悍,设计合理,故以SCA 为主 SC为辅

使用IDEA创建SC步骤:

1.创建父项目(Maven)

2.项目设置,设置中将全局编码,工程编码,默认编码设置为UTF-8,编译器设置为8,将package改为pom意为父工程,聚合管理其他模块,同时配置各个依赖的版本如lombok,druid,springboot等防止出现版本兼容问题,如有需要可再配,同时使用pom+impot解决maven单继承机制,在引入Spring Cloud(注意对应springboot版本)

3.创建子模块(Modul不是project)测试各个服务

引入相关依赖,无需引入版本(版本制裁,引入的是父项目版本),但注意boot-starter和boot是不同的依赖,父项目没配置的子项目要自己配置

举例:微服务之会员中心微服务:处理与会员相关的功能,例如会员注册、登陆、信息修改、积分管理、订单管理、优惠券管理等。

后端基本环境:

首先创建application.yml,  配置端口,mybatis,druid数据池及数据库相关配置

再创建SpringBoot主程序@SpringBootApplication

接着创建相关数据库信息,对应的在idea创建entity映射

创建返回结果成功与否的类Result,同时能包装json数据

再创建Dao接口定义相关的方法,再在Mapper.xml文件中实现方法,同样先写Service接口再加以实现

完成Controller类,添加方法/接口,通过Postman测试

完成基本框架

浏览器  =》 服务消费模块 =》 基本框架

添加消费服务模块:

创建新Moduel,引入相关maven依赖,创建配置文件application.yml及主启动类,创建对应entity类以及Result类

注入ResTemplate,Spring提供的用于访问Rest服务的模板类,,这个类提供了许多便捷访问远程HTTP服务的方法,且支持REST风格就像浏览器发出http请求调用对应API。 配置RestTempLate

@Configuration
public class CustomizationBean{
   
  
//注入RestTemplate对象/bean
   @Bean
   public RestTemplate getRestTemplate(){
          return new RestTemplate;
   
  }
}

创建Controller类

@RestController
public class MemberConsumerController{
     //定义一个基础url地址,打到服务端
     public static final Stirng member_service_url ="...10000";

     //装配RestTemplate对象
     @Resource
     private RestTemplate restTemplate;

     @PostMapping("/member/consumer/save")
     public Result<Member> save(Member member){
          //打到服务端接口,相当于http://localhost.../member/save
           return restTemplate.postForObject(member_service_url+
             "/member/save",member,Result.class);
          
          //传的参数是member,返回对象类型为Result
          //注意此时发送到服务端数据已经是json格式,故要在服务端用@RequetBody注解
         //同时因为数据以流形式传输故需两emtity类实现Serilazibale接口
   }
}

Run Dashboard:当springcloud的服务有多个时,可以用其方便管理,无需切换启动

要在 /.idea/workspace.xml  加入RunDashboard

公用模块,抽取公共模块使用maven打成jar包,其他模块引入即可,如Member,Result类

//完成公共模块配置
pom.xml:
lombok
<optional>true</optional>      //表示其他模块引入该模块时不会引入此依赖,默认false


建立公共包包含 Member类和Result类

使用Maven右侧菜单栏 上方不build-生命周期-clean和install 即可生成jar文件夹,即可在其他模块引入
相关信息在maven-archiver下的pom.properties

其他模块引入:
<dependency>
  相关信息在pom.properties中有(上面有讲)
</dependency>

Eureka(虽然过时了但有些老项目可能还要用,方便Nacos会学的更快)

假如服务提供方有多个,而服务消费方只有一个,它如何找到对应的提供方?

1.provider服务端可注册服务到Eureka Service

2.consumer消费端亦可注册服务或者从Eureka Service得到服务,即远程调用

3.Eureka包含两个组件:Eureka Server和Eureka Client

4.各个子模块(Consumer和Provider)通过配置启动后,会在Eureka Server注册,Provider会周期性(默认30s)发送心跳(心跳)给Eureka Server,若过了几个周期(默认90s)没有收到则会把服务节点移除

服务治理:传统管理各个服务直接依赖关系复杂,可以用服务治理进行优化实现 服务调用,负载均衡,容错,服务发现与注册等,Eureka就是服务治理

RPC:远程调用,即实现不同计算机之间通讯,因为各个模块可能在不同计算机上,上面的RESTTemplate就可以远程调用

单机版Eureka(后面集群)

消费服务(80),提供服务(10000),Eureka Service(9001)

创建Eureka Service模块,加入相关依赖(不要从其他模块直接复制粘贴)

引入Eureka场景启动器

<dependency>
 org.springframework.cloud
 spring-cloud-starter-netflix-eureka-server
</dependency>

application.yml配置Eureka-server

eureka:
 instance:
  hostname:localhost  #服务实例名
 client:
  rgister-wiht-eureka: false       #配置不向注册中心注册自己且多个eureka集群之间可能亦需要交流
  ftech-registry:false            #表示自己就是注册中心,作用维护服务实例,不需检索服务
  service-url:
    defaultZone:http://localhost:9001/euraka/ #交互模块,查询服务和注册服务依赖这个地址

创建主启动类EurekaApplication

@EnableEurekaServer     #表示该程序作为EurekaServer
@SpringBootApplication
...
将提供服务(10000)注册到Eureka,成为服务提供者即(Eureka Server)

引入Eureka-client场景启动器

<dependency>
 org.springframework.cloud
 spring-cloud-starter-netflix-eureka-client
</dependency>

在application.yml配置eureka-client

eureka:
 client:
  register-with-eureka:true #将自己注册到Eureka-Server
  fetch-registry:true      #如果是单节点可不要,若是集群必须true才能配合Ribbon使用负载均衡
  service-url:
   defaultZone:http://localhost:9001/eureka       #将自己注册到那个eureka-server

修改启动类

@EnableEurekaClient     #将该模块标识为eureka-client
@SpringBootApplication

 将消费服务(80)注册到Eureka,可以获得Eureka Service(9001)的注册信息

加入依赖,eureka client场景启动器

<dependency>
 org.springframework.cloud
 spring-cloud-starter-netflix-eureka-client
</dependency>

application.yml得配置和eureka类似

eureka:
 client:
  rgister-wiht-eureka: true       
  ftech-registry: true       
  service-url:
    defaultZone:http://localhost:9001/euraka/

同样修改主启动类

@EnableEurekaClient
@...

大致流程:

1.启动Eureka Server和服务器提供者

2.提供者将自己信息注册到Eureka Server,通过心跳机制维护状态

3.消费者需要调用接口时,去注册中心获取实际RPC远程调用地址(即提供者地址)

4.消费者获取的地址会储存到JVM内存,默认每隔30s去更新一次服务调用地址

Eureka自我保证机制

EurekaClient定时向EurekaServer发送心跳包,若server端一段时间没收到client心跳包则会剔除改服务。如果开启自我保证机制,则不会剔除该服务(因为可能由于网路延迟原因造成的)

此机制为CAP理论之一

CAP理论:一致性:所有节点在同一时间数据完全一致

                  可用性:服务在正常时间内一直可用

                  区分容错性:系统遇到某节点或网络故障时仍能对外满足可用性或一致性服务

一个分布式系统只能同时满足三个中的两个

PS:自我保证机制可用关闭,但一般不关闭

Eureka集群

如果Eureka只有一个,那么它宕掉整个程序就会垮掉,为安全得引入集群

集群中Eureka之间可相互注册

创建第二个Eureka(9002)

pom.xml配置类似9001

application.yml(同时注意修改9001的yml文件)

Server:
 port:9002 
   .
   .hostname:eureka9002.com
   .
 client:
  ...
   service-url:
    defaultZone:http://eureka9001.com:9001/eureka/  注册到eureka9001.com的9001端口

主启动类类似

为让浏览器直接访问Eureka,要修改host文件(C/Windows/System32/divers/etc)

#配置eureka主机和ip映射
127.0.0.1 eureka9001.com   //这名字是在yml配置的
127.0.0.1 eureka9002.com

即可在浏览器直接访问如:eureka9002.com:9002

将服务消费(80)和提供服务(10000)注册到Eureka集群(略)

例如80:

yml文件:
 

...
defaultZone:http://eureka9001.com:9001/eureka,
            http://eureka9002.com:9002/eureka

服务提供方也通常有集群(因为可能有多个请求打到服务提供方)

如添加个provider(10002),各个配置与10000一样(可拷贝java和source文件,不建议直接拷贝模块)

改进:provider-10000与provider-10002是一个集群提供服务,需将spring.application.name统一 服务别名这样才能方便进行负载均衡

修改10000与10002的yml文件
Spring:
 application:
  name:member-service-provider

修改服务消费方配置

Controller中

public static final String SERVICE_URL = "http://MEMBER-SERVICE-PROVIDER"

//MEMBER-SERVICE-PROVIDER就是服务提供方(集群),名字就是注册到注册中心的名字,目前两个(10000和100002)

Bean中

@Bean
@LoadBalanced       //此注解表示赋予RestTemplate负载均衡能力即(默认)轮流使用100002和10000
public RestTemplate getRestTemplate(){
   return new RestTemplate;
}

服务消费方(Eureka Client)得到服务方注册信息

消费方

Controller类修改

@Resource
private DiscoverClient discoverryClient;
//先注入DiscoverClient

@GetMapping("/member/consumer/discovery")
public Object discovery(){
  List<String> services = discoveryClient.getServices();  //得到服务方集群
  for(String service:services){
            //所有服务实例列表
    List<ServiceInstance> instances = discoveryClient.getInstances(service); 
      for(ServiceInstance instance:instances){    //得到单个实例
           getServiceId()....
   }
  }
}

同时bean类加入注解

@EnableDiscoveryClient

Ribbon

一套客户端负载均衡工具进程内LB,主要向客户端提供负载均衡算法和调用,基于某种规则(如之前的轮询,随机连接等,可以自己定制算法)连接指定服务

负载均衡LB(Load Balance),分为集成LB和进程内LB

切换均衡算法

例如更换成随机访问,一般情况下轮询就行了

在消费服务中

添加RibbonRule类

@Configuration
public class RibbonRule{
 
  //配置自己的均衡算法
  @Bean
  public IRule myRibbonRule(){
    return new RoundomRule();      //切换成随机访问
   }
}

修改主程序类

@RibbonClient(name = "MEMBER_SERVICE_PROVIDER_URL",configuration="RobonnRule")

Open Feign

一个声明式WebService客户端,可以让编写Web Service客户端(远程调用)更简单,使用方法只需在一个服务接口上添加注解即可,SC对其进行了封装,配合Eureka和Ribbon组合支持负载均衡       PS:因为Feign不支持SpringMvc注解故被淘汰,使用Open Feign

现在获得服务可选择Ribbon+RestTemplate 或 OpenFeign

使用OpenFeign

创建新消费服务consumer-openfeign-80来测试

基本配置与上一个一样

在pox.xml引入

<dependency>
org.springframework.cloud
spring-cloud--starter-openfeign
</dependency> 

//引入场景启动器

配置yml文件

port:80
name:e-commerce-consumer-openfeign-80
eureka:...   //类似之前的

主启动器

@SpringBootApplication
@EnableEureaClient
@EnableFeignClient    //启用Open Feign
...

不同的是,定义远程调用接口

@Compoent
@FeignClient(value = "MEMBER-SERVICE-PROVIDER")      //这定义url
public interface MemberFeignService{
   

 //结合上面的注解就是调用远程url http://MEMBER-SERVICE-PROVIDER/member/get/{id}

    @GetMapping("/member/get/{id}")                           
    public Result getMemberById(@PathVariable("id") Long id);  //定义方法  

}

还要来个Controller

@RestContoller
public class MemberConsumerFeignController{
     

    接口代理对象装配进去
    @Resource
    private MemberFeignService memberFeignService;
    
    @GetMapping(value = "/member/.../{id}")
    public Result getMemberById(@PathVariable("id" Long id){
      memberFeignService.getMemberById(id);
    }
}

OpenFeign 好处是支持MVC注解+接口解耦

Feign提供日志打印功能,可调控日志级别对Feign接口调用情况监控

级别:

NONE:默认的,不显示日志

BASIC:只记录请求方法,url,响应头信息

HEADERS:包含上面信息,还要请求和响应的头信息

FULL:包括以上全部还有请求和响应的正文

一般不用

网关服务SpringCloud Gateway

可对提供统一的调用接口,根据不同请求url,转发到对应的后端服务(配置即可),还具备限流,熔断,鉴权,负载均衡,反向代理等能力

Gateway是在Spring生态系统上构建的API网关服务,旨在对API进行路由,以及提供上面所述功能,比Zuul强太多故不用Zuul

Gateway核心组件:    

路由(Routing):构建网关的基本模块,由ID,目标URI,一系列断言和过滤器组成,断言为true则匹配该路由

断言(Predicate):对HTTP请求中所有内容(请求头和请求参数)进行匹配,若请求与断言匹配则路由

过滤(Filter):在请求被路由前或之后对请求进行处理,如对Http请求断言匹配成功后,可以通过网关过滤机制对http请求处理(如修改请求带上参数)

大致工作机制

客户端发送请求 -> Spring Cloud Gateway -> 在Gateway Handler Mapping找到与请求相匹配的路由,将

其发送给Gateway Web Handler -> Handler通过过滤器链执行逻辑

构建GateWay项目架构(本身也是微服务模块)

本身是服务消费方的升级,故构架可参考消费方

e-commerce-gateway-20000:

pom.xml

//引入gateway
<dependency>
 org.springframework.cloud
 spring-cloud-starter-gateway
</dependency>

PS:得注销actuator和 spring-boot-starter,否则会出错启动不起来
他是服务网关,不需要web

yml文件(配置路由也可以写个配置类但通常就在yml文件):

port:20000
name:e-commerce-gateway

cloud:
 gateway:
  routes:                                      //配置路由,可多个,内部是List
     - id: member_route01                       //路由id唯一
      uri: http://localhost:10000              //服务端的uri,gate最终访问:url=uri+path
      predicates:                              //此为断言,自己规定规则
        - Path=/member/get/**                    

Euera:...9001                               //配置Eureka,只注册一个

接上例如 客户端/浏览器 请求http://localhost:20000/member/get/1

只有前(蓝色)后(红色)都匹配成功才访问到http://localhost:10000/member/get/1,匹配失败则404

PS:此处uri太单调,后期会调灵活

主启动器

@SpringBootApplication
@EnableEurekaClient

PS:主程序启动顺序:Eureka注册中心 9001 => 提供服务端10000 => 网关20000

现在将路由改为动态路由

如 将服务器接口切换成10000或10002

前提得将10000和100002注册到Eureka

    uri:lb//member-service-provider  
    #lb协议名,member-service-provider是注册到Eureka的服务名(必须小写),
    #默认情况下负载均衡算法轮询
    predicates:
      - Path=/member/get/**

这样每次服务器/客户端发送http请求,Eureka会切换不同的接口给它(负载均衡),负载均衡算法可以自己定义,前面说过

Predicate/断言(细节)

就是一组匹配规则,当匹配成功就执行对应Route,失败则放弃处理/转发,Spring Cloud Gateway,包含许多内置Route Predicate工厂,这些Predicate都与http请求不同属性匹配,组合使用

比如时间:After,Before

http请求:请求头,带参数

 组合:After Route Predicate,Before Route Predicate,Between Route Predicate...

例一:只有2023-11-18 12:35:50之后的请求才能匹配,否则不处理
...
 predicates:
    - Path=/member/get/**
    - After=2023-11-18T12:35:00.000+08:00[Asain/Shanghai]

//8:00为毫秒

PS:必须满足两个条件才能匹配

例二:请求带有cookie键:user值:liwei 才能匹配成功
 predictes:
  - Cookie=user, liwei        //注意逗号后面有空格
例三:请求头有Id 值为hello
predicates:
  - Header=Id, hello
例四:请求参数有email并满足电子邮件基本格式,才能成功(如localhost:20000/.../email=...)
predicates:
  - Query=eamil, (正则表达式)
例五:需客户端请求ip为127.0.0.1
predicates:
  - RemoteAddr=127.0.0.1
补充path可定义多个路径中间用逗号隔开即可
...
predicates:
  - Path=/member/get/**,/member/save

Filter(过滤器)

在请求被路由前或之后对请求进行处理,如对Http请求断言匹配成功后,可以通过网关过滤机制对http请求处理(如修改请求带上参数)

大致分为GatewayFilter和GlobalFilter,即 官方自带(使用较少) 和 用户自定义(常用)

例:GatewayFilter在进行断言后再添加参数
filter和uri,predicates是一个层级的

uri:lb//member-service-provider
predicates:...
filters:
 - AddRequestParameter=color,blue

传入请求http://localhost:10000后经过过滤器后变为http:localhost:10000?color=blue

例:自定义GlobalFilter,如果请求参数user=sb,pwd=123456则放行,否则不能通过

自定义Filter类:

@Compoent
public class GatewayFilter implements GlobalFilter,Ordered{

  @Override
   filter方法(exchange,chain){



   //先获取对应参数值,如 http://.../get/1?user=sb&pwd=123456
   String user =
      exchange.getRequest().getQueryParams().getFirst("user");
   String pwd =
      exchange.getRequest().getQueryParams().getFirst("pwd");
   if(!("sb".equals(user) && "123456".equals(pwd))){                      #如果不满足
      exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
      return exchange.getResponse().setComplete();
   }
      return chain filter(exchange);         //满足则带过
   }

   
   @Override
   getOrder方法(...){                   //表示过滤器执行顺序,数字越小优先级越高
        
   }
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值