Spring cloud Gateway与Consul 与Nacos踩的坑

采用Spring gate的时候,服务注册与发现,到底是用Consul还是Nacos?先是用Consul尝试了一下,发现用Spring boot编写的微服务,一定要与Consul在同一台机器上,Consul不支持远程注册(不知道是个不是与我用的参数-dev模式有关,仅支持127.0.0.1的服务注册);遂放弃,有试了试Nacos,Nacos到是很惊喜的支持Spring boot编写的Service与其不在同一台机器上,但是坑又来了

nacos现在选的版本是0.8,nacos在

routes:
– id: bi-admin
uri: lb://bi-admin

仅仅支持http协议,不支持lb协议,尝试了很久,gateway没转发到Springboot编写的service ,久久不能释怀,遂又改回Consul,一下又成功了。

第二天不甘心,又开始折腾Nacos,Spring cloud gateway和nacos环境时好时坏,只好从网上下载nacos.io 原始实例,在原始实例上调试,居然调通了gateway–> nacos –>Spring boot Service provider ,还是非常高兴的

如何修改 WordPress 5.0 ReadMore 为“阅读全文”

以前都是在Centos+Nginx/Apache+Php 环境下安装WordPress,但是我其实根本不熟悉Linux,连二把刀也算不上,但是基本环境按照网上教程也都能配置好,包括Java/Tomcat/MySQL环境,所以也一直在Linux服务器下工作,好多个月不重启,也没任何事情。

由于客户的环境是Oracle,实在担心自己在Linux下安装Oracle不顺利,所以就购买了华为云的Windows 2000 R2环境的服务器。安装Oracle倒是挺顺利的,但是安装Php环境,遇到了很多坑。

关于WordPress中的 ReadMore修改为“阅读全文”,网上也有很多文档,但不是过时了,就是无效,我翻来翻去,在 \wp-content\themes\adventure-travelling\template-parts\post 目录下,看到了content.php这个文件,打开后看到了

esc_attr_e( ‘ReadMore’, ‘adventure-travelling’ )和 esc_html_e(‘ReadMore’,’adventure-travelling’),把这个
ReadMore 修改为“阅读全文”,文件编码格式另存为UTF-8,重新打开,“ReadMore”就变成了“阅读更多”

adventure-travelling 是我用的一个一个模板。

什么是前置仓

永辉生活近日在福州试点前置仓模式来管理线下业务,那么这个前置仓模式是什么,与后置仓模式区别在于哪里?

的盒马鲜生所采取的仓配模式。它的每个门店都是一个中小型的仓储配送中心,这使得总部中央大仓只需对门店供货,也能够覆盖最后一公里。消费者下单后,商品从附近的零售店里发货,而不是从远在郊区的某个仓库发货。这便是支撑它在门店3公里范围内可以做到30分钟送达的重要前提。

“沃尔玛和亚马逊的可移动仓库好比一种更为极致的前置仓模式,它把仓库建在空中,距离客户更近了。你下单之后,无人机会选择最快捷的路径把商品运送给你,全程无阻碍、耗能低。”

在空中漂浮着的可移动仓库配上无人机,不仅解决了最后一公里的距离问题,也解决了仓库的空间问题,以及物流运输途中的交通问题。

在冷链物流服务中,最难的部分就是“最后一公里”配送。据陈锐介绍,先发国家和地区早有生鲜产品的配送服务,如日本和台湾采取的是“宅配”的服务模式,即生鲜产品销售方通过冷链物流(如冷藏车运输)直接将产品配送至客户家中。

在我国,也存在这种冷链物流服务,一般情况是在生鲜电商平台自配体系中,商家会在特定区域、特定时段、针对特定产品进行冷链“宅配”。由于产品直接从商家城市中心仓直发至最终客户手中,一般称之为“中心仓”模式。这种模式对生鲜产品品质保护更好,但是运营成本高,并且受到诸多限制。同时,由于我国市场的特殊情况,这种冷链物流服务目前并不是主流的冷链物流服务模式,主要有两个原因:第一,中国城市交通管制和停车设施等问题,使得冷藏车很难在白天在城市通行,因此很难为个人客户提供上门配送服务;第二,中国城市人口密度大,电商规模更大,生鲜电商订单数量也远超日本和台湾地区,消费者对订单响应速度要求更高,这都使得这种宅配模式无法满足中国生鲜电商的发展需求。

目前我国企业更普遍采用的冷链物流模式是“泡沫箱+冷袋”的模式。用“泡沫箱+冷袋”把生鲜产品打包成一个包裹,包裹内部形成适合生鲜产品保存的局部空间,包裹在物流运作时被视为普通包裹,走现有常温物流配送体系。这种模式成本较低,但是对生鲜产品的品质保护难以保证,对冷链环境要求特别高的生鲜产品无法用这种物流服务模式。

