F1V3.0-17 微服务常用功能开发
来源:互联网 发布:天下霸唱抄袭 知乎 编辑:程序博客网 时间:2024/06/12 22:06
引言
本文中的功能是以7.1快速开发一个微服务为基础, 如果不了解,请先阅读那一篇博客
本文介绍了F1平台的一些常用功能:使用统一权限、使用缓存、使用统一配置、获取常用配置参数、微服务自定义配置参数、使用模型服务对模型数据增删改查、使用工作流服务、BD控件事件定制、Bp控件服务定制、异构数据库支持、多数据源支持、即时推送、jms消息、kafka消息、自动装配组件开发、interface组件开发、服务调用、服务事件扩展
使用统一权限
在当前的微服务中依赖f1-starter-auth(如果已经引入了f1-starter,就会间接引入f1-starter-auth),如果没有授权的请求来访问,就会被拒绝。
application.properties中加入权限服务器参数:
###########################oauth服务器相关配置###################### 认证服务器凭证security.sessions:neversecurity.oauth2.client.client-id: client-idsecurity.oauth2.client.client-secret: client-secretsecurity.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=oauth2security.userOauth.tokenName=access_tokensecurity.userOauth.scope.code=writesecurity.userOauth.scope.desc=writeapp.key=f1swaggerapp.name=F1平台微服务请求APIapp.desc=更多的下载资源和信息请查看:http://192.168.1.173/f1-platform/f1-microService/ app.version=3.0.0app.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.0app.licenseUrl=http://http://blog.csdn.net/zhbr_f1
加入redis的配置,因为权限认证信息要缓存在redis中
####################### REDIS (RedisProperties)# Redis数据库索引(默认为0spring.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数据库索引(默认为0spring.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# 配置中心服务idspring.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]:2181spring.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}") //第一种方式,用@Valueprivate 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进行增删改查
@Autowiredprivate SingleBDClient singleBDClient;@Overridepublic 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@Autowiredprivate GenericDao genericDao; @Overridepublic 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;}@Overridepublic 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@Autowiredprivate GenericDao genericDao;@Overridepublic 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;}@Overridepublic 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消费者
@Componentpublic 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:61616spring.activemq.user=adminspring.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 {@Autowiredprivate MsgSender msgSender;/** * @Title: kafkaSend * @Description: kafka消息发送 * @param * @return void * @throws */public void kafkaSend() {msgSender.send("topic01", "发送kafka消息");}
kafka消费者:
@Componentpublic 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注解我们的类
@Configurationpublic class DaoAutoConfigure {@Autowiredprivate 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")//@Autowiredpublic 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方法添加服务需要的变量。
@RestControllerpublic 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
@RestControllerpublic 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 {@Overridepublic 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, 只能有一个扩展,如果再有其它的注册就会报错。
- F1V3.0-17 微服务常用功能开发
- F1V3.0-9 微服务功能介绍
- F1V3.0-12 微服务开发规范
- F1V3.0-14 微服务开发环境
- F1V3.0-16 快速开发一个微服务
- F1V3.0-20 UI模块常用功能开发
- F1V3.0-5 平台3.0微服务架构设计
- F1V3.0-21 微服务旧版本升级
- F1V3.0-23 微服务打包发布部署
- F1V3.0-图形-微服务模块打包部署
- F1V3.0-10 前端功能介绍
- F1V3.0-13 UI模块开发规范
- F1V3.0-15 前端开发环境搭建
- F1V3.0-3 前端开发问题之解决之道
- F1V3.0-18 快速开发一个UI模块
- F1V3.0-11 开发规范——常规模式
- QBit开发微服务
- F1V3.0-图形-GIS基础知识
- codeforces 58A(Chat room) Java
- 好好好好好好好好好好好跟哥哥
- qt5插件开发QString转码问题
- React-组件生命周期
- 集合运算符
- F1V3.0-17 微服务常用功能开发
- 路由算法
- 隐写术加密方法
- 课堂/会议同屏教学解决方案之RTSP/RTP over UDP组播解决方案
- 如何做一个不被喷的产品经理?
- Map的6种遍历方式总结
- elixir spwan以及spawn_link(:trap_exit)
- Android APK 瘦身实践(减小apk的大小)
- 组函数