系统架构

  • 单体架构
  • 一个应用打包成一个服务,部署到一个服务器上
  • 适合小项目,维护成本低
  • 问题:代码耦合、迭代困难、扩展受限、阻碍创新、技术债务
  • 分布式架构
  • 按功能拆分系统,拆分为多个应用,分别部署到不同的服务器
  • 一个子项目负责一个功能,功能解耦合
  • 可以为某一模块加集群
  • 问题:有业务的重叠(订单服务、用户管理)
  • SOA架构(面向服务的架构)
  • 系统整体拆分为服务层和表现层
  • 服务层封装了具体的业务逻辑供表现层调用
  • 表现层则负责处理与页面的交互操作
  • 将重复的代码抽象出来,形成统一的服务供其他系统或者业务模块来进行调用
  • 问题:出现服务集群地址硬编码的问题,需要增加一个注册中心来解决各个服务之间的注册与发现,抽取服务的粒度较大
  • 微服务
  • 在SOA架构的基础上进一步扩展,将其彻底拆分为一个个小的可以独立部署的微服务
  • 隔离性强:服务调用的隔离、容错、避免出现级联问题
  • 面向服务:微服务对外暴露Restful等轻量协议的接口
  • 单一职责:微服务拆分粒度更小,每一个服务都对应唯一的业务能力,做到单一职责,避免重复业务开发
  • 自治:独立打包、部署和升级,小团队的交付周期将缩短,运维成本也将大幅度下降

DubboX框架(服务远程调用)

  • Dubbo是阿里巴巴公司开源的一个基于Java的高性能RPC(远程调用)框架,后期阿里巴巴停止了该项目的维护,于是当当网在这之上推出了自己的Dubbox。
  • 节点角色说明:
    Provider: 暴露服务的服务提供方,服务消费者(controller、jsp、html)
    Registry: 服务注册与发现的注册中心,注册中心(zookeeper)
    Consumer: 调用远程服务的服务消费方,服务提供者(service、mapper)
    Container: 服务运行容器。
    Monitor: 统计服务的调用次调和调用时间的监控中心。

Zookeeper(注册中心)

  • zookeeper负责地址的注册与查找,是服务提供者和服务消费者的注册中心
  • 可以为分布式应用程序协调服务,适合作为Dubbo服务的注册中心
  • 服务提供方(Provider)暴露服务可被服务消费方(Consumer)使用dobbo协议远程调用服务

Ribbon(负载均衡)

  • Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具。
  • 负载均衡就是将负载(工作任务,访问请求)进行分摊到多个操作单元(服务器,组件)上进行执行。
  • 在nacos里面已经集成了ribbon的依赖,我们不需要去引入ribbon的依赖
  • Ribbon默认提供 很多种负载均衡算法,例如轮询、随机 等等。
  • 在consumer中使用Ribbon
  • application.yml
server:
  port: 80
spring:
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.145.131:8848 #nacos注册中心的地址
  application:
    name: ribbon-consumer #注册到nacos的服务名
  • ConfigBean
@Configuration
public class BeanConfig {
    /**
     * 拦截器
     *  1.通过ribbon-provider-->List<Service>--->使用负载均衡策略返回Service
     *  2.把ribbon-provider换成ip和端口
     */
    @LoadBalanced//开启负载均衡,默认是轮训策略
    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

    //随机策略
    @Bean
    public IRule iRule(){
        return new RandomRule();
    }
}
  • ConsumerController
@RestController
@RequestMapping(value = "/consumer")
public class ConsumerController {

    @Autowired
    private RestTemplate restTemplate;
    //springcloud提供的发现服务的工具栏
    @Autowired
    private DiscoveryClient discoveryClient;
    private int index;