而前置仓模式,是指更靠近消费者的小型仓储单位,一般设置在消费者集中的社区附近。其运营模式是:生鲜产品销售方利用冷链物流(冷藏车)提前将产品配送至前置仓存储待售,客户下单后,由前置仓经营者组织完成包裹生产和“最后一公里”的上门配送。无论是订单响应速度还是配送成本,前置仓模式相比直接配送都具有很大优势。

而在现代零售环境当中,仓库的前置变得越来越重要。简单说就是把仓库设在离消费者更近的地方,可能是某个办公楼,可能是某个社区,也可能是直接把零售门店附以仓库功能,用户下单后,能够尽可能在最短的距离和时间内送货上门。

个人对于2019年企业数据与营销的思考

目前存在的绝大部分企业以后要么专注于生产,要么专注于落地服务(例如餐厅,连锁店),数据、营销只能外包。数据整合包括三部分:1)企业自己各个独立系统之间的数据整合,例如OA与ERP与HRM之类的企业自己内部系统之间数据整合;2)企业与企业间外部系统之间数据采集、清洗,整合;例如我现在做的超市数据与企业内部ERP之间数据整合;3)数据自身之间的整合,例如企业内部生产、批发(B2B)数据与外部销售数据(B2C)之间的整合,从而形成自己独特BI。

营销之所以发微博、做直通车、朋友圈转发、做百度推广无效是因为IP基础不存在,无品牌,所以无法落地,当然则是传统营销弊端。IP营销显然超出了绝大部分企业的能力;即使有IP品牌影响力企业也因为碎片化流量造成营销渠道太多而超出自身能力,所以也只能外包,自身专注在设计和生产。

目前IP红利是抖音,草根也能快速品牌化,流量化,快手的草根性显然距离个人IP比较远

低代码开发在企业软件开发中的应用技巧4:再谈低代码开发与快速开发平台的关系

在上一篇 低代码开发在企业软件开发中的应用技巧3:低代码开发与快速开发平台的关系中我说到:快速开发平台的核心还是开发,低代码开发的理念是不开发或者少开发; 快速开发平台的使用对象是程序员,低代码开发平台的使用对象是业务员或者产品经理, 这是两者最大的区别。

昨天我给一个朋友的公司灌输这个理念,老板听了还比较感兴趣,低代码,低成本。跟对方公司负责技术的人聊,该 负责技术的人也听懂了,无法反驳,最后说这种技术只能运用在小公司,上不了档次。我心中呵呵,如果是美团、淘宝这样的互联网公司的业务量,每天几千万订单或者数亿订单,我如果拿出这种低代码的解决方案,我还不如一头撞死,我也丢不起这个人啊。

低代码解决方案的客户群是企业客户,特点如下:1)业务访问量不大,一家企业,即使是集团企业,一块业务,肯定在几万人同时使用以内,即使我自己做平台,做SaaS的,理论上有百万、千万人访问,我也会根据不同的企业,转到不同的服务器上,肯定不会在一台机器上吊死。在一台机器上,会严格控制人数,所以性能不是问题;2)低代码开发针对的是独立功能模块的企业应用,解决单一问题,例如:工单系统,特定需求的报表查询,简单进销存,自定义特殊需求BI,而不是复杂庞大的ERP;

朋友公司本来规模不大,百人以内,系统自用或者给合作伙伴用,理论上是SaaS的,但就目前使用量来说,不会超过千人。所以说自己就是一个简单应用,说低代码开发只适合小公司,是没认清自己的形式。

对于一个程序员来讲,将来的出路,最直接的无非三个:

1)靠近管理,逐步升级,提高自己的管理能力,原来管3个人,逐步能管30人,300人,3000人,3万人,这种能力是稀缺的;

2)靠近技术,市面上流行什么,猛钻研进去,形成自己核心竞争力;可绝大部分程序员估计都是这么想的,可惜的是,99.99%的程序员都做不到这一点,真正的猛钻研,绝大部分只是掌握了皮毛,会说一些前沿名词,自己动手去做的时候,我又分为三大类人:(A)会按照Read Me或者Help搭建环境,但是解决不了任何实际问题(占70%以上);(B)能把该前沿技术应用到实际项目中,能填一些实际的坑 (占20%),应该是公司核心主力;(C)全面超越开源技术,例如各大厂的核心负责人,见过猪跑,杀过猪,吃过猪肉(占10%以内) ;所以说,绝大部分程序员口中所谈的前沿技术,只是皮毛而已,这时候,就得靠嘴巴了,看谁会向老板汇报,这时实际就是靠情商了;

