写点什么

Dapr 在 Java 中的实践 之 服务调用

作者:万猫学社
  • 2022 年 8 月 08 日
  • 本文字数:3608 字

    阅读完需:约 12 分钟

Dapr在Java中的实践 之 服务调用

服务调用

通过服务调用(Service-to-service Invocation),服务可以使用 gRPC 或 HTTP 这样的标准协议来发现并可靠地与其他服务通信。


Dapr 采用边车(Sidecar)、去中心化的架构。 要使用 Dapr 来调用服务,可以在任意 Dapr 实例上使用 invoke 这个 API。 边车编程模型鼓励每个服务与自己的 Dapr 实例对话。 Dapr 实例会相互发现并进行通信。

创建项目

创建两个 SpringBoot 项目,分别命名为:invoke-serverinvoke-clientinvoke-server作为下游服务,被invoke-client调用,具体调用过程如下图:



调用过程包括:


  1. invoke-client服务对invoke-server服务发起 HTTP 或 gRPC 调用的时候,访问invoke-client服务的 Dapr 实例。

  2. invoke-client服务的 Dapr 实例通过运行在给定托管平台上服务名解析组件(Name Resolution Component)发现了运行在此 Dapr 环境中的invoke-server服务。

  3. invoke-client服务的 Dapr 实例将消息转发到服务invoke-server服务的 Dapr 实例。Dapr 实例之间的所有调用考虑到性能都优先使用 gRPC。 仅服务与 Dapr 实例之间的调用可以是 HTTP 或 gRPC。

  4. invoke-server服务的 Dapr 实例将请求转发至invoke-server服务上的特定端点或方法,随后运行其业务逻辑代码。

  5. invoke-server服务返回响应信息给invoke-client服务时,响应信息给将转至invoke-server服务的 Dapr 实例。

  6. invoke-server服务的 Dapr 实例消息转发至invoke-client服务的 Dapr 实例。

  7. invoke-client服务接收到其 Dapr 实例的响应信息。

编写 invoke-server 的代码

调用/send接口时,返回对应信息,主要代码如下:


package one.more.society.invoke.server;
import lombok.extern.slf4j.Slf4j;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.RestController;
@Slf4j@RestControllerpublic class InvokeServerController {
@RequestMapping(value = "/send", method = RequestMethod.POST) public InvokeResponse send(@RequestBody InvokeRequest request) { log.info("send - request:{}", request);
InvokeResponse response = new InvokeResponse(); response.setCode(1); response.setStatus("ok"); response.setMsgId(System.nanoTime()); response.setMsgContent("I konw you said: " + request.getMsgContent());
return response; }}
复制代码


其中,InvokeRequestInvokeResponse的源码如下:


package one.more.society.invoke.server;
import lombok.Data;
@Datapublic class InvokeRequest {
private Long msgId; private String msgContent;}
复制代码


package one.more.society.invoke.server;
import lombok.Data;
@Datapublic class InvokeResponse {
private int code; private String status; private Long msgId; private String msgContent;}
复制代码


application.properties中配置:


server.port=30001
复制代码

编写 invoke-client

invoke-client项目的pom.xml文件中添加如下依赖:


<dependency>    <groupId>io.dapr</groupId>    <artifactId>dapr-sdk-springboot</artifactId>    <version>1.4.0</version></dependency><dependency>    <groupId>com.squareup.okhttp3</groupId>    <artifactId>okhttp</artifactId>    <version>4.9.3</version></dependency>
复制代码


注入一个DaprClient的 bean:


@Configurationpublic class DaprConfig {
private static final DaprClientBuilder BUILDER = new DaprClientBuilder();
@Bean public DaprClient buildDaprClient() { return BUILDER.build(); }}
复制代码


调用invoke-server/send接口,主要代码如下:


package one.more.society.invoke.client;
import io.dapr.client.DaprClient;import io.dapr.client.domain.HttpExtension;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.RestController;
@Slf4j@RestControllerpublic class InvokeClientController {
@Autowired private DaprClient client;
private static final String SERVICE_APP_ID = "invoke-server"; private static final String METHOD_NAME = "send";
@RequestMapping(value = "/say", method = RequestMethod.GET) public InvokeResponse say(String message) { log.info("send - message:{}", message);
InvokeRequest request = new InvokeRequest(); request.setMsgId(System.nanoTime()); request.setMsgContent(message);
InvokeResponse response = client.invokeMethod( SERVICE_APP_ID, METHOD_NAME, request, HttpExtension.POST, InvokeResponse.class).block();
return response; }}
复制代码


