Springcloud Basic

架构演变

单体架构

single

  • 容易测试,没有外部依赖
  • 容易运行
  • 开发效率低
  • 维护困难
  • 部署不灵活,整体部署
  • 稳定性不高
  • 拓展性不够

基于Ajax前后端分离

ajax

ajax2

分布式系统与集群

  • 分布式:不同工种
  • 集群:同一工种

简单的微服务架构

micro

微服务的架构和组件

服务注册发现

  • 服务提供方的注册
  • 服务调用方的发现

服务网关

  • 将服务暴露给外部
  • 屏蔽后台服务细节
  • 路由功能
  • 限流、容错

后端通用服务(中间层服务)

前端服务(边缘服务)

  • 将后端服务聚合裁剪,从而暴露给外部
  • 聚合:多个API聚合为一个
  • 裁剪:将信息减少返回

Eureka

  • Eureka Server

    注册中心

  • Eureka Client

    服务注册

心跳检测、健康检查、负载均衡

Eureka Server

记录应用的信息和状态

建立Eureka项目并配置

注意@EnableEurekaServer

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
    register-with-eureka: false
  server:
    enable-self-preservation: false
spring:
  application:
    name: eureka
server:
  port: 8761

eureka

使用maven后台启动

mvn clean package

java -jar

Eureka Client

注意@EnableDiscoveryClient

配置

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
#   instance:
#     hostname: clientName
spring:
  application:
    name: client

hostname可以diy此处的链接

diyurl

Eureka高可用

使两个Eureka互相注册

eu2

同时启动两个eureka在两个端口

eu3

使这两个交叉相互注册

两个eureka信息同步(client只需要注册一个)

为保险起见,应该在client中配置多个eureka注册

eu4

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/,http://localhost:8761/eureka/
#    instance:
#      hostname: clientName
spring:
  application:
    name: client

结构:eureka两两注册,client注册所有eureka

eu5

分布式系统中服务发现的作用

避免了动态配置和配置过多问题

注册中心是分布式系统中最重要的部分

eu6

  • 客户端发现

    A来挑选B从而完成服务或者负载均衡

    客户可以知道所有可用的服务地址

    Eureka

  • 服务端发现

    代理替代A来挑选B,完成服务与负载均衡

    使得服务对A不可见

    Nginx

    Zookeeper

    K8S

微服务支持异构(不同语言、数据库),通过轻通讯(http&RPC)

服务的拆分

康威定律

交付结构与沟通结构一致

扩展立方模型

fu1

  • 拆功能

    单一职责(松耦合,高内聚)

    关注点分离:按职责、通用性、粒度级别

  • 服务与数据

    优先考虑业务服务,再考虑数据

    无状态服务

应用间通信

RPC

  • Dubbo

HTTP

  • Springcloud

    1、RestTemplate

    2、Feign

RestTemplate三种方式

tran

第三种,使用注解

tran1

tran2

Ribbon

客户端负载均衡器@LoadBalanced注解

  • 服务发现
  • 服务选择规则
  • 服务监听

主要组件:ServerList、IRule、ServerListFilter

默认轮询规则,可以在yml文件中设置具体规则

serviceName: 
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

Feign

声明式Rest客户端(伪RPC)本质是个http客户端

接口+注解方式

  • 加入Feign组件

  • @EnableFeignClients

  • 创建接口

调用远程服务

tran3

service端

tran4

同步&异步

逐步将必要的同步过程改为异步(后面详解)

安装rabbitmq

docker run -d –hostname my-rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:management

统一配置中心

普通配置不方便维护、存在安全隐患、需要重启等问题

Config Server

conf1

将参数放在远程git上并配置

conf3

conf5

basedir配置本地路径

路由规则:

conf2

Config Client

创建bootstrap.yml配置文件

conf4

拿到相应配置

conf6

加载config-test.yml时,也会同时加载config.yml(如果存在),并合并两份配置,所以可以把通用配置存于config.yml

配置中心的高可用

多起几个配置中心,配置中心本身就是一个微服务

conf7

自动刷新配置

conf8

引入依赖

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bus-amqp</artifactId>
            <version>2.0.0.RELEASE</version>
</dependency>

conf9

使config server和client连通mq

conf10

需要在config server中暴露接口

management: 
  endpoints: 
    web:
      expose: "*"

conf11

更改controller注解,刷新配置

做到git实时刷新

conf12

设置git WebHooks

conf13

消息与异步

  • 异步

    客户端请求不会阻塞进程

    服务端的响应是非及时的

异步的常见形式:通知、请求/异步响应、消息

MQ常解决的问题:异步处理、流量削峰、日志处理(Kafka)、应用解耦

RabbitMQ

spring:
  application:
    name: config
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/

接受MQ消息

conf14

发送MQ,test

conf15

绑定queue和exchange

conf16

根据routing key转发到不同终端

Spring Cloud Stream

对消息中间件进一步封装、做到中间件不可见(只适用rabbitmq、Kafka)