3)贴近业务,技术就是为业务服务的,保证质量,及时响应,快速满足业务需求,最后是一个技术+业务的专家。 贴近业务,实际就是最贴近钱。我自己的目标以及低代码开发平台的理念就是贴近业务。

低代码开发在企业软件开发中的应用技巧3:低代码开发与快速开发平台的关系

无聊的时候,常去逛oschina.net 上逛,oschina.net 有个码云(https://gitee.com)的项目,类似于github.com ,我观察 码云上的企业应用,有很多是快速开发平台性质的项目,例如:JFinal、Gun、Jeesite 等等,比较奇葩的是 JFinal,企业后台在Spring Boot一统江湖的今天,JFinal 也能一枝独秀,处处能看到其身影,无论是B2C的网上商城,还是微信小程序,还是其他企业应用,都能看到基于JFinal的应用,没用过,也不知道其奥秘在哪里。 这些快速开发平台往往侧重于Admin后台管理,例如,用户注册、角色、权限、菜单,一般是基于Spring Boot/Spring MVC+Hibernate/Mybatis+Spring Security/Shiro等组合,对于企业应用,都有一些自己的特色。不过,快速开发平台与低代码开发平台还是完全不同的两回事。

一、面向受众不同,即客户不同。快速开发平台的受众(客户)是程序员,程序员基于这些平台做二次开发,比如:用户权限等都是现成的,所以往往能有效提高项目的交付速度,能给程序员节省不少时间放在企业逻辑的梳理和开发上,但是核心还是源代码的开发。而低代码开发平台的受众是实施人员(不是程序员), 低代码开发平台的目标是任何需求都实施出来(理论上),而不是开发出来,国内比较典型的例如泛微,通过自己的实施平台,到处做20-30万项目,一个实施人员做一个月,今年拿到投资人钱的还有搭搭云等等,国内做类似平台的还有好几家,国外例如微软也投入到了这个市场上。对于Salesforce和SAP,其产品核心交付人员也是实施人员而不是程序员,当然, Salesforce和SAP实施人员的工资很高,甚至比程序员还高,达不到降低费用的目的,这是另外一个话题。

二、理念不同,快速开发平台的核心还是开发,总比不过提供了很多快速开发工具,适应客户需求的多变,是源代码级的交付。不过代码写的越多,即使是自动生成出来的,理论上Bug也不少。而实施平台交付的是产品和基于这个产品上搭建的模型,新开发的代码量是很少的,往往是模型驱动。

三、低代码平台的核心目标是快速交付,降低客户费用,使得客户能够快速看到原型,而快速开发平台的目标理论上也是差不多的这个目标,但是由于底层价值观完全不一样,所以实现方式上也完全不同, 快速开发平台由于还是程序员+开发模式,交付风险并不能有效降低。程序员对业务的理解不可能有实施人员理解的那么深入,虽然快速做出来了,但是很可能不是用户想要的。

低代码开发在企业软件开发中的应用技巧2:忘记O/R Mapping

还是在那个大厂做项目的过程当中,甲方架构师力推Hibernate/JPA,极力反对MyBatis,在这里,我并不想比较JPA与MyBatis的孰优孰劣,这种低层次的比较,就跟比较Java、.Net、PHP、Python、React、VUE等语言孰优孰劣一样,离开使用上下文,说哪个语言是最牛B的语言,只能说自己too native,too young。

就低代码开发而言,我真的不喜欢JPA,就拿最常用的Excel数据上传来讲,Excel表格数据上传,最常用的就是POI,但是POI有个问题,就是性能不佳,Excel数据超过30万,就会内存溢出,虽然也有很多别的技巧,例如采用异步调用等等,但总是有各种各样的问题。所以寻来寻去,就看到了阿里巴巴的开源项目,easyexcel,下载后试用了一下,性能确实就如其宣称的那样强劲,也宣称支持百万条记录以上,所以非常满意。但是将其应用到项目当中时,发现 easyexcel 有个很大的问题,就是其接口支持的形式是:

List data = EasyExcelFactory.read(is, new Sheet(1, 2, Xhd.class));

那个Xhd.class就是自定义的ValueObject或者Entity实体类,跟表结构一致或者自定义的值对象或者O/R Mapping对象,为啥我说低代码开发在企业软件开发中的应用要忘记O/R Mapping呢?企业应用开发,往往表结构很多,很复杂或者很随意,比较动态,虽然针对某个企业来讲,表结构设计好后,很少会在大规模改变,但是代码在从项目往产品转变,或者在做下一个客户的下一个项目时,表结构就千差万别,完全不一样了。如果这里绑定死了某个Entity的名字,那么下一个项目该段代码就得完全重新开发,那就不是低代码开发了,就是完全订制化了。

而使用MyBatis,我们完全可以动态注入SQL,而不是绑定死表结构或者绑定死O/R Mapping对象。虽然 O/R Mapping对象我们也可以采用生成器之类的工具虽然生成这些对象,但是我确实也不喜欢纯粹为了O/R Mapping而 生成一大堆无意义的Entity。 也许Hibernate/JPA也支持动态注入 SQL而我不知道,即使真的支持,这也与 Hibernate/JPA=O/R Mapping工具的初衷相违背,失去了初心,使用它就是其门绝技了,不值得提倡。

低代码开发在企业软件开发中的应用技巧:开篇

企业软件追求的是性价比,即在预定时间内保质按时完成,而不是代码质量高。这里的保质是业务使用上无Bug,性能满足用户平时工作要求。这里并不是暗示程序员可以随意写烂代码,不讲究架构,而是优先级让位于开篇讲的保质按时完成。

我曾在某大厂与TW同事一起参与某个项目的短暂开发,在这里,我并不想做戳穿TW的敏捷开发无用论的皇帝新衣的那个小男孩,事实上,短暂的与TW架构师一起合作开发,还是学到了敏捷开发的理念并深以为有用,不过,在实践中过分强调技术上的代码质量和代码技巧,个人并不认同。个人的理念是在企业软件尤其是业务相关的管理类软件,应该采用低代码开发技巧而不是强调敏捷开发或者高质量代码开发技巧。当然,如果团队成员素质普遍较高,资金、时间又比较充裕,严格要求代码质量,采用敏捷开发过程,对团队和交付都是有好处的。不过,企业软件开发的现状绝大部分都是时间紧迫,预算少,成员普遍工作经验少,或者没有在大公司和互联网公司工作过。这种情况下,再强调代码技巧和质量项目很可能就做不下去了。

当然,开发过程中代码质量如果真的比较低,对客户,对参与开发项目的企业、成员也是极大的损失和浪费时间、浪费金钱,也是不能接受的。所以,这里强调的是如何使用低代码开发(不是低质量)技巧在预算有限,成员素质参差不齐情况下,按时保质完成任务。

String boot @Scheduled和数据库连接池的 APPARENT DEADLOCK各种异常

一般来说,采用Spring Boot+MyBatis,无论是使用c3p0这样的老牌数据库连接池,还是使用Druid这样的阿里出品的数据库连接池,一般都不会有问题。我用Spring Boot做了一个基于JWT统一登录的微服务,采用的就是c3p0,几个月不登陆,也没出现数据库 APPARENT DEADLOCK!!! Complete Status:,No operations allowed after connection closed等等这样的异常,但是使用了@Scheduled后,数据库连接池频繁出现上述两种异常,天天死给我看,搞得我非常不爽,我想肯定是@Scheduled的问题,但是究竟出了什么问题?网上搜索了一阵资料后,大致明白了其中道理。

1)无论是APPARENT DEADLOCK!!!还是No operations allowed after connection closed都是数据库连接池超时或者使用了已经关闭的连接池造成的。那么为什么会出现这样的问题呢?

