AW07-Event Driven Architecture

https://spring.io/projects/spring-cloud-stream

https://github.com/sa-spring/stream-loan

https://piotrminkowski.com/2020/06/05/introduction-to-event-driven-microservices-with-spring-cloud-stream/

调研

开发

添加orderService,配置好事件驱动

source/发送者用的是supplier,连续的发送事件,但是我的期望设计是由特定事件驱动,需要用到streamBridge.参考

处理者也可以返回消息,需要使用consumer参考

测试

先测试事件驱动架构,在自动化生成请求的情况下;

测试pos-order和pos-delivery在有网页访问的情况下

测试由pos-cart驱动pos-order

Tech Solution

伪代码+配置文件

pos-order

  • 配置文件

    copy pos-cart

    spring.io 生成一个micro service文件

    作为微服务的配置/discovery配置/gateaway配置/

    cloudstream 配置 ???

    直接去掉?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    spring:
    cloud:
    function:
    definition: placeOrder
    stream:
    function:
    bindings:
    deliver-order: String
    bindings:
    loan:
    destination: output
  • 补充api

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    /order/place
    post:
    sumary: place an order
    operationId: placeOrder
    tags:
    - order
    requestBody:
    description: order info
    content:
    application/json:
    schema:
    type: string
    required: true
    responses:
    '200':
    description: Expected
    content:
    application/json:
    schema:
    type: boolean
    default:
    description: Unexpected
    content:
    application/json:
    schema:
    $ref: "#/components/schemas/Error"

    //Info format: productA|productB|addr:CityA

  • rest

    • restController implements OrdersApi
    • override method 调用service中方法
    1
    2
    3
    4
    5
    6
    public ResponseEntity<boolean> placeOrder(@RequestBody String orderInfo){
    boolean res = orderService.placeOrder(orderInfo);
    if(!res)
    return new ResponseEntity<>(HttpStatus.NOT_FOUND);
    return new ResponseEntity<>(res,HttpStatus.OK);
    }
  • service

    • OrderService

      • placeOrder
    • OrderServiceImpl

      • EDA-Impl
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      @Service
      OrderServiceImp implements OrderService{
      StreamBridge streamBridge
      @Override
      boolean placeOrder(String orderInfo){
      //calculate number of products
      for(str : info){
      if(str.startsWith("addr:")){
      addr = new StringBuilder(str.substring(4)).toString();
      }
      else
      cnt++;
      }
      String deliveryInfo = String.format("%d|%s",cnt,addr);
      return streamBridge.send("deliver-order",deliveryInfo);
      }
      }
      //单向

postman跑不动:用curl来测试

1
curl -H "Content-Type:application/json" -X POST -d '{"orderInfo":"3|CityA}"' http://localhost:8085/api/order/place

pos-delivery

  • 配置:类似loan-checker √

    • application.yml
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    spring:
    cloud:
    function:
    definition: #Consume方法名
    stream:
    function:
    bindings:
    checkOrder-in-0: receive
    #consumer方法名-in-0: 发送方send的首个参数
    bindings:
    #发送的消息名称 保持一致
    order-declined:
    destination: order-declined
    order-approved:
    destination: order-approved
  • DeliveryApplication √

    1
    2
    3
    4
    5
    main{}
    @Bean
    public Consumer<String> deliverCart(){
    return new CartDelivery();
    }
  • CartDelivery √

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    CartDelivery implements Consumer<String>{
    public static final Logger log = LoggerFactory.getLogger(CartDelivery.class);

    private StreamBridge streamBridge;
    @Override
    public void accept(String info){
    //number to calculate fee
    String[] infos = info.split("\\|");
    int cnt = Integer.parseInt(infos[0]);
    double fee = 1.25*cnt;
    String msg = String.format("deliver %d product(s) to %s,fee is %f",cnt,infos[1],fee);
    log.info(msg+"\n");
    streamBridge.send("delivery",message(msg));
    }
    private static final <T> Message<T> message(T val) {
    return MessageBuilder.withPayload(val).build();
    }
    }

    进阶版:添加DeliveryInfo 记录重量和地区信息

cart中添加用rest发送消息的controller方法 参考

在CartServiceImp:checkout方法中添加生成信息

todo:在vmware上做

discovery&&gateway能正常运转

其他微服务controller问题

cart中添加调用

一些插曲——遇到的问题和解决措施

因为内存不足在另一台虚拟机上重配了环境,配置maven的时候需要移除旧的未成功安装的Java环J境并重新配置Java环境

持久化javahome设置

export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64/

/usr/local/src/java/jdk-18.0.1

springboot公共类打包成依赖

1
mvn install:install-file -Dfile=/home/avril/SA-2022/HW07/stream-loan/loan-model/target/loan-model-0.0.1.jar -DgroupId=com.example -DartifactId=loan-model -Dversion=0.0.1 -Dpackaging=jar

换环境后maven-compiler-plugin版本过高

rest-controller无法调用:

  • api之后不需要写getMapping
  • 扫描包