conf17

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>

定义接口

conf18

实现接收端

conf19

定义发送端

conf20

多实例发送消息到mq,会多次接受,因而需要使用分组

spring:
  cloud:
    stream: 
      bindings:
        myMessage:
          group: groupname

同组队列将合并

在MQ面板,反序列化对象为json方便查看

spring:
  cloud:
    stream: 
      bindings:
        myMessage:
          group: groupname
          content-type: application/json

接受消息后再返回信息

conf21

服务网关与Zuul

网关同一入口

zuul1

  • 稳定性、高可用
  • 安全性
  • 性能、并发性
  • 扩展性

常见的网关方案

  • Nginx + Lua
  • Kong
  • Tyk
  • Spring Cloud Zuul

Zuul特点

Zuul=路由+过滤器

四种过滤器API

  • 前置(Pre)
  • 后置(Post)
  • 路由(Route)
  • 错误(Error)

zuul2

zuul3

Zuul的使用

1、导入依赖

2、添加enable注释@EnableZuulProxy

3、修改配置文件

通过路由得到对应服务

localhost:9000/service-name/list

添加规则路由

zuul

修改安全策略,查看路由详情

zuul4

zuul5

或者简洁写法

zuul6

可以添加忽视路由

ignored-patterns:
  - /**/ignore/list

zuul会默认过滤cookie,加入敏感头过滤,设置为空

zuul:
  routes: 
    myProduct:
      sensitiveHeaders:

动态配置路由

zuul7

在github webhook激活动态配置

高可用

部署多台Zuul避免单点故障

直接作为微服务注册在Eureka Server上

与Nginx混搭

Pre&Post过滤器

Zuul在各个Service之前进行统一校验,避免多服务重复校验

zuul8

  • prefilter

    设置必须包含token认证

@Component
public class TokenFilter extends ZuulFilter {
    @Override
    public String filterType() {
        return PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        return PRE_DECORATION_FILTER_ORDER - 1;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() throws ZuulException {
        RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletRequest request = requestContext.getRequest();

        String token = request.getParameter("token");
        if(StringUtils.isEmpty(token)){
            requestContext.setSendZuulResponse(false);
            requestContext.setResponseStatusCode(HttpStatus.SC_UNAUTHORIZED);
        }
        return null;
    }
}

请求生命周期

zuul9

pre->routing->post

  • postfilter
@Component
public class AddResponseHeaderFilter extends ZuulFilter {

    @Override
    public String filterType() {
        return POST_TYPE;
    }

    @Override
    public int filterOrder() {
        return SEND_RESPONSE_FILTER_ORDER - 1;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() {
        RequestContext requestContext = new RequestContext();
        HttpServletResponse response = requestContext.getResponse();
        response.setHeader("X=Foo", UUID.randomUUID().toString());
        return null;
    }
}

限流

请求被转发前调用

令牌桶算法

zuul10

@Component
public class RateFilter extends ZuulFilter {
    // 100 allowed per second
    private static final RateLimiter RATE_LIMITER = RateLimiter.create(100);


    @Override
    public String filterType() {
        return PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        return SERVLET_DETECTION_FILTER_ORDER - 1;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() {
        // check if it has allowed
        if (!RATE_LIMITER.tryAcquire()){
            throw new RateLimitException();
        }
        return null;
    }
}

跨域

  • 在被调用的类或方法上增加@CrossOrigin注解
  • 在Zuul里增加CorsFilter过滤器

zuul12

服务容错与Hystrix

雪崩效应

由于调用失败重复重试、导致整个系统不可用

zuul13

服务降级

优先核心服务、非核心服务不可用或弱可用

通过HystrixCommand注解指定

fallbackMethod函数中(回退函数)具体实现降级逻辑

引入依赖

zuul14

添加注解@EnableCircuitBreaker

zuul15

服务出错后执行回滚函数,如果出现异常,同样回滚,因而可以自己定制回滚情况

zuul16

从而实现统一降级

如果访问时间过长会自动触发降级,因而可以配置超时时间

zuul17

@HystrixCommand(commandProperties = {
            @HystrixProperty(name = "default_executionTimeoutInMilliseconds",value = "3000")
    })

或者写在yml配置文件中

zuul20

服务熔断

zuul18

zuul19

时间窗口、最小请求数、断路阈值百分比

Feign与Hystrix结合

Feign中集成了Hystrix,需要配置可用

feign:
  hystrix: 
    enabled: true

zuul21

可视化

@EnableHystrixDashboard

引入依赖

zuul22

zuul23

management:
  context-path: /

zuul24

服务追踪

Sleuth链路监控

引入依赖

sl1

配置日志级别

logging:
  level:
    org.springframework.cloud.netflix.feign: debug

执行后,查看日志

sl2

opentrace标准

划分为四种事件类型

sl3

Zipkin

sl4

sl5

  • Copyrights © 2019-2020 Rex