版本介绍

  • Java 版本:8
  • Dapr Java SKD 版本:0.9.2

Dapr Java-SDK HTTP 调用文档 有个先决条件,内容如下:

  • Dapr and Dapr CLI.
  • Java JDK 11 (or greater): Oracle JDK or OpenJDK.
  • Apache Maven version 3.x.

大家看到 Java JDK 版本最低要求是 11,但是本文显示使用的 JDK 8,这么做的原因是什么呢,可以参考 Java-SDK Issues,Issues 中回答如下:

We want to validate that the SDK is built with Java 8 and apps can use it with Java 11.

意思是他们想通过 Java 11 写的应用程序验证 Java 8 写的 SDK 是否能正常使用。本文不需要验证 Java 11 能否使用 Java-SDK ,因此本文将使用 Java 8 构建应用程序。

工程结构

3 个子工程,一个 client,两个 service。新建两个 service 的意义在于展示 http 链路调用使用 dapr 如何实现。3 个工程项目都集成了 Spring Boot。Spring Boot 启动后会自动注册 Controller、Config 之类的 bean。

graph LR;
java-client-a--1-->java-service-b;
java-service-b--2-->java-service-c;
java-service-c--3-->java-service-b;
java-service-b--4-->java-client-a;
  1. java-client-a 做为客户端调用 java-service-b;
  2. java-service-b 接收请求,并调用 java-service-c;
  3. java-service-c 接收请求,并响应;
  4. java-service-b 收到 java-service-c 应答,并响应 java-client-a 请求。

java-service-c

java-service-c 做为 http 调用链路末端,只需监听 http 请求即可。

package com.dapr.service;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.Options; /**
* @author Zhang_Xiang
* @since 2020/11/7 10:51:22
*/
public class ServiceC { /**
* Starts the service.
*
* @param args Expects the port: -p PORT
* @throws Exception If cannot start service.
*/
public static void main(String[] args) throws Exception {
Options options = new Options();
options.addRequiredOption("p", "port", true, "Port to listen to."); CommandLineParser parser = new DefaultParser();
CommandLine cmd = parser.parse(options, args); // If port string is not valid, it will throw an exception.
int port = Integer.parseInt(cmd.getOptionValue("port")); DaprApplication.start(port);
}
}

DaprApplication.start(port); 集成 SpringBoot 启动。

package com.dapr.service;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; /**
* Dapr's HTTP callback implementation via SpringBoot.
* Scanning package io.dapr.springboot is required.
*
* @author zhangxiang
*/
@SpringBootApplication(scanBasePackages = {"com.dapr.service"})
public class DaprApplication { /**
* Starts Dapr's callback in a given port.
*
* @param port Port to listen to.
*/
public static void start(int port) {
SpringApplication app = new SpringApplication(DaprApplication.class);
app.run(String.format("--server.port=%d", port));
} }

启动命令:

dapr run --app-id java-service-c --app-port 9100 --dapr-http-port 3510 -- java -jar target/dapr-java-service-exec.jar com.dapr.service.ServiceC -p 9100

java-service-b

java-service-b 需要配置一个 DaprClient Bean,以在需要使用 Http 客户端的地方注入。

package com.dapr.service.config;

import io.dapr.client.DaprClient;
import io.dapr.client.DaprClientBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; /**
* @author Zhang_Xiang
* @since 2020/11/8 08:46:49
*/
@Configuration
public class Client { @Bean
public DaprClient getClient(){
return (new DaprClientBuilder()).build();
}
}

接下来在需要调用的 Controller 中添加构造器注入。


/**
* SpringBoot Controller to handle input binding.
*
* @author zhangxiang
*/
@RestController
public class HelloController { private final DaprClient client;
... public HelloController(DaprClient client) {
this.client = client;
}
...
}

发起 http 请求。

...

byte[] response = client.invokeService(SERVICE_APP_ID, "say", message, HttpExtension.POST, null,
byte[].class).block();
if (response != null) {
...
}
...

启动命令:

dapr run --app-id java-service-b --app-port 9101 --dapr-http-port 3511 -- java -jar target/dapr-java-service-exec.jar com.dapr.service.ServiceB -p 9101

java-client-a