    @RequestMapping("/getUserById/{id}")
    public User getUserById(@PathVariable Integer id){
        //List<ServiceInstance> instanceList = discoveryClient.getInstances("ribbon-provider");

        //随机策略
        //int currentIndex = new Random().nextInt(instanceList.size());

        //轮训策略
        //index = index+1;
        //int currentIndex = index % instanceList.size();

        //ServiceInstance instance = instanceList.get(currentIndex);
        //String url = "http://"+instance.getHost()+":"+instance.getPort()+"/provider/getUserById/"+id;

        String url = "http://ribbon-provider/provider/getUserById/"+id;
        return restTemplate.getForObject(url, User.class);
    }
}
  • RibbonConsumerApp
@SpringBootApplication
@EnableDiscoveryClient //开启nacos,允许注册当前服务并发现其他服务
public class RibbonConsumerApp {
    public static void main(String[] args) {
        SpringApplication.run(RibbonConsumerApp.class,args);
    }
}
  • ribbon问题:拼接url和参数显得好傻
    使用Feign可以解决url拼接的问题

Nacos(注册中心配置中心)

  • Nacos(Naming Configuration Sevice)是阿里开源的一个注册中心和配置中心
  • nacos的启动器
spring-cloud-starter-alibaba-nacos-config
spring-cloud-starter-alibaba-nacos-discovery
  • 具体使用
# 服务提供者:nacos_provider   &&    服务消费者:nacos_consumer
1.pom.xml
	web、nacos-discovery、springcloud_common
2.application.yml
    spring:
       cloud:
         nacos:
            discovery:
                server-addr: 192.168.145.131:8848
    spring:
       application:
         name: nacos-provider
            3、app
                @EnableDiscoveryClient
  • 配置中心——配置隔离
    namespace ------------------- 环境:dev、test
    group ------------------- 项目名:xx医疗系统、yy物流系统
    Dat Id ------------------- 工程名:配置文件名
  • nacos持久化
    原因:nacos自带derby数据库,集群环境下每台nacos的数据不一样
    1、修改conf/application.properties
    2、建库建表
    1.创建nacos库
    2.找到conf/nacos-mysql.sql并执行
    3、测试
    重启nacos,上传配置文件,测试是否把配置文件持久化到mysql
  • nacos集群搭建
数量:3台
3台原因:投票
投票原因:选leader
选leader原因:同步数据

Nginx(代理服务)

  • Nginx (engine x) 是一款轻量级的Web 服务器 、反向代理服务器及电子邮件(IMAP/POP3)代理服务器。
  • 反向代理:是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端。总结:反向代理就是代替服务器接受用户的请求,正向代理就是代理服务器代替用户去访问服务器
  • Nginx特点:反向代理 负载均衡 动静分离,高并发、高性能、高可靠性、可扩展性好、热部署、BSD许可证
  • 负载均衡:数据流量分摊到多个服务器上执行,多台服务器共同完成工作任务,提高了数据的吞吐量。
    6种负载均衡策略

负载均衡策略

说明

轮询

默认

weight

权重方式

ip_hash

依据ip分配方式

least_conn

按连接数

fair

按响应时间

url_hash

依据URL分配

  • 动静分离:将静态的资源放到反向代理服务器,节省用户的访问时间
  • 项目中的作用:在集群时,我们需要指定IP和端口号才能使用Nacos,使用Nginx后可以只使用IP就会自动调用集群中的其中一个
配置nginx代理nacos集群
    vim /usr/local/nginx/conf/nginx.conf:
    upstream nacosList{
        server 192.168.116.131:8848;
        server 192.168.116.131:8849;
        server 192.168.116.131:8850;
    }
    server{
        listen 90;
        server_name localhost;
        location /{
            proxy_pass http://nacosList;
        }
    }



