<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          這破玩意是規(guī)則引擎?

          共 10815字,需瀏覽 22分鐘

           ·

          2023-04-07 12:20

          前陣子卷了幾天,發(fā)了一版hades,已經(jīng)發(fā)到maven的中央倉庫了,項(xiàng)目里有example的模塊,README也已經(jīng)補(bǔ)充完整了。但是,還是有好些同學(xué)說有點(diǎn)抽象,還是不知道怎么接入和使用。

          今天還是以austin為例,詳細(xì)來說下接入hades的過程。

          0、需求背景

          之前有聊到過,austin作為消息推送平臺,它是會接入多個(gè)短信渠道的。一方面是不同的渠道會有不同的價(jià)格,我們可能會嘗試接入發(fā)送成本更低的渠道,另一方面,有多個(gè)短信渠道可以做容災(zāi)(假設(shè)只有一個(gè)短信渠道,要是該渠道掛了,那austin就相當(dāng)于發(fā)不了短信了)

          接入短信渠道這塊,在austin是有設(shè)計(jì)過的(至少可以說是面向接口編程吧),每個(gè)渠道都要實(shí)現(xiàn)SmsScript接口。

          04197023244d081a7edadbb894492a46.webp

          而接入短信的代碼往往很簡單,核心邏輯只是編寫代碼調(diào)用其HTTP接口去下發(fā)短信,對于整個(gè)系統(tǒng)而言都沒什么新的依賴要引入(很輕量)。

          而每次接入短信(就相當(dāng)于寫一個(gè)類),我都要重啟發(fā)布上線嗎?這不靠譜吧?效率這么低?

          解決方案:上規(guī)則引擎(hades)將業(yè)務(wù)代碼抽離,無須上下線即可實(shí)現(xiàn)功能。

          c18612545c0fdefae94e19834280d9f8.webp

          注:比較輕的邏輯是適合用規(guī)則引擎去做這種抽離的,這會提高我們的開發(fā)效率。而如果業(yè)務(wù)是核心鏈路上的主流程或者要引入各種的SDK才能實(shí)現(xiàn)的,這種就不適合用規(guī)則引擎了。

          1、本地寫好代碼

          比如,我們現(xiàn)在系統(tǒng)已經(jīng)接入了騰訊云短信了,現(xiàn)在商務(wù)說云片這個(gè)渠道更便宜,讓我去接入下。這時(shí)候,我還是正常在IDE上開發(fā),加入云片這個(gè)渠道。

          于是我寫出以下的代碼(實(shí)現(xiàn)了SmsScript接口,剩下就是組裝參數(shù),調(diào)用HTTP的過程哈):

                
                package?com.java3y.austin.handler.script.impl;

          import?cn.hutool.core.date.DatePattern;
          import?cn.hutool.core.date.DateUtil;
          import?cn.hutool.core.util.ArrayUtil;
          import?cn.hutool.core.util.StrUtil;
          import?cn.hutool.http.Header;
          import?cn.hutool.http.HttpRequest;
          import?com.alibaba.fastjson.JSON;
          import?com.google.common.base.Throwables;
          import?com.java3y.austin.common.constant.CommonConstant;
          import?com.java3y.austin.common.dto.account.sms.YunPianSmsAccount;
          import?com.java3y.austin.common.enums.SmsStatus;
          import?com.java3y.austin.handler.domain.sms.SmsParam;
          import?com.java3y.austin.handler.domain.sms.YunPianSendResult;
          import?com.java3y.austin.handler.script.SmsScript;
          import?com.java3y.austin.support.domain.SmsRecord;
          import?com.java3y.austin.support.utils.AccountUtils;
          import?org.apache.commons.lang3.StringUtils;
          import?org.slf4j.Logger;
          import?org.slf4j.LoggerFactory;
          import?org.springframework.beans.factory.annotation.Autowired;
          import?org.springframework.stereotype.Component;

          import?java.util.*;

          /**
          ?*?@author?3y
          ?*?@date?2022年5月23日
          ?*?發(fā)送短信接入文檔:https://www.yunpian.com/official/document/sms/zh_CN/domestic_list
          ?*/
          //@Slf4j
          @Component("YunPianSmsScript")
          public?class?YunPianSmsScript?implements?SmsScript?{

          ????private?static?Logger?log?=?LoggerFactory.getLogger(YunPianSmsScript.class);
          ????@Autowired
          ????private?AccountUtils?accountUtils;

          ????@Override
          ????public?List<SmsRecord>?send(SmsParam?smsParam)?{

          ????????try?{
          ????????????YunPianSmsAccount?account?=?Objects.nonNull(smsParam.getSendAccountId())???accountUtils.getAccountById(smsParam.getSendAccountId(),?YunPianSmsAccount.class)
          ????????????????????:?accountUtils.getSmsAccountByScriptName(smsParam.getScriptName(),?YunPianSmsAccount.class);
          ????????????Map<String,?Object>?params?=?assembleParam(smsParam,?account);

          ????????????String?result?=?HttpRequest.post(account.getUrl())
          ????????????????????.header(Header.CONTENT_TYPE.getValue(),?CommonConstant.CONTENT_TYPE_FORM_URL_ENCODE)
          ????????????????????.header(Header.ACCEPT.getValue(),?CommonConstant.CONTENT_TYPE_JSON)
          ????????????????????.form(params)
          ????????????????????.timeout(2000)
          ????????????????????.execute().body();
          ????????????YunPianSendResult?yunPianSendResult?=?JSON.parseObject(result,?YunPianSendResult.class);
          ????????????return?assembleSmsRecord(smsParam,?yunPianSendResult,?account);
          ????????}?catch?(Exception?e)?{
          ????????????log.error("YunPianSmsScript#send?fail:{},params:{}",?Throwables.getStackTraceAsString(e),?JSON.toJSONString(smsParam));
          ????????????return?null;
          ????????}

          ????}

          ????@Override
          ????public?List<SmsRecord>?pull(Integer?accountId)?{
          ????????//?.....
          ????????return?null;
          ????}

          ????/**
          ?????*?組裝參數(shù)
          ?????*
          ?????*?@param?smsParam
          ?????*?@param?account
          ?????*?@return
          ?????*/
          ????private?Map<String,?Object>?assembleParam(SmsParam?smsParam,?YunPianSmsAccount?account)?{
          ????????Map<String,?Object>?params?=?new?HashMap<>(8);
          ????????params.put("apikey",?account.getApikey());
          ????????params.put("mobile",?StringUtils.join(smsParam.getPhones(),?StrUtil.C_COMMA));
          ????????params.put("tpl_id",?account.getTplId());
          ????????params.put("tpl_value",?"");
          ????????return?params;
          ????}


          ????private?List<SmsRecord>?assembleSmsRecord(SmsParam?smsParam,?YunPianSendResult?response,?YunPianSmsAccount?account)?{
          ????????if?(Objects.isNull(response)?||?ArrayUtil.isEmpty(response.getData()))?{
          ????????????log.error("YunPianSmsScript#assembleSmsRecord?response?null?:{}"?,?JSON.toJSONString(response));

          ????????????return?null;
          ????????}

          ????????List<SmsRecord>?smsRecordList?=?new?ArrayList<>();

          ????????for?(YunPianSendResult.DataDTO?datum?:?response.getData())?{
          ????????????SmsRecord?smsRecord?=?SmsRecord.builder()
          ????????????????????.sendDate(Integer.valueOf(DateUtil.format(new?Date(),?DatePattern.PURE_DATE_PATTERN)))
          ????????????????????.messageTemplateId(smsParam.getMessageTemplateId())
          ????????????????????.phone(Long.valueOf(datum.getMobile()))
          ????????????????????.supplierId(account.getSupplierId())
          ????????????????????.supplierName(account.getSupplierName())
          ????????????????????.msgContent(smsParam.getContent())
          ????????????????????.seriesId(datum.getSid())
          ????????????????????.chargingNum(Math.toIntExact(datum.getCount()))
          ????????????????????.status(0?==?datum.getCode()???SmsStatus.SEND_SUCCESS.getCode()?:?SmsStatus.SEND_FAIL.getCode())
          ????????????????????.reportContent(datum.getMsg())
          ????????????????????.created(Math.toIntExact(DateUtil.currentSeconds()))
          ????????????????????.updated(Math.toIntExact(DateUtil.currentSeconds()))
          ????????????????????.build();

          ????????????smsRecordList.add(smsRecord);
          ????????}

          ????????return?smsRecordList;
          ????}


          }


          注:hades是基于Groovy實(shí)現(xiàn)的,雖然看起來就是Java代碼。但是,這里不能用lombok和最好別用Javalambda

          如上的代碼,我如果使用了lombok去生成Logger對象,這會在代碼執(zhí)行時(shí)會報(bào)錯(cuò):

          c17970c57ddd02a47052796769fc660a.webp

          經(jīng)過一輪驗(yàn)證之后,我們覺得這代碼沒啥問題了。正常是要走發(fā)布流程,把新寫的代碼發(fā)布上線生效的,接入了hades的話,就可以動態(tài)生效了

          2、接入hades規(guī)則引擎

          目前hades提供兩個(gè)客戶端(apollonacos),你項(xiàng)目用哪個(gè)分布式配置中心,你就引入哪個(gè),后期有可能還會新增別的客戶端

                
                <!--如果你用apollo,則引入該dependency-->
          <dependency>
          ????<groupId>io.github.ZhongFuCheng3y</groupId>
          ????<artifactId>hades-apollo-starter</artifactId>
          ????<version>1.0.2</version>
          </dependency>

          <!--如果你用nacos,則引入該dependency-->
          <dependency>
          ????<groupId>io.github.ZhongFuCheng3y</groupId>
          ????<artifactId>hades-nacos-starter</artifactId>
          ????<version>1.0.2</version>
          </dependency>

          你也可以引入hades-core包,繼承BaseHadesConfig,自行實(shí)現(xiàn)獲取配置和配置實(shí)時(shí)通知的邏輯。這里我就不再多說了,先回到apollonacos這兩個(gè)客戶端吧。

          3、使用apollo接入

          當(dāng)我們的本身項(xiàng)目環(huán)境使用的是apollo時(shí),我們就用hades-apollo-starter包。于是在項(xiàng)目需要引入以下pom

                
                <!--如果你用apollo,則引入該dependency-->
          <dependency>
          ????<groupId>io.github.ZhongFuCheng3y</groupId>
          ????<artifactId>hades-apollo-starter</artifactId>
          ????<version>1.0.2</version>
          </dependency>

          接入apollo本身就會需要指定以下配置:

                
                app.id=austin
          apollo.bootstrap.enabled=true
          apollo.meta=192.0.0.1

          所以這不是接入hades的重點(diǎn),因?yàn)槟沩?xiàng)目本身就已經(jīng)接入了apollo了(至少你需保證你的項(xiàng)目跟apollo是通的)。

          而接入hadeshades-apollo-starter下需要有以下的配置:

                
                hades.main.config.enabled=true
          hades.main.config.file-name=hades

          這兒的hades.main.config.file-name其實(shí)指的就是apollonamespace。于是乎,我們需要在austin這個(gè)app.id下創(chuàng)建namespace,名為hades

          0810f55b04267bfb4be5f2324921b37d.webp

          注:使用hades中,創(chuàng)建出來的所有namespace配置格式都需要是txt

          然后,往hades這個(gè)namespace填充值,如下:

                
                {
          ????"instanceNames":?[
          ????????"YunPianSmsScript"
          ????],
          ????"updateTime":?"2023年3月20日10:26:0133"
          }
          37861ebfe99daa537b6aaad1b754cd19.webp

          然后創(chuàng)建出YunPianSmsScript這個(gè)namespace,填入我們本地已經(jīng)寫好的代碼:

          f5b7e614ffe1d3481be1c075ccd3d349.webp

          到這一步,啟動項(xiàng)目就會有以下日志打印出來:

                
                ?INFO??com.java3y.hades.core.utils.GroovyUtils?-?Groovy解析:class=[YunPianSmsScript]語法通過
          ?INFO??c.j.hades.core.service.bootstrap.BaseHadesConfig?-?bean:[com.java3y.austin.handler.script.impl.YunPianSmsScript]已注冊到Spring?IOC中
          ?INFO??com.java3y.hades.starter.config.ApolloStarter?-?分布式配置中心配置[hades]監(jiān)聽器已啟動

          項(xiàng)目設(shè)計(jì)之初就考慮到這種情況了,所以在代碼上我是通過ScriptName去得到Bean,然后去調(diào)用對應(yīng)的方法的。

          c1060d896b709a639a07c56d6ac03924.webp

          那么,當(dāng)我在頁面選中的是云片發(fā)送渠道,在沒有重啟發(fā)布的情況下, 就可以直接調(diào)用對應(yīng)的邏輯了(就是YunPianSmsScript的代碼)。如果修改了YunPianSmsScript的代碼,那先在apollo發(fā)布YunPianSmsScript的代碼,然后手動把hades主配置改了,只要改時(shí)間updateTime就好了。

          4、使用nacos接入

          當(dāng)我們的本身項(xiàng)目環(huán)境使用的是nacos時(shí),我們就用hades-nacos-starter包。于是在項(xiàng)目需要引入以下pom

                
                <!--如果你用nacos,則引入該dependency-->
          <dependency>
          ????<groupId>io.github.ZhongFuCheng3y</groupId>
          ????<artifactId>hades-nacos-starter</artifactId>
          ????<version>1.0.3</version>
          </dependency>

          接入apollo本身就會需要指定以下配置:

                
                nacos.config.server-addr=${austin.nacos.addr.ip:austin-nacos}:${austin.nacos.addr.port:8848}
          nacos.config.username=${austin.nacos.username:nacos}
          nacos.config.password=${austin.nacos.password:nacos}
          nacos.config.namespace=${austin.nacos.namespace:60e2b165-d830-4163-a0e9-b97ec2f7164c}
          nacos.config.enabled=${austin.nacos.enabled}
          1f722f4bdf5a236253ca772be7e2f4f0.webp

          所以這不是接入hades的重點(diǎn),因?yàn)槟沩?xiàng)目本身就已經(jīng)接入了nacos了(至少你需保證你的項(xiàng)目跟nacos是通的)。而接入hadeshades-nacos-starter下需要有以下的配置:

                
                hades.main.config.enabled=true
          hades.main.config.file-name=hades
          hades.main.config.group-name=hades

          這兒的hades.main.config.file-name其實(shí)指的就是nacosdataId。于是乎,我們需要在60e2b165-d830-4163-a0e9-b97ec2f7164c這個(gè)namespace下創(chuàng)建dataId,名為hadesgroup-name也為hades

          ffb0550d2bb689cb998475740744566b.webp

          注:使用hades中,創(chuàng)建出來的所有dataId配置格式都需要是text!然后,往hades這個(gè)dataId填充值,如下:

                
                {
          ????"instanceNames":?[
          ????????"YunPianSmsScript"
          ????],
          ????"updateTime":?"2023年3月20日10:26:0133"
          }

          然后創(chuàng)建出YunPianSmsScript這個(gè)dataId,填入我們本地已經(jīng)寫好的代碼:

          a6ae6bd2e776a23be51f0c6e30e52159.webp

          到這一步,啟動項(xiàng)目就會有以下日志打印出來:

                
                ?INFO??com.java3y.hades.core.utils.GroovyUtils?-?Groovy解析:class=[YunPianSmsScript]語法通過
          ?INFO??c.j.hades.core.service.bootstrap.BaseHadesConfig?-?bean:[com.java3y.austin.handler.script.impl.YunPianSmsScript]已注冊到Spring?IOC中
          ?INFO??com.java3y.hades.starter.config.ApolloStarter?-?分布式配置中心配置[hades]監(jiān)聽器已啟動

          項(xiàng)目設(shè)計(jì)之初就考慮到這種情況了,所以在代碼上我是通過ScriptName去得到Bean,然后去調(diào)用對應(yīng)的方法的。

          c1060d896b709a639a07c56d6ac03924.webp

          那么,當(dāng)我在頁面選中的是云片發(fā)送渠道,在沒有重啟發(fā)布的情況下, 就可以直接調(diào)用對應(yīng)的邏輯了(就是YunPianSmsScript的代碼)。如果修改了YunPianSmsScript的代碼,那先在nacos發(fā)布YunPianSmsScript的代碼,然后手動把hades主配置改了,只要改時(shí)間updateTime就好了。

          c41141286e822019be6b0214f5ea45b9.webp

          05、最佳實(shí)踐

          如果云片YunPianSmsScript這個(gè)腳本邏輯確定要接入長期使用了,建議在下一次發(fā)布的時(shí)候,將其帶上。(畢竟腳本是易動的,而固定的邏輯下來的應(yīng)該要在項(xiàng)目中的程序代碼里的)

          這時(shí)當(dāng)發(fā)布過后,需要把hades主配置手動更新下,把YunPianSmsScript給刪掉:

                
                {
          ????"instanceNames":?[],
          ????"updateTime":?"2023年3月20日10:26:0133"
          }

          既然能在已發(fā)布的應(yīng)用上,動態(tài)新增一個(gè)SpringBean,這個(gè)SpringBean還能多次動態(tài)修改其邏輯。

          那自然在已發(fā)布的應(yīng)用上,動態(tài)修改一個(gè)已有SpringBean的邏輯,也是能做到的。(靈活性會帶來風(fēng)險(xiǎn),我是建議每次改這種代碼邏輯,是要走beta/pre環(huán)境的,最后才上prod

          推薦項(xiàng)目

          如果想學(xué)Java項(xiàng)目的,我還是 強(qiáng)烈推薦 我的開源項(xiàng)目消息推送平臺Austin,可以用作 畢業(yè)設(shè)計(jì) ,可以用作 校招 ,可以看看 生產(chǎn)環(huán)境是怎么推送消息 的。

          時(shí)間不等人,猶豫就會敗北

          倉庫地址:https://gitee.com/zhongfucheng/austin

          現(xiàn)在報(bào)名還? 480/年 從4月10號起改為580/年 文檔資料和答疑服務(wù)有效期 一年

          我就不搞報(bào)名前幾名優(yōu)惠多少,或者定價(jià)打個(gè)折什么的營銷了, 就是一口價(jià) ,不整那些虛的

          如果你想報(bào)名,可以加我的微信weixin403686131加的時(shí)候記得備注??報(bào)名

          沒備注或備注錯(cuò)誤 ,不會通過好友請求的喲!

          瀏覽 268
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  天天爱夜夜爱 | 国产欧美精品在线一区三级 | 国产精品久久久久久久下载地址 | 日韩欧美一卡二卡 | 久操综合 |