其中,InvokeRequestInvokeResponse的源码与invoke-server中是一样的。


application.properties中配置:


server.port=30002
复制代码

启动服务

在启动之前先用mvn命令打包:


mvn clean package
复制代码


invoke-server项目的目录中执行以下命令,启动invoke-server服务:


dapr run --app-id invoke-server --app-port 30001 --dapr-http-port 31001 -- java -jar target/invoke-server-0.0.1-SNAPSHOT.jar
复制代码


invoke-client项目的目录中执行以下命令,启动invoke-client服务:


dapr run --app-id invoke-client --app-port 30002 --dapr-http-port 31002 -- java -jar target/invoke-client-0.0.1-SNAPSHOT.jar
复制代码


在 Dapr Dashboard 中看到:



两个服务都已经启动成功。


访问http://localhost:30002/say?message=OneMoreSociety验证整个调用流程:



可以看到服务之间的调用没有问题,并返回了预想的结果。

名称解析组件

为了启用服务发现和服务调用,Dapr 使用可插拔的名称解析组件。 Kubernetes 名称解析组件使用 Kubernetes DNS 服务来解析集群中运行的其他服务的位置;自托管机器可以使用 mDNS 名称解析组件。


Consul 名称解析组件可以在任何托管环境中使用,包括 Kubernetes 或自托管环境。下面让我们来尝试一下,使用 Consul 作为名称解析组件。


在用户目录下的.dapr文件夹中,找到config.yaml文件。在该文件中,添加一个nameResolutionspec ,并将component字段设置为consul,比如:


apiVersion: dapr.io/v1alpha1kind: Configurationmetadata:  name: daprConfigspec:  nameResolution:    component: "consul"    configuration:      client:        address: "127.0.0.1:8500"      selfRegister: true
复制代码


重新启动服务,可以在日志中看到注册到了 Consul 上:


time="14:28:54.4540593+08:00" level=info msg="service:invoke-client registered on consul agent" app_id=invoke-client instance=OneMoreSociety scope=dapr.contrib type=log ver=1.7.3time="14:28:54.4550937+08:00" level=info msg="Initialized name resolution to consul" app_id=invoke-client instance=OneMoreSociety scope=dapr.runtime type=log ver=1.7.3
复制代码


在 Consul 中也可以看到两个服务都已经注册上去了,如下图:



值得注意的是:Consul 名称解析组件目前还处于 Alpha 状态,最好不要在生产环境使用。


更详细的配置说明见下表:



配置示例:


apiVersion: dapr.io/v1alpha1kind: Configurationmetadata:  name: appconfigspec:  nameResolution:    component: "consul"    configuration:      client:        address: "127.0.0.1:8500"      selfRegister: true      checks:        - name: "Dapr Health Status"          checkID: "daprHealth:${APP_ID}"          interval: "15s",          http: "http://${HOST_ADDRESS}:${DAPR_HTTP_PORT}/v1.0/healthz"        - name: "Service Health Status"          checkID: "serviceHealth:${APP_ID}"          interval: "15s",          http: "http://${HOST_ADDRESS}:${APP_PORT}/health"      tags:        - "dapr"        - "v1"        - "${OTHER_ENV_VARIABLE}"      meta:        DAPR_METRICS_PORT: "${DAPR_METRICS_PORT}"        DAPR_PROFILE_PORT: "${DAPR_PROFILE_PORT}"      daprPortMetaKey: "DAPR_PORT"              queryOptions:        useCache: true        filter: "Checks.ServiceTags contains dapr"
复制代码


最后,感谢你这么帅,还给我点赞

发布于: 刚刚阅读数: 2
用户头像

万猫学社

关注

资深研发工程师 2018.04.15 加入

微信搜索「万猫学社」,关注并回复「电子书」,免费获取12本必读技术书籍。

评论

发布
暂无评论
Dapr在Java中的实践 之 服务调用_微服务_万猫学社_InfoQ写作社区