A)数据库连接池也是客户端/服务器端两端通信的问题,要么客户端连接服务器时,服务器无响应(例如mysql8小时的问题,mysql数据库把来自客户端的请求关闭了),要么

B) 是客户端自身本身的Connection 已经关闭了,但是新的Object Instance 还是使用了该Connection,则就出错了,这时可通过配置

<property name=”maxPoolSize” value=”300″ />
<property name=”minPoolSize” value=”10″ />
<property name=”maxIdleTime” value=”1800″ />
<property name=”maxStatements” value=”0″ />
<property name=”maxStatementsPerConnection” value=”100″/>
<property name=”initialPoolSize” value=”10″ />
<property name=”idleConnectionTestPeriod” value=”1800″ />
<property name=”testConnectionOnCheckin” value=”true” />
<property name=”automaticTestTable” value=”Test” />
<property name=”testConnectionOnCheckout” value=”false” />
<property name=”breakAfterAcquireFailure” value=”false” />

调整这些参数解决,Connection 即该关闭的及时关闭,不要长时间打开不关

C)Spring Boot @Scheduled的坑,@Scheduled长期打开的是同一个Class的同一个Object Instance,即Java的 JVM并没有把该Object销毁并进行内存回收,那么同一个Object Instance—–>Mybatis –>JDBC–>Database获取数据库连接池的路径,C3P0还是返回原来的那老一个,但是那老一个早已经被销毁,所以就会出现 APPARENT DEADLOCK!!!的问题。

解决的办法要么通过Http Client请求该定时器,例如采用Quartz或者Elstic JOB等外部中间件,Http调用每次都会生成一个新的线程,新的线程会生成新的Object,这时新的Object 从C3P0数据库连接池获取Connection时,就会返回新的Connection

要么定制一下Spring @Scheduled源代码(待续)