引言
本文中的功能是以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, 只能有一个扩展,如果再有其它的注册就会报错。