Spring Cloud (1)

SpringCloud

微服务定义

  1. 是一种架构风格
  2. 一系列微小的服务共同组成
  3. 每一个服务跑在本身的进程里
  4. 每一个服务为独立的业务开发
  5. 独立部署
  6. 分布式的管理

image-20180920185214111

架构形态

单体架构

image-20180920185310014

特色:
  1. 打包成一个war包,没有外部依赖
  2. 共用一个db
  3. 容易测试
  4. 容易部署
  5. 开发效率低
  6. 代码维护困难
  7. 部署不够灵活html

    • 构建时间特别长
  8. 稳定性不高
  9. 扩展性不够,不能知足高并发需求

基于ajax的先后端分离

image-20180920185619575

分布式架构

定义:支持应用程序和服务的开发,能够利用物理架构和多个自制的处理元素(多节点),不共享主内存,但经过网络发送消息前端

简单的微服务

image-20180920185838902

基础框架组件

服务注册发现

服务网关

  • 链接内外大门
  • 屏蔽后台细节,让用户无感知
  • 路由功能,外部请求反向路由到内部某个服务
  • 网关功能,控制流量,监控和日志java

    • 用户认证
    • 受权
    • 服务网关

前端服务(边缘服务)

聚合

把两个接口聚合在一块儿返回出去mysql

裁剪

经过不一样需求返回不一样数据,pc和手机端淘宝返回详情数据不一致git

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

阿里系

Dubboweb

Zookeeperajax

Spring MVC or SpringBootspring

Spring Cloud

定义

  • 是一个开发工具集
  • 利用spring boot的开发遍历
  • 基于对netflix 开源组件的进一步封装
  • 简化了分布式开发

来源

基于Netflix Eureka作了二次封装sql

文档

http://projects.spring.io/spr...docker

版本查看

image-20180920192026017

Eureka Server(注册中心)

-Dserver.port=

pom文件

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

配置方式

image-20180920204856378

image-20180920204912734

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8080/eureka/
    # 不把本身注册到注册中心
    register-with-eureka: false
# 设置应用名
spring:
  application:
    name: eureka

打包

mvn clean package install

Eureka Client(服务注册)

pom文件

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

yml配置方式

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

image-20180920231533665

开启注解

@EnableDiscoveryClient@EnableEurekaClient区别

