引言
本文中的功能是以7.1快速開發一個微服務爲基礎, 如果不瞭解,請先閱讀那一篇博客
本文介紹了F1平臺的一些常用功能:使用統一權限、使用緩存、使用統一配置、獲取常用配置參數、微服務自定義配置參數、使用模型服務對模型數據增刪改查、使用工作流服務、BD控件事件定製、Bp控件服務定製、異構數據庫支持、多數據源支持、即時推送、jms消息、kafka消息、自動裝配組件開發、interface組件開發、服務調用、服務事件擴展
使用統一權限
在當前的微服務中依賴f1-starter-auth(如果已經引入了f1-starter,就會間接引入f1-starter-auth),如果沒有授權的請求來訪問,就會被拒絕。
application.properties中加入權限服務器參數:
###########################oauth服務器相關配置#####################
# 認證服務器憑證
security.sessions:never
security.oauth2.client.client-id: client-id
security.oauth2.client.client-secret: client-secret
security.oauth2.client.access-token-uri: http://IP地址/uaa/oauth/token
security.oauth2.client.user-authorization-uri: http://IP地址/uaa/oauth/authorize
security.oauth2.resource.user-info-uri: http://IP地址/uaa/user
# 斷路器配置共享security上下文
hystrix.shareSecurityContext: true
###########################swagger兼容授權配置#####################
security.userOauth.type=oauth2
security.userOauth.tokenName=access_token
security.userOauth.scope.code=write
security.userOauth.scope.desc=write
app.key=f1swagger
app.name=F1平臺微服務請求API
app.desc=更多的下載資源和信息請查看:http://192.168.1.173/f1-platform/f1-microService/
app.version=3.0.0
app.termsOfServiceUrl=http://192.168.1.173/f1-platform/f1-microService/
app.contact.name=平臺組
app.contact.url=http://http://blog.csdn.net/zhbr_f1
app.contact.email=**
app.license=The F1 Platform, Version 3.0
app.licenseUrl=http://http://blog.csdn.net/zhbr_f1
加入redis的配置,因爲權限認證信息要緩存在redis中
####################### REDIS (RedisProperties)
# Redis數據庫索引(默認爲0
spring.redis.database=0
# Redis連接密碼
spring.redis.password=****
# Redis數據庫服務地址
spring.redis.host=192.168.***.***
# Redis服務器連接端口
spring.redis.port=6379
# 連接池最大連接數(使用負值表示沒有限制)
spring.redis.pool.max-active=8
# 連接池最大阻塞等待時間(使用負值表示沒有限制)
spring.redis.pool.max-wait=-1
# 連接池中的最大空閒連接
spring.redis.pool.max-idle=8
# 連接池中的最小空閒連接
spring.redis.pool.min-idle=0
# 連接超時時間(毫秒)
spring.redis.timeout=0
在啓動類上加上標註:
@EnableOAuth2Sso
這樣就只有授權的請求可以訪問當前微服務了
在剛纔那個url後邊加上權限相關的參數(用戶名密碼認證通過後返回的,真正系統中是自動加上的,這裏加到url後邊只是爲了演示)就可以訪問了
使用緩存
redis配置
依賴f1-starter會級聯依賴f1-starter-cache
然後在application.properties中加入redis的配置
# REDIS (RedisProperties)
# Redis數據庫索引(默認爲0
spring.redis.database=0
# Redis連接密碼
spring.redis.password=sys
# Redis數據庫服務地址
spring.redis.host=localhost
# Redis服務器連接端口
spring.redis.port=6379
# 連接池最大連接數(使用負值表示沒有限制)
spring.redis.pool.max-active=8
# 連接池最大阻塞等待時間(使用負值表示沒有限制)
spring.redis.pool.max-wait=-1
# 連接池中的最大空閒連接
spring.redis.pool.max-idle=8
# 連接池中的最小空閒連接
spring.redis.pool.min-idle=0
# 連接超時時間(毫秒)
spring.redis.timeout=0
緩存使用
下邊在service方法上加一個用name作爲key的緩存
@Cacheable(value="queryDb", key="#name")
public String queryDb(String name) {
//查詢數據庫示例
List<?> ls = genericDao.getDataWithSQL("select count(1) from us_sys.tb_sys_person");
int ranNum = new Random().nextInt(100);
return ls.get(0).toString()+"人 "+ranNum+name;
}
請求時的name不變,返回的值就還是原來的值,注意中間的隨機數不會變,因爲是從緩存中取的,方法沒有被執行
更新
通常都是查詢的時候從緩存中查,當這個值更新到數據庫中時就更新這條緩存(從緩存中把這條數據清除)
@Override
@CacheEvict(value="queryDb", key="#name")
public void update(String name) {
System.out.println("更新數據時,更新緩存");
}
這樣就可以用@CacheEvict把對應的記錄在更新時從緩存中清除
使用統一配置
1.添加依賴
<!-- 需要配置服務器,依賴此項可以讀到配置服務器中相應配置文件 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<!-- 監控:配置文件實時可見 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- kafka消息總線 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-kafka</artifactId>
</dependency>
2.配置參數
# 能否發現配置
spring.cloud.config.discovery.enabled=true
# 配置中心服務id
spring.cloud.config.discovery.serviceId=f1-configserver
# 場景
spring.cloud.config.profile=dev
# 分支
spring.cloud.config.label=master
# 配置中心地址
spring.cloud.config.uri=http://localhost:7001/
# 是否使用本地配置文件(當此配置項爲native時,將使用本地配置文件)
# spring.profiles.active=native
# kafka,服務器上搭建的kafka(依賴kafka服務器)
spring.cloud.stream.kafka.binder.zk-nodes=[kafkaIp]:2181
spring.cloud.stream.kafka.binder.brokers=[kafkaIp]:9092
至此,我們的微服務已經使用了配置服務器提供的配置文件,當我們將git上的配置文件修改了該如何通知所有服務刷新呢?通過發送post請求:http://ip:配置服務器端口/bus/refresh進行配置信息的刷新。
3. 讀取配置參數值
在配置服務器指定的配置文件中加一條參數
configServer.testValue=aaaaaaaallllll
下邊可以用@Value把值注入到變量
/** 獲取配置服務器中的參數 */
@Value("${configServer.testValue}")
private String testConfigValue;
@Override
@Cacheable(value="queryDb", key="#name")
public String queryDb(String name) {
//查詢數據庫示例
List<?> ls = genericDao.getDataWithSQL("select count(1) from us_sys.tb_sys_person");
int ranNum = new Random().nextInt(100);
return ls.get(0).toString()+"人 "+ranNum+name+testConfigValue;
}
獲取常用配置參數
例如:
String dbtype = Platform.INSTANCE.getString("dbtype"); 就可以得到數據庫類型
微服務自定義配置參數
##自定義的參數
self.test.arg1=111111
在java代碼中讀取 有兩種方式,如下所示
@Value("${self.test.arg1}") //第一種方式,用@Value
private String selfArgs;
private Environment environment;
public String getSelfArgs() {
return selfArgs;
}
public String getPlatformSelfArgs() {
return Platform.getPlatform().getString("self.test.", "arg1"); //第二種方式,用Platform取值
}
使用模型服務
有時我們需要在後臺對模型數據進行增刪改查的操作,這時我們就要調用平臺提供的模型服務接口,這裏以singleDBClient爲例對一個模型數據進行操作,具體如下。
增加依賴:
<dependency>
<groupId>com.joinbright.f1</groupId>
<artifactId>f1-interface-model</artifactId>
</dependency>
在啓動類中加標註
@EnableFeignClients("com.jb.*.client")
@ComponentScan
調用SingleDBClient進行增刪改查
@Autowired
private SingleBDClient singleBDClient;
@Override
public void operModelData() {
String clsID = "3AC9D405-B3A7-488B-A662-01ED06D73B60";
String appID = "3883C374-E197-4216-83C6-ACE77EB7A0E2";
//新增
String newGUID = GUID.newGUID();
String createData = "{\"GUID\":\""+newGUID+"\",\"BDZMC\":\"變電站修改測試1\",\"TYRQ\":\"2012-09-21 21:23:33\"}";
String createReturn = singleBDClient.cmdCreateBD(createData, appID, clsID);
System.out.println(createReturn);
//查詢
String getReturn = singleBDClient.cmdGetBD(newGUID, appID, clsID);
System.out.println(getReturn);
//修改
createData = "{\"GUID\":\""+newGUID+"\",\"BDZMC\":\"變電站修改測試1修改\",\"TYRQ\":\""+DateUtil.formatLongtime(new Date())+"\"}";
String saveReturn = singleBDClient.cmdSaveBD(createData, appID, clsID);
System.out.println(saveReturn);
//刪除
String delReturn = singleBDClient.cmdDeleteBD(newGUID, appID, clsID);
System.out.println(delReturn);
}
使用工作流服務
在pom中加依賴
<dependency>
<groupId>com.joinbright.f1</groupId>
<artifactId>f1-interface-websocket</artifactId>
</dependency>
在啓動類中加標註:
@EnableFeignClients("com.jb.*.client")
@ComponentScan
在下邊的代碼中調用WorkFlowControlClient進行流程操作
@Autowired
private WorkFlowControlClient workFlowControlClient;
@Override
publicvoid operWorkflow() {
//發送流程
TransferInfotransferInfo = newTransferInfo();
transferInfo.setContent("你好,這有一個流程");
transferInfo.setForkStep("34870934");//如果流程經過fork節點進行遷移,則該節點設置爲fork節點的stepid。
transferInfo.setJoinStep("349875994");//如果流程經過join節點進行遷移,則該節點設置爲join節點的stepid。
/*發送信息參數,該對象爲list數組,裏邊存儲了下發遷移的具體信息,如果不經過fork
節點則該集合僅有一個元素,進過fork節點可設置多個元素對應發送的多個環節,其子元素SendParam對象的屬性有"
orderNo(業務數據主鍵),nextActorId(下一步執行者),orderType(流程對象類型),"
taskId(任務id,流程啓動時設置爲0),content(流程審批意見),stepId(遷移到的環節id),"
sendModel(發送模式,多條流程進行發送還是單條流程進行發送,1101702爲多條,1101702爲單條)"
isSendInteracStep(是否需要發送到分佈式流程的交互節點,通常不需要進行設置,如果要遷移到交互節點設置爲'true')"
interacStep(分佈式流程需要遷移到的交互環節id)" + "carbonCopyPersons(list數組,設置發送到該環節接收站內消息的人員id)"
sendType(發送模式,如果爲發送設置爲'send',如果是回退設置爲'back')*/
transferInfo.setSendParam(newArrayList<SendParam>(){
{
add(new SendParam(){
{
setOrderNo("orderNo");
setNextActorId("nextActorId");
setOrderType("orderType");
setTaskId("taskId");
setContent("content");
setStepId("stepId");
setSendModel("sendModel");
setIsSendInteracStep("isSendInteracStep");
setInteracStep("interacStep");
setCarbonCopyPersons(Arrays.asList(new String[]{"293749","2303948"}));
setSendType("sendType");
}
});
}
});
//設置一些附加的參數
transferInfo.setVariables(newHashMap<String, Object>(){
{
put("args1", "20973403297");
put("args2", "34083049549");
}
});
InvokeResult irt = workFlowControlClient.sendFlow(transferInfo);
//從當前待處理環節遷移到任意環節
/*BackParam任意環節發送對象,對應的屬性介紹如下:orderNo:業務數據主鍵。
backTaskActorId:處理當前任務的處理人。nextActorId:遷移到的環節的任務處理者。content:審批意見。
orderType:流程對象標識taskId:當前處理任務的id。
sendModel:發送模式,多條流程進行發送還是單條流程進行發送,1101702爲多條,1101702爲單條backNodeName:遷移到的環節名稱
isSendInteracStep:是否需要發送到分佈式流程的交互節點,通常不需要進行設置,如果要遷移到交互節點設置爲'true'
interacStep:分佈式流程需要遷移到的交互環節id
carbonCopyPersons:list數組,設置發送到該環節接收站內消息的人員id*/
BackParam freeSendParam = new BackParam();
freeSendParam.setOrderNo("orderNo");
freeSendParam.setBackTaskActorId("backTaskActorId");
freeSendParam.setNextActorId("nextActorId");
freeSendParam.setContent("content");
freeSendParam.setOrderType("orderType");
freeSendParam.setTaskId("taskId");
freeSendParam.setSendModel("sendModel");
freeSendParam.setBackNodeName("backNodeName");
freeSendParam.setIsSendInteracStep("isSendInteracStep");
freeSendParam.setInteracStep("interacStep");
freeSendParam.setCarbonCopyPersons(Arrays.asList(new String[]{"293749","2303948"}));
irt = workFlowControlClient.sendFreeNode(freeSendParam);
//暫停流程
longpid = 1L;
irt = workFlowControlClient.cmdSuspendProcess(pid);
//恢復流程
pid = 1L;
irt = workFlowControlClient.cmdResumeTask(pid);
//發送流程到結束環節
longtaskId = 1L;
irt = workFlowControlClient.endFlow(taskId);
//通過任務id刪除流程
taskId = 1L;
irt = workFlowControlClient.deleteFlowByTaskId(taskId);
//通過流程實例id刪除流程
pid = 1L;
irt = workFlowControlClient.deleteFlowByPdId(pid);
//通過業務主鍵獲取流程實例id
String orderNo = "2934792374928734987";
longpdid = workFlowControlClient.getPdIdByOrderNo(orderNo);
}
BD控件事件定製
所用到的依賴:f1-interface-script
如下新建一個service, 繼承 BaseClsScript
有創建前後、保存前後、刪除前後、查詢後,共七個事件,需要哪個事件就重寫一下哪個事件。
然後在模型工具中對類型進行右鍵掛接腳本,輸入當前微服務的id以及新做的service的id
Bp控件服務定製
bp控件有bpgrid, bptree, combobox,具體實現如下。
引入依賴f1-starter-ui:
如果引入了f1-starter,就不用單獨引入f1-starter-ui了
在啓動類上加componentScan標註,添加對com.jb.ui包的掃描。
在啓動類上加EntityScan標註,添加對新添加實體類的掃描。
@ComponentScan(basePackages={"com.jb.mst","com.jb.ui"})
@EntityScan("com.jb.mst.model")
實體類就是一般的Hibernate實體類的基礎上,在字段上加@FieldEditor和@JsonProperty
FieldEditor是字段的一些顯示屬性,JsonProperty是指明實體對象轉成JSON時的字段對應的屬性名
@Entity
@Table(name="tb_app_bdz", catalog="us_app")
public class TbAppBdz extends PersistClass {
/** TODO */
private static final long serialVersionUID = -8103749525304011660L;
@GenericGenerator(name = "generator", strategy = "com.jb.dao.id.UIDGenerator")
@Id
@GeneratedValue(generator = "generator")
@Column(name = "GUID", nullable = false, length = 42)
@FieldEditor(caption = "唯一標識", isReadOnly=true, dispIndex = 0, hidden = true)
@JsonProperty("GUID")
private String guid;
@Column(name = "OBJ_CAPTION", nullable = true, length = 200)
@FieldEditor(caption = "對象標識", isReadOnly=true, dispIndex = 1, hidden = true)
@JsonProperty("OBJ_CAPTION")
private String obj_caption;
bpgrid的service 繼承EntityOperationServiceAdapter
@Service("testBpGridService")
@Transactional(value="transactionManager", propagation=Propagation.REQUIRED)
publicclass TestBpGridService extends EntityOperationServiceAdapter<TbAppBdz> {
}
bptree的service 繼承TreeService
@Service("testBpTreeService")
@Transactional(value="transactionManager", propagation=Propagation.REQUIRED)
public class TestBpTreeService extends TreeService {
//測試url: http://192.168.1.20:8081/zuul/f1-microserviceoftenuse/tree/query.do?service=testBpTreeService&filterStr={}&nodeAttr={"depth":0}&token_seat=token_seat&access_token=5f865d14-5efc-4522-a81c-882c6935ba66
@Autowired
private GenericDao genericDao;
@Override
public List<TreeNodeModel> query(TreeNodeModel queryModel, List<FilterModel> filterModels, UserModel userModel) {
List<TreeNodeModel> model = new ArrayList<TreeNodeModel>();
String sql = ""; // 如果是第0層
if (queryModel.getDepth() == 0) {
sql = "SELECT DEPT_ID,DEPT_NAME,IFNULL(SUPER_DEPT,'空') SUPER_DEPT " + " FROM US_SYS.TB_SYS_DEPARTMENT " + " WHERE SUPER_DEPT is null";
} else if (queryModel.getDepth() == 1) {
sql = "SELECT DEPT_ID,DEPT_NAME,IFNULL(SUPER_DEPT,'空') SUPER_DEPT " + " FROM US_SYS.TB_SYS_DEPARTMENT " + " WHERE SUPER_DEPT = '"
+ queryModel.getId()
+ "' AND (SFZF IS NULL or sfzf = 'F') AND DEPT_TYPE = 0100104 ORDER BY DEPT_NAME ";
} else {
System.out.println(queryModel.getId());
sql = "SELECT PERS_ID, PERS_NAME ,'' DN FROM US_SYS.TB_SYS_PERSON WHERE DEPT_ID = '" + queryModel.getId()
+ "'";
}
@SuppressWarnings("unchecked")
List<Object[]> list = (List<Object[]>) genericDao.getDataWithSQL(sql);
for (Object[] objs : list) {
TreeNodeModel tree = new TreeNodeModel(objs[0].toString(), objs[1].toString());
tree.setChecked(queryModel.isChecked());
if (queryModel.getDepth() == 2)
tree.setLeaf(true);
Map<String, String> attr = new HashMap<String, String>();
attr.put("bmbm", objs[2].toString());
tree.setAttr(attr);
model.add(tree);
}
return model;
}
@Override
public Map<Object, String> putDisplay(Set selectedValueSet) {
Map<Object, String> display = new HashMap<Object, String>();
DataTable table = null;
StringBuffer buffer = new StringBuffer();
Iterator it = selectedValueSet.iterator();
while (it.hasNext()) {
String dStr = (String) it.next();
if (buffer.length() > 0) {
buffer.append(",");
}
buffer.append("'");
buffer.append(dStr);
buffer.append("'");
}
String values = buffer.toString();
if (!StringUtils.isNullOrEmpty(values)) {
String sql = String.format(
"SELECT PERS_ID,PERS_NAME FROM US_SYS.TB_SYS_PERSON " + " WHERE PERS_ID in (%s) ", values);
table = genericDao.exeSql(sql);
}
for (int i = 0; i < table.getRows().size(); i++) {
DataRow dataRow = table.getRows().get(i);
display.put(dataRow.getValue(0), dataRow.getValue(1).toString());
}
return display;
}
}
combobox的service 繼承ComboBoxService
@Service("testComboboxService")
@Transactional(value="transactionManager", propagation=Propagation.REQUIRED)
public class TestComboboxService extends ComboBoxService {
//測試url: http://192.168.1.21:8088/zuul/f1-microserviceoftenuse/comboBox/query.do?service=testComboboxService&token_seat=token_seat&access_token=5f865d14-5efc-4522-a81c-882c6935ba66
@Autowired
private GenericDao genericDao;
@Override
public ComboBoxModel query(Map<String, ?> filterMap, boolean blankItem, UserModel userModel) {
String sql = "select code_name,code from us_sys.tb_sys_code where label_code='02001'";
@SuppressWarnings("unchecked")
List<Object[]> list = (List<Object[]>)genericDao.getDataWithSQL(sql);
ComboBoxModel cm = new ComboBoxModel();
if(list==null) {return cm;}
for(int i=0; i<list.size();i++){cm.addData(
new String[]{
list.get(i)[0].toString(),
list.get(i)[1].toString()
});
}
return cm;
}
@Override
public Map<Object, String> putDisplay(Set selectedValueSet) {
Map<Object, String> display = new HashMap<Object, String>();
DataTable table = null;
StringBuffer buffer = new StringBuffer();
Iterator it = selectedValueSet.iterator();
while (it.hasNext()) {
String dStr = (String) it.next();
if (buffer.length() > 0) {
buffer.append(",");
}
buffer.append("'");
buffer.append(dStr);
buffer.append("'");
}
String values = buffer.toString();
if (!StringUtils.isNullOrEmpty(values)) {
String sql = String.format(
"select code,code_name from us_sys.tb_sys_code where code in (%s) ", values);
table = genericDao.exeSql(sql);
}
for (int i = 0; i < table.getRows().size(); i++) {
DataRow dataRow = table.getRows().get(i);
display.put(dataRow.getValue(0), dataRow.getValue(1).toString());
}
return display;
}
}
把當前微服務的id和bp的service的beanid交給前臺控件,就可以調用這些自定義的service
異構數據庫支持
首先要有依賴:f1-starter-configure 如果已經引入了f1-starter就不用再引入了
在application.properties中加參數
##異構數據庫配置(true時每次請求都會加載)
platform.config.debugMode=true
在resources下加resource.xml
以查詢消息發送時間的sql爲例,把幾種數據庫的sql寫到這裏邊
下邊用ResourceManager.getInstance().getDBSS("queryMsgSendTime")從resource.xml中讀出對應當前數據庫的sql
多數據源支持
首先要在pom中引入f1-starter-data。已經引入了f1-starter就不用再單引了
啓動類上用@Import引入DynamicDataSourceRegister 和 DynamicDataSourceAspect
在application中加入自定義的數據源配置ds1和ds2, 這時系統中連默認數據源,共有三個數據源
如下圖中是在方法上使用自定義的數據源,就是加上@TargetDataSource("數據源的名字"),也可以加在類上
用jdbc的方式:
用genericDao的方式:
即時推送
加入依賴:
<dependency>
<groupId>com.joinbright.f1</groupId>
<artifactId>f1-interface-websocket</artifactId>
</dependency>
在啓動類上加標註:@EnableFeignClients("com.jb.**.client")
消息推送示例如下:
@ApiOperation(value = "即時推送示例", httpMethod = "GET", response = String.class, notes = "即時推送示例")
@RequestMapping(value = "immediatePush", method=RequestMethod.GET)
publicvoid immediatePush() {
Message message = new Message("topicid1", "identityId", "消息內容", "發送者", "消息類型");
webSocketClient.sendMessage(message );
}
其中消息內容是字符串,發送者是pers_id,消息類型是(whole:發送消息體Json字符串、content:僅發送消息內容)
jms消息
jms可以解決一個地方發生了某個事件後,可以觸發連鎖的多個地方的操作,是觀察者模式的具體實現,是消息隊列的具體應用,F1平臺用的activeMQ消息隊列,下面是具體的實現方法。
加入依賴:
<dependency>
<groupId>com.joinbright.f1</groupId>
<artifactId>f1-message</artifactId>
</dependency>
在application.properties中配置消息中間件爲jms
# 使用jms或者是kafka作爲消息中間件(jms/kafka)
platform.config.messageSwitch=jms
jms生產者
@Service("jmsSendService")
public class JmsSendServiceImpl {
publicvoid jmsSend() {
msgSender.send("topic01","發送jms消息");
}
}
jms消費者
@Component
public classMyJmsListener {
@JmsListener(destination = "topic01",containerFactory = "myFactory")
public void listen1(String foo) {
System.out.println(foo);
}
}
以上jms的配置只適用於生產者和消費者在同一個項目中(因爲默認是使用的是內存中的activeMQ,所以二者在不同的項目中無法消息監聽),這種情況在不啓動activeMQ Server時用於測試還是可以的。
# 使用jms或者是kafka作爲消息中間件(jms/kafka)
platform.config.messageSwitch=jms
##################### 給jms配置的activeMQ配置
spring.activemq.broker-url=tcp://localhost:61616
spring.activemq.user=admin
spring.activemq.password=admin
kafka消息
加入依賴:
<dependency>
<groupId>com.joinbright.f1</groupId>
<artifactId>f1-message</artifactId>
</dependency>
在application.properties中加kafka連接參數:
#Kafka配置
#地址
platform.config.kafka_server=127.0.0.1:64846
#消費者組號
platform.config.kafka_consumer_group_id=testT
#消費者是否自動提交
platform.config.kafka_consumer_auto_commit=false
#消費者提交間隔
platform.config.kafka_consumer_commit_interval=100
#消費者session超時時間
platform.config.kafka_consumer_session_timeout=15000
#在ZK中沒有offset值時,consumer應該從哪個offset開始消費
platform.config.kafka_consumer_auto_offset_reset=earliest
#Key反序列化類
platform.config.kafka_consumer_key_deserializer=org.apache.kafka.common.serialization.IntegerDeserializer
#value反序列化類
platform.config.kafka_consumer_value_deserializer=org.apache.kafka.common.serialization.StringDeserializer
#生產者重試次數
platform.config.kafka_producer_retries=0
#生產者數據塊大小
platform.config.kafka_producer_batch_size=16384
#生產者逗留時間
platform.config.kafka_producer_linger_ms=1
#生產者緩存的大小
platform.config.kafka_producer_buffer_memory=33554432
#Key序列化類
platform.config.kafka_producer_key_serializer=org.apache.kafka.common.serialization.IntegerSerializer
#value序列化類
platform.config.kafka_producer_value_serializer=org.apache.kafka.common.serialization.StringSerializer
#監聽容器的數量(併發的數量)
platform.config.kafka_concurrency=3
#監聽容器poll的超時時長
platform.config.kafka_poll_timeout=3000
在application.properties中配置消息中間件爲kafka
# 使用jms或者是kafka作爲消息中間件(jms/kafka)
platform.config.messageSwitch=kafka
kafka生產者:
@Service("kafkaSendService")
public class KafkaSendServiceImpl implements KafkaSendService {
@Autowired
private MsgSender msgSender;
/**
* @Title: kafkaSend
* @Description: kafka消息發送
* @param
* @return void
* @throws
*/
public void kafkaSend() {
msgSender.send("topic01", "發送kafka消息");
}
kafka消費者:
@Component
public class MyKafkaListener {
@KafkaListener(topics = "topic01")
public void listen1(String foo) {
System.out.println(foo);
}
}
自動裝配組件開發
類似於springboot 的starter,提供一些開箱即用的功能。我們以f1-starter-data爲例,來初始化sessionFactory,transactionManager來說明自定義starter
編寫pom文件
<parent>
<groupId>com.joinbright.f1</groupId>
<artifactId>f1-parent</artifactId>
<version>3.0.0-SNAPSHOT</version>
</parent>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<optional>true</optional>
</dependency>
編寫一個DaoAutoConfigure類內容如下,我們需要使用@Configuration註解我們的類
@Configuration
public class DaoAutoConfigure {
@Autowired
private EntityManagerFactory entityManagerFactory;
@Bean("sessionFactory")
public SessionFactory getSessionFactory() {
if (entityManagerFactory.unwrap(SessionFactory.class) == null) {
throw new NullPointerException("factory is not a hibernate factory");
}
return entityManagerFactory.unwrap(SessionFactory.class);
}
@Bean("genericDao")
// @Autowired
public GenericDao getDao(SessionFactory sessionFactory){
GenericDao dao=new GenericDaoImpl();
dao.setSessionFactory(sessionFactory);
return dao;
}
//txManager事務開啓
@Bean("transactionManager")
public HibernateTransactionManager txManager(SessionFactory sessionFactory) throws SQLException {
HibernateTransactionManager hibernateTransactionManager = new HibernateTransactionManager();
hibernateTransactionManager.setSessionFactory(sessionFactory);
return hibernateTransactionManager;
}
}
最後resources/META-INF/spring.factories中加入這個DaoAutoConfigure:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.jb.data.autoconfigure.DaoAutoConfigure
這樣一個標準的starter就編寫完成,然後我們只需要在具體微服務中引入這個starter就能做到開箱即用。
interface組件開發
我們當前微服務的名字:f1-microServiceOftenUse,其中有一個控制器:HelloWorldController, 我們想要把這個控制器中的功能暴露給其它的服務。
我們新建一個maven項目命名爲f1-interface-microServiceOftenUse
pom 中引入如下依賴
<parent>
<artifactId>f1-parent</artifactId>
<groupId>com.joinbright.f1</groupId>
<version>3.0.0-SNAPSHOT</version>
</parent>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
然後在新建一個類com.jb.mst.client.HelloWorldControllerClient
@FeignClient(name="f1-microServiceOftenUse")
public interface HelloWorldControllerClient {
@RequestMapping(method = {RequestMethod.GET}, value = "/first/operModel.do")
String gethello(String guid);
}
對於其他微服務而言只需要引入f1-interface-microServiceOftenUse,就能訪問HelloWorldController中的方法了
具體調用方法請參考:使用模型服務、使用工作流服務、BD控件事件定製、即時推送(websocket)這幾個部分都是調用的對應微服務的interface來實現的。
服務調用
指的就是微服務間調用服務,有時候一個微服務中的功能要依賴其它微服務的功能來實現,這時就需要服務間的調用,有兩種方式:1.Ribbon方式(serviceId可以動態改變)、2.Fegin方式(serviceId是靜態的),下邊是具體的實現方法。
Ribbon 方式訪問
這裏通過ribbon方式訪問model-service中的attr/cmdGetAttribute.do方法
通過postParameters.add方法添加服務需要的變量。
@RestController
public class RibbonController extends BaseAgent{
private static final Log log = LogFactory.getLog(RibbonController.class);
@Autowired
private RestTemplate restTemplate;
private String serviceid="model-service";
@RequestMapping("/ribbon")
@ResponseBody
public String service1() throws Exception {
HttpHeaders headers = setHeader();
// 獲取請求參數
MultiValueMap<String, String> postParameters = new LinkedMultiValueMap<String, String>();
postParameters.add("guid", "00370C16-4CA9-43BE-88B4-4DA5E4FF4FB8-00096");
String url = "http://" + serviceid + "/attr/cmdGetAttribute.do";
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<MultiValueMap<String, String>>(
postParameters, headers);
return restTemplate.postForObject(url, requestEntity, String.class);
}
}
Feign方式
我們需要編寫feignClient客戶端, 方式和上一小節中的interface組件的實現是一樣的。
@FeignClient(name="model-service")
public interface FeginClient {
@RequestMapping(method = {RequestMethod.POST}, value = "/attr/cmdGetAttribute.do")
String gethello(String guid);
}
在調用feign接口的地方,將上一步的FeignClient 注入進來。下邊示例是在一個控制器中調用了feignClient
@RestController
public class FeignControl {
@Autowired
private FeignClient feginClient;
@RequestMapping("/feign")
public String fegin(){
return feignClient.gethello("00370C16-4CA9-43BE-88B4-4DA5E4FF4FB8-00096");
}
}
服務事件擴展
有時候平臺中一些公共的service的處理結果不滿足實際的需要,這時候就需要用事件擴展,事件擴展就是對平臺中一些暴露出擴展點service進行擴展,用自定義的方法來實現自己的邏輯。具體實現方法如下。
這裏以擴展usermenu的service爲例
1.引入依賴(如果已經引入了f1-starter就不用再單獨引入f1-starter-listener了):
<dependency>
<groupId>com.joinbright.f1</groupId>
<artifactId>f1-starter-listener</artifactId>
</dependency>
2.加入配置項:
# 啓動後執行類(自動註冊本服務中擴展類)
context.listener.classes=com.jb.listener.client.starter.RegisterListener
# 通用接口實現類
platform.config.listeners=服務名:擴展類型
服務名是自定義的beanid, 擴展類型是被擴展的類型,
# 啓動後執行類(自動註冊本服務中擴展類)
context.listener.classes=com.jb.listener.client.starter.RegisterListener
# 通用接口實現類
platform.config.listeners=usermenu:usermenuExtend:0
3.擴展類
@Service("usermenuExtend")
public class UserMenuExtend extends Listener {
@Override
public Object extend(Event event) {
DataTable t = (DataTable) event.getOrignalData();
System.out.println("事件中數據量爲:"+t.getRows().size());
System.out.println("修改後,事件中數據量爲:"+t.getRows().size());
return t;
}
}
然後先啓動usermenu原service的微服務,再啓動當前擴展的微服務就可以覆蓋之前的usermenu服務了。
注:對於一個原service, 只能有一個擴展,如果再有其它的註冊就會報錯。