Feign(封装请求 负载均衡)

  • 能解决的问题
    Ribbon使用RestTemplate调用其它服务的API时,所需要的参数须在请求的URL中进行拼接
  • RestTemplate:远程调用一个 HTTP 接口,这个类是 Spring 框架提供的一个工具类。
  • 简介
    feign是springcloud提供的声明式的http客户端,工作在consumer端
    feign支持springmvc注解
    feign集成了ribbon也支持负载均衡
  • feign原理
    1、将feign接口扫描到spring容器:
    @EnableFeignClients开启feign注解扫描:扫描被@FeignClient标识的接口生成代理类,并把代理类交给spring容器管理;
    2、为接口的方法创建RequestTemplate
    当consumer调用feign代理类时,代理类会调用SynchronousMethodHandler.invoke()创建RequestTemplate(url,参数,httpMethod);
    3、发出请求
    发请求时会通过RequestTemplate创建Request对象,然后client(HttpClient、OkHttp、UrlConnect)使用Request对象发送请求
  • feign接口传参
    1、?传参
    @RequestParam(“id”)
    2、restful传参
    @PathVariable(“id”)
    3、pojo传参
    @RequestBody
  • 具体使用
  • 服务消费者----》feign接口-----》服务提供者
  • feign启动器:spring-cloud-starter-openfeign
  • 创建feign_provider
  • 创建feign_interface
  • pom.xml
openfeign、springcloud_common
  • feign接口
@FeignClient("服务名")
public interface UserFeign{
    @RequestMapping("/getUserById/{id}") //请求的url
    public User getUserById(@PathVariable("id") Integer id);
}
  • 创建feign_consumer
  • pom.xml
feign_interface
  • controller
public class ConsumerController {
    @Autowired
    private UserFeign userFeign;//代理类
}
  • app
@EnableFeignClients
  • 优化
  • 开启feign日志
feign:
  client:
    config:
      default:
        loggerLevel: full #开启feign日志
logging:
  level:
    com.bjpowernode.feign: debug
  • http连接池
<dependency>
        <groupId>io.github.openfeign</groupId>
        <artifactId>feign-httpclient</artifactId>
</dependency>
  • gzip压缩
server:
  compression:
    enabled: true #浏览器<------>consumer的gzip压缩
feign:
  compression:
    request:
      enabled: true #consumer<------>provider的gzip压缩
    response:
      enabled: true
  • feign超时
#方式一
ribbon:
  ConnectionTimeout: 5000
  ReadTimeout: 5000
#方式二
feign:
  client:
    config:
      feign-provider:
        ConnectionTimeout: 5000
        ReadTimeout: 5000

Sentinel(哨兵 服务保护)

  • 使用的版本:1.8.1
  • 作用:微服务架构中,如果单个服务出现问题,可能拖死上游服务,造成整个链路中的所有微服务都不可用(服务雪崩)。使用服务保护组件来避免这个问题。
  • sentinel是阿里的一套开源的服务保护框架,主要作用:流量控制、熔断降级
  • 流量控制:
    qps:不被上游服务压死
    thread:不被下游服务拖死
  • 熔断降级:
    当慢调用或异常比例超过阈值,暂时切断对下游服务的调用,使用兜底方案,避免级联故障
流量控制
  • 流控规则
    1.阈值类型
    QPS:每秒访问次数
    线程数:线程数
    2.流控模式
    直接:对当前资源的流量控制
    关联:当关联的资源达到阈值时,限流自己
    链路:指定资源从入口资源进来的流量(api级别的针对来源)
  • 3.流控效果
    快速失败:直接流控
    Warm Up(预热):开始时阈值只有9/3,等5s后,阈值才提升到9
    排队等待:每秒接受1次请求,超过阈值则排队等待,等待时间为10s
  • 热点规则
    1.规则说明:将规则具体到参数是,eg:统计一段时间内最常购买(或频繁访问)的商品 ID 并进行限制
    2.参数例外项:给参数开个后门,eg:参数=2,则5秒钟之内可以携带20次
  • 系统规则
    流控规则是针对方法设定的,系统规则是针对一个应用设定的。eg:sentinel-consumer服务的所有接口qps=1
  • 授权规则
    根据调用来源来判断该次请求是否允许放行
    若配置白名单,则只有请求来源位于白名单内时才可通过;
    若配置黑名单,则请求来源位于黑名单时不通过,其余的请求通过。
