当前位置: 首页 > news >正文

学院网站建设目标阜南县城乡建设局官方网站

学院网站建设目标,阜南县城乡建设局官方网站,广州市网站建设需要审批,帝国cms7.0模板 绿色企业网站模板(整站带数据)开篇词 毫无疑问微服务架构是目前最主流的大型互联网应用系统架构方式#xff0c;因为一个大型系统被拆分为若干个子应用#xff0c;故子应用之间相互调用进行数据读写这件事情变得像呼吸一样普遍。每个一个程序员都能够写代码实现一个RPC服务的调用#xff0c;但不同的实现…开篇词 毫无疑问微服务架构是目前最主流的大型互联网应用系统架构方式因为一个大型系统被拆分为若干个子应用故子应用之间相互调用进行数据读写这件事情变得像呼吸一样普遍。每个一个程序员都能够写代码实现一个RPC服务的调用但不同的实现方式体现着程序员的不同境界今天就来探讨一下调用一个RPC服务的三重境界。 初阶平铺直叙 先上结论平铺直叙的方式没有隔离技术复杂度与业务复杂度随着时间的推移很难从代码中还原业务场景。 简单需求 假定现在有这么一个很简单的需求你需要根据交易单号查询到交易单解析交易单获取到该笔交易单对应的收货地址信息根据收货地址信息查询快递资源信息并返回。你可能不假思索地就写出如下代码。 public class ExpressBizService {Autowiredprivate TradeRpcService tradeRpcService;Autowiredprivate ResourceRpcService resourceRpcService;public ResourceInfo queryExpressResourceByTradeId(Long tradeId) {SingleQueryResultDO order tradeRpcService.getOrderById(tradeId);LogisticsOrderDO logisticsOrder order.getLogisticsOrder();String city logisticsOrder.getCity();String area logisticsOrder.getArea();String town logisticsOrder.getTown();return resourceRpcService.queryResourceByAddressInfo(city, area, town);;} } 但是RPC调用和本地调用相比RPC调用是不可控的第一要有网络开销第二服务是别人提供的万一被人挂了直接抛异常了怎么办 那就加个try catch呗把异常catch并处理一下。于是代码变成了这样 public class ExpressBizService {Autowiredprivate TradeRpcService tradeRpcService;Autowiredprivate ResourceRpcService resourceRpcService;public ResourceInfo queryExpressResourceByTradeId(Long tradeId) {try {SingleQueryResultDO order tradeRpcService.getOrderById(tradeId);LogisticsOrderDO logisticsOrder order.getLogisticsOrder();String city logisticsOrder.getCity();String area logisticsOrder.getArea();String town logisticsOrder.getTown();return resourceRpcService.queryResourceByAddressInfo(city, area, town);} catch (Throwable t) {// 网络异常进行重试if (t instanceof NetworkException) {throw new RetryException(t);}// 其他异常不重试return null;}} } 加个缓存 上线运行了几天时间你发现RPC查询速度太慢了调用交易域提供的服务查询交易单需要15ms左右导致queryExpressResourceByTradeId方法需要20ms左右。而业务预期是10ms左右那怎么办加缓存呗 先查缓存若命中缓存则直接从缓存中解析地址信息若未命中缓存则先查询交易单再写缓存再查询资源域并返回结果处理缓存相关的异常 于是代码又进化成了这样 public class ExpressBizService {Autowiredprivate TradeRpcService tradeRpcService;Autowiredprivate ResourceRpcService resourceRpcService;Autowiredprivate RedisDataSource redisDataSource;public ResourceInfo queryExpressResourceByTradeId(Long tradeId) {try {String json resourceRpcService.get(tradeId);// 若命中缓存则直接从缓存中解析地址信息 if (StringUtils.isNotBlank(json)) {LogisticsOrderDO cache JSONObject.parseObject(json, LogisticsOrderDO.class);return resourceRpcService.queryResourceByAddressInfo(cache.getCity(), cache.getArea(), cache.getTown());} else {//若未命中缓存则先查询交易单再写缓存再查询资源域并返回结果SingleQueryResultDO order tradeRpcService.getOrderById(tradeId);LogisticsOrderDO logisticsOrder order.getLogisticsOrder();String city logisticsOrder.getCity();String area logisticsOrder.getArea();String town logisticsOrder.getTown();resourceRpcService.put(tradeId, JSONObject.toJSONString(logisticsOrder));return resourceRpcService.queryResourceByAddressInfo(city, area, town);}} catch (Throwable t) {// RPC网络异常进行重试if (t instanceof NetworkException) {throw new RetryException(t);}// Redis异常进行重试if(t instanceof RedisBusyException){throw new RetryException(t);}// json解析异常要记录原因秋后算账if(t instanceof JsonParseException){log.error(JsonParseExceptiontradeId);}// 其他异常不重试return null;}} } 加个鉴权 代码又在线上跑了几天然后你收到一封交易域发来的邮件因为数据安全原因将不再提供明文的收货地址信息需要先查询加密的收货地址信息然后根据AK SK去解密出明文收货地址信息。 得嘞~这又得改动。于是代码又变成了这样 public class ExpressBizService {Autowiredprivate TradeRpcService tradeRpcService;Autowiredprivate TradeDecryptRpcService tradeDecryptRpcService;Autowiredprivate ResourceRpcService resourceRpcService;Autowiredprivate RedisDataSource redisDataSource;private final String APP_KEY xxx;private final String SECRET_KEY xxx;public ResourceInfo queryExpressResourceByTradeId(Long tradeId) {try {String json resourceRpcService.get(tradeId);// 若命中缓存则直接从缓存中解析地址信息然后查询资源域并返回结果if (StringUtils.isNotBlank(json)) {LogisticsOrderDO cache JSONObject.parseObject(json, LogisticsOrderDO.class);return resourceRpcService.queryResourceByAddressInfo(cache.getCity(), cache.getArea(), cache.getTown());} else {//若未命中缓存则先查询交易单再写缓存再查询资源域并返回结果SingleQueryResultDO order tradeRpcService.getOrderById(tradeId);// 查询加密的物流信息EncryptedLogisticsOrderDO encryptedLogisticsOrderDO order.getEncryptedLogisticsOrderDO();// 解密物流信息DecryptedLogisticsOrderDO decryptedLogisticsOrderDO tradeDecryptRpcService.decryptLogisticsOrderDO(APP_KEY,SECRET_KEY,encryptedLogisticsOrderDO);// 写缓存resourceRpcService.put(tradeId, JSONObject.toJSONString(decryptedLogisticsOrderDO));String city decryptedLogisticsOrderDO.getCity();String area decryptedLogisticsOrderDO.getArea();String town decryptedLogisticsOrderDO.getTown();// 查询资源并返回结果return resourceRpcService.queryResourceByAddressInfo(city, area, town);}} catch (Throwable t) {// RPC网络异常进行重试if (t instanceof NetworkException) {throw new RetryException(t);}// Redis异常进行重试if(t instanceof RedisBusyException){throw new RetryException(t);}// json解析异常要记录原因秋后找负责写缓存的同学算账if(t instanceof JsonParseException){log.error(JsonParseExceptiontradeId);}// 其他异常不重试return null;}} } 模型变了 这时候你又发现代码怎么这么红呢原因是因为之前缓存的明文的地址信息LogisticsOrderDO现在交易域改动了LogisticsOrderDO没有了变成了EncryptedLogisticsOrderDO。代码里之前依赖的LogisticsOrderDO找不到了所以代码一片红。所以又得适配交易域模型的变化。 于是写缓存的代码又要修改 // 若命中缓存则直接从缓存中解析地址信息然后查询资源域并返回结果if (StringUtils.isNotBlank(json)) {DecryptedLogisticsOrderDO cache JSONObject.parseObject(json, DecryptedLogisticsOrderDO.class);return resourceRpcService.queryResourceByAddressInfo(cache.getCity(), cache.getArea(), cache.getTown());} 腐化的味道 至此这段代码已经不能一眼看出最初的业务逻辑是什么了。而且这是在最初的业务逻辑特别简单的情况下实际情况下业务逻辑可不会那么简单呀。后续如果有什么技术上面的变更比如增加各种各样的灰度逻辑为了应对高并发场景增加限流逻辑或者对交易提供的服务进行熔断或者为了提高接口的处理效率将此接口变为批量接口然后使用多线程查询交易单和资源信息那代码会进一步复杂。 技术上变完了业务逻辑也可能会变业务做大了各种By特殊场景的定制逻辑也来了比如通用场景下 根据交易单收货地址信息查询资源域的快递资源信息特殊case1 根据发货单地址信息查询资源域的快递资源信息特殊case 2……。 技术在变复杂业务也在变复杂假设技术的复杂度为N1业务的复杂度为N2。那么整个代码的复杂度不是N1N2而是N1*N2。如果没有及时的治理与重构那么后面的同学只能干一些屎上雕花的活了。 中阶封装变化 先上结论适配器与防腐层。隔离技术复杂度与业务复杂度隔离自己领域的模型与外部领域的模型。 定义模型隔离依赖 我们再来看一遍这个需求需要根据交易单号查询到交易单解析交易单获取到该笔交易单对应的收货地址信息根据收货地址信息查询快递资源信息并返回。 查询交易单并解析是一个行为是一个技术手段其目的在于获取收货地址信息。就相当于坐火车去北京坐火车是一个去北京的方式但坐火车或者可以分为坐卧铺、坐硬座、站票甚至可以不做火车坐飞机、坐轮船、坐火箭。但不管怎样北京这一目的地是不变的。如同上面一样查交易单可以直接查询、可以先查缓存再查交易单、也可以先查缓存再查加密后的交易单再解密但最后都为为了获取到收货地址信息。行为是多变的目的是相对稳定的那如果整个需求都改了那就没办法了。 所以真正需要的是什么是收货地址信息。在中国 省、市、区、街道这四个字段已经能描述一个较为准确的地址了而且是不变的。虽然收货地址这一模型是稳定的但获取收货地址的方式是多变的那我们就把变化的部分封装起来把变化控制在最小范围不让其扩散污染到其他的代码。以此来保持主要业务逻辑代码是整洁清晰易于维护的。 这时候可以写一个包装类Wrapper把查询交易单这个技术动作进行包装封装技术细节以及把交易单模型转化为业务所需的地址模型防止外部模型污染内部业务逻辑。同样地也可以对查询缓存的技术动作以及缓存模型进行封装。查询交易单查询缓存都是为了获取地址信息地址那就抽象出一个地址仓库Repository把获取地址这段逻辑进行封装。坐火车坐飞机都是去北京所以可以抽象出乘坐交通工具去北京后面如果想要打车去北京也可以说是乘坐交通工具去北京所以从交易单、缓存查询地址的逻辑的代码结构会变成这样 后面如果获取地址的方式再发生变化只需要修改包装类以及仓库即可不会影响到上层的业务逻辑。 打破封装破窗效应 这个时候代码结构合理模型内聚ExpressBizService.java又恢复了一眼就能看懂业务逻辑的状态然后因为业务调整这块代码由小A交给小B同学了小B同学接手后了一个新需求是根据快递信息快递公司品牌、快递公司编码查询快递资源详细信息并返回而且快递信息就在交易单上面。因为是紧急需求本着怎么来怎么快的原则新同学没做抽象没搞封装直接又把tradeRpcService.java放进了ExpressBizService.java技术复杂度与业务复杂度又糅合在了一起小B同学干了一段时间离职了小C同学又接手了这段代码。这时候需求变更来了 小A时代的逻辑你需要根据交易单号查询到交易单解析交易单获取到该笔交易单对应的收货地址信息根据收货地址信息查询快递资源信息 小C时代的逻辑你需要根据交易单号查询到交易单解析交易单获取到该笔交易单对应的收货地址信息根据收货地址信息查询快递资源信息。但是在预售场景下需要使用预售交易单。解析预售交易单获取到该笔交易对应的收货地址信息根据收货地址信息查询快递资源信息。假定针对预售场景创建一张预售单据是合理的此处只做举例使用。而tradeRpcService.java的queryPreSaleOrder()方法查询预售交易单。 小C一看tradeRpcService.java已经在ExpressBizService.java里面了呀。那我直接调用queryPreSaleOrder然后再处理就行了呗。于是即使ExpressBizService.java的queryExpressResourceByTradeId()方法已经做到了结构合理、模型内聚因为ExpressBizService.java被污染了导致queryExpressResourceByTradeId()方法也不能幸免。风平浪静的大海掀起波涛海上的小船怎么能独善其身呢这就是破窗效应。 破窗效应是社会学概念之一也可以理解为“窗户理论”或“破窗理论”。它最早由美国学者威尔逊·凯利Wilson Kelling和乔治·凯利George Kelling于1982年提出。 破窗效应的基本观点是如果一个社区或环境中存在一个被破坏的窗户如果不及时修复其他人很可能会受到影响导致整个环境的质量逐渐下降。这是因为人们会认为这个社区或环境已经被人放任不管没有秩序和规则从而产生更多的破坏行为。 破窗效应主要强调环境的外在形象和秩序对个体行为产生的影响。它认为人们的行为受到周围环境的影响。在一个有序、整洁、规范的社区中人们更有可能遵守法律和规则保持社会秩序。相反在一个杂乱、破坏、缺乏秩序的环境中人们更容易产生不良行为、违法行为甚至破坏行为。 后面的故事也很容易被猜到出于性能考虑需要对于预售交易单加缓存出于安全合格考虑预售交易单上面的用户信息也需要加密解密然后代码又走上了腐坏的道路…… 高阶依赖倒置 先上结论高层代码定义标准底层代码实现标准。 高层代码指的是那些容易变动的业务代码底层代码指的较为稳定的基础代码访问数据库、缓存、调用外域HSF接口。在逻辑层面高层代码虽然要依赖底层代码对数据进行读写但是数据的模型以及对数据操作的行为是高层代码所定义的。 落实到代码层面是这样的高层代码和底层代码分别位于两个模块之中高层代码在应用层底层代码在基础设施层。数据模型以及对数据的操作抽象成接口且接口放在应用层底层代码要实现接口则基础设施层需要依赖应用层这样应用层就不能再依赖基础设施层了循环依赖编译报错。所以后面接手的同学很难去打破前人的合理设计。 这样在编码过程中高层代码是不可能感知到底层代码的模型与行为的因为已经通过接口隔离了。从而让高层代码专注业务逻辑底层代码专注技术细节。程序运行时高层代码通过接口注入真正的底层实现类在逻辑上完成调用底层代码。 正常情况下调用顺序和依赖顺序应该是一致要调用基础设施层的代码就应该在POM文件里明确依赖基础设施层。但是在这里却是基础设施层依赖了应用层这就是依赖倒置。通过依赖倒置就能实现高层代码定标准底层代码做实现同时两个层次的代码通过接口隔离互不干涉影响。 面向过程vs面向对象 结论有面向对象的思维才能写出面向对象的代码即高内聚、低耦合、易拓展、易维护的代码。 初阶与高阶看似是写代码的不同实际上是背后思维方式的不同。 初阶是面向过程高阶是面向对象。 从最开始学习Java之时我们都知道Java是一门面向对象的语言关于面向对象的理论大家都是滚瓜烂熟信手拈来但是当真正写代码的时候却是基于贫血模型写了一段又一段冗长的面向过程的脚本代码。没有抽象没有封装也没有内聚几经转手之后就变成了开发同学们所憎恶的“屎山代码”。 只具备面向过程思维的程序员当面对一个需求时首先考虑的是具体的实现步骤先干嘛再干嘛最后干嘛。如果一开始就陷入了细节那么产出的代码一定是没有抽象的过程式的脚步代码。 面向过程的思维方式在应对简单的业务逻辑之时毫无问题但是当业务逻辑变复杂且由多个程序员共同开发实现的时候代码大概率会变得复杂难以维护。因为面向过程的代码它描述的都是细节。人脑善于思考善于逻辑推理但不善于存储也就是说我们拥有一个非常牛逼的CPU但是一级缓存、二级缓存、三级缓存以及内存都少得可怜。因此通读面向过程的代码很容易就迷失在细节里面尤其是遇到多层方法嵌套的场景不知不觉就陷入代码的泥潭里面。 换句话说从抽象到具体从概要到详细是容易理解的但是从具体到抽象、从详细到概要是比较困难的。举个极端例子你能很容易地根据EM*C^2算出来一个苹果的能量抽象--具体但是让你从复杂的现实世界推到出这个方程能做到吗?具体--抽象 所以我们需要具备面向对象的思维方式来应对复杂的业务需求当遇到一个问题时首先考虑的是这个问题域里面有哪些对象这些对象有哪些属性以及行为这些对象直接的关系如何业务代码如何编排这些对象来解决问题这种思维方式会引导者我们去分解问题去不断剖析问题的本质最终产出的代码也是从抽象到具体的。 具体的代码大家都会写那么抽象的代码怎么写呢其实业界也早就有了答案简单来说就四个字“面向对象”。复杂来说就是solid原则、各种设计模式、领域驱动设计……首先要学会这些知识然后尝试应用知识应用之后再反过来体悟已学过的知识如此反复螺旋式地不断加深理解最终方能游刃有余地驾驭“面向对象”这把屠龙宝刀在代码的江湖里面独占一席之地。
http://www.zqtcl.cn/news/311435/

