dapr实战(2):使用go和java来实现dapr 官方的Hello world和对其过程的研究

引言

在前面上一次的实战中,我们搭建了dapr的本地开发环境,并且部署了dapr官方的Hello World示例代码,为了加深对Dapr的理解,在本文中使用go 和 java语言重写Dapr的官方示例代码

Dapr的官方示例代码Hello World的分析

dapr示例

在服务端中提供了三个对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:<DAPR_HTTP_PORT>/v1.0/invoke/<APP_ID>/method/<Method> 来访问才行,如下:

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

微信公众号

发表评论

电子邮件地址不会被公开。 必填项已用*标注