引言
在前面上一次的实战中,我们搭建了dapr的本地开发环境,并且部署了dapr官方的Hello World示例代码,为了加深对Dapr的理解,在本文中使用go 和 java语言重写Dapr的官方示例代码
Dapr的官方示例代码Hello World的分析
在服务端中提供了三个对Order的处理逻辑:增加订单,查询订单和删除订单,这三个业务处理都是以REST的方式对外提供服务,而这三个业务处理都会通过Dapr Runtime与Redis交互来完成数据的存放,读取和删除;而python所开发的客户端,周期的通过Dapr Runtime去调用服务端的增加订单的接口
官方示例代码重新实现
服务端的代码分为两个部分:提供的REST服务 和 访问Redis,后面将对这两个部分进行分析
使用golang实现服务端
提供REST服务
REST服务这部分的开发与一般的rest服务的开发并没有什么不同之处,所以在这里我选择自己比较熟悉的echo框架作为了开发框架,分别开发了order的增查删的接口,代码如下:
路由注册部分,完整代码参考
e := echo.New()
e.POST("/neworder", orderResource.Neworder)
e.GET("/order", orderResource.Queryorders)
e.DELETE("/order/:id", orderResource.DeleteOrder)
REST请求处理部分,完整代码参考
func (o *OrderResource) Neworder(c echo.Context) error {
data := &Data{}
err := c.Bind(data)
if err != nil {
return err
}
err = o.cache.Add(data)
if err != nil {
return c.String(500, err.Error())
}
return c.String(200, "add success")
}
func (o *OrderResource) Queryorders(c echo.Context) error {
data, err := o.cache.Query()
if err != nil {
return c.String(500, err.Error())
}
return c.JSON(200, data)
}
func (o *OrderResource) DeleteOrder(c echo.Context) error {
id := c.Param("id")
err := o.cache.delete(id)
if err != nil {
return c.String(500, err.Error())
}
return c.String(200, "delete success")
}
在这里需要关注的一点就是在这里注册的path和访问这个rest接口的path还是有所不同,通过Dapr来访问时,还需要增加http://xxxx:
http://localhost:3500/v1.0/invoke/nodeapp/method/neworder
访问redis
要通过Dapr runtime去访问redis服务,必须按照Dapr的state规范来实现(注:Dapr中所涉及到的规范,将在以后的文章中来进行介绍)
相关代码如下,完整代码请参考
func (cache *CacheService) Add(data *Data) error {
request := cache.httpclient.Post()
request.Path(cache.stateUrl)
state := &State{
Key: "order",
Value: data,
}
states := []*State{state}
response, err := request.JSON(&states).Send()
if err != nil {
return err
}
if !response.Ok {
return fmt.Errorf("add state error,statuCode:%d,messag:%s", response.StatusCode, response.String())
}
log.Info("Successfully persisted state.")
return nil
}
func (cache *CacheService) Query() (*Data, error) {
request := cache.httpclient.Get()
request.Path(cache.stateUrl + "/order")
response, err := request.Send()
if err != nil {
return nil, err
}
if !response.Ok {
return nil, fmt.Errorf("query state error,statuCode:%d,messag:%s", response.StatusCode, response.String())
}
data := &Data{}
err = response.JSON(&data)
return data, err
}
func (cache *CacheService) delete(id string) error {
request := cache.httpclient.Delete()
request.Path(cache.stateUrl + "/" + id)
request.AddHeader("Content-Type", "application/json")
response, err := request.Send()
if err != nil {
return err
}
if !response.Ok {
return fmt.Errorf("delete state error,id:%s,statuCode:%d,messag:%s", id, response.StatusCode, response.String())
}
return nil
}
使用java实现客户端
相关代码如下,完整代码参考
public class DaprHelloworldClientApplication {
private final static String bodyTmpl="{\"data\": {\"orderId\": \"%s\"}}";
public static void main(String[] args) throws IOException, InterruptedException {
Thread sendThread = new Thread(new Runnable() {
public void run() {
int i=0;
while(!Thread.interrupted()){
try {
Thread.sleep(5000);
i++;
String body=String.format(bodyTmpl,String.valueOf(i));
sendRequest(body);
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
sendThread.start();
Thread.sleep(5*60*60*1000);
}
public static void sendRequest(String body) throws IOException {
String httpPort = System.getenv("DAPR_HTTP_PORT");
if (httpPort == null) {
httpPort = "3500";
}
String resultUrl = "http://localhost:" + httpPort + "/v1.0/invoke/goapp/method/neworder";
URL url = new URL(resultUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
connection.setRequestProperty("Connection", "Keep-Alive");
connection.setUseCaches(false);
connection.setDoOutput(true);
connection.setDoInput(true);
System.out.println("request body:"+body);
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
out.write(body.getBytes("UTF-8"));
out.flush();
out.close();
connection.connect();
BufferedReader bReader = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));
String line, resultStr = "";
while (null != (line = bReader.readLine())) {
resultStr += line;
}
System.out.println(resultStr);
bReader.close();
}
}
在上面的代码中,比较特殊的情况是其访问服务端REST的URL是:"http://localhost:" + httpPort + "/v1.0/invoke/goapp/method/neworder"
小结
从上面的代码看来使用Drap后,REST的开发并没有变的麻烦,并且访问Redis的代码还更加的简洁,不在需要使用特定的redis客户端,这样降低了业务开发人员的学习成本,向着云原生又迈进的一步
部署程序
用于使用java开发,所以整个系统都使用maven来进行编译,当然在
启动服务端
dapr run --app-id goapp --app-port 3000 --port 3500 ./helloworld-server
ℹ️ Starting Dapr with id goapp. HTTP Port: 3500. gRPC Port: 39669
== APP ==
== APP == ____ __
== APP == / __/___/ / ___
== APP == / _// __/ _ \/ _ \
== APP == /___/\__/_//_/\___/ v4.1.16
== APP == High performance, minimalist Go web framework
== APP == https://echo.labstack.com
== APP == ____________________________________O/_______
== APP == O\
== APP == ⇨ http server started on [::]:3000
启动客户端
dapr run --app-id javaapp `java -jar dapr-helloworld-client-0.0.1-SNAPSHOT-jar-with-dependencies.jar`
在启动客户端后,可以在服务端的终端上看到如下的代码输出:
== APP == {"time":"2020-06-01T07:25:37.690025853-04:00","level":"INFO","prefix":"-","file":"cache_service.go","line":"48","message":"Successfully persisted state."}
== APP == {"time":"2020-06-01T07:25:42.712922528-04:00","level":"INFO","prefix":"-","file":"cache_service.go","line":"48","message":"Successfully persisted state."}
== APP == {"time":"2020-06-01T07:25:47.725423292-04:00","level":"INFO","prefix":"-","file":"cache_service.go","line":"48","message":"Successfully persisted state."}
总结
通过用java和golang开发了官方的实例代码,个人感觉dapr对代码的限制比较少,开发难度不大,在后面的实战中,将试试在Kubernetes来部署Dapr