相关文章:

  • 建设一个网站要多少钱建设银行信用卡卡网站首页
  • 百度推广效果怎样seo网上培训课程
  • 广州品牌网站建设 优美wordpress 跳转页面
  • 成都专业的网站建设制作公司哪家好m3u8视频可以永久保存吗
  • 上海做兼职的网站如何在凡科建设网站
  • 沙朗镇做网站公司企业管理咨询师是干什么的
  • 起名网站建设商汇通网站
  • 网站开发费税率是多少钱建设小网站教程
  • 企业制作网站服务物联网应用有哪些
  • 中国建设网站银行个人做网站要注意什么条件
  • 莆田哪里有学做网站的wordpress可视化函数
  • 网站规划书500字上海金融网站制作网站制作公司好
  • 郑州网站制做钓鱼网站的公司
  • 网站域名授权怎么做以鹦鹉做头像的网站
  • 后端开发网站做一些什么WordPress能做门户网吗
  • 石家庄市住房和城乡建设厅网站好的企业型网站模板下载
  • 哈尔滨快速建站公司推荐网站刷流量会怎么样
  • 网站开发 顺德做新闻微网站有哪些
  • 如何阿里巴巴网站做推广苏州高新区建设局网站管网
  • 个人网站设计构思做汽车保养的网站
  • 在什么网站可以接国外的模具做中英文网站是怎么做的
  • 网站中的文字滑动怎么做的沈阳模板建站定制
  • 国内网站开发二手房网站开发背景
  • 电商网站建设与管理实践电商网站前端制作分工
  • 外贸公司怎么接订单网站编辑做seo好做吗
  • 科技企业网站建设珠海微信网站开发
  • 科凡网站建设小程序自己开发
  • 专门做金融的招聘网站wordpress 价格
  • 微商城网站建设信息定制程序网站
  • 电子政务网站开发餐饮品牌设计包括哪些