4267a229创建于 2024年8月2日历史提交
/*
    Copyright (c) [2023] [squallzhao]
    fountain is licensed under APACHE LICENSE, VERSION 2.0.
    You can use this software according to the terms and conditions of the APACHE LICENSE, VERSION 2.0.
    You may obtain a copy of APACHE LICENSE, VERSION 2.0 at: https://www.apache.org/licenses/LICENSE-2.0
*/
package microservice.resttemplate;

import net.http.*
import std.io.*
import encoding.url.*
import std.fs.*
import encoding.json.*
import std.collection.*
import std.sync.*
import std.time.*
import microservice.trace.*
import microservice.config.*
import microservice.common.*
import microservice.registry.*
import microservice.web.server.reqres.*
import std.convert.Parsable

public open class RestTemplate { 
   var ers: IRegistry;
   let mtxHashMap = ReentrantMutex()
   var hm = HashMap<String,ArrayList<String>>() 
   var refer :Refer= Refer()
   var readTimeout: Int64 = 3000

   public init(e:IRegistry){
      this.ers = e;
      var r = ResourceFile.getProperty(Constant.CLIENT_REFER)
      if (let Some(v) <- r){
         var arr = v.split(',')
         for(i in 0..arr.size){
            refer.add(arr[i])
         }
      }

      var name = ResourceFile.getProperty(Constant.SERVER_NAME)
      if (let Some(v) <- name){
         refer.add(v)
      }

      var timeout = ResourceFile.getProperty(Constant.CLIENT_READ_TIMEOUT)
      if (let Some(v) <- timeout){
         readTimeout = Int64.parse(v)
      }
   }

   public func start(): RestTemplate{
      refreshAllServiceSync()
      Logger.info("RestTemplate start.")
      return this;
   }

   public func getRefer(): Refer{
      return refer
   }

   public func getServiceAddressList(service: String): Option<ArrayList<String> >{
      var arr : Option<ArrayList<String> > = Option<ArrayList<String> >.None
      synchronized(mtxHashMap){
         arr = hm.get(service)
      }
      return arr;
   }

   private func getServiceAddress(service: String):String{
      var arr : Option<ArrayList<String> > = Option<ArrayList<String> >.None
      synchronized(mtxHashMap){
         arr = hm.get(service)
      }

      if (let Some(v) <- arr){
         return v.get(0).getOrThrow();
      } else{
         
      }
      return "";
   }

   private func refreshAllServiceSync(): Unit{
      for(service  in refer.getRefers()){
            refreshService(service)
      }
      spawn{
         Common.sleeps(Common.discoveryInterval)
         refreshAllService()
      }
   } 

   private func refreshAllService(): Unit{
      //refer.dump()
      for(service  in refer.getRefers()){
         spawn{ =>
            refreshService(service)
         }
      }
      spawn{
         Common.sleeps(Common.discoveryInterval)
         refreshAllService()
      }
   } 

   private func refreshService(service: String): Unit{
      try{
         var serviceInfo = ers.discovery(service)
         var j = JsonValue.fromStr(serviceInfo)
         var inst = j.asObject().get("application").getOrThrow().asObject().get("instance").getOrThrow().asArray();
         var arr = ArrayList<String>()
         for(i in 0..inst.size()){
            var ipAddr: String = inst.get(i).getOrThrow().asObject().get("ipAddr").getOrThrow().toJsonString()
            ipAddr = ipAddr[1..ipAddr.size-1]
            var port = inst.get(i).getOrThrow().asObject().get("port").getOrThrow().asObject().get("$").getOrThrow().toJsonString()
            arr.append(ipAddr+":"+port)
         }
         synchronized(mtxHashMap){
            hm.put(service,arr)
         }
         Logger.trace("refreshService " + service +" success ")
      } catch(e: Exception){
         Logger.warn("refreshService " + service +" fail")
      }
   } 

   public func postForResponse(url:String, data:String): HttpResponse{
      var nurl = handleService(url)
      var b = ByteArrayStream()
      b.write(data.toArray())
      var client = ClientBuilder().build()
      var req = HttpRequestBuilder().method("POST")
        .url(nurl)
        .header("Content-Type","application/json")
        .readTimeout(readTimeout*Duration.millisecond)
        .body(b)
        .build()

       let v = client.send(req)

       return v
   }

   public func postForEntity(url:String, data:String): String{
      let v = postForResponse(url, data) 
      return StringReader(v.body).readToEnd()
   } 

   public func getForResponse(url: String, data: String): HttpResponse{
      var newUrl = handleService(url)
      Logger.trace("getForEntity handleService "+newUrl)

      var client = ClientBuilder().build()
      var req = HttpRequestBuilder().method("GET")
        .url(newUrl+"?"+data)
        .header("Content-Type","application/json")
        .readTimeout(readTimeout*Duration.millisecond)
        .build()

       let v = client.send(req)
       return v
   }

   public func getForEntity(url: String, data: String): String{
       let v = getForResponse(url,data)
       return StringReader(v.body).readToEnd()
   }
   
   public func getForEntity(url: String, data: String, headers: HashMap<String,String>): String{
       var newUrl = handleService(url)
       Logger.trace("getForEntity handleService "+newUrl)

      var client = ClientBuilder().build()
      var builder = HttpRequestBuilder().method("GET")
        .url(newUrl+"?"+data)
        .header("Content-Type","application/json")
        .readTimeout(readTimeout*Duration.millisecond)

       for((k,v)in headers){
         builder.header(k,v)
       }
       var req = builder.build()
       let v = client.send(req)
  

       return StringReader(v.body).readToEnd()
   }

   public func handleService(url: String):String{
      if (url.startsWith("http://")) {
         var sub = url[7..url.size];
         var serviceName = sub[0..sub.indexOf('/').getOrThrow()]
         var serviceAddress = getServiceAddress(serviceName)
         return url.replace(serviceName,serviceAddress)
      }
      return url;
   }
}