熔断降级
  • 当满足条件的时候,切断对下游服务的调用,执行降级逻辑(兜底方案)

1.慢调用比例

属性

说明

最大RT(请求数量)

需要设置的阈值,超过该值则为慢调用

比例阈值

慢调用占所有的调用的比率,范围:[0~1]

熔断时长

在这段时间内发生熔断、拒绝所有请求

最小请求数

即允许通过的最小请求数,在该数量内不发生熔断

  1. 异常比例

属性

说明

异常比例阈值

异常比例=发生异常的请求数÷请求总数取值范围:[0~1]

熔断时长

在这段时间内发生熔断、拒绝所有请求

最小请求数

即允许通过的最小请求数,在该数量内不发生熔断

  1. 异常数

属性

说明

异常数

请求发生异常的数量

熔断时长

在这段时间内发生熔断、拒绝所有请求

最小请求数

即允许通过的最小请求数,在该数量内不发生熔断

整合Feign
  • 微服务远程调用都是基于Feign来完成的,可以将Feign与Sentinel整合,在Feign里面实现服务熔断
  • 配置feign,打开支持
feign:
  sentinel:
    enabled: true #开启Feign对Sentinel的支持
  • 为feign接口指定降级逻辑
@FeignClient(value="sentinel-provider",fallbackFactory = UserFeignFallback.class)
@RequestMapping(value = "/provider")
public interface UserFeign {
    @RequestMapping(value = "/getUserById/{id}")
    public User getUserById(@PathVariable("id") Integer id);
}
  • 一旦Feign远程调用服务失败,就会执行容错逻辑
@Component
public class UserFeignFallback implements FallbackFactory<UserFeign> {
    private Logger log = LoggerFactory.getLogger(UserFeignFallback.class);
    @Override
    public UserFeign create(Throwable t) {
        return new UserFeign() {
            @Override
            public User getUserById(Integer id) {
                return new User(id,"feign调用失败:"+t,0);
            }
        };
    }
}
持久化
  • 一旦服务重启,当前配置的针对某个接口的规则就丢掉了,使用持久化保存配置的规则
  • 将配置放到Nacos中
  • 使用sentinel-dashboard-nacos-1.8.1.jar,修改nacos的ip地址
  • 在nacos中配置jar中指定的命名空间名:sentinel

Gateway

  • 前端调用微服务的问题
  1. 客户端请求不同的微服务,就要维护不同的ip,硬编码问题
  2. 客户端无法实现负载均衡
    解决方案有两种:1.Ngnix+lua 2. Spring Cloud Gateway
  • 执行流程
  1. Gateway Client向Gateway Server发送请求
  2. HandlerMapping负责路由查找,并根据路由断言判断路由是否可用
  3. WebHandler创建过滤器链并调用
  4. 请求会一次经过PreFilter–微服务–PostFilter的方法,最终返回响应给Gateway Client
路由
  • 通过断言条件路由,浏览器访问:http://127.0.0.1:9527/consumer/getUserById/1
spring:
  cloud:
    gateway:
      routes:
        - id: sentinel-consumer #自定义的路由ID,保持唯一
          uri: http://localhost:80 # 请求要转发到的地址
          predicates: #断言
            - Path=/consumer/** #只有断言条件返回true(请求路径包含“/consumer”)时,才进行路由转发
  • 通过服务名路由,浏览器访问:http://127.0.0.1:9527/sentinel-consumer/consumer/getUserById/1
spring:
  cloud:
    gateway:
      routes:
        - id: sentinel-consumer #自定义的路由ID,保持唯一
          uri: lb://sentinel-consumer #lb代表从注册中心获取服务
          predicates: #断言
            - Path=/consumer/** #只有断言条件返回true(请求路径包含“/consumer”)时,才进行路由转发