本文用來介紹Titan Framework的使用和下載, Titan Framework的多數據庫支持,各個微服務之間的調用,微服務的配置與部署。java
Titan Framework包能夠作在Titan Framework PaaS平臺上進行下載,提供各個版本。node
使用以下代碼把Titan Framework 包加到本地maven倉庫中:mysql
Echo move titan-framework-1.2.jarweb mvn install:install-file -Dfile=.\titan-framework-1.2.jar -DgroupId=org.titan.framework -DartifactId=titan-framework -Dversion=1.2.RELEASE -Dpackaging=jarredis |
在微服務的pom文件中添加Titan Framework 包依賴:spring
<properties> sql <titan.version>1.2.RELEASE</titan.version>數據庫 </properties>sass
<dependencies>服務器 <dependency> <groupId>org.titan.framework</groupId> <artifactId>titan-framework</artifactId> <version>${titan.version}</version> </dependency> </dependencies> |
Titan Framework 單個微服務結構圖:
在Titan Framework中,每一個微服務的核心工程結構分爲Controller、Entity、Event、Launch。
在同臺服務器上使用Titan Framework中多個微服務,需建立application.properties文件,目錄\resources,由於Titan Framework中每一個微服務使用的都是默認端口7070。
resources目錄以下:
application.properties文件:
server.port=8012 |
如未添加報以下錯誤:
org.springframework.boot.context.embedded.PortInUseException: Port 7070 is already in use at org.springframework.boot.context.embedded.jetty.JettyEmbeddedServletContainer.start(JettyEmbeddedServletContainer.java:131) at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.startEmbeddedServletContainer(EmbeddedWebApplicationContext.java:297) at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.finishRefresh(EmbeddedWebApplicationContext.java:145) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:545) at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122) at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:761) at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:371) at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) at org.springframework.boot.SpringApplication.run(SpringApplication.java:1186) at org.springframework.boot.SpringApplication.run(SpringApplication.java:1175) at org.titan.framework.facade.launcher.LauncherFrontend.ALLATORIxDEMO(ig:50) at org.titan.framework.facade.launcher.LauncherFrontend.lunch(ig:161) at org.titan.loginRegister.loginRegisterc.launch.LaunchLoginRegisterC.main(LaunchLoginRegisterC.java:14) |
集羣使用還需修改**/project/conf/cluster.properties工具包。
cluster.local.port端口不能相同,
A服務
cluster.system.name=TitanFrameworkSystem cluster.local.port=1818 cluster.local.host=192.168.199.170 cluster.seeds=192.168.199.170:1818 |
B服務
cluster.system.name=TitanFrameworkSystem cluster.local.port=2828 cluster.local.host=192.168.199.170 cluster.seeds=192.168.199.170:2828, 192.168.199.170:1818 |
C服務
cluster.system.name=TitanFrameworkSystem cluster.local.port=3838 cluster.local.host=192.168.199.170 cluster.seeds=192.168.199.170:3838,192.168.199.170:1818 |
A,B,C,D四個微服務的項目,B,C,D三個微服務都去鏈接A服務,這樣就構建成一個集羣,在這個集羣中BCD之間是沒有鏈接的,可是他們都鏈接了A服務,因此他們之間也是能夠進行通訊的。
以下圖:
B,C,D鏈接A服務打印信息:
[INFO] [06/02/2018 13:07:50.781] [TitanFrameworkSystem-akka.actor.default-dispatcher-4] [akka.cluster.Cluster(akka://TitanFrameworkSystem)] Cluster Node [akka.tcp://TitanFrameworkSystem@192.168.199.170:1818] - Node [akka.tcp://TitanFrameworkSystem@192.168.199.170:2828] is JOINING, roles [worker] [INFO] [06/02/2018 13:07:51.177] [TitanFrameworkSystem-akka.actor.default-dispatcher-2] [akka.cluster.Cluster(akka://TitanFrameworkSystem)] Cluster Node [akka.tcp://TitanFrameworkSystem@192.168.199.170:1818] - Leader is moving node [akka.tcp://TitanFrameworkSystem@192.168.199.170:2828] to [Up] |
B,C,D斷開鏈接A服務打印信息:
[WARN] [06/02/2018 13:09:24.932] [New I/O worker #4] [NettyTransport(akka://TitanFrameworkSystem)] Remote connection to /192.168.199.170:52593 failed with java.io.IOException: 遠程主機強迫關閉了一個現有的鏈接。 [WARN] [06/02/2018 13:09:24.953] [TitanFrameworkSystem-akka.remote.default-remote-dispatcher-26] [akka.tcp://TitanFrameworkSystem@192.168.199.170:1818/system/endpointManager/reliableEndpointWriter-akka.tcp%3A%2F%2FTitanFrameworkSystem%40192.168.199.170%3A2828-0] Association with remote system [akka.tcp://TitanFrameworkSystem@192.168.199.170:2828] has failed, address is now gated for [5000] ms. Reason: [Disassociated] [INFO] [06/02/2018 13:09:27.729] [TitanFrameworkSystem-akka.actor.default-dispatcher-2] [akka://TitanFrameworkSystem/deadLetters] Message [akka.cluster.pubsub.DistributedPubSubMediator$Internal$Status] from Actor[akka://TitanFrameworkSystem/system/distributedPubSubMediator#1184035447] to Actor[akka://TitanFrameworkSystem/deadLetters] was not delivered. [10] dead letters encountered, no more dead letters will be logged. This logging can be turned off or adjusted with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'. [WARN] [06/02/2018 13:09:29.173] [TitanFrameworkSystem-akka.actor.default-dispatcher-5] [akka.tcp://TitanFrameworkSystem@192.168.199.170:1818/system/cluster/core/daemon] Cluster Node [akka.tcp://TitanFrameworkSystem@192.168.199.170:1818] - Marking node(s) as UNREACHABLE [Member(address = akka.tcp://TitanFrameworkSystem@192.168.199.170:2828, status = Up)]. Node roles [worker] [WARN] [06/02/2018 13:09:31.268] [New I/O boss #3] [NettyTransport(akka://TitanFrameworkSystem)] Remote connection to null failed with java.net.ConnectException: Connection refused: no further information: /192.168.199.170:2828 [WARN] [06/02/2018 13:09:31.279] [TitanFrameworkSystem-akka.remote.default-remote-dispatcher-26] [akka.tcp://TitanFrameworkSystem@192.168.199.170:1818/system/endpointManager/reliableEndpointWriter-akka.tcp%3A%2F%2FTitanFrameworkSystem%40192.168.199.170%3A2828-0] Association with remote system [akka.tcp://TitanFrameworkSystem@192.168.199.170:2828] has failed, address is now gated for [5000] ms. Reason: [Association failed with [akka.tcp://TitanFrameworkSystem@192.168.199.170:2828]] Caused by: [Connection refused: no further information: /192.168.199.170:2828] |
controller定義,系統默認接收Json body ,基於JsonToEntity模式進行對象轉換,調用方法以下:JsonParserFactory.getParser().getObject(body, TplAppEntity.class);
@RestController @RequestMapping("/demoa") public class ADemoController extends RestfulController { /** 系統默認HTTP POST方法**/ @Override protected <T> Command<?> getCreateCommand(String service, T body) { String _body = (String) body; DemoAEntity entity = JsonParser.getObject(_body, DemoAEntity.class); return new Create<DemoAEntity>(entity); } /** 系統默認HTTP Delete方法**/ @Override protected Command<?> getDeleteCommand(String service, String id) { DemoAEntity entity = new DemoAEntity(); entity.setId(id); return new Delete<DemoAEntity>(entity); } /** 系統默認HTTP Get方法**/ @Override protected Command<?> getGetCommand(String service, String arg1) { return null; } /** 系統默認HTTP Patch方法**/ @Override protected Command<?> getPatchCommand(String arg0, String arg1, String arg2) { return null; } /** 系統默認HTTP Put方法**/ @Override protected Command<?> getPutCommand(String arg0, String arg1, String arg2) { return null; } /** 系統默認query方法**/ @Override protected Command<?> getQueryCommand(String arg0, Object arg1) { return null; } } |
Command Handler定義
Command Handler是用來響應API (Controller的部分的請求),基於類上的註解在啓動時自動完成handler與Command的註冊過程。
@CmdHandler public class DemoACreateHandler implements CommandHandler<Create<DemoAEntity>> { @Override public Result handle(Create<DemoAEntity> cmd) { DemoAEntity entity = cmd.getEntity(); System.out.println(entity.getName() + "," + entity.getText()); Result result = Publish.Send(Ways.Remote(Paths.DemoB.service.getServerName(), new DemoACreateEvent())).Retry(); return result; } } |
Titan Framework的 Command模型
Titan Framework經過Command將Controller與對應的CommandHandler進行關聯。在上面的例子中,咱們能夠看到POST請求將會被getCreateCommand方法捕獲,捕獲後經過建立一個Create類型的Command將具體的請求內容提交到一個CommandHandler。
具體的綁定邏輯關鍵點在於Command類型以及Command中綁定的Entity類型。在上面的例子中,getCreateCommand提交了一個Create<DemoAEntity>的command。對應的CommandHandler(注意:必須經過@CmdHandler註釋進行標註,不然Titan Framework沒法有效識別)實現了一個CommandHandler<Create<DemoAEntity>>的接口。在這個狀況下,Titan Framework會在底層經過Create<DemoAEntity>這個Command類型完成Controller層與CommandHandler層的匹配。
做爲Titan Framework的高階用法,咱們在getCreateCommand中能夠經過serviceName(getCreateCommand有兩個參數,分別是serviceName與T body),分離出不一樣的分支,向多個不一樣的CommandHandler發送不一樣的Command。這一點,你們能夠在實際使用中進一步探索。
在這裏,咱們假設有兩個Titan微服務,其中A微服務做爲API接入服務,B微服務做爲業務邏輯處理服務。
A微服務與B微服務之間的通信就是由上面說到的Event進行驅動。例如A能夠在Controller的Handler主鍵中調用遠程請求,將特定的Event發送給B微服務。B微服務經過EventHandler捕獲這個特定的遠程請求。
handler代碼以下:
@CmdHandler public class LoginCreateHandler implements CommandHandler<Create<LoginEntity>> {
@Override public Result handle(Create<LoginEntity> cmd) { LoginEntity entity = cmd.getEntity(); LoginCreateEvent loginCreateEvent = new LoginCreateEvent(); loginCreateEvent.setPhone(entity.getPhone()); loginCreateEvent.setOtp(entity.getOtp()); Result result = Publish.Send(Ways.Remote(Paths.LoginRegisterE.service.getServerName(), loginCreateEvent)).Retry(); return result; } }
|
B服務接收event代碼以下:
@EvtHandler(Role = "loginRegister", Service = "e") public class CreateLoginEventHandler implements EventHandler<LoginCreateEvent> {
@Override public void handle(LoginCreateEvent event) { String phone = event.getPhone(); String otp = event.getOtp(); } } |
Titan Framework提供了Repository包,集成了關係型與非關係型數據庫的驅動。
mysql
Respository集成:
@Repository public class DemoCRespository extends SpringMysqlRepository<CLoginRegisterEntity> {
@Override protected String getTableName() { return "testentity"; } } |
/project/conf/mysql.properties代碼:
#Database Configuration df.db.driver=com.mysql.cj.jdbc.Driver # for master master.db.url=jdbc:mysql://127.0.0.1:3306/dbname?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=true master.db.username=root master.db.password= # for slave slave.db.url=jdbc:mysql://127.0.0.1:3306/dbname?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=true slave.db.username=root slave.db.password= #jOOQ Configuration
|
MongoDB
Respository集成:
@Repository public class ProjectDetailsRespository implements StorageDataProcessor.Mongo {
@Override public MongoDBConverter extConverter() { // TODO Auto-generated method stub return null; } public void insertUser(ProjectDetailsEntity projectDetails) { Curd crud = curd(); crud.add(ProjectDetailsEntity.class.getSimpleName(), projectDetails); } public void addList(List<ProjectDetailsEntity> projectDetails) { Curd crud = curd(); crud.add(ProjectDetailsEntity.class.getSimpleName(), projectDetails); } public List<ProjectDetailsEntity> queryList(String key,String value) { Curd crud = curd(); List<ProjectDetailsEntity> list = new ArrayList<ProjectDetailsEntity>(); list = crud.queryByCondition(MongoDBQueryCondition.Condition(query -> { query.Table(ProjectDetailsEntity.class.getSimpleName(), ProjectDetailsEntity.class).Condition(key, value); })); return list ; } } |
/project/conf/mongoDB.properties代碼:
mongo.host=127.0.0.1 mongo.port=27017 #mongo.host=192.168.10.135 #mongo.port=13131 #mongo.dbname=dbkita #mongo.host=139.196.6.164 #mongo.port=32771 #mongo.host=47.91.145.54 #mongo.port=13131 mongo.dbname=titan-paas |
Redis
Respository集成:
@Repository public class DemeoRepository extends SpringMysqlRepository<DemoEntity> implements StorageDataProcessor.Redis {
@Override protected String getTableName() { return "DemoEntity"; }
public Result modify(DemoEntity entity) { QueryParams queryParams = new QueryParams.Builder().equal("Id").build(); Map<String, Object> queryMap = new HashMap<String, Object>(); queryMap.put("Id", entity.getId()); return update(entity, queryParams, queryMap); } } |
/project/conf/redis.properties代碼:
#最大分配的對象數 jedisPool.maxActive=20 #最大可以保持idel狀態的對象數 jedisPool.maxIdle=20 #最小可以保持idel狀態的對象數 jedisPool.minIdle=10 #當池內沒有返回對象時,最大等待時間 jedisPool.maxWait=-1 #當調用borrow Object方法時,是否進行有效性檢查 #指明是否在從池中取出鏈接前進行檢驗,若是檢驗失敗,則從池中去除鏈接並嘗試取出另外一個 jedisPool.testOnBorrow=true #在return給pool時,是否提早進行validate操做 jedisPool.testOnReturn =false #表示當pool中的jedis實例都被allocated完時,pool要採起的操做; #默認有三種WHEN_EXHAUSTED_FAIL(表示無jedis實例時,直接拋出NoSuchElementException)、 #WHEN_EXHAUSTED_BLOCK(則表示阻塞住,或者達到maxWait時拋出JedisConnectionException)、 #WHEN_EXHAUSTED_GROW(則表示新建一個jedis實例,也就說設置的maxActive無用) jedisPool.whenExhaustedAction=1 #表示idle object evitor兩次掃描之間要sleep的毫秒數; jedisPool.timeBetweenEvictionRunsMillis = 100000 #若是爲true,表示有一個idle object evitor線程對idle object進行掃描,若是validate失敗,此object會被從pool中drop掉; #這一項只有在timeBetweenEvictionRunsMillis大於0時纔有意義; jedisPool.testWhileIdle=true #表示一個對象至少停留在idle狀態的最短期,而後才能被idle object evitor掃描並驅逐; #這一項只有在timeBetweenEvictionRunsMillis大於0時纔有意義 jedisPool.minEvictableIdleTimeMillis=300000 #在minEvictableIdleTimeMillis基礎上,加入了至少minIdle個對象已經在pool裏面了。 #若是爲-1,evicted不會根據idle time驅逐任何對象。若是minEvictableIdleTimeMillis>0,則此項設置無心義, #且只有在timeBetweenEvictionRunsMillis大於0時纔有意義 jedisPool.softMinEvictableIdleTimeMillis = -1 #表示idle object evitor每次掃描的最多的對象數 jedisPool.numTestsPerEvictionRun = 50 #borrowObject返回對象時,是採用DEFAULT_LIFO(last in first out,即相似cache的最頻繁使用隊列),若是爲False,則表示FIFO隊列 jedisPool.lifo = true #IP jedisPool.shard1.host=127.0.0.1 #Port jedisPool.shard1.port=6379 #password redis.password= #客戶端超時時間單位是毫秒 redis.timeout=100000 #name redis.name=queue #database redis.database=2 |
Titan Framework中使用的包是slf4j,須要把slf4j包配置pom文件中。
pom文件配置以下:
<properties> <akka.version>2.4.19</akka.version> </properties> <dependency> <groupId>com.typesafe.akka</groupId> <artifactId>akka-slf4j_2.11</artifactId> <version>${akka.version}</version> <scope>runtime</scope> </dependency> |
log日誌的使用:
@EvtHandler(Role = "loginRegister", Service = "e") public class CreateLoginEventHandler implements EventHandler<LoginCreateEvent> {
private static final Logger logger = LoggerFactory.getLogger(CreateLoginEventHandler.class);
@Override public void handle(LoginCreateEvent event) { logger.error("錯誤信息"); }
} |
Titan Framework的打包:
第一步:在微服務的pom文件中添加以下代碼:
<build> <plugins> <plugin> <artifactId>maven-dependency-plugin</artifactId> </plugin> <plugin> <artifactId>maven-assembly-plugin</artifactId> <configuration> <descriptorRefs> <descriptorRef>deploy-backend-assembly</descriptorRef> </descriptorRefs> <appendAssemblyId>true</appendAssemblyId> </configuration> </plugin> </plugins> </build> |
第二步:打開cmd,進入到項目目錄,運行命令mvn clean install,cd {project_folder_location}:\MyEclipse10Workspace\demo\demo,運行命令mvn clean install。打包生成的文件會在target下生成.zip文件。
Titan Framework服務器部署:
第一步:把打包生成的.zip文件上傳至服務器解壓,把conf配置添加到解壓文件demoa-titandemo-1.0-SNAPSHOT-bin\demoa-titandemo-1.0-SNAPSHOT\conf路徑下。
如圖:
第二步:把微服務中的spring配置文件cope到\demoa-titandemo-1.0-SNAPSHOT路徑下。
第三步:修改conf目錄下cluster.properties文件裏的IP地址,修改爲服務器IP地址
第四步:java -jar **.jar 啓動服務,未報錯出現以下日誌服務啓動成功。
2018-06-02 16:32:53.103 INFO 26195 --- [ main] application : Initializing Spring FrameworkServlet 'dispatcherServlet' 2018-06-02 16:32:53.103 INFO 26195 --- [ main] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization started 2018-06-02 16:32:53.119 INFO 26195 --- [ main] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization completed in 16 ms 2018-06-02 16:32:53.130 INFO 26195 --- [ main] o.e.jetty.server.AbstractConnector : Started ServerConnector@693f2c89{HTTP/1.1,[http/1.1]}{0.0.0.0:7070} 2018-06-02 16:32:53.134 INFO 26195 --- [ main] .s.b.c.e.j.JettyEmbeddedServletContainer : Jetty started on port(s) 7070 (http/1.1) 2018-06-02 16:32:53.140 INFO 26195 --- [ main] o.t.l.l.launch.LaunchLoginRegistera : Started LaunchLoginRegistera in 6.297 seconds (JVM running for 6.768) |