一、序言:
Dapr作为微软开源的云原生架构,旨在解决应用间相互调用以及分布式事务控制等云原生的关键问题,并立足于从开发者角度提出的新一代云原生架构。
作为继承第三代微服务ServiceMesh理念的Dapr(第四代),贯彻SideCar模式的,并且把SideCar推到了一定高度。第一次接触dapr的同学可以在查看Dapr的官网前,了解下什么是SideCar以及其好处,个人认为SideCar是ServiceMesh的一种实现方式,也许在未来也会出现有另一种模式。
二、Dapr JAVA-SDK(invoke方法)
Dapr的文档中提到了其提供了很多的building-blocks构建块,其中便有服务间的调用称为invoke的功能。可以通过"/v1.0/invoke/"的HTTP调用获取对应目标方法的返回结果
(本篇不再叙述常规invoke方法通过sidecar端口调用的方式,例如http://localhost:3500/v1.0/invoke/{targetAppSidecarId}/method/{targetApiuri}的常规方式也可以获取目标服务接口结果)
以此实现服务间的方法调用,给大家展现了基于sidecar的ServiceMesh Faas服务云原生的优势。
本篇我们仅根据微软官网提供的JAVA-SDK简单讲解在java代码中用invoke()实现对其他服务方法的调用,并给出具体代码阐述。
三、Dapr java 中invoke()方法实现
首先给出构建两个java应用,dapr-demo-one和dapr-demo-two,我们将one作为client也就是客户端(发起invoke请求的一方)two作为service也就是服务端(提供服务的一方,提供正常的REST接口)。
首先,pom文件展示dapr javaSdk的依赖:(至于基础的spring等pom此处省略)
注意一点,在现存的dapr官网的javaSdk中,跑examples时会出现okhttp的报错和缺少v1.0package的错误,可能是sdk-examples引入的jar冲突或版本未适配,所以我们这里手动引入dapr javaSdk的1.1.0版本,并且补上1.1.0版本对于okhttp的版本要求为4.9.0(可以在官网javaSdk中1.1.0版本pom中查看该版本使用okhttp的版本)
<!-- okHttp3 -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.0</version>
</dependency>
<!-- dapr sdk -->
<dependency>
<groupId>io.dapr</groupId>
<artifactId>dapr-sdk</artifactId>
<version>1.1.0</version>
</dependency>
这里给出two的接口代码(service服务端):
package com.hcw.dapr.demo.controller;
import com.alibaba.fastjson.JSONObject;
import com.hcw.dapr.demo.dapr.dto.DaprDto;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
@RestController
@RequestMapping("/api/service")
public class DemoTwoController {
@GetMapping("/invoke/{id}")
public JSONObject invokeServiceTest(@PathVariable String id) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("id", id);
return jsonObject;
}
@PostMapping("/invoke")
public JSONObject invokeServicePostTest(@RequestBody @Valid DaprDto daprDto) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("name", daprDto.getName());
jsonObject.put("age", daprDto.getAge());
return jsonObject;
}
}
这里给出one的接口代码(客户端invoke发起者,需要使用dapr javaSdk)
注意:invokeMethod()方法中有一处"demo-two-dapr" 这里是需要手动填写的 代表的是对方应用的sideCar名称,也就是–app-id <> (应用sideCar的名称)(dapr规定了sidecar的名称必须是唯一无重复) 定义two服务端的sidecar是在开发时就已被开发者知晓,所以可以在代码中手动填写,当然也可以写配置文件中都可,后期该名称也可以在代码中定义成静态变量,这里仅是为了演示。
package com.hcw.dapr.demo.controller;
import com.alibaba.fastjson.JSONObject;
import io.dapr.client.DaprClient;
import io.dapr.client.DaprClientBuilder;
import io.dapr.client.domain.HttpExtension;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/client")
public class DemoOneController {
@GetMapping("/invoke/{id}")
public JSONObject invokeClientTest(@PathVariable String id) {
DaprClient daprClient = new DaprClientBuilder().build();
JSONObject jsonObject = daprClient.invokeMethod("demo-two-dapr", "/api/service/invoke/" + id,
null, HttpExtension.GET, null, JSONObject.class).block();
return jsonObject;
}
@GetMapping("/invoke")
public JSONObject invokeClientPostTest() {
JSONObject jsonObject = new JSONObject();
jsonObject.put("name", "HCW");
jsonObject.put("age", 666);
DaprClient daprClient = new DaprClientBuilder().build();
JSONObject result = daprClient.invokeMethod("demo-two-dapr", "/api/service/invoke",
jsonObject, HttpExtension.POST, JSONObject.class).block();
return result;
}
}
四、启动Dapr
可以采用dapr官网的dos命名启动对应jar的方式带起其对应的sidecar服务(例如dapr run --app-id myapp – java -jar myapp.jar。但是使用此方法需要避开sidecar的运行端口在localhost冲突,默认是http:3500/ grpc:50001,可以查看启动命令的其他参数来定义不同端口即可)。也可以使用博主的docker-compose方式启动;这几种方式在dapr官网都有例子可以查看。
给出docker-compose的脚本:(应用端口须和各自应用的application.yaml对应 例如server port 7001)
version: '3.7'
services:
#dapr-demo-one & dapr-demo-one-dapr 客户端
dapr-demo-one:
image: dapr-demo-one
ports:
- 7001:7001
networks:
- dapr-demo
dapr-demo-one-dapr:
image: "daprio/dapr:1.4.3"
command: [
"./daprd",
"-app-id", "demo-one-dapr",
"-app-port", "7001",
"-placement-host-address", "placement:50006"
]
depends_on:
- dapr-demo-one
network_mode: "service:dapr-demo-one"
#dapr-demo-two & dapr-demo-two-dapr 服务端
dapr-demo-two:
image: dapr-demo-two
ports:
- 7002:7002
networks:
- dapr-demo
dapr-demo-two-dapr:
image: "daprio/dapr:1.4.3"
command: [
"./daprd",
"-app-id", "demo-two-dapr",
"-app-port", "7002",
"-placement-host-address", "placement:50006"
]
depends_on:
- dapr-demo-two
network_mode: "service:dapr-demo-two"
#dapr placement
placement:
image: "daprio/dapr:1.4.3"
command: [ "./placement", "-port", "50006" ]
ports:
- "50006:50006"
networks:
- dapr-demo
networks:
dapr-demo:
driver: bridge
成功启动后,可以看到如下docker运行时情况。(Windows的docker-desktop可以这样查看,Linux的需要docker ps查看但是无法看到启动的dapr进程)
五、验证
1.1 查看服务端two的POST接口是否正常:
1.2 查看服务端two的GET接口是否正常:
确保服务端自身没有问题后,再验证客户端调用服务端的接口是否可用
2.1 查看客户端one调用two中GET接口的服务是否可以调通:
2.2 查看客户端one调用two中POST接口的服务是否可以调通:
六、总结
可以发现,客户端可以通过java sdk的invokeMethod()方法实现对目标应用方法的调用。其他REST方法也可以仿照上述实现调用,具体的invoke方法的参数定义可以看官网的说明;但是对于@RequestParam的服务端方法好像现在的invoke方法并无法调用成功。读者也可以自行构建@RequestParam风格的接口服务端,使用invoke提供的众多重载方法进行选择尝试,当然dapr的java-sdk中invoke家族还有其他封装的方法,也值得后期我们继续研究探讨使用。