本文主要分三部分,分別是:後臺核心業務邏輯、橋樑輔助控制和前臺顯示頁面。css
本Web開發環境綜合了多種工具,包括Maven包管理與編譯工具、Dubbo分佈式服務框架、MyBatis數據持久化工具、Linner頁面管理工具和Handlebars Js模板引擎等前衛的開發工具。html
首先介紹一下:後臺核心業務邏輯搭建。前端
後臺核心業務邏輯大體分爲三個層次:最底層的核心數據邏輯層(biz層),中間層Dubbo服務實現層(service-impl層)和Dubbo服務接口層(service-client層)。此外還有公共的jar包管理父工程(parent工程),公共業務邏輯工程(common工程),任務調度工程(task工程)和總的maven管理工程。java
示例以下圖:node
接下來我會按照搭建順序簡單介紹一下相關工程的配置:web
基本的順序是parent工程→biz工程→common工程→task工程→service-client工程→service-impl工程。ajax
parent工程結構以下圖所示:redis
parent工程主要包括pom工程配置文件,filters文件下的三個開發環境配置文件和maven打包bat批處理命令文件。spring
首先介紹一下parent工程的pom文件:sql
(1) 不一樣的打包環境配置,使用不一樣的打包配置便利了項目整個開發,測試到生產流程的統一化管理。默認將開發環境激活,配置以下圖所示:
(2) 編譯時設置不對指定的資源文件進行替換。包括Freemarker模板文件ftl,靜態-dynamic.xml文件,mybatis動態SQL Mapper文件。以下圖:
(3) 工程編碼和打包插件配置:包括工程編碼插件Java版本1.7,生成javadoc插件,打包Java源碼插件,導入eclipse工程插件等的配置。以下圖所示:
(4) 單元測試覆蓋率統計插件配置:
忽略對如下路徑文件的單元測試覆蓋率統計:action、webapp、interceptor、taglib、domain、model、dto和util/DataFormat.class。下圖相應配置信息:
按模塊劃分的動態SQLMapper文件:示例:SamStateInfoMapper.xml,如圖1-21
序號①指定mybatis基本配置文件sqlMapConfig.xml的路徑。
序號②指定動態SQL Mapper文件的路徑。
序號③爲基礎的baseDAO Bean的聲明。
a包類文件,主資源文件;測試Java包類文件,測試資源文件四大部分組成。主Java包類文件的文件路徑爲com.ouc.mkhl.supplier,下按模塊劃分,示例中爲jmx、security和util 3個模塊。security模塊又細分爲advice包、dao數據訪問對象包、domain包、model數據實體類包和service服務接口包,service服務接口包下包含impl服務接口實現包。
主資源文件包含email郵箱模板文件、logs日誌配置文件、mybatis配置文件、spring相關配置文件。以後將對各配置文件作逐一介紹。
測試Java包類文件包含與主Java包相關的各單元測試文件。
測試資源文件包含springcache文件和mybatis數據持久層generator配置文件。
1) biz工程的pom配置文件:以下圖所示:
序號①爲工程的父maven工程配置。
序號②爲工程的繼承和引用工程關係配置。
2) biz工程主資源配置文件說明:
(1) mybatis配置文件:首先介紹基本的SQLMap配置文:sqlMapConfig.xml
按模塊劃分的動態SQLMapper文件:示例:SamStateInfoMapper.xml,以下圖
(2) spring相關配置文件:
① 緩存cache配置文件:ehcache.xml,spring-cache-security.xml,spring-cache.xml。
② jmx運行期系統管理配置文件:spring-jmx-mbean.xml。
③ 按模塊劃分的自定義Bean聲明配置文件:示例:spring-upload.xml
序號①爲相似DAO的數據庫接口操做類Bean。
序號②爲服務接口Bean方法。
b爲聲明的切面,a爲前置通知和後置通知要執行的方法。
④ 公用操做的配置文件spring-common.xml。
序號①指定mybatis基本配置文件sqlMapConfig.xml的路徑。
序號②指定動態SQL Mapper文件的路徑。
序號③爲基礎的baseDAO Bean的聲明。
⑤ spring配置工具配置:spring-config-toolkit.xml
⑥ 數據源配置:spring-datasource.xml
⑦ Dubbo服務接口發佈配置:spring-dubbo.xml
⑧ 服務擴展接口配置:spring-external.xml
⑨ 業務日誌配置:spring-log.xml
⑩ Quartz監控配置:spring-monitor.xml
傳輸事物管理配置:spring-transaction.xml
biz工程代碼示例:
用戶暴露系統相關管理接口,以實現運行期調用:ExposeMethodInterface
運行期管理系統接口實現:Configuration
b.系統初始化:SystemBootstrap
1 package com.ouc.mkhl.supplier.util; 2 import java.io.IOException; 3 import java.io.InputStream; 4 import java.util.Properties; 5 import mx4j.tools.adaptor.http.HttpAdaptor; 6 import org.apache.commons.logging.Log; 7 import org.apache.commons.logging.LogFactory; 8 import org.springframework.beans.factory.InitializingBean; 9 import org.springframework.core.env.AbstractEnvironment; 10 import com.ouc.openplatform.SysException; 11 import com.ouc.openplatform.console.audit.AuditInfoCollector; 12 import com.ouc.openplatform.session.listener.MaxSessionUtil; 13 import com.ouc.openplatform.util.Env; 14 import com.ouc.openplatform.util.HOPConstant; 15 16 /** 17 * @author WuPing 18 */ 19 public class SystemBootstrap implements InitializingBean { 20 /* 21 * CONFIG_FILE_PATH 系統變量配置文件路徑 22 */ 23 private static final String CONFIG_FILE_PATH = "/env.properties"; 24 private static final Log LOG = LogFactory.getLog(SystemBootstrap.class); 25 private HttpAdaptor httpAdaptor; 26 public static void init() { 27 InputStream inputStream = null; 28 Properties properties = new Properties(); 29 try{ 30 inputStream SystemBootstrap.class.getResourceAsStream(CONFIG_FILE_PATH); 31 properties.load(inputStream); 32 LOG.info("系統配置項:"+properties); 33 }catch (Exception e) { 34 LOG.error("讀取系統配置文件時發生錯誤:",e); 35 throw new SysException(e); 36 }finally{ 37 if(inputStream != null){ 38 try { 39 inputStream.close(); 40 } catch (IOException e) { 41 LOG.error("關閉文件輸入流失敗:",e); 42 } 43 } 44 } 45 Env.init(properties); 46 AuditInfoCollector.setAppNM(Env.getProperty(Env.KEY_SERVER_NAME)); 47 HOPConstant.setAppName(Env.getProperty(Env.KEY_SERVER_NAME)); 48 //設置一些全局參數 49 MaxSessionUtil.setMaxSessionKey(Env.getProperty(Env.KEY_SERVER_NAME)+"_MAX_SESSION_KEYS"); 50 //使用spring的profile 51 System.setProperty(AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME, Env.getProperty(Env.ENV_TYPE)); 52 } 53 54 @Override 55 public void afterPropertiesSet() throws Exception { 56 httpAdaptor.start(); 57 } 58 59 public void setHttpAdaptor(HttpAdaptor httpAdaptor) { 60 this.httpAdaptor = httpAdaptor; 61 } 62 }
c.DAO:SupplyUserDAO:
1 package com.ouc.mkhl.supplier.security.dao; 2 import java.util.List; 3 import com.ouc.mkhl.supplier.security.model.SupplyUser; 4 public interface SupplyUserDAO { 5 public int deleteByPrimaryKey(String supplycode); 6 public int insert(SupplyUser record); 7 public SupplyUser selectByPrimaryKey(String supplycode); 8 public int updateByPrimaryKey(SupplyUser record); 9 public List<SupplyUser> selectAllSupplyUser(); 10 public SupplyUser selectByVCode(String supplycode); 11 }
d. Model:SupplyUser
1 package com.ouc.mkhl.supplier.security.model; 2 3 import java.io.Serializable; 4 5 public class SupplyUser implements Serializable{ 6 7 private static final long serialVersionUID = -123120032141L; 8 9 private String supplycode; 10 11 private String supplypass; 12 13 private String supplyname; 14 15 public String getSupplycode() { 16 return supplycode; 17 } 18 19 public void setSupplycode(String supplycode) { 20 this.supplycode = supplycode == null ? null : supplycode.trim(); 21 } 22 23 public String getSupplypass() { 24 return supplypass; 25 } 26 27 public void setSupplypass(String supplypass) { 28 this.supplypass = supplypass == null ? null : supplypass.trim(); 29 } 30 31 public String getSupplyname() { 32 return supplyname; 33 } 34 35 public void setSupplyname(String supplyname) { 36 this.supplyname = supplyname == null ? null : supplyname.trim(); 37 } 38 }
service-client工程結構如圖:
分爲傳輸DTO包,輔助功能helper包,客戶端服務接口聲明service包和客戶端工具類util包。
service-client示例代碼:
a. DTO:SupplyUserDTO
1 package com.ouc.mkhl.supplier.dto; 2 3 import java.io.Serializable; 4 5 import com.ouc.openapi.dubbo.governance.annotation.DubboField; 6 7 public class SupplyUserDTO implements Serializable{ 8 9 private static final long serialVersionUID = -223120032141L; 10 11 @DubboField(description = "供應商V碼") 12 private String supplycode; 13 14 @DubboField(description = "供應商登陸密碼-未加密") 15 private String supplypass; 16 17 @DubboField(description = "供應商全稱") 18 private String supplyname; 19 20 public String getSupplycode() { 21 return supplycode; 22 } 23 24 public void setSupplycode(String supplycode) { 25 this.supplycode = supplycode == null ? null : supplycode.trim(); 26 } 27 28 public String getSupplypass() { 29 return supplypass; 30 } 31 32 public void setSupplypass(String supplypass) { 33 this.supplypass = supplypass == null ? null : supplypass.trim(); 34 } 35 36 public String getSupplyname() { 37 return supplyname; 38 } 39 40 public void setSupplyname(String supplyname) { 41 this.supplyname = supplyname == null ? null : supplyname.trim(); 42 } 43 }
1 package com.ouc.mkhl.supplier.service; 2 3 import io.terminus.pampas.client.Export; 4 5 import com.ouc.openapi.dubbo.governance.annotation.DubboMethod; 6 import com.ouc.openapi.dubbo.governance.annotation.DubboParam; 7 import com.ouc.openapi.dubbo.governance.annotation.DubboService; 8 import com.ouc.mkhl.supplier.dto.SupplyUserDTO; 9 10 import java.util.List; 11 12 /** 13 * 供客戶端調用的遠程接口 14 * 15 * @author WuPing 16 * 17 */ 18 @DubboService(description = "供應商用戶服務", displayName = "SupplyUser服務") 19 public interface SupplyUserServiceClient { 20 21 // get請求 22 // @return List<SupplyUserDTO> 23 @Export(paramNames = {}) 24 @DubboMethod(description = "獲取全部供應商用戶信息", displayName = "getAllSupplyUsers", returnParamDes = "供應商用戶DTO列表") 25 public List<SupplyUserDTO> getAllSupplyUsers(); 26 27 // 用戶登陸處理 28 @Export(paramNames = { "username", "password" }) 29 @DubboMethod(description = "用戶登陸驗證", displayName = "userLogin", returnParamDes = "驗證結果") 30 public String userLogin( 31 @DubboParam(name = "username", description = "登陸用戶名", example = "V187") String username, 32 @DubboParam(name = "password", description = "密碼", example = "123456") String password); 33 34 }
Service-impl工程結構如圖:
該工程包括異常處理filter包和客戶端服務接口具體實現impl包。主資源文件有客戶端Dubbo接口發佈基本配置文件spring-dubbo.xml和用戶自定義服務配置文件spring-user-provider.xml。以下圖所示:
序號①爲代理平臺分配的應用S碼。
序號②爲後臺接口代理服務。
序號③爲服務方法認證相關服務接口。
序號④爲服務自動註冊接口。
Service-impl示例代碼:
1 package com.ouc.mkhl.supplier.service.impl; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 6 import javax.annotation.Resource; 7 import javax.ws.rs.BeanParam; 8 import javax.ws.rs.Consumes; 9 import javax.ws.rs.DefaultValue; 10 import javax.ws.rs.FormParam; 11 import javax.ws.rs.GET; 12 import javax.ws.rs.POST; 13 import javax.ws.rs.Path; 14 import javax.ws.rs.PathParam; 15 import javax.ws.rs.Produces; 16 import javax.ws.rs.QueryParam; 17 import javax.ws.rs.core.MediaType; 18 19 import org.springframework.stereotype.Service; 20 21 import com.alibaba.dubbo.rpc.protocol.rest.support.ContentType; 22 import com.ouc.openapi.dubbo.governance.annotation.DubboParam; 23 import com.ouc.mkhl.supplier.dto.SupplyUserDTO; 24 import com.ouc.mkhl.supplier.service.SupplyUserServiceClient; 25 import com.ouc.mkhl.supplier.security.service.SupplyUserService; 26 import com.ouc.mkhl.supplier.security.model.SupplyUser; 27 import com.ouc.mkhl.supplier.helper.EncryptHelper; 28 29 @Path("supplyUser") 30 @Consumes({ ContentType.APPLICATION_JSON_UTF_8, ContentType.TEXT_XML_UTF_8 }) 31 // 參數類型 32 @Produces({ ContentType.APPLICATION_JSON_UTF_8, ContentType.TEXT_XML_UTF_8 }) 33 // 返回值類型 34 @Service("supplyUserServiceClient") 35 public class SupplyUserServiceClientImpl implements SupplyUserServiceClient { 36 37 @Resource 38 private SupplyUserService supplyUserService; 39 40 public SupplyUserService getSupplyUserService() { 41 return supplyUserService; 42 } 43 public void setSupplyUserService(SupplyUserService supplyUserService) { 44 this.supplyUserService = supplyUserService; 45 } 46 47 @GET 48 @Path("getAllSupplyUser") 49 @Override 50 public List<SupplyUserDTO> getAllSupplyUsers() { 51 52 List<SupplyUserDTO> supplyUserDTOList = new ArrayList<SupplyUserDTO>(); 53 54 List<SupplyUser> supplyUserList = supplyUserService.getAllSupplyUser(); 55 for (int i = 0; i < supplyUserList.size(); i++) { 56 SupplyUserDTO tempSupplyUserDTO = new SupplyUserDTO(); 57 tempSupplyUserDTO.setSupplycode(supplyUserList.get(i) 58 .getSupplycode()); 59 tempSupplyUserDTO.setSupplypass(supplyUserList.get(i) 60 .getSupplypass()); 61 tempSupplyUserDTO.setSupplyname(supplyUserList.get(i) 62 .getSupplyname()); 63 supplyUserDTOList.add(tempSupplyUserDTO); 64 } 65 66 return supplyUserDTOList; 67 } 68 69 @SuppressWarnings({ "unused" }) 70 @GET 71 @Path("getUserByIdAndName") 72 @Override 73 public String userLogin( 74 @DefaultValue("V187") @QueryParam("username") String username, 75 @DefaultValue("123456") @QueryParam("password") String password) { 76 System.out.println("當前登陸用戶:" + username); 77 String md5Pass = ""; // MD5處理過的密碼 78 79 // 設置登陸反饋信息變量:1—成功;2—用戶名不存在;3—密碼無效登陸失敗。 80 String msg = null; 81 82 EncryptHelper md5Helper = new EncryptHelper(); 83 84 // 應用本地庫 85 SupplyUser suppUser = new SupplyUser(); 86 87 try { 88 suppUser = supplyUserService.getSupplyUserByVCode(username); 89 } catch (Exception e) { 90 suppUser = null; 91 e.printStackTrace(); 92 } 93 94 if (suppUser == null) { 95 suppUser = new SupplyUser(); 96 if (username.equals("suptest")){ 97 suppUser.setSupplycode("V187"); 98 suppUser.setSupplyname("供應商端測試"); 99 suppUser.setSupplypass("123456"); 100 } 101 else if(username.equals("ouctest")) { 102 suppUser.setSupplycode("ouctest"); 103 suppUser.setSupplyname("ouc端測試"); 104 suppUser.setSupplypass("123456"); 105 } 106 } else { 107 try { 108 md5Pass = md5Helper.md5Encode(password).toUpperCase(); 109 } catch (Exception e) { 110 // TODO Auto-generated catch block 111 e.printStackTrace(); 112 } 113 System.out.println("MD5後的密碼:" + md5Pass); 114 password = md5Pass; 115 } 116 117 // 反饋信息設置 118 if (suppUser == null) { 119 msg = "2"; // 用戶名不存在 120 } else { 121 if (suppUser.getSupplypass().equals(password)) { 122 if (suppUser.getSupplycode().equals("haiertest")){ 123 //msg = "1"; //ouc端用戶登陸 124 msg = "ouc"; //ouc端 125 } 126 else { 127 //msg = "0"; //供應商端用戶登陸 128 msg = suppUser.getSupplycode(); //供應商端V碼 129 } 130 } else { 131 msg = "3";// 密碼無效 132 } 133 } 134 return msg; 135 } 136 137 }
配置文件示例spring-lts-job.xml:如圖1-35所示。
序號①爲設置做業執行週期。
包括控制controller、輔助類domain、excel處理、環境配置過濾器filter、操做攔截器interceptor、輔助工具utils等。
資源文件有:spring接口和代理服務配置文件root-context.xml、網頁servlet攔截器配置文件servlet-context.xml、redis配置文件redis-persistence-context.xml。 項目環境配置文件filter,網頁web.xml配置文件。
1) 項目環境配置文件filter:dev.properties,以下圖所示。
序號①爲項目環境類別說明。
序號②爲日誌配置。
序號③爲項目驗證url地址。
序號④爲redis相關配置。
序號⑤爲dubbo相關配置。
序號⑥爲項目中央倉庫地址配置。
序號⑦爲集羣平臺terracotta配置。
★序號⑧爲與頁面工程相關的配置:
應用簡稱(app.name),服務器名稱(server.name),與頁面工程有關的服務Key(server.key),dubbo應用名(dubbo.application.name),頁面工程路徑(publicPathPrefix),瀏覽器訪問域名(domain.name)和默認跳轉頁面(login.after.jump.url)。
序號⑨爲項目集團內網和外網環境安全配置。
2) Web.xml配置文件:
主要內容有配置系統啓動監聽器startupListeners,基於緩存實現的集羣session配置和編碼過濾等內容。
1) 頁面工程結構以下.
典型的被linner管理的項目前端工程結構以下:
├─a: app ##app是開發者自開發代碼的存儲目錄.
│ ├─a1: components ##存放項目的組件文件.
│ │ └─ dropdown ##示例組件(此處假設爲dropdown).
│ │ └─ templates ##組件自身的前端模板.
│ │ ├─ view.coffee | view.js ##組件js,可使coffee也能夠是js.
│ │ ├─view.hbs ##組件模板
│ │ └─ view.scss | view.css ##組件樣式文件,能夠是scss,也能夠是css.
│ ├─ a3: images ##存放項目相關的圖片文件.
│ │ └── logo.png
│ ├─ a5: scripts ##存放項目相關的JavaScript文件.
│ │ └── app.coffee
│ ├─ a6: styles ##存放項目相關的StyleSheet文件.
│ │ └── app.scss
│ ├─ a7: templates ##存放項目相關的前端模板文件.
│ │ └── welcome.hbs
│ ├─ a8: views ##用於存放項目相關的頁面文件.
│ │ └──index.html
│ └─ a2: files ##用於存放相關配置文件,例如front_config.yaml等.
├─bin ##用戶可基於此啓動本地服務器(以當前文件夾爲根),固然更好的選擇是適用jigglepuff來啓動一個帶渲染邏輯的服務器.
│ └── server.
├─config.yml ##是整個項目的配置文件.
├─ b: public ##是項目執行linner build後生成的打包文件位置,是發佈項目所須要的全部文件.
├── test ##測試前端項目的單元測試文件所在目錄.
└─ c: vendor ##存放引入的第三方代碼組件,例如jQuery、Underscore等.
2) 頁面調用後臺服務
3) 整個項目的配置文件config.yml。
4) 後端服務back_config.yaml配置:
5) 前端頁面引用服務front_config.yaml配置:
6) 頁面hbs示例:
組件類型調用服務示例
ajax類型服務調用方式
友情連接:
handlebars官網:http://handlebarsjs.com/
Node.js官網:https://nodejs.org/en/
CoffeeScript 中文網:http://coffee-script.org/#top
Redis中文官網:http://www.redis.cn/
A.LTS輕任務調度框架介紹:
• LTS框架概況:
LTS是一個輕任務調度框架,參考hadoop的部分思想。有三種角色, JobClient, JobTracker,TaskTracker。各個節點都是無狀態的,能夠部署多個,來實現負載均衡,實現更大的負載量, 而且框架具備很好的容錯能力。 採用Zookeeper暴露節點信息,master選舉。Mongo存儲任務隊列和任務執行日誌, netty作底層通訊。
▶ JobClient : 主要負責提交任務, 和接收任務執行反饋結果。
▶ JobTracker : 負責接收並分配任務,任務調度。
▶ TaskTracker: 負責執行任務,執行完反饋給JobTracker。
• 架構圖:
• 節點組:
▶ 一個節點組等同於一個集羣,同一個節點組中的各個節點是對等的,外界不管鏈接節點組中的任務一個節點都是能夠的。
▶ 每一個節點組中都有一個master節點,採用zookeeper進行master選舉(master宕機,會自動選舉出新的master節點),框架會提供接口API來監聽master節點的變化,用戶能夠本身使用master節點作本身想作的事情。
▶ JobClient和TaskTracker均可以存在多個節點組。譬如JobClient 能夠存在多個節點組。譬如:JobClient 節點組爲‘QN_WEB’中的一個節點提交提交一個只有節點組爲‘QN_TRADE’的TaskTracker 才能執行的任務。
▶ (每一個集羣中)JobTacker只有一個節點組。
▶ 多個JobClient節點組和多個TaskTracker節點組再加上一個JobTacker節點組, 組成一個大的集羣。
▶ JobClient提交一個任務給 JobTracker, 這裏我提供了兩種客戶端API, 一種是若是JobTracker 不存在或者提交失敗,直接返回提交失敗。另外一種客戶端是重試客戶端, 若是提交失敗,先存儲文件,返回給客戶端提交成功的信息,待JobTracker可用的時候,再將任務提交。
▶ JobTracker收到JobClient提交來的任務,先生成一個惟一的JobID。而後將任務儲存在Mongo集羣中。JobTracker 發現有(任務執行的)可用的TaskTracker節點(組)以後,將優先級最大,最早提交的任務分發給TaskTracker。這裏JobTracker會優先分配給比較空閒的TaskTracker節點,達到負載均衡。
▶ TaskTracker收到JobTracker分發來的任務以後,執行。執行完畢以後,再反饋任務執行結果給JobTracker(成功or 失敗[失敗有失敗錯誤信息]),若是發現JobTacker不可用,那麼存儲文件,等待TaskTracker可用的時候再反饋。反饋結果的同時,詢問 JobTacker有沒有新的任務要執行。
▶ JobTacker收到TaskTracker節點的任務結果信息,生成並插入(mongo)任務執行日誌。根據任務信息決定要不要反饋給客戶端。不須要反饋的直接刪除, 須要反饋的(一樣JobClient不可用存儲文件,等待可用重發)。
▶ JobClient收到任務執行結果,進行本身想要的邏輯處理。
▶ 負載均衡:
▷ JobClient和 TaskTracker會隨機鏈接JobTracker節點組中的一個節點,實現JobTracker負載均衡。當鏈接上後,將一直保持鏈接這個節點,保持鏈接通道,知道這個節點不可用,減小每次都從新鏈接一個節點帶來的性能開銷。
▷ JobTracker分發任務時,是優先分配給最空間的一個TaskTracker節點,實現TaskTracker節點的負載均衡。
▶ 健壯性:
▷ 當節點組中的一個節點當機以後,自動轉到其餘節點工做。當整個節點組當機以後,將會採用存儲文件的方式,待節點組可用的時候進行重發。
▷ 當執行任務的TaskTracker節點當機以後,JobTracker會將這個TaskTracker上的未完成的任務(死任務),從新分配給節點組中其餘節點執行。
▶ 伸縮性:
由於各個節點都是無狀態的,能夠動態增長機器部署實例, 節點關注者會自動發現。
安裝 zookeeper 和 mongo , 執行 data/mongo目錄下的 mongo.md 中的語句見 job-example 這裏給出的是java API(設置配置)方式啓動,也可使用配置文件中。
▶ JobTracker端
▶ JobClient端
▶ TaskTracker端