毕设项目,系统搭建笔记文档
代码结构:
主体系统启动流程:
在dynamic web project中添加全局监听类RootListener,继承ServletContextListener接口,实现public void contextInitialized(ServletContextEvent sce),调用系统初始化入口类ArchtechRoot。在ArchtechRoot中根据spring配置文件进行系统各模块的初始化。从root bean读取SystemInitDO,用于获取配置文件中的子配置文件,即各层模块配置文件,并将其中的bean进行装配。RPCServer为RPC服务的基础server模块,对应rpcRoot bean。
1 ApplicationContext context = new ClassPathXmlApplicationContext("springContext-*.xml"); 2 SystemInitDO initDO = (SystemInitDO) context.getBean("root"); 3 RPCServer rpcRoot = (RPCServer) context.getBean("rpcRoot");
子配置文件:
1 <bean id="root" class=" com.multiagent.hawklithm.davinci.init.SystemInitDO"> 2 <property name="configFileList"> 3 <list> 4 <value>spring-config/spring-asyntask.xml</value> 5 <value>spring-config/spring-EventListener.xml</value> 6 <value>spring-config/spring-dao.xml</value> 7 <value>spring-config/spring-manager.xml</value> 8 <value>spring-config/spring-module.xml</value> 9 <value>spring-config/spring-ibatis.xml</value> 10 <value>spring-config/spring-net.xml</value> 11 <value>spring-config/spring-transaction.xml</value> 12 <value>spring-config/spring-session.xml</value> 13 </list> 14 </property> 15 </bean>
子配置文件中几个关键注册管理机
bean 分类注册管理机
1 <!-- bean分类注册管理机 --> 2 <bean class="com.multiagent.hawklithm.davinci.init.AutoRegister"/>
异步任务注册机
1 <!-- 异步任务注册机 --> 2 <bean id="asynTaskRegManager" class="com.multiagent.hawklithm.davinci.AsynTaskRegisterMachine" />
消息注册管理机
1 <!-- 消息注册管理机 --> 2 <bean id=" wardenManager" class="com.multiagent.hawklithm.shadowsong.manager.WardenManager"/>
生产过程模块管理机
1 <!-- 生产过程模块管理注册机 --> 2 <bean id="pmRegManager" class="com.multiagent.hawklithm.leon.manager.ProcessModuleRegisterManager"/>
bean注册管理机
1 public class AutoRegister implements BeanPostProcessor { 2 private AsynTaskRegisterMachine asynTaskRegManager; 3 private ProxyFactoryBean accountService; 4 private ProcessModuleRegisterManager pmRegManager;
27 public Object postProcessAfterInitialization(Object bean, String beanName) 28 throws BeansException { 29 if (bean instanceof AsynTaskRegister) { 30 System.out.println("bean name: "+beanName+" regist AsynTaskRegister"); 31 asynTaskRegManager.regist(bean);//异步任务注册 32 }else if (bean instanceof IProcessModule){ 33 System.out.println("bean name: "+beanName+" regist EquipmentObject"); 34 pmRegManager.regist(bean);//过程模块注册 35 } 36 return bean; 37 } 38 64 }
如代码所示,根据类类型就行分类注册
RPC接口注册:
RPC接口定义在springContext-rpc.xml中
如以下示例接口定义
1 <bean class="com.multiagent.hawklithm.davinci.rpc.DO.RPCSystemServerProxy"> 2 <property name="interfaceName" value="com.multiagent.hawklithm.rpc.Interfacetest"/> 3 <property name="version" value="1.0.0.hawk"/> 4 <property name="className" value="com.multiagent.hawklithm.rpc.impl.ImplTest"/> 5 <property name="comment" value="此处填写接口介绍"/> 6 <property name="visible" value="true" /> 7 </bean>
RPCServer创建
1 <bean id="rpcRoot" class="com.multiagent.hawklithm.davinci.rpc.Server.RPCServer"> 2 <constructor-arg> 3 <value>10007</value> 4 </constructor-arg> 5 6 </bean>
在RPCServer.java中,Rpc接口bean注册
1 public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { 2 if (bean instanceof RPCSystemServerProxy) { 3 RPCregManager.regist((RPCSystemServerProxy) bean); 4 } 5 return bean; 6 }
将已注册RPC接口与已注册模块关联:
ArchtechRoot.java
对已填充beanId字段的RPC接口类进行关联
1 for (RPCSystemServerProxy object : rpcRoot.getRPCregManager().getRegList()) { 2 if (StringUtils.hasLength(object.getBeanId())) { 3 try { 4 Object target = tempContext.getBean(object.getBeanId()); 5 object.setTarget(target); 6 System.out.println(object.getInterfaceName() + "关联成功"); 7 } catch (NoSuchBeanDefinitionException e) { 8 // TODO 打印日志RPC配置文件中bean缺失 9 System.out.println("RPC配置文件中缺失bean:[" + object.getBeanId() + "]"); 10 } 11 } 12 }
对未填充beanId字段的RPC接口类进行实例创建(目前均使用单例模式)
1 for (RPCSystemServerProxy object : rpcRoot.getRPCregManager().getRegList()) { 2 if (!object.isLinked()) { 3 object.setTarget(Class.forName(object.getClassName()).newInstance()); 4 System.out.println(object.getClassName() + "生成实例成功"); 5 } 6 }
以上完成所有模块的创建和注册关联工作
启动异步任务注册机中的所有异步任务:
1 regManager.startAll();
消息注册部分Shadowsong
消息注册管理机WardenManager.java
1 public class WardenManager implements IRegisterManager, IMessagePusher<WardenMessage> { 2 3 public boolean regist(Object object) { 4 /**具体见源码*/ 5 } 6 7 /** 8 * Warden接收消息的异步线程 9 * @author hawklithm 10 * 11 */ 12 private class WardenThread implements Runnable { 13 /**具体见源码*/ 14 @Override 15 public void run() { 16 object.receiveMessage(message); 17 } 18 19 } 20 21 /** 22 * 推送消息 23 */ 24 public void push(WardenMessage message) { 25 Set<Warden> list = map.get(message.getKind()); 26 for (Warden object : list) { 27 exec.execute(new WardenThread(object, message)); 28 } 29 } 30 31 }
使用消息注册机时先进行消息注册
1 wardenManager.registWarden(new Warden(“MessageReceiver”, “MessageType”) { 2 3 //message对应消息结构体WardenMessage中的note属性 4 @Override 5 public void asynchronizedProcess(String message) { 6 /**接收到消息后的行为*/ 7 } 8 9 });
发送消息:
1 wardenManager.push(message);
message类型为WardenMessage
纯虚类EquipmentObjetc类型继承了WardenOperator接口实现方法
1 public void sendOutMessage(WardenMessage message) { 2 wardenManager.push(message); 3 }
所以继承了EquipmentObject的类直接调用sendOutMessage(WardenMessage message)即可。
举例:
读卡器模块初始化时(读卡器模块继承了EquipmentObject)
1 this.registWarden(new Warden(String.valueOf(this.getRfid()) + WardenMessage.TARGET_TYPE_READER, WardenMessage.KIND_NEW_DATA_COMING + WardenMessage.DIR_ENTER) { 2 3 @Override 4 public void asynchronizedProcess(String message) { 5 WardenMessage wardenMessage = new WardenMessage(); 6 wardenMessage.setNote(message); 7 wardenMessage.setTarget(String.valueOf(targetRFID) + targetKind); 8 wardenMessage.setKind(targetMessageKind + targetMessageDir); 9 sendOutMessage(wardenMessage); 10 } 11 12 });
以上代码截取自Reader.java表示在接收到warden消息后将其信息实体取出并重新定义warden消息的目标和类型并转发,我们现在做一个约定:
消息接收者:目标RFID+目标类型
消息类型:消息类型+消息流向
WardenMessage.java中展示预设了几种消息类型
1 /** 2 * 消息类型 3 */ 4 public static String KIND_RFID_FROM_READER = "wm_rfidfromreader";//从读卡器模块发送来的RFID数据 5 public static String KIND_NEW_DATA_COMING = "new_data_coming";//接收到从RFID实体来的数据 6 /** 7 * 目标类型 8 */ 9 public static String TARGET_TYPE_READER = "target_type_reader";//数据接收目标类型为读卡器 10 public static String TARGET_TYPE_MACHINE = "target_type_machine";//数据接收目标类型为设备模块 11 /** 12 * 数据流方向 13 */ 14 public static String DIR_ENTER = "wm_enter"; 15 public static String DIR_EXIT = "wm_exit";
模块间信息交互和数据传输均可使用该消息系统
生产过程管理模块(静态模块系统leon)
1 public class ProcessModuleRegisterManager implements IRegisterManager { 2 private List<IProcessModule> regList = new ArrayList<IProcessModule>(); 3 public boolean regist(Object thing) { 4 /**过程模块注册*/ 5 } 6 7 public IProcessModule getProcessModuleByName(String name) throws ProcessModuleNotFoundException { 8 /**根据模块名称(ID)获取已注册的过程模块*/ 9 } 10 11 public EquipmentObject getEquipmentByRFID(int id) throws ProcessModuleNotFoundException { 12 /**根据设备名称(ID)获取已注册的过程模块中的设备模块*/ 13 } 14 }
过程注册模块中包含所有已注册的过程模块信息
生产过程模块(静态模块):
统一继承IProcessModule,具体见IProcessModule.java
1 public interface IProcessModule { 2 /** 3 * 获取模块名称 4 * @return 模块名称 5 */ 6 String getName(); 7 /** 8 * 根据设备RFID从过程模块中获取设备 9 * @param id 设备RFID 10 * @return 11 * @throws EquipmentNotFoundException 12 */ 13 EquipmentObject getEquipmentByRFID(int id) throws EquipmentNotFoundException; 14 }
在过程模块中建立设备列表,通过spring bean注入设备,可达到过程对设备的一个管理,实例见SortingProcess.java
对应bean的spring配置参考spring-module.xml,如下:
1 <bean id="sorting_process_module" class="com.multiagent.hawklithm.leon.process.SortingProcess"> 2 <property name="equipmentList"> 3 <list> 4 <ref bean="reader1"/> 5 <ref bean="sorting_equipment1" /> 6 <ref bean="reader2" /> 7 <ref bean="singleRead3"/> 8 <ref bean="sorting_equipment2" /> 9 10 </list> 11 </property> 12 </bean>
设备模块
统一继承纯虚类EquipmentObject,在EquipmentObject.java中已实现设备的基本功能方法
1 public abstract class EquipmentObject implements Module, WardenOperator { 2 private int rfid ; 3 private Set<Integer> itemRFIDs = new HashSet<Integer>(); 4 private Set<Integer> packageRFIDs = new HashSet<Integer>(); 5 private int staffRFID; 6 private WardenManager wardenManager; 7 8 9 /** 10 * 设置设备属性 11 */ 12 abstract public void doSetEquipmentParameter(); 13 14 /** 15 * 获取设备属性 16 * @return 17 */ 18 abstract public String doGetEquipmentSummaryInfo(); 19 20 /** 21 * 初始化Warden 22 */ 23 abstract public void initWarden(); 24 25 public void sendOutMessage(WardenMessage message) { 26 wardenManager.push(message); 27 } 28 29 public void registWarden(Warden warden) { 30 wardenManager.regist(warden); 31 } 32 33 public void addItem(int RFID) { 34 itemRFIDs.add(Integer.valueOf(RFID)); 35 } 36 37 public void addPackage(int RFID) { 38 packageRFIDs.add(Integer.valueOf(RFID)); 39 } 40 41 public boolean removeItem(int RFID) { 42 return itemRFIDs.remove(Integer.valueOf(RFID)); 43 } 44 45 public boolean removePackage(int RFID) { 46 return packageRFIDs.remove(Integer.valueOf(RFID)); 47 } 48 49 // public EquipmentObject() { 50 // warden = new Warden(this); 51 // wardenManager.regist(warden); 52 // } 53 54 public String doGetModuleSummaryInfo() { 55 return doGetEquipmentSummaryInfo(); 56 } 57 58 public int getStaffRFID() { 59 return staffRFID; 60 } 61 62 public void setStaffRFID(int staffRFID) { 63 this.staffRFID = staffRFID; 64 } 65 66 public WardenManager getWardenManager() { 67 return wardenManager; 68 } 69 70 public void setWardenManager(WardenManager wardenManager) { 71 this.wardenManager = wardenManager; 72 } 98 }
在配置设备bean时注意设置init-method="initWarden"从而调用initWarden函数进行消息注册
例:
1 <bean id="sorting_equipment1" class="com.multiagent.hawklithm.leon.module.SortingEquipmentModule" init-method="initWarden" > 2 <property name="rfid" value="1024"/> 3 </bean>
initWarden中的注册warden见上文消息注册管理Shadowsong部分,具体实现见com.multiagent.hawklithm.leon.module包下的模块类
RPC系统
rpc client端在rpc工程中,server端在davinci中,rpc所有spring bean配置文件均在springContext-rpc.xml中,rpc接口信息通过interfaceInfoDAO保存到数据库中。
RPCServer.java
在initNetty()函数中创建Netty服务端实现通信
1 private void initNetty() { 2 ServerBootstrap bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory(Executors.newCachedThreadPool(), Executors.newCachedThreadPool())); 3 bootstrap.setPipelineFactory(new ChannelPipelineFactory() { 4 5 @Override 6 public ChannelPipeline getPipeline() throws Exception { 7 ChannelPipeline pipeline = Channels.pipeline(); 8 pipeline.addLast("UP_FRAME_HANDLER", new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 2, 0, 2)); 9 pipeline.addLast("DOWN_FRAME_HANDLER", new LengthFieldPrepender(2, false)); 10 pipeline.addLast("myHandler", rpcServerNettyHandler); 11 // return Channels.pipeline(rpcServerNettyHandler); 12 return pipeline; 13 } 14 15 }); 16 bootstrap.bind(new InetSocketAddress(port)); 17 System.out.println("RPC server开启成功"); 18 }
NettyHandler继承SimpleChannelHandler对通信接口进行一些基本封装
public class NettyHandler extends SimpleChannelHandler { @Override public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { /**接收到数据将通信相关数据取出并传入onMessageReceived方法*/ ChannelBuffer buffer = (ChannelBuffer) e.getMessage(); String recvMsg=buffer.toString(Charset.defaultCharset()); System.out.println("receive: " +recvMsg); onMessageReceived(recvMsg,e.getChannel()); } @Override public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception { /**异常处理*/ } public void sendMessage(String message,Channel channel) throws MessageTransportException { if (channel == null) { throw new MessageTransportException(new NullPointerException()); } ChannelBuffer buffer = ChannelBuffers.buffer(message.length()); buffer.writeBytes(message.getBytes()); channel.write(buffer); } /** * 重写该函数可获取传输数据 */ public void onMessageReceived(String message,Channel channel) throws MessageTransportException { } }
RPCServerNettyHandler继承NettyHandler
重写onMessageReceived
1 @Override 2 public void onMessageReceived(String msg, Channel cha) throws MessageTransportException { 3 System.out.println(msg); 4 RPCSystemProtocol rpcMessage = gson.fromJson(msg, RPCSystemProtocol.class); 5 if (!aclManager.verifyRPCInterfaceCall(rpcMessage)) {//权限控制 6 rpcMessage.setReturnObject("have no permission"); 7 return; 8 } 9 if (!rpcMessage.selfCheck()) {//数据自检 10 throw new MessageTransportException("RPC包错误"); 11 } 12 try { 13 Object instance = RPCregManager.getTarget(rpcMessage); 14 Object ret = rpcExec.exec(rpcMessage.getClassName(), rpcMessage.getMethodName(), rpcMessage.getParamsType(), instance, rpcMessage.getParameters());//通过反射机制调用响应类的方法执行并得出结果 15 rpcMessage.setReturnObject(gson.toJson(ret)); 16 if (!watcher.insert(new RPCConnectInfo(gson.toJson(rpcMessage), cha))) {//将需要返回的结果插入发送队列,由于开发RPC时还没有开始设计消息管理中心,所以使用其独立的RPCBufferCallBackWatcher类进行返回数据队列的管理,详见RPCBufferCallBackWatcher 17 // TODO 打印日志 18 } 19 } catch (RPCInterfaceNotFoundException e) { 20 // TODO 打印日志 21 e.printStackTrace(); 22 return; 23 } catch (IllegalAccessException e) { 24 // TODO Auto-generated catch block 25 e.printStackTrace(); 26 return; 27 } catch (IllegalArgumentException e) { 28 // TODO Auto-generated catch block 29 e.printStackTrace(); 30 return; 31 } catch (InvocationTargetException e) { 32 // TODO Auto-generated catch block 33 e.printStackTrace(); 34 return; 35 } 36 }
rpc client端
客户端进行RPC调用思路:
在配置文件中注册需要使用rpc的bean,通过该bean获取一个实例并使用对应的接口方法,利用spring的特性对该接口方法进行代理,将方法的实现转换成netty通信,把调用方法的参数发送给rpc server,并在rpcLockManager中进行一个注册并阻塞当前方法执行线程,等待server返回结果;当server返回结果后,在rpcLockManager中对等待结果的调用进行解锁并唤醒线程,传入server发过来的结果,再又代理返回方法调用结果。使客户端程序员的编码结构不发生任何改变的情况下实现远程调用。
RPCClient.java
基本上和RCPServer.java类似,不过这里创建的是netty服务端
1 public void initRPCClient() { 2 ClientBootstrap bootstrap = new ClientBootstrap(new NioClientSocketChannelFactory(Executors.newCachedThreadPool(), Executors.newCachedThreadPool())); 3 handler = new NettyHandler() { 4 5 private Gson gson = new Gson(); 6 7 @Override 8 public void onMessageReceived(String msg, Channel cha) throws MessageTransportException { 9 RPCSystemProtocol rpcMessage = gson.fromJson(msg, RPCSystemProtocol.class); 10 if (!rpcMessage.selfCheck()) { 11 throw new MessageTransportException("RPC包错误"); 12 } 13 // announcer.insert(rpcMessage); 14 lockManager.AnswerReceived(rpcMessage.uuid, rpcMessage);//接收到服务端回复后将结果返回并将rpc调用解锁 15 16 } 17 18 @Override 19 public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { 20 channel = e.getChannel(); 21 } 22 }; 23 bootstrap.setPipelineFactory(new ChannelPipelineFactory() { 24 public ChannelPipeline getPipeline() throws Exception { 25 ChannelPipeline pipeline = Channels.pipeline(); 26 pipeline.addLast("UP_FRAME_HANDLER", new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 2, 0, 2)); 27 pipeline.addLast("DOWN_FRAME_HANDLER", new LengthFieldPrepender(2, false)); 28 // return Channels.pipeline(handler); 29 pipeline.addLast("myHandler", handler); 30 return pipeline; 31 } 32 }); 33 bootstrap.connect(new InetSocketAddress(address, port)); 34 }
用SpringMethodInterceptor对rpc调用类的实现进行代理
1 public class SpringMethodInterceptor implements MethodInterceptor { 2 3 4 public Object invoke(MethodInvocation inv) throws Throwable { 5 System.out.println(inv.getMethod().getDeclaringClass().getName() + " " + inv.getArguments()); 6 String version=rpcProxyRegManager.getVersion(inv.getMethod().getDeclaringClass().getName()); 7 RPCSystemProtocol message=RPCSystemProtocolPackageUtil.getRPCProtocolPackage(inv.getMethod(),inv.getArguments(),version); 8 rpcClient.sendRPCProtocol(message);//发送调用协议 9 UUID uuidOrigin=message.uuid; 10 RPCSystemProtocol recvMessage=lockManager.waitforAnswer(uuidOrigin);//阻塞当前线程,等待rpcServer返回调用结果 11 Class<?> returnType=inv.getMethod().getReturnType(); 12 return gson.fromJson(recvMessage.getReturnObject(),returnType); 13 14 } 15 16 }
读卡器部分:
采用netty接收读卡器数据
1 public void initReaderServer() { 2 ServerBootstrap bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory(Executors.newCachedThreadPool(), Executors.newCachedThreadPool())); 3 bootstrap.setPipelineFactory(new ChannelPipelineFactory() { 4 5 @Override 6 public ChannelPipeline getPipeline() throws Exception { 7 ChannelPipeline pipeline = Channels.pipeline(); 8 pipeline.addLast("UP_FRAME_HANDLER", new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 2, 0, 2)); 9 pipeline.addLast("DOWN_FRAME_HANDLER", new LengthFieldPrepender(2, false)); 10 pipeline.addLast("myHandler", readerNettyHandler); 11 // return Channels.pipeline(rpcServerNettyHandler); 12 return pipeline; 13 } 14 15 }); 16 bootstrap.bind(new InetSocketAddress(port)); 17 System.out.println("reader data server开启成功"); 18 }
接收到读卡器数据后的操作如下,详见ReaderDataManager.java
1 public void OriginalDataDealing(RFIDOriginalInfos infos){ 2 submitData(infos.getInfos());//存入数据库 3 sendOutDataComingMessage(infos);//发送消息给读卡器模块进行状态定义 4 }
通过消息中心将接收到的读卡器数据推送给读卡器模块
1 private void sendOutDataComingMessage(RFIDOriginalInfos infos){ 2 WardenMessage msg=new WardenMessage(); 3 msg.setKind(WardenMessage.KIND_NEW_DATA_COMING+WardenMessage.DIR_ENTER); 4 String target=""; 5 for (String s:infos.getTargets()){ 6 target+=s+WardenMessage.TARGET_TYPE_READER+"|"; 7 } 8 msg.setTarget(target); 9 10 msg.setNote(gson.toJson(infos.getInfos())); 11 readerMessageComingPusher.sendOutMessage(msg); 12 }
由上文设备模块的创建方法可创建读卡器设备模块,详情见Reader.java
1 public class Reader extends EquipmentObject { 2 3 private int targetRFID = 0; 4 private String targetMessageKind = ""; 5 private String targetKind = ""; 6 private String targetMessageDir = ""; 7 8 @Override 9 public void initWarden() { 10 11 this.registWarden(new Warden(String.valueOf(this.getRfid()) + WardenMessage.TARGET_TYPE_READER, WardenMessage.KIND_NEW_DATA_COMING + WardenMessage.DIR_ENTER) { 12 13 @Override 14 public void asynchronizedProcess(String message) { 15 WardenMessage wardenMessage = new WardenMessage(); 16 wardenMessage.setNote(message); 17 wardenMessage.setTarget(String.valueOf(targetRFID) + targetKind); 18 wardenMessage.setKind(targetMessageKind + targetMessageDir); 19 sendOutMessage(wardenMessage); 20 } 21 22 }); 23 } 24 25 }
在reader模块中注册了对读卡器新接收到数据的信息的监听