若是注册中心是eureka那么推荐使用EnableEurekaClient`

若是是其余注册中心那么推荐使用Enable

Eureka的高可用

image-20180920232137448

两个eureka都能看到client的信息

若是一个client挂掉了那么两个eureka都没有信息

解决:

image-20180920232310374

多台

image-20180920232433410

分布式中为何须要服务发现

image-20180920232826038

a和b沟通彻底经过注册中心

服务端发现

  • Nginx
  • Zookeeper
  • Kubernetes

客户端发现

  • Eureka

微服务的特色

  1. 异构
  2. 不一样类型的数据库
  3. Spring Cloud经过rest方式实现服务注册
  4. Node.js的eureka-js-client

服务拆分方法

  1. 分清楚起点和终点
  2. 了解现有架构是否支持微服务架构
  3. 架构是不断引进的

不适合微服务的系统

  1. 系统中包含不少强事物场景的
  2. 业务相对稳定,迭代周期长
  3. 访问压力不大,可用性要求不高

康威定律

任何组织在设计一套系统时,所交互的设计方案在结构上都与该组织的沟通结构保持一直

微服务特色

  • 一系列微小的服务共同组成
  • 单独部署,跑在本身的进程里
  • 每一个服务为独立的业务开发
  • 分布式的管理

image-20180921113617844

扩展立方模型

image-20180921113854873

  • X轴 水平复制
  • Y轴功能解耦

    • 单一职责,每一个服务负责单一功能
    • 相关功能汇集在一个服务内
    • 关注点分离

      • 职责分离
      • 通用性分离

        • 基础组件划分
        • 消息服务
        • 用户服务
        • 公共组件拆分
      • 粒度分离

        • 不是越小越好
  • Z轴 数据分区

点餐业务拆分

拆分服务

image-20180921114336491

服务拆分方法论

  • 每一个微服务都有单独的数据存储
  • 依据服务特色选择不一样结构的数据库类型

    • 搜索->elasticsearch
    • 订单类-》mysql
  • 难点在难以肯定边界

    • 针对边界设置api
    • 依据边界权衡数据冗余

应用通讯

调用的三种方式

  1. 直接使用RestTemplate

    • String s = restTemplate.getForObject("http://localhost:8083/hello", String.class);
  2. 使用LoadbalanceClient

    • ServiceInstance serviceInstance = loadBalancerClient.choose("PRODUCT");
      String url = String.format("http://%s:%s", serviceInstance.getHost(), serviceInstance.getPort() + "/hello");
      String result = restTemplate.getForObject(url, String.class);
  3. 使用注解

    • @Bean
      @LoadBalanced
      public RestTemplate restTemplate() {
          return new RestTemplate();
      }

客户端负载均衡器(Ribbon)

  • RestTemplate
  • Feign
  • Zuul

发现方式

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

主要组件

  • ServerList
  • IRule
  • ServerListFilter

源码解析

  1. image-20180925174557136
  2. image-20180925174625714
  3. image-20180925174655200
  4. 用来获取全部列表image-20180925174741878
  5. 轮循策略默认是roundRobinRuleimage-20180925174826179

    image-20180925175053677

    修改默认轮询策略

    1. http://cloud.spring.io/spring...

      users:
        ribbon:
          NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule
    2. 轮循策略image-20180925175858487

Feign使用(内部也使用了rabbion作负载均衡)

  1. Pom文件

    1. <dependency>
          <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-starter-feign</artifactId>
         </dependency>
  2. 启动主类增长注解@FeignClient
  3. 编写接口

    1. @FeignClient("product")
      public interface FeignConfig {
          @GetMapping("hello")
          String hello();
      }
  4. 注入和调用

    1. @Autowired
          private FeignConfig feignConfig;
          
          @RequestMapping("/feign")
          public String feign() {
              String result = feignConfig.hello();
              return result;
          }

多模块拆分服务

问题

  1. 商品和订单dto重复定义
  2. 同一个对象屡次重复定义
  3. 订单服务里面不该该知道商品服务的uri

模块拆分

  • product-server

    • 业务逻辑
  • product-client

    • 对外暴露的接口
  • Product-common

    • 公用的对象
  • 依赖关系

    • image-20180926111404689

打包命令

  • mvn -Dmaven.test.skip=true -U clean install

同步仍是异步

  1. image-20180926171455253

    1. 消费者经过消息中间件进行解耦
    2. 用户调用短信服务,积分服务,其余服务,服务耦合过大,用户登陆成功须要多个服务同步响应后才告诉成功
  2. image-20180926171800592

    1. 商品服务库存变化发布消息,订单服务订阅消息,好比商品信息

      1. 订单不须要查询商品服务而是查询本身服务中的信息
      2. 保证数据最终一致性,只须要订阅对应服务就能保证
  3. 常见消息队列

    • RabbitMQ
    • Kafka
    • ActiveMQ

RabbitMQ安装

5672->默认RabbitMQServer端口

15672->RabbitMQ管理页面端口,页面只须要配置这个

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

默认密码:guest

Docker和DevOps

  1. docker可以解决不一样环境下应用程序都能运行
  2. 轻量
  3. 进程隔离和管理
  4. 可复用,版本化(tag机制)
  5. 微服务架构师核心,devops和docker是手段

统一配置中心(Spring Cloud Config)

缘由

  1. 配置方式不方便维护
  2. 配置内容与权限(针对线上)
  3. 每更新一个配置都要重启项目

ConfigServer使用

image-20180926175911819

configServer从远端git拉下配置放到本地git若是远端不可用使用本地的

  • pom
  1. <dependency>
         <groupId>org.springframework.cloud</groupId>
         <artifactId>spring-cloud-config-server</artifactId>
     </dependency>
     <dependency>
         <groupId>org.springframework.cloud</groupId>
         <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
     </dependency>
  • 开启注解

    @EnableDiscoveryClient
    @EnableConfigServer
  • 没有配置对应的gituriimage-20180926181337220
  • 在git上新建项目

    • image-20180926182910218
  • 在配置中填写对应git uri,username,password
  • image-20180926183121728
  • 访问对应配置文件

    1. .properties展现为properties ,.yml为yml
    2. /{label}/{name}-{profiles}

      1. label->分支(不写默认master)
      2. name->文件名
      3. profiles->环境
  • 设置配置文件基础目录

    • spring.cloud.config.server.git.basedir

ConfigClient使用

导入依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-config-client</artifactId>
</dependency>

更改配置

spring:
  application:
    name: order
  cloud:
    config:
      discovery:
        enabled: true
        service-id: config
      profile: dev

修改application.yml

修改成bootstrap.yml这样会优先启动

注意

配置中心会读取拼接order.yml+order-xx.yml的内容

Spring Cloud Bus

经过git hook访问配置文件变动,同步信息到消息队列,(/bus/refresh)

Config-server 经过消息队列同步到其余服务

使用

更新版本

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.0.BUILD-SNAPSHOT</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring-cloud.version>Finchley.BUILD-SNAPSHOT</spring-cloud.version>
    </properties>

导入jar包

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

yml开放端口用来刷新对应变动

management:
  endpoints:
    web:
      exposure:
        include: bus-refresh

访问端口刷新配置

curl -v -X POST "http://localhost:8991/actuator/bus-refresh"

须要变动的类上面加上注解@RefreshScope

@RestController
@RequestMapping("/env")
@RefreshScope
public class EnvController {
    @Value("${env}")
    public String env;

    @GetMapping("/profile")
    public String getenv() {
        return env;
    }
}

经过 git HOOK自动访问地址

image-20180928005701388

有bug

异步和消息

定义

  1. 异步:客户端请求不会阻塞进程,服务端的响应能够是非及时的

    1. 通知
    2. 请求异步响应
    3. 客户端不会阻塞
    4. 经过消息实现一对多

      1. 客户端发送请求消息等待服务端响应

MQ

应用场景

  1. 异步处理

    1. 用户注册后须要发短信和加积分
  2. 流量削锋

    1. 秒杀场景,丢弃请求,控制活动人数
  3. 日志处理(kafka)

    1. 经过日志采集定时写入队列
  4. 应用解耦

    1. 用户下单后须要调用商品服务,使用mq用户下单后把消息写入商品服务,商品使用拉或者推的方式,订单服务写入订单后就能够不关注后续流程了

使用

导入依赖

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

yml配置

spring:
  application:
    name: order
  cloud:
    config:
      discovery:
        enabled: true
        service-id: config
      profile: test
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    guest: guest

发送方式

@Autowired
    private AmqpTemplate amqpTemplate;

    @Test
    public void send() {
        amqpTemplate.convertAndSend("myQueue", "now time:" + System.currentTimeMillis());
    }

接收方式

第一种(须要手动建立队列)

image-20180928173139793

@Slf4j
@Component
public class MqReceiver {
    @RabbitListener(queues = "myQueue")
    public void process(String message) {
        log.info("MqReceiver:{}", message);
    }
}
第二种 自动建立队列
/**
     * 自动建立队列
     *
     * @param message
     */
    @RabbitListener(queuesToDeclare = @Queue("myQueue2"))
    public void autoCreateQueue(String message) {
        log.info("autoCreateQueue:{}", message);
    }

第三种 Exchange和Queue绑定

@RabbitListener(bindings = {@QueueBinding(
            value = @Queue("myQueueExchange"),
            exchange = @Exchange("myExchange")
    )})
    public void exchange(String message) {
        log.info("myExchange:{}", message);
    }

image-20180928174927568

消息分组使用第三种方式

@RabbitListener(bindings = @QueueBinding(
        exchange = @Exchange("myOrder"),
        key = "computer",
        value = @Queue("computerOrder")
))
public void computerOrder(String message) {
    log.info("computerOrder:{}", message);
}

@RabbitListener(bindings = @QueueBinding(
        exchange = @Exchange("myOrder"),
        key = "fruit",
        value = @Queue("fruitOrder")
))
public void fruitOrder(String message) {
    log.info("fruitOrder:{}", message);
}
@Test
    public void sendToProduct() {
        amqpTemplate.convertAndSend("myOrder", "computer", "now time:" + System.currentTimeMillis());
    }

Spring Cloud Stream

为微服务应用构建消息能力的应用,对于消息中间件的封装,代码对于中间件的无感知,可是只支持rabbitMQ 和Kafka

image-20180929181444018

使用

导入pom
<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
        </dependency>
定义接口
public interface StreamClient {
    @Input("myMessage")
    SubscribableChannel input();

    @Output("myMessage")
    MessageChannel output();
}
接受
@Component
@EnableBinding(StreamClient.class)
@Slf4j
public class StreamReceiver {
    @StreamListener("myMessage")
    public void process(Object message) {
        log.info("StreamReceiver:{}",message);
    }
}
发送
@RestController
public class SendMessageController {
    @Autowired
    private StreamClient streamClient;

    @GetMapping("/sendMessage")
    public void process() {
        String message = "now " + new Date();
        streamClient.output().send(MessageBuilder.withPayload(message).build());
    }
}

参考:https://coding.imooc.com/learn/list/187.html

相关文章
相关标签/搜索