对于 java-client-a 来说,集成 Springboot 是可选项,此处构造一个每隔 5 秒发起一次请求的客户端。

package com.dapr.client;

import com.alibaba.fastjson.JSON;
import com.common.ResponseResult;
import io.dapr.client.DaprClient;
import io.dapr.client.DaprClientBuilder;
import io.dapr.client.domain.HttpExtension; import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.TimeZone; /**
* @author Zhang_Xiang
* @since 2020/11/7 17:30:26
*/
public class ClientA {
/**
* Identifier in Dapr for the service this client will invoke.
*/
private static final String SERVICE_APP_ID = "java-service-b"; /**
* Format to output date and time.
*/
private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); /**
* Starts the invoke client.
*
* @param args Messages to be sent as request for the invoke API.
*/
public static void main(String[] args) throws IOException {
try (DaprClient client = (new DaprClientBuilder()).build()) {
while (true) {
Calendar utcNow = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
String utcNowAsString = DATE_FORMAT.format(utcNow.getTime());
String msg = String.format("%s:this this java client A", utcNowAsString);
byte[] response = client.invokeService(SERVICE_APP_ID, "say", msg.getBytes(), HttpExtension.POST, null,
byte[].class).block();
if (response != null) {
String responseResultStr = new String(response);
ResponseResult responseResult = JSON.parseObject(responseResultStr, ResponseResult.class);
System.out.println(responseResult.getMessage());
}
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}

启动命令:

dapr run --app-id java-client-a  --dapr-http-port 3006 -- java -jar target/dapr-java-client-exec.jar com.dapr.client.ClientA

总结

各个模块的启动顺序应为:

graph LR;
java-service-c-->java-service-b;
java-service-b-->java-client-a;

这里限定顺序的原因是,如果先启动 java-client-a ,java-client-a 会立刻通过 dapr 开始发起请求到 java-service-b ,而这时 java-service-b 并未启动。这将触发 dapr 的重试机制。

重试

服务调用在事件调用失败和瞬态错误时,将执行带避退时间间隔(backoff time periods)的自动重试。

引起重试的错误:

  • 网络错误,包括终端不可用和拒绝连接。
  • 身份认证错误,由于在调用方/被调用方的 dapr 边车证书更新导致。

每次重试都以 1 秒的时间避退时间为间隔,最大重试次数为 3 次。和目的地边车通过 gRPC 建立连接 5 秒超时。

java-client-a 打印:

== APP == This is java-service-b,receive the message:2020-11-08 14:21:14.336:this this java client A,and request java-service-c get the response:{"message":"This is java-service-c,receive the message:\"2020-11-08 14:21:14.336:this this java client A\""}

java-service-b 打印:

== APP == This is java-service-b,receive the message:2020-11-08 14:21:44.454:this this java client A

java-service-c 打印:

== APP == This is java-service-c,receive the message:"2020-11-08 14:22:19.571:this this java client A"

打开新的命令行窗口,输入 dapr list

启动示例

源码地址:https://github.com/ZhangX-Byte/dapr-java

克隆仓库

git clone https://github.com/ZhangX-Byte/dapr-java.git
cd dapr-java

构建 dapr-java 项目

mvn install

然后各个项目各自 install 就能正常启动了。

Dapr Java Http 调用的更多相关文章

  1. Java jacob调用打印机打印word文档

    前面说了Java如何生成复杂的Word文档,今年记录下Java如何调用打印机打印word文档. 起初用的是自带的PrintJob,但是系统提供的打印机制并不成熟完整.网上的代码也是千篇一律,在我的打印 ...

  2. 使用Runtime.getRuntime().exec()在java中调用python脚本

    举例有一个Python脚本叫test.py,现在想要在Java里调用这个脚本.假定这个test.py里面使用了拓展的包,使得pythoninterpreter之类内嵌的编译器无法使用,那么只能采用ja ...

  3. java servlet调用带有多个返回结果集的存储过程

    一.mysql存储过程 这里我先说下我这个功能实现的逻辑及途中遇到的一些问题.这个存储过程一共带两个输入参数,一共关联到两张表的查询,每个参数都对应查询表中的一个判断,所以一共返回了两个结果集(当然要 ...

  4. java程序调用存储过程

    java程序调用存储过程       PL/SQL子程序,很多情况下是给应用程序来调用的,所有我们要掌握使用其他编程语言来调用我们写好的存储过程.下面我们介绍下使用java调用Oracle的存储过程. ...

  5. JAVA如何调用C/C++方法

    JAVA如何调用C/C++方法 2013-05-27 JAVA以其跨平台的特性深受人们喜爱,而又正由于它的跨平台的目的,使得它和本地机器的各种内部联系变得很少,约束了它的功能.解决JAVA对本地操作的 ...

  6. Java中调用c/c++语言出现Exception in thread "main" java.lang.UnsatisfiedLinkError: Test.testPrint(Ljava/lang/String;)V...错误

    错误: Exception in thread "main" java.lang.UnsatisfiedLinkError: Test.testPrint(Ljava/lang/S ...

  7. OpenCV4Android开发之旅(一)----OpenCV2.4简介及 app通过Java接口调用OpenCV的示例

    转自:  http://blog.csdn.net/yanzi1225627/article/details/16917961 开发环境:windows+ADT Bundle+CDT+OpenCV-2 ...

  8. asyn4j -- java 异步方法调用框架

    asyn4j 是一个java异步方法调用框架,基于消费者与生产者模式.包括了异步方法执行,异步回调执行,异步工作缓存模块.支持Spring. 让我们写异步方法不再写很多的相关多线程代码.用asyn4j ...

  9. java中调用dll文件的两种方法

    一中是用JNA方法,另外是用JNative方法,两种都是转载来的, JNA地址:http://blog.csdn.net/shendl/article/details/3589676   JNativ ...

随机推荐

  1. Python3——字典

    Python 字典(Dictionary) 字典是另一种可变容器模型,且可存储任意类型对象. 字典的每个键值(key=>value)对用冒号(:)分割,每个对之间用逗号(,)分割,整个字典包括在 ...

  2. C++中cout和cerr

    参考:https://blog.csdn.net/garfield2005/article/details/7639833 之前一直在用,但就是没在意两者到底有啥却别,今天又想到这个问题,总结下吧(以 ...

  3. lens distortion

    来源:http://michel.thoby.free.fr/Fisheye_history_short/International_Standards_about_Distortion.html H ...

  4. Cypress系列(62)- 改造 PageObject 模式

    如果想从头学起Cypress,可以看下面的系列文章哦 https://www.cnblogs.com/poloyy/category/1768839.html PO 模式 PageObject(页面对 ...

  5. 本溪6397.7539(薇)xiaojie:本溪哪里有xiaomei

    本溪哪里有小姐服务大保健[微信:6397.7539倩儿小妹[本溪叫小姐服务√o服务微信:6397.7539倩儿小妹[本溪叫小姐服务][十微信:6397.7539倩儿小妹][本溪叫小姐包夜服务][十微信 ...

  6. MeteoInfoLab脚本示例:合并数组

    对于全球数据来说,经度要么是-180 - 180,要么是0 - 360,都会存在边界数据不连续的问题.比如0 - 360的数据,怎么得到 -20 - 30度的连续格点数据就是个问题(跨越了数据的经度边 ...

  7. 为什么说 Python 内置函数并不是万能的?

    本文出自"Python为什么"系列,请查看全部文章 在Python猫的上一篇文章中,我们对比了两种创建列表的方法,即字面量用法 [] 与内置类型用法 list(),进而分析出它们在 ...

  8. 【原创】xenomai3.1+linux构建linux实时操作系统-基于X86_64和arm

    版权声明:本文为本文为博主原创文章,转载请注明出处.如有问题,欢迎指正.博客地址:https://www.cnblogs.com/wsg1100/ 目录 一.概要 二.环境准备 1.1 安装内核编译工 ...

  9. linux(centos8):安装Jenkins持续集成工具(java 14 / jenkins 2.257)

    一,什么是Jenkins? 1,jenkins是什么? Jenkins是一个开源软件项目,是基于Java开发的一种持续集成工具, 用于监控持续重复的工作,旨在提供一个开放易用的软件平台, 使软件的持续 ...

  10. ps 批量kill进程

    Linux下批量kill掉进程   ps -ef|grep java|grep -v grep|cut -c 9-15|xargs kill -9 管道符"|"用来隔开两个命令,管 ...