数据专栏

智能大数据搬运工,你想要的我们都有

科技资讯:

科技学院:

科技百科:

科技书籍:

网站大全:

软件大全:

【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
小程序中获取用户基本信息,可以通过直接全局变量中的globalData.userInfo: var app=getApp(); console.log(app.globalData.userInfo);
显而易见,这些基本信息并没有太大用处,我们需要更深层次的数据:
首先通过wx.login获取到一个code
然后通过微信的接口: https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code
获取到我们想要的openId。
代码如下: wx.login({ success: function (res) { console.log(res) if (res.code) { //发起网络请求 wx.request({ url: 'https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code', data: { appid:'wxda*******3b9', secret:'088b866*********90bc603', js_code: res.code, grant_type: 'authorization_code' }, success(v){ console.log(v) } }) } else { console.log('登录失败!' + res.errMsg) } } });
四个参数分别是:
appid和secret可以去微信公众号平台去查看
1.可以看到小程序的后台页面。然后点击左下角的“设置”,进入设置页面。
2.在设置页面右侧上部,点击“开发设置”,切换到开发设置页面。在该页面可以看到AppID和AppSecret。其中AppSecret的查看需要点击该行后面的“重置”或者“查看”链接。查看后要记得把AppSecret在其他位置记录下来,因为AppSecret只能查看一次,后续无法再次查看了。
获取到的数据如下:
项目管理
2019-06-14 15:35:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
eval的语法: eval(expression[, globals[, locals]])
>>参数
expression -- 表达式。
globals -- 变量作用域,全局命名空间,如果被提供,则必须是一个字典对象。特指当前的模块。如果模块定义了一个名为 x 的变量,函数或类,Python 将使用它然后停止搜索。
locals -- 变量作用域,局部命名空间,如果被提供,可以是任何映射对象。特指当前函数或类的方法。如果函数定义了一个局部变量 x, 或一个参数 x,Python 将使用它,然后停止搜索。
>>返回值
返回表达式计算结果。 可以把list,tuple,dict和string相互转化。 ################################################# 字符串转换成列表 >>>a = "[[1,2], [3,4], [5,6], [7,8], [9,0]]" >>>type(a) >>> b = eval(a) >>> print b [[1, 2], [3, 4], [5, 6], [7, 8], [9, 0]] >>> type(b) ################################################# 字符串转换成字典 >>> a = "{1: 'a', 2: 'b'}" >>> type(a) >>> b = eval(a) >>> print b {1: 'a', 2: 'b'} >>> type(b) ################################################# 字符串转换成元组 >>> a = "([1,2], [3,4], [5,6], [7,8], (9,0))" >>> type(a) >>> b = eval(a) >>> print b ([1, 2], [3, 4], [5, 6], [7, 8], (9, 0)) >>> type(b)
eval()使用原因:
1)在编译语言里要动态地产生代码,基本上是不可能的,但动态语言是可以,意味着软件已经部署到服务器上了,但只要作很少的更改,只好直接修改这部分的代码,就可立即实现变化,不用整个软件重新加载。
2)在machin learning里根据用户使用这个软件频率,以及方式,可动态地修改代码,适应用户的变化。
全局变量与局部变量:

in()判断你在不在宿舍成员里面。
修改上面一道题的内容,实现可以循环输入姓名检查,提示,采用while:
项目管理
2019-06-10 11:20:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
前面几讲涉及到了用户登陆成功了,那么我们本次讲的是用户登陆成功,并做完一些操作有,如果实现退出。本讲涉及到session。
我们要实现系统对用户在各个页面的权限进行识别,最有效的方式是采用session或cookie,两者的区别是:
cookie以文本格式存储在浏览器上,存储量有限;
而会话session存储在服务端,可以无限量存储多个变量并且比cookie更安全.
所以我们下面采用的是session进行操作,在登陆成功的时候新建一个用户的Session["User"],然后系统会根据这个session来判断用户的一些权限问题,当我们把这个session置空,则系统认为用户已经退出登陆。
以下是步骤。
1、我们需要新建一个用户模型类,用户存储用户的一些数据在session里面。
进入models文件夹,通过新建类的方式来新建一个模型。
下面是User类里面的代码,主要是根据数据库来进行了一些设置(最后面的power字段是临时添加的,后面再对权限进行讲解,理论上用户模型要包含数据库对应的表的所有字段,我这里只是用了几个字段)。
2、新建用户模型的数据工具类,当然这个是为了方便管理,在项目根目录下新建一个DBTools文件夹,然后在文件夹里面新建一个UserTools.cs类
在UserTools类里面代码如下,目前主要用户通过输入一个数据表用来生成一个用户模型:
修改下login控制器里面的post方式的index函数,在用户登陆成功的时候,新建一个用户对象并设置好改对象。
对admin控制器代码进行修改,主要是对所有的action进行用户登陆的判断,还有新增一个登出的action,对head头部的action进行代码修改。
我们要实现的是在head头部显示用户的姓名,并且在head头部预留有一个退出的链接,所以head前台代码如下:
标签加上 target="_top",含义是这个链接就会在整个页面中打开,而不是在某个块中打开,也就是跳出了frameset这个框架,如果没有这个参数,则只是头部文件跳转到了登陆视图,其他页面还是原始视图。
效果如下:
下面是效果的操作步骤:
1、启用调试的是admin/head视图,由于加入了session验证,所以页面会自动跳转到登陆界面

2、登陆成功后如下所示:
3、点击退出按钮后,系统直接退出到登陆界面
课堂视频地址如下:
链接:https://pan.baidu.com/s/1nTE4o1CRww5XtwSXTDomCQ
提取码:ull2
复制这段内容后打开百度网盘手机App,操作更方便
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
在第一讲完成的基础上,我们对登陆框的程序进行完善,本讲完善的是实现数据库的链接。
步骤:
1、建立数据库test,新建数据表RL_Employee,并新增一条数据,这个数据主要是EmpName、EmpUserName、EmpPassword三个字段要有数据。
2、新建数据库的用户名test 密码是123456,并确保用户能正常登陆使用test数据库
3、配置网站的web.config的数据库的connectionStrings链接字符串
这里要注意的是web.config在项目里面会有多处存在,所以要选择项目根目录下面的web.config

4、新建数据库控制类,SQLHelper.cs可以放在其他地方。
类的代码,下面的ExecuteSql()函数后续才会用到:
5、修改Login控制器里面的用于接受Post请求的Index代码
6、结果如下:
将密码输入错误:
输入正确数据:
结果如下:
下一讲将完善验证码的刷新。
视频下载地址:
链接:https://pan.baidu.com/s/1AqZKZ53rVkYMvy5DrdZBdg
提取码:eugw
复制这段内容后打开百度网盘手机App,操作更方便
项目管理
2019-05-21 00:07:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>


1.教程
参考:
https://blog.csdn.net/u012111923/article/details/80705141

链接:https://pan.baidu.com/s/1N9e_vT7SuJU7f67ZOR4f5Q
提取码:5prr
2. 汉化方法
参考: https://blog.csdn.net/him2014/article/details/79603887
3. 性能解释
3.1 qps
每秒完成的事务数量,如上面的吞吐量是:91.8
测试服务器QPS:
方法1:使用jmeter进行压力测试
方法2: 计算公式
https://www.cnblogs.com/sunbeidan/p/8477196.html ( 总PV数 * 80% ) / ( 每天秒数 * 20% ) = 峰值时间每秒请求数(QPS)
3.2 多少QPS才算高并发


项目管理
2019-05-08 14:50:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
从容不迫——《稀缺》的读书笔记范文3700字:
我们似乎经常处于这种状态:看起来忙忙碌碌,却还是焦头烂额、丢三落四;看起来足够努力,起早贪黑辛辛苦苦,日子还是紧紧巴巴捉襟见肘。我们不是不想改变,可就是不知为何,生活总把我们置入这样的陷阱里,越努力越困惑,越奔波越贫穷。问题出在哪里了呢?
数不尽的励志书籍、心里鸡汤为我们诊断病症,但是却众说纷纭。有的说我们缺乏梦想,有的说我们习惯糟糕,还有人让我们自我反思,是不是心态不好、格局不够、眼界太窄、缺乏规划......是的,确实有这些问题。但真正的病源是什么?有本书给了我们答案:“稀缺”。
《稀缺——我们是如何陷入贫穷与忙碌的》,作者是美国的赛德希尔·穆来纳森和埃尔德·沙菲尔。前者是行为经济学家,后者是认知心理学家。
稀缺?英文:Scarcity。简言之,就是资源不足的状态。狭小是空间的稀缺,忙碌是时间的稀缺,贫穷是金钱的稀缺,孤独是社交的稀缺……稀缺无处不在。事实上,经济学管理学都是研究稀缺的学问,即合理配置有限的资源,以实现最大的效益。
这本书的不同之处在于,它不仅把稀缺看成的资源的客观束缚,也当成一种主观心态。作者认为稀缺是一种心态,是“拥有”少于“需要”的感觉。
稀缺有稀缺的好处,它让我们更加珍惜眼前、专注当下,在应对急迫要求时做得更好、更有效率。但长远来看,对我们的生活损害更大。
具体来说,有这样几个不利影响:
一、减少带宽。什么是带宽?你一定有这样的经验,当你的电脑同时处理多个任务时,电脑的反应速度会变慢。或者同一时间,多个人联网一个WIFI时,会出现掉线或网页刷新缓慢的情况。这就是带宽不足的典型表现。人的心智与此类似。枪炮病菌与钢铁读后感(http://m.simayi.net/duhougan/5158.html)穆来纳森用带宽一词形容心智的容量,它包括认知能力和执行控制力。稀缺会俘获我们的大脑,占据我们的神经处理系统,降低这些带宽的容量,使我们在应对生活的变化时,缺乏洞察力、反应迟钝或者经常“掉线”。
举个栗子,如果一个人很饥饿,饥饿就会俘获他的大脑,对食物的需求会一直萦绕在他脑海中挥之不去,这时给他安排任何事情,他都既无兴趣也无精力更好地处理。事实上,科学家通过实验已经证明,当一个人被稀缺俘获时,他的情商、智商和意志力都是下降的。对于一个被贫穷或忙碌困扰的人,会更容易愤怒和情绪低落,更自闭,也更缺乏自控力。
二、管窥思维。管窥(tunneling),指我们专注于某一事物就会忽略其他事物,也叫“隧道视野”。当稀缺俘获大脑时,人们的注意力会集中在紧急的事情上,并将其他事物排除在外。这种专注会让人们从稀缺中获益,让人们获得“专注红利”。由于“目标抑制”的作用,人们在专注于某项重要事物的同时,就不容易想到其他重要事物。因此,专注也会导致管窥,让人们的视野变窄,从而付出沉重代价。眼前的稀缺问题总是会被放大,而与此无关的事物则会被忽略。稀缺会迫使我们专注在迫近的重要事情上;管窥则是消极的:稀缺导致我们有了管窥之见,让我们忽略了其他可能更重要的事情。举个栗子,一个被事务缠身的人,可能对新出现的情况充耳不闻、视而不见。一个为生计发愁的人,也更难看到生活的多种可能和改变命运的机会。
三、短视。稀缺迫使我们产生了权衡式思维。所有那些没有被满足的需要俘获了我们的大脑,成了我们时时刻刻念念不忘的事情。不仅如此,稀缺还会令我们犯下更为严重的失误。需求的压力始终存在于穷人心中,挥之不去,从而造就了他们自身的内化尺度,所有这些不同行为都有着同样的明显特征:短视。我们为了解决眼下的难题而极度专注时,就无法有效地规划未来。俘获我们的稀缺,就存在于当下,它所产生的管窥负担,令我们带着短视的眼光做人做事。经常会有这样一种情况,我们想热锅上的蚂蚁一样忙碌,疲于应付生活的各种稀缺,以至于根本没有时间,也没有精力停下来思考和规划未来。因为没有规划,所以所有的东西都是匆忙应对,临时起意。贫穷也是一样。一个人越贫穷,就在那些钱该花那些不该花上纠结,在省吃俭用上绞尽脑汁,以至于忘了怎样着眼未来,做好规划开拓资源。贫穷的更贫穷,富足者更富足。稀缺导致更严重的稀缺,这是个怪圈。
四、杂耍。从表现来看,稀缺的人更容易发生“借用”的行为。这其实很好理解,贫穷的人面对金钱,忙碌的人面对时间,都非常紧迫。为了解决眼前的问题,稀缺的人常常不择手段的借贷和透支未来。生活在稀缺陷阱之中,你所拥有的就会比实际应拥有的更少。稀缺陷阱就是一直在落后一步,一直在偿还上一个任务的支出,不管时间还是金钱。就像杂耍艺人,稀缺的人拆东墙补西墙,每天像救火队长一样,小心翼翼维持着艰难的生计。任何一点小小的不稳定都会威胁到生存于稀缺陷阱边缘的人,因为他们没有足够的余闲去吸收这些不稳定因素,只能任由其影响自己的生活。这种生活总是需要我们紧赶慢赶,在每个球就要落下的刹那开始应对,结果就在匆忙间织出了一张乱七八糟的补丁图。
稀缺陷阱尤其会令人感到辛酸的就是,它会让人产生一种感觉:只要再多一点资源,就能摆脱欠下的所有债务,就能逃脱这个恶性循环。然而事实并非如此。它的根源在于挥霍和对资源的错误利用,从而出现了实际的短缺。就像月光族一样,稀缺的种子早在资源充裕时就已经种下。在资源充裕阶段,我们会浪费时间与金钱。我们太过松懈了。以扶贫为例,有些贫困地区的人生活十分艰难,甚至还挣扎在贫困线以下。为了改善他们的生活,政府会给予他们物质和金钱上的补助。按照一般理解,有了补助,他们的生活应该会好些,可以从被动输血到自主造血,逐渐过上自给自足的生活。真实情况是,贫民拿到钱后,的确会稍解燃眉之急。然而短暂的狂欢过后,生活还是会重归原样。
稀缺会加剧稀缺,像电脑在运行太多的任务一样,如果一个人头脑中一直萦绕着眼前的稀缺,疲于应对生活事件源源不断地侵袭,就无暇思考和规划未来。所以贫穷之人会一直贫困僚倒,忙碌之人会永远日理万机,而孤独之人会终日形单影只,节食者只会越来越胖。
那么,怎样解决稀缺问题呢?
作者认为,稀缺心态是环境造就的结果,更有可能通过一些措施来加以改变。稀缺并非个人特质,而是自身创造的环境条件所引发的结果,而这些条件是可以进行管理的。我们越是深入了解稀缺在大脑中的发展历程,就越有可能找到办法去避免稀缺陷阱,或至少去减轻稀缺陷阱的影响程度。
把重要的事情拉入“管子”视野,我们可以用便签等工具,像超市把巧克力摆在收银台一样,把重要的事项放在案头,提醒我们随时关注,并且一有可能,就要将警醒型行为转变成一次性行动。
让“疏忽”等同于“默许”,意即把需要主动付出的任务变成悄悄奉献的部分。当忽略问题存在时,改变行为的结果往往比直接与其对抗更有效。很多商家就深谙其道。比如视频网站和物业中心,他们不会每个月打电话给你,或者通知你及时续费,而是先与你签订合约,在你的默许下每月扣款,在不知不觉中攫取你的财富。这一做法也可用于存储。如果你不是一个存储习惯良好的人,设置定期转账10%的钱到专有储蓄账户就是一个不错的做法;如果你总忘记信用卡还款,设定自动还款就是个好主意。
节约利用带宽。利用带宽,就是要去分配我们有限的信息处理能力。任何形式的技能习得,无论是去学习社交技巧还是养成良好的消费习惯,都需要带宽。如果穷人缺乏带宽,那么他们就无法很好地掌握这些实用技能。关系时,都会用到。带宽会影响我们的思维方式,影响我们做出的选择。在充满稀缺的世界中,漫长的截止日期就是酿造麻烦的问题所在。所以,将漫长的一次性截止日期改为渐进式的阶段性截止日期,有助于问题的改善。如果农民们不是一次性收款,而是更为频繁地收到报酬,问题就可以得到有效的改善。
要在富足和带宽充裕时开始行动。更重要的是,要留有应对突发事件的余闲。沿着稀缺的线索向上追溯,我们就能看到充裕:萧条,是由我们在繁荣阶段的行为所导致的;最后一刻的拼凑,是由我们之前几周的不作为所造成的。一定要有这样一种心态来悬浮注意,它的存在,能够保证专注于当下工作目标,又不会向未来的项目借用资源,并因此耗尽余闲,将自身拉入未来的带宽陷阱。
亨利·戴维·梭曾说,真正有效率的劳动者,不会整天马不停蹄地工作,而是闲庭信步般轻松愉悦地处理事务。各种类型的稀缺都有着相同的元素:管窥、借用、余闲缺乏、带宽负担。通过外力将人们所处的环境进行小小的改变,把重要的事情拉入“管子”视野,就能缓解稀缺带来的不良后果。当带宽有限时,让“疏忽”等同于“默许”就是一个不错的办法。稀缺会产生带宽负担,因此节约利用带宽是对稀缺进行管理的重要内容。应对稀缺,要在富足和带宽充裕时开始行动。更重要的是,不要给自己的人生排的太满,要留有应对突发事件的余闲。
为什么穷人越来越穷,富人越来越富,拖延症者永远没有时间?针对这一现象,社会学家成为马太效应,经济学家称为稀缺现象,但从心理学上分析,则是稀缺影响了我们的心智,格局、视野、洞察力和自控力。太阳底下无新事,《稀缺》一书用行为经济学和心理学的视角,帮我们重新认识了稀缺,但解决方案并非前所未见。重要的是我们要自觉惊醒,改变观念,相信物随心转境由心生。罗振宇说,我们的肉身刚刚步入富足时代,但精神还滞留在稀缺的恐惧之中。穷人思维根植在人类的基因里。率先用理性击碎它的人,也将获得身心灵的富足。
项目管理
2019-04-23 21:10:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
开始
啥不说,直接开始。
写了个示例来演示Spring揭秘一文当中的Bean生命周期。
上图
上代码 /** * 演示bean的生命周期 * Spring揭秘一文中写道的周期:实例化bean对象 --> 设置对象属性 --> 检测Aware接口 * --> BeanPostProcessor前置逻辑 --> InitializingBean逻辑 --> init-method逻辑 * --> BeanPostProcessor后置逻辑 --> 使用中 --> DisposableBean逻辑 --> destroy-method逻辑 * * @Author guchenbo * @Date 2019/4/7. */ public class BeanLifeCycleTest implements ApplicationContextAware, InitializingBean, DisposableBean { private String name; public BeanLifeCycleTest() { System.out.println("实例化bean对象"); } public void setName(String name) { this.name = name; System.out.println("设置对象属性"); } public void doing() { System.out.println("使用中"); } public void initMethod() { System.out.println("init-method逻辑"); } public void destroyMethod() { System.out.println("destroy-method逻辑"); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { System.out.println("检测Aware接口"); } @Override public void destroy() throws Exception { System.out.println("DisposableBean逻辑"); } @Override public void afterPropertiesSet() throws Exception { System.out.println("InitializingBean逻辑"); } public static class BeanFactoryProcessorTest implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("BeanPostProcessor前置逻辑"); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("BeanPostProcessor后置逻辑"); return bean; } } public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("lifecycle.xml"); BeanLifeCycleTest lifeCycleTest = (BeanLifeCycleTest) context.getBean("lifeCycleTest"); lifeCycleTest.doing(); context.destroy(); } }
上结果 BeanLifeCycleTest : 实例化bean对象 BeanLifeCycleTest : 设置对象属性 BeanLifeCycleTest : 检测Aware接口 BeanLifeCycleTest : BeanPostProcessor前置逻辑 BeanLifeCycleTest : InitializingBean逻辑 BeanLifeCycleTest : init-method逻辑 BeanLifeCycleTest : BeanPostProcessor后置逻辑 BeanLifeCycleTest : 使用中 BeanLifeCycleTest : DisposableBean逻辑 BeanLifeCycleTest : destroy-method逻辑
项目管理
2019-04-07 23:33:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
在java以及其他的面向对象设计模式中,类与类之间主要有6种关系,他们分别是:依赖、关联、聚合、组合、继承、实现。他们的耦合度依次增强。
1. 依赖(Dependence)
依赖关系的定义为:对于两个相对独立的对象,当一个对象负责构造另一个对象的实例,或者依赖另一个对象的服务时,这两个对象之间主要体现为依赖关系。定义比较晦涩难懂,但在java中的表现还是比较直观的:类A当中使用了类B,其中类B是作为类A的方法参数、方法中的局部变量、或者静态方法调用。类上面的图例中:People类依赖于Book类和Food类,Book类和Food类是作为类中方法的参数形式出现在People类中的。
代码样例:
[java] view plain copy
public class People{ //Book作为read方法的形参 public void read(Book book){ System.out.println(“读的书是”+book.getName()); } }
2.关联(Association) 、 、
单向关联:

双向关联:
对于两个相对独立的对象,当一个对象的实例与另一个对象的一些特定实例存在固定的对应关系时,这两个对象之间为关联关系。关联关系分为单向关联和双向关联。在java中,单向关联表现为:类A当中使用了类B,其中类B是作为类A的成员变量。双向关联表现为:类A当中使用了类B作为成员变量;同时类B中也使用了类A作为成员变量。
代码样例:
[java] view plain copy
public class Son{ //关联关系中作为成员变量的类一般会在类中赋值 Father father = new Father(); public void getGift(){ System.out.println(“从”+father.getName()+”获得礼物”); } } public class Father{ Son son = new Son(); public void giveGift(){ System.out.println(“送给”+son.getName()+“礼物”); } }
3.聚合(Aggregation)
聚合关系是关联关系的一种,耦合度强于关联,他们的代码表现是相同的,仅仅是在语义上有所区别:关联关系的对象间是相互独立的,而聚合关系的对象之间存在着包容关系,他们之间是“整体-个体”的相互关系。
代码样例:
[java] view plain copy
public class People{ Car car; House house; //聚合关系中作为成员变量的类一般使用set方法赋值 public void setCar(Car car){ This.car = car; } public void setHouse(House house){ This.house = house; } public void driver(){ System.out.println(“车的型号:”+car.getType()); } public void sleep(){ System.out.println(“我在房子里睡觉:”+house.getAddress()); } }
4.组合(Composition)
相比于聚合,组合是一种耦合度更强的关联关系。存在组合关系的类表示“整体-部分”的关联关系,“整体”负责“部分”的生命周期,他们之间是共生共死的;并且“部分”单独存在时没有任何意义。在下图的例子中,People与Soul、Body之间是组合关系,当人的生命周期开始时,必须同时有灵魂和肉体;当人的生命周期结束时,灵魂肉体随之消亡;无论是灵魂还是肉体,都不能单独存在,他们必须作为人的组成部分存在。
[java] view plain copy
Public class People{ Soul soul; Body body; //组合关系中的成员变量一般会在构造方法中赋值 Public People(Soul soul, Body body){ This.soul = soul; This.body = body; } Public void study(){ System.out.println(“学习要用灵魂”+soul.getName()); } Public void eat(){ System.out.println(“吃饭用身体:”+body.getName()); } }
5.继承(Generalization)
继承表示类与类(或者接口与接口)之间的父子关系。在java中,用关键字extends表示继承关系。UML图例中,继承关系用实线+空心箭头表示,箭头指向父类。
6.实现(Implementation)
表示一个类实现一个或多个接口的方法。接口定义好操作的集合,由实现类去完成接口的具体操作。在java中使用implements表示。UML图例中,实现关系用虚线+空心箭头表示,箭头指向接口。
在java中继承使用extends关键字,实现使用implements关键字,很直观。

在学习面向对象设计对象关系时,依赖、关联、聚合和组合这四种关系之间区别比较容易混淆。特别是后三种,仅仅是在语义上有所区别,所谓语义就是指上下文环境、特定情景等。他们在编程语言中的体现却是基本相同的,但是基本相同并不等于完全相同。
下面就来详细的论述一下在java中如何准确的体现依赖、关联、聚合和组合。
首先看一看书上对这四种关系的定义: 依赖(Dependency)关系是类与类之间的联接。依赖关系表示一个类依赖于另一个类的定义。例如,一个人(Person)可以买车(car)和房子(House),Person类依赖于Car类和House类的定义,因为Person类引用了Car和House。与关联不同的是,Person类里并没有Car和House类型的属性,Car和House的实例是以参量的方式传入到buy()方法中去的。 一般而言,依赖关系在Java语言中体现为局域变量、方法的形参,或者对静态方法的调用。 关联(Association)关系是类与类之间的联接,它使一个类知道另一个类的属性和方法。 关联可以是双向的,也可以是单向的。 在Java语言中,关联关系一般使用成员变量来实现。 聚合(Aggregation) 关系是关联关系的一种,是 强的关联关系。聚合是整体和个体之间的关系。 例如,汽车类与引擎类、轮胎类,以及其它的零件类之间的关系便整体和个体的关系。与关联关系一样,聚合关系也是通过实例变量实现的。但是关联关系所涉及的两个类是处在同一层次上的,而在聚合关系中,两个类是处在不平等层次上的,一个代表整体,另一个代表部分。 组合(Composition) 关系是关联关系的一种,是 比聚合关系强的关系。它要求普通的聚合关系中代表整体的对象负责代表部分对象的生命周期,组合关系是不能共享的。 代表整体的对象需要负责保持部分对象和存活,在一些情况下将负责代表部分的对象湮灭掉。代表整体的对象可以将代表部分的对象传递给另一个对象,由后者负责此对象的生命周期。换言之,代表部分的对象在每一个时刻只能与一个对象发生组合关系,由后者排他地负责生命周期。部分和整体的生命周期一样。
——摘自《Java面向对象编程》,作者:孙卫琴
以上关系的 耦合度 依次增强(关于耦合度的概念将在以后具体讨论,这里可以暂时理解为当一个类发生变更时,对其他类造成的影响程度,影响越小则耦合度越弱,影响越大耦合度越强)。由定义我们已经知道,依赖关系实际上是一种比较弱的关联,聚合是一种比较强的关联,而组合则是一种更强的关联,所以笼统的来区分的话,实际上这四种关系、都是关联关系。
依赖关系比较好区分,它是耦合度最弱的一种,在java中表现为局域变量、方法的形参,或者对静态方法的调用,如下面的例子:Driver类依赖于Car类,Driver的三个方法分别演示了依赖关系的三种不同形式。
[java] view plain copy
class Car { public static void run(){ System.out.println("汽车在奔跑"); } } class Driver { //使用形参方式发生依赖关系 public void drive1(Car car){ car.run(); } //使用局部变量发生依赖关系 public void drive2(){ Car car = new Car(); car.run(); } //使用静态变量发生依赖关系 public void drive3(){ Car.run(); } }
关联关系在java中一般使用成员变量来实现,有时也用方法形参的形式实现。依然使用Driver和Car的例子,使用方法参数形式可以表示依赖关系,也可以表示关联关系,毕竟我们无法在程序中太准确的表达语义。在本例中,使用成员变量表达这个意思:车是我自己的车,我“拥有”这个车。使用方法参数表达:车不是我的,我只是个司机,别人给我什么车我就开什么车,我使用这个车。
[java] view plain copy
class Driver { //使用成员变量形式实现关联 Car mycar; public void drive(){ mycar.run(); } ... //使用方法参数形式实现关联 public void drive(Car car){ car.run(); } }
聚合关系是是一种比较强的关联关系,java中一般使用成员变量形式实现。对象之间存在着整体与部分的关系。例如上例中
[java] view plain copy
class Driver { //使用成员变量形式实现聚合关系 Car mycar; public void drive(){ mycar.run(); } }
假如给上面代码赋予如下语义:车是一辆私家车,是司机财产的一部分。则相同的代码即表示聚合关系了。聚合关系一般使用setter方法给成员变量赋值。
假如赋予如下语义:车是司机的必须有的财产,要想成为一个司机必须要先有辆车,车要是没了,司机也不想活了。而且司机要是不干司机了,这个车就砸了,别人谁也别想用。那就表示组合关系了。一般来说,为了表示组合关系,常常会使用构造方法来达到初始化的目的,例如上例中,加上一个以Car为参数的构造方法
[java] view plain copy
public Driver(Car car){ mycar = car; }
所以,关联、聚合、组合只能配合语义,结合上下文才能够判断出来,而只给出一段代码让我们判断是关联,聚合,还是组合关系,则是无法判断的。
项目管理
2019-03-21 11:09:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
< 一>
基础数据类型(Value type)直接在栈(stack)空间分配,方法的形式参数,直接在栈空间分配,当方法调用完成后从栈空间回收。
引用数据类型,需要用new来创建,既在栈空间分配一个地址空间(reference),又在堆空间分配对象的类变量(object) 。方法的引用参数,在栈空间分配一个地址空间,并指向堆空间的对象区,当方法调用完成后从栈空间回收。局部变量 new 出来时,在栈空间和堆空间中分配空间,当局部变量生命周期结束后,栈空间立刻被回收,堆空间区域等待GC回收。 方法调用时传入的 literal 参数,先在栈空间分配,在方法调用完成后从栈空间分配。字符串常量在 DATA 区域分配 ,this 在堆空间分配。数组既在栈空间分配数组名称, 又在堆空间分配数组实际的大小!
哦 对了,补充一下static在DATA区域分配。
从Java的这种分配机制来看,堆栈又可以这样理解:堆栈(Stack)是操作系统在建立某个进程时或者线程(在支持多线程的操作系统中是线程)为这个线程建立的存储区域,该区域具有 先进后出 的特性。
每一个Java应用都唯一对应一个JVM实例,每一个实例唯一对应一个堆。应用程序在运行中所创建的所有类实例或数组都放在这个堆中,并由应用所有的 线程共享 .跟C/C++不同,Java中分配堆内存是自动初始化的。Java中所有对象的存储空间都是在堆中分配的,但是这个对象的引用却是在堆栈中分配,也就是说在建立一个对象时从两个地方都分配内存,在堆中分配的内存实际建立这个对象,而在堆栈中分配的内存只是一个指向这个堆对象的指针(引用)而已。
<二>
参考自 深入浅出 JVM这本书,对了解JAVA的底层和运行机制有比较大的帮助。
废话不想讲了.入主题:
先了解具体的概念:
JAVA的JVM的内存可分为3个区:堆(heap)、栈(stack)和方法区(method)
堆区 :
1.存储的全部是对象,每个对象都包含一个与之对应的class的信息。(class的目的是得到操作指令)
2.jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身
栈区 :
1.每个线程包含一个栈区,栈中只保存基础数据类型的对象和自定义对象的引用(不是对象),对象都存放在堆区中
2.每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。
3.栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。
方法区 :
1.又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量。
2.方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。
为了更清楚地搞明白发生在运行时数据区里的黑幕,我们来准备2个小道具(2个非常简单的小程序)。
AppMain.java
Java代码 AppMain.java public class AppMain //运行时, jvm 把appmain的信息都放入方法区 { public static void main(String[] args) //main 方法本身放入方法区。 { Sample test1 = new Sample( “ 测试1 ” ); //test1是引用,所以放到栈区里, Sample是自定义对象应该放到堆里面 Sample test2 = new Sample( “ 测试2 ” ); test1.printName(); test2.printName(); } } Sample.java public class Sample //运行时, jvm 把appmain的信息都放入方法区 { private name; //new Sample实例后, name 引用放入栈区里, name 对象放入堆里 public Sample(String name) { this .name = name; } public void printName() //print方法本身放入 方法区里。 { System.out.println(name); } }
OK,让我们开始行动吧,出发指令就是:“java AppMain”,包包里带好我们的行动向导图,Let’s GO!
系统收到了我们发出的指令,启动了一个Java虚拟机进程,这个进程首先从classpath中找到AppMain.class文件,读取这个文件中的二进制数据,然后把Appmain类的类信息存放到运行时数据区的方法区中。这一过程称为AppMain类的加载过程。
接着,Java虚拟机定位到方法区中AppMain类的Main()方法的字节码,开始执行它的指令。这个main()方法的第一条语句就是:
Sample test1=new Sample(“测试1”);
语句很简单啦,就是让java虚拟机创建一个Sample实例,并且呢,使引用变量test1引用这个实例。貌似小case一桩哦,就让我们来跟踪一下Java虚拟机,看看它究竟是怎么来执行这个任务的:
1、 Java虚拟机一看,不就是建立一个Sample实例吗,简单,于是就直奔方法区而去,先找到Sample类的类型信息再说。结果呢,嘿嘿,没找到@@,这会儿的方法区里还没有Sample类呢。可Java虚拟机也不是一根筋的笨蛋,于是,它发扬“自己动手,丰衣足食”的作风,立马加载了Sample类,把Sample类的类型信息存放在方法区里。
2、 好啦,资料找到了,下面就开始干活啦。Java虚拟机做的第一件事情就是在堆区中为一个新的Sample实例分配内存, 这个Sample实例持有着指向方法区的Sample类的类型信息的引用。这里所说的引用,实际上指的是Sample类的类型信息在方法区中的内存地址,其实,就是有点类似于C语言里的指针啦~~,而这个地址呢,就存放了在Sample实例的数据区里。
3、在JAVA虚拟机进程中,每个线程都会拥有一个方法调用栈,用来跟踪线程运行中一系列的方法调用过程,栈中的每一个元素就被称为栈帧,每当线程调用一个方法的时候就会向方法栈压入一个新帧。这里的帧用来存储方法的参数、局部变量和运算过程中的临时数据。OK,原理讲完了,就让我们来继续我们的跟踪行动!位于“=”前的Test1是一个在main()方法中定义的变量,可见,它是一个局部变量,因此,它被会添加到了执行main()方法的主线程的JAVA方法调用栈中。而“=”将把这个test1变量指向堆区中的Sample实例,也就是说,它持有指向Sample实例的引用。
OK,到这里为止呢,JAVA虚拟机就完成了这个简单语句的执行任务。参考我们的行动向导图,我们终于初步摸清了JAVA虚拟机的一点点底细了,COOL!
接下来,JAVA虚拟机将继续执行后续指令,在堆区里继续创建另一个Sample实例,然后依次执行它们的printName()方法。当JAVA虚拟机执行test1.printName()方法时,JAVA虚拟机根据局部变量test1持有的引用,定位到堆区中的Sample实例,再根据Sample实例持有的引用,定位到方法区去中Sample类的类型信息,从而获得printName()方法的字节码,接着执行printName()方法包含的指令。
<三>
在windows中使用taskmanager查看java进程使用的内存时,发现有时候会超过 -Xmx制定的内存大小, -Xmx指定的是java heap,java还要分配内存做其他的事情,包括为每个线程建立栈。
VM的每个线程都有自己的栈空间,栈空间的大小限制vm的线程数量,太大了,实用的线程数减少,太小容易抛出java.lang.StackOverflowError异常。windows默认为1M,linux必须运行ulimit -s 2048。
在C语言里堆(heap)和栈(stack)里的区别
简单的可以理解为:
heap:是由malloc之类函数分配的空间所在地。地址是由低向高增长的。
stack:是自动分配变量,以及函数调用的时候所使用的一些空间。地址是由高向低减少。
一个由c/C++编译的程序占用的内存分为以下几个部分
1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
2、在Java语言里堆(heap)和栈(stack)里的区别
1. 栈(stack)与堆(heap)都是Java用来在Ram中存放数据的地方。与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆。
  2. 栈的优势是,存取速度比堆要快,仅次于直接位于CPU中的寄存器。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。另外,栈数据可以共享,详见第3点。堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态分配内存,存取速度较慢。
  3. Java中的数据类型有两种。
  一种是基本类型(primitive types), 共有8种,即int, short, long, byte, float, double, boolean, char(注意,并没有string的基本类型)。这种类型的定义是通过诸如int a = 3; long b = 255L;的形式来定义的,称为自动变量。值得注意的是,自动变量存的是字面值,不是类的实例,即不是类的引用,这里并没有类的存在。如int a = 3; 这里的a是一个指向int类型的引用,指向3这个字面值。这些字面值的数据,由于大小可知,生存期可知(这些字面值固定定义在某个程序块里面,程序块退出后,字段值就消失了),出于追求速度的原因,就存在于栈中。
  另外,栈有一个很重要的特殊性,就是存在栈中的数据可以共享。假设我们同时定义
  int a = 3;
  int b = 3;
  编译器先处理int a = 3;首先它会在栈中创建一个变量为a的引用,然后查找有没有字面值为3的地址,没找到,就开辟一个存放3这个字面值的地址,然后将a指向3的地址。接着处理int b = 3;在创建完b的引用变量后,由于在栈中已经有3这个字面值,便将b直接指向3的地址。这样,就出现了a与b同时均指向3的情况。
  特别注意的是,这种字面值的引用与类对象的引用不同。假定两个类对象的引用同时指向一个对象,如果一个对象引用变量修改了这个对象的内部状态,那么另一个对象引用变量也即刻反映出这个变化。相反,通过字面值的引用来修改其值,不会导致另一个指向此字面值的引用的值也跟着改变的情况。如上例,我们定义完a与 b的值后,再令a=4;那么,b不会等于4,还是等于3。在编译器内部,遇到a=4;时,它就会重新搜索栈中是否有4的字面值,如果没有,重新开辟地址存放4的值;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。
  另一种是包装类数据,如Integer, String, Double等将相应的基本数据类型包装起来的类。这些类数据全部存在于堆中,Java用new()语句来显示地告诉编译器,在运行时才根据需要动态创建,因此比较灵活,但缺点是要占用更多的时间。
4.每个JVM的线程都有自己的私有的栈空间,随线程创建而创建,java的stack存放的是frames ,java的stack和c的不同,只是存放本地变量,返回值和调用方法,不允许直接push和pop frames ,因为frames 可能是有heap分配的,所以j为ava的stack分配的内存不需要是连续的。java的heap是所有线程共享的,堆存放所有 runtime data ,里面是所有的对象实例和数组,heap是JVM启动时创建。
  5. String是一个特殊的包装类数据。即可以用String str = new String(“abc”);的形式来创建,也可以用String str = “abc”;的形式来创建(作为对比,在JDK 5.0之前,你从未见过Integer i = 3;的表达式,因为类与字面值是不能通用的,除了String。而在JDK 5.0中,这种表达式是可以的!因为编译器在后台进行Integer i = new Integer(3)的转换)。前者是规范的类的创建过程,即在Java中,一切都是对象,而对象是类的实例,全部通过new()的形式来创建。Java 中的有些类,如DateFormat类,可以通过该类的getInstance()方法来返回一个新创建的类,似乎违反了此原则。其实不然。该类运用了单例模式来返回类的实例,只不过这个实例是在该类内部通过new()来创建的,而getInstance()向外部隐藏了此细节。那为什么在String str = “abc”;中,并没有通过new()来创建实例,是不是违反了上述原则?其实没有。
  5. 关于String str = “abc”的内部工作。Java内部将此语句转化为以下几个步骤:
  (1)先定义一个名为str的对String类的对象引用变量:String str;
  (2)在栈中查找有没有存放值为”abc”的地址,如果没有,则开辟一个存放字面值为”abc”的地址,接着创建一个新的String类的对象o,并将o 的字符串值指向这个地址,而且在栈中这个地址旁边记下这个引用的对象o。如果已经有了值为”abc”的地址,则查找对象o,并返回o的地址。
  (3)将str指向对象o的地址。
  值得注意的是,一般String类中字符串值都是直接存值的。但像String str = “abc”;这种场合下,其字符串值却是保存了一个指向存在栈中数据的引用!
为了更好地说明这个问题,我们可以通过以下的几个代码进行验证。
  String str1 = “abc”;
  String str2 = “abc”;
  System.out.println(str1==str2); //true
  注意,我们这里并不用str1.equals(str2);的方式,因为这将比较两个字符串的值是否相等。==号,根据JDK的说明,只有在两个引用都指向了同一个对象时才返回真值。而我们在这里要看的是,str1与str2是否都指向了同一个对象。
  结果说明,JVM创建了两个引用str1和str2,但只创建了一个对象,而且两个引用都指向了这个对象。
  我们再来 更进一步 ,将以上代码改成:
  String str1 = “abc”;
  String str2 = “abc”;
  str1 = “bcd”;
  System.out.println(str1 + “,” + str2); //bcd, abc
  System.out.println(str1==str2); //false
  这就是说,赋值的变化导致了类对象引用的变化,str1指向了另外一个新对象!而str2仍旧指向原来的对象。上例中,当我们将str1的值改为”bcd”时,JVM发现在栈中没有存放该值的地址,便开辟了这个地址,并创建了一个新的对象,其字符串的值指向这个地址。
  事实上,String类被设计成为不可改变(immutable)的类。如果你要改变其值,可以,但JVM在运行时根据新值悄悄创建了一个新对象,然后将这个对象的地址返回给原来类的引用。这个创建过程虽说是完全自动进行的,但它毕竟占用了更多的时间。在对时间要求比较敏感的环境中,会带有一定的不良影响。
  再修改原来代码:
  String str1 = “abc”;
  String str2 = “abc”;
  str1 = “bcd”;
  String str3 = str1;
  System.out.println(str3); //bcd
  String str4 = “bcd”;
  System.out.println(str1 == str4); //true
  str3 这个对象的引用直接指向str1所指向的对象(注意,str3并没有创建新对象)。当str1改完其值后,再创建一个String的引用str4,并指向因str1修改值而创建的新的对象。可以发现,这回str4也没有创建新的对象,从而再次实现栈中数据的共享。
  我们再接着看以下的代码。
  String str1 = new String(“abc”);
  String str2 = “abc”;
  System.out.println(str1==str2); //false
  创建了两个引用。创建了两个对象。两个引用分别指向不同的两个对象。
  String str1 = “abc”;
  String str2 = new String(“abc”);
  System.out.println(str1==str2); //false
  创建了两个引用。创建了两个对象。两个引用分别指向不同的两个对象。
  以上两段代码说明,只要是用new()来新建对象的,都会在堆中创建,而且其字符串是单独存值的,即使与栈中的数据相同,也不会与栈中的数据共享。
  6. 数据类型包装类的值不可修改。不仅仅是String类的值不可修改,所有的数据类型包装类都不能更改其内部的值。
  7. 结论与建议:
  (1)我们在使用诸如String str = “abc”;的格式定义类时,总是想当然地认为,我们创建了String类的对象str。担心陷阱!对象可能并没有被创建!唯一可以肯定的是,指向 String类的引用被创建了。至于这个引用到底是否指向了一个新的对象,必须根据上下文来考虑,除非你通过new()方法来显要地创建一个新的对象。因此,更为准确的说法是,我们创建了一个指向String类的对象的引用变量str,这个对象引用变量指向了某个值为”abc”的String类。清醒地认识到这一点对排除程序中难以发现的bug是很有帮助的。
  (2)使用String str = “abc”;的方式,可以在一定程度上提高程序的运行速度,因为JVM会自动根据栈中数据的实际情况来决定是否有必要创建新对象。而对于String str = new String(“abc”);的代码,则一概在堆中创建新对象,而不管其字符串值是否相等,是否有必要创建新对象,从而加重了程序的负担。这个思想应该是享元模式的思想,但JDK的内部在这里实现是否应用了这个模式, 不得而知 。
  (3)当比较包装类里面的数值是否相等时,用equals()方法;当测试两个包装类的引用是否指向同一个对象时,用==。
  (4)由于String类的immutable性质,当String变量需要经常变换其值时,应该考虑使用StringBuffer类,以提高程序效率。
项目管理
2019-03-12 09:32:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
Cultural Change 文化变革
A great deal of the changes necessary for enterprise IT shops to adopt cloud-native architectures will not be technical at all. They will be cultural and organizational changes that revolve around eliminating structures, processes, and activities that create waste.
企业IT团队采用云原生架构所需变革的并不是技术上的。而是围绕造成产生浪费的结构、过程和活动的文化和组织变革。

From Silos to DevOps 从独立组织到DevOps

企业IT通常被组织成以下许多独立组织: 软件开发 质量保证 数据库管理 系统管理 IT操作 发布管理 项目管理

These silos were created in order to allow those that understand a given specialty to manage and direct those that perform the work of that specialty. These silos often have different management hierarchies, toolsets,communication styles, vocabularies, and incentive structures.
这些独立的组织的创建是为了让那些了解特定专业的人能够管理和指导执行该专业工作的人。这些组织通常具有不同的管理层次结构、工具集、沟通方式、

Collaboration, communication, and simple handoff of work product becomes tedious and painful at best, and absolutely chaotic (even dangerous) at worst. Enterprise IT often tries to “fix” the situation by creating heavyweight processes driven by ticket-based systems and committee meetings. And the enterprise IT value stream slows to a crawl under the weight of all of the nonvalue-adding waste.
低效地协作、沟通和简单的工作产品交接方式,往好了说是乏味和痛苦的,往坏了说是绝对混乱的(甚至是危险的)。企业IT经常试图通过创建由投票方式和委员会会议驱动的重量级流程来“修复”改变这种情况。企业IT价值流在所有非增值浪费的重压下步履瞒珊。

At its heart, DevOps represents the idea of tearing down these silos and building shared toolsets, vocabularies, and communication structures in service of a culture focused on a single goal: delivering value rapidly and safely. Incentive structures are then created that reinforce and award behaviors that lead the organization in the direction of that goal. Bureaucracy and process are replaced by trust and accountability.
DevOps的核心思想是拆除独立组织,构建共享的工具集、词汇表和通信结构,以服务于一个专注于单一目标的文化:快速安全地交付价值。然后建立激励结构,以强化和奖励那些引导组织朝着目标前进的行为。官僚主义和流程被信任和责任所取代。

From Punctuated Equilibrium to Continuous Delivery 从间断均衡到持续交付

Rather than each iteration resulting in value delivered to the customer and valuable feedback pouring back into the development team, we continue a “punctuated equilibrium” style of delivery. Punctuated equilibrium actually short-circuits two of the key benefits of agile delivery: ustomers will likely go several weeks without seeing new value in the software. They perceive that this new agile way of working is just “business as usual,” and do not develop the promised increased trust relationship with the development team. Teams may go several weeks without real feedback.That feedback provides valuable course corrections that enable teams to “build the right thing.” By delaying this feedback, the likelihood that the wrong thing gets built only increases, along with the associated costly rework.
我们不是每次迭代都向客户交付价值,并将有价值的反馈反馈回开发团队,而是继续“间断均衡”交付风格。间断的均衡实际上会缩短敏捷交付的两个关键好处: 客户可能会在数周内看不到该软件的新价值。他们认为这种新的敏捷的工作方式只是“照常工作”,并没有与开发团队建立承诺的信任关系。 团队可能几周都没有真正的反馈。这种反馈提供了有价值的课程修正,使团队能够“构建正确的东西”。通过延迟这种反馈,错误的东西被构建的可能性只会增加,随之而来的是代价高昂的返工。

Gaining the benefits of cloud-native application architectures requires a shift to continuous delivery. Rather than punctuated equilibrium driven by a water scrumfall organization, we embrace the principles of value from end to end.
获得云原生应用程序架构的好处需要转向持续交付。我们不是由一个水瀑布组织驱动的间断均衡,而是从头到尾拥抱价值原则。

The idea of “Concept to Cash”considers all of the activities necessary to carry a business idea from its conception to the point where it generates profit, and constructs a value stream aligning people and process toward the optimal achievement of that goal.
“从概念到现金”的概念考虑了将业务概念从概念到产生利润点所必需的所有活动,并构建了一个价值流,将人员和流程调整到实现该目标的最佳状态。

We technically support this way of working with the engineering practices of continuous delivery, where every iteration (in fact, every source code commit!) is proven to be deployable in an automated fashion.We construct deployment pipelines which automate every test which would prevent a production deployment should that test fail.
我们在技术上支持这种与持续交付的工程实践一起工作的方式,在这种情况下,每个迭代(实际上,每个源代码提交!)都被证明可以以自动化的方式部署。我们构建了自动化每个测试的部署管道,这将防止在测试失败时进行生产部署。

Centralized Governance to Decentralized Autonomy 集中治理到分散自治

"瀑布文化"已经成为阻碍cloud-native发展的障碍。

Enterprises normally adopt centralized governance structures around application architecture and data management, with committees responsible for maintaining guidelines and standards, as well as approving individual designs and changes. Centralized governance is intended to help with a few issues: It can prevent widespread inconsistencies in technology stacks,decreasing the overall maintenance burden for the organization. It can prevent widespread inconsistencies in architectural choices, allowing for a common view of application development across the organization. Cross-cutting concerns like regulatory compliance can be handled in a consistent way for the entire organization Ownership of data can be determined by those who have a broad view of all organizational concerns

企业通常采用围绕应用架构和数据管理的集中治理结构,委员会负责维护指导方针和标准,以及批准单独的设计和更改。集中式治理旨在帮助解决以下几个问题: 它可以防止技术栈大范围出现不一致,减少组织的整体维护负担。 它可以防止架构选型出现大范围不一致,从而在整个组织中形成应用程序开发的公共观点。 对于整个组织,可以一致地处理跨部门关切。 数据所有权可由具有全局视野的人来决定

These structures are created with the belief that they will result in higher quality, lower costs, or both. However, these structures rarely result in the quality improvements or cost savings desired, and further prevent the speed of delivery sought from cloud-native application architectures.
之前说提到的那些措施,是因为我们相信它们将有助于提高质量、降低成本或两者兼而有之。然而,这些结构很少能带来所需的质量改进或成本节约,并且进一步阻碍了从云原生应用程序架构中寻求的交付速度。

Adoption of cloud-native application architectures is almost always coupled with a move to decentralized governance. The teams building cloud-native applications own all facets of the capability they’re charged with delivering.They own and govern the data, the technology stack, the application architecture, the design of individual components, and the API contract delivered to the remainder of the organization. If a decision needs to be made, it’s made and executed upon autonomously by the team.
采用云原生应用程序架构通常伴随着向分散治理的迁移。构建云原生应用程序的团队拥有他们负责交付的功能的所有方面。它们拥有并管理数据、技术堆栈、应用架构、每个组件的设计以及交付给组织的API接口。如果需要做出决策,则由团队自主制定并执行。

The decentralization and autonomy of individual teams is balanced by minimal, lightweight structures that are imposed on the integration patterns used between independently developed and deployed services (e.g., they prefer HTTP REST JSON APIs rather than many different styles of RPC).
独立开发和部署的服务之间使用的集成模式(例如,他们更喜欢HTTP REST JSON api,而不是许多不同风格的RPC)上使用轻量级交互模式平衡各个团队的分散和自治需要。


Organizational Change 组织变革
设计系统的组织,最终产生的设计等同于组织之内、之间的沟通结构。
—Melvyn Conway

利用『康威定律』进行组织变革。

Our solution is to create a team combining staff with many disciplines around each long-term product,instead of segregating staff that have a single discipline in each own team, such as testing.
我们的解决方案是在长周期的产品开发中,创建一个包含了各方面专业员工的团队,而不是将他们分离在单一的团队中,例如测试人员。


Business Capability Teams 业务能力团队
Each of these tiers spans multiple identifiable business capabilities,making it very difficult to innovate and deploy features related to one business capability independently from the others.Companies seeking to move to cloud-native architectures like microservices segregated by business capability have often employed what Thoughtworks has called the Inverse Conway Maneuver.
Rather than building an architecture that matches their org chart, they determine the architecture they want and restructure their organization to match that architecture. If you do that, according to Conway, the architecture that you desire will eventually emerge.
每一层都跨越多个业务能力领域,因此很难独立于其他业务功能创新和部署新功能。寻求迁移到云本地架构(如按业务能力划分的微服务)的公司,常常采用Thoughtworks所称的逆康威策略。
他们没有建立一个与其组织结构图相匹配的架构,而是决定了他们想要的架构,并重组组织以匹配该架构。按照Conway的说法,如果你这样做,你想要的架构最终会出现。

So, as part of the shift to a DevOps culture, teams are organized as crosfunctional, business capability teams that develop products rather than projects. Products are long-lived efforts that continue until they no longer provide value to the business.
因此,作为转向DevOps文化的一部分,我们组织了跨职能、业务能力的团队,开发的是产品而不再是项目。产品是长期存在的工作,直到它们不再为业务提供价值为止。

All of the roles necessary to build, test, deliver, and operate the service delivering a business capability are present on a team, which doesn’t hand off code to other parts of the organization.
构建、测试、交付和操作交付业务功能的服务所需的所有角色都存在于团队中,团队不会将代码传递给组织的其他部分。

Business capability teams own the entire development-to-operations lifecycle for their applications.
业务能力团队掌握应用程序从开发到运营的整个生命周期。


The Platform Operations Team 平台运营团队
业务能力团队需要依赖于自助敏捷基础设施。

we can express a special business capability defined as “the ability to develop, deploy, and operate business capabilities.” This capability is owned by the platform operations team.
我们可以将一种特殊的业务能力定义为“开发、部署和运营的能力”。这个功能属于平台运营团队。

The platform operations team operates the self-service agile infrastructure platform leveraged by the business capability teams. This team typically includes the traditional system, network, and storage administrator roles. If the company is operating the cloud platform on premises, this team also either owns or collaborates closely with teams managing the data centers themselves, and understands the hardware capabilities necessary to provide an infrastructure platform.
平台运营团队操作业务能力团队利用的自助敏捷基础架构平台。这个团队通常包括传统的系统、网络和存储管理员角色。如果公司在本地运行云平台,那么这个团队还拥有或与管理数据中心本身的团队紧密合作,并且了解提供基础设施平台所需的硬件功能。

Just as the business capability teams collaborate with one another around defined API contracts, the platform operations team presents an API contract for the platform. Rather than queuing up requests for application environments and data services to be provisioned, business capability teams are able to take the leaner approach of building automated release pipelines that provision environments and services on-demand.
正如业务能力团队围绕已定义的API接口彼此协作一样,平台操作团队也为平台提供API接口。业务能力团队能够采用更精简的方法来构建自动发布管道,以按需提供环境和服务,而业务能力团队不需要排队等待应用程序环境和数据服务的服务。

Technical Change 技术变革
Decomposing Monoliths 拆分单体应用
Traditional n-tier, monolithic enterprise applications rarely operate well when deployed to cloud infrastructure, as they often make unsupportable assumptions about their deployment environment that cloud infrastructures simply cannot provide.
传统的n层单体企业架构应用程序在部署到云基础设施时很少能很好地运行,因为它们常常对部署环境做出云基础设施无法提供的不可支持的需求。

Most of these assumptions are coupled with the fact that monoliths are typically deployed to long-lived infrastructure.
单体架构应用通常部署在寿命较长的基础设施上并与之紧密结合。

But let’s assume that we could build a monolith that does not make any of these assumptions. We still have trouble: Monoliths couple change cycles together such that independent business capabilities cannot be deployed as required, preventing speed of innovation. Services embedded in monoliths cannot be scaled independently of other services, so load is far more difficult to account for efficiently. Developers new to the organization must acclimate to a new team, often learn a new business domain, and become familiar with an extremely large codebase all at once. This only adds to the typical 3–6 month ramp up time before achieving real productivity. Attempting to scale the development organization by adding more people further crowds the sandbox, adding expensive coordination and communication overhead. Technical stacks are committed to for the long term. Introducing new technology is considered too risky, as it can adversely affect the entire monolith.
单体架构应用面临的问题: 整体架构将变更周期耦合在一起,这样独立的业务功能就无法按需部署,从而阻碍了创新的速度。 嵌入到单体应用中的服务不能独立于其他服务进行伸缩,因此要有效地计算负载要困难得多。 新加入组织的开发人员必须适应新的团队,经常要学习新的业务领域,并且一次熟悉一个非常大的代码库。这只会增加通常3-6个月的加速时间,然后才能实现真正的生产力。 试图通过增加更多的人员来扩展开发组织会进一步拥挤沙箱,增加昂贵的协调和通信开销。 技术栈是长期致力于此的。引进新技术被认为风险太大,因为它会对整个整体产生不利影响。

The decomposition of the organization into business capability teams also requires that we decompose applications into microservices. Only then can we harness the maximum benefit from our move to cloud infrastructure.
将组织分解为业务能力团队还需要将应用程序分解为微服务。只有这样,我们才能最大地享受到向云基础设施迁移的好处。

Decomposing Data 拆分数据
前面拆分了单体应用,但这还远远不够,数据模型也必须解耦。

For a domain model to be effective, it must also be internally consistent—we should not find terms or concepts with inconsistent definitions within the same model.
要使域模型有效,它还必须具有内部一致性——我们不应该在同一个模型中发现定义不一致的术语或概念。

It is incredibly difficult and costly (and arguably impossible) to create a federated domain model that does not suffer from such inconsistencies. Evans refers to internally consistent subsets of the overall domain model of the business as bounded contexts.
想要创建一个不受不一致性影响的领域模型是极其困难的(可以说是不可能的)。Evans将业务的整个域模型的内部一致子集称为有界上下文。

Bounded contexts allow you to keep inconsistent definitions of a single concept across the organization, as long as they are defined consistently within the contexts themselves.
有界上下文允许您在整个组织中保持单个概念的不一致定义,只要它们在上下文中定义一致即可。

So we begin by identifying the segments of the domain model that can be made internally consistent. We’re then able to align business capability teams with these contexts, and those teams build microservices providing those capabilities.
首先确定可以在内部保持一致的域模型的各个部分,将业务能力团队与领域驱动中上下文联系起来,这些团队构建提供这些功能的微服务。

This definition of microservice provides a useful rubric for defining what your twelve-factor apps ought to be. Twelve-factor is primarily a technical specification, whereas microservices are primarily a business specification. We define our bounded contexts, assign them a set of business capabilities, commission capability teams to own those business capabilities, and have them build twelve-factor applications. The fact that these applications are independently deployable provides business capability teams with a useful set of technical tools for operation.
微服务的这一定义为定义12个因素的应用程序提供了一个有用的准则。12因素主要是技术规范,而微服务主要是业务规范。我们定义我们的边界上下文,为它们分配一组业务功能,委托功能团队拥有这些业务功能,并让它们构建12个因素的应用程序。这些应用程序是可独立部署的,这一事实为业务能力团队提供了一组有用的操作技术工具。

We couple bounded contexts with the database per service pattern,where each microservice encapsulates, governs, and protects its own domain model and persistent store. In the database per service pattern, only one application service is allowed to gain access to a logical data store, which could exist as a single schema within a multitenant cluster or a dedicated physical database. Any external access to the concepts is made through a well-defined business contract implemented as an API (often REST but potentially any protocol).
我们将有界上下文与每个服务模式的数据库结合起来,其中每个微服务封装、管理和保护自己的域模型和持久存储。在每个服务模式的数据库中,只允许一个应用程序服务访问逻辑数据存储,逻辑数据存储可以作为多租户集群或专用物理数据库中的单个模式存在。对概念的任何外部访问都是通过定义良好的业务契约实现的(通常是REST,但可能是任何协议)。

This decomposition allows for the application of polyglot persistence, or choosing different data stores based on data shape and read/write access patterns. However, data must often be recomposed via event-driven techniques in order to ask cross-context questions. Techniques such as command query responsibility segregation (CQRS) and event sourcing, beyond the scope of this report, are often helpful when synchronizing similar concepts across contexts.
这种分解允许应用多语言持久性,或者根据数据形状和读写访问模式选择不同的数据存储。然而,为了提出跨上下文的问题,通常必须通过事件驱动技术重新组合数据。在跨上下文同步类似概念时,命令查询责任隔离(command query responsibility离析,CQRS)和事件来源等超出本报告范围的技术通常很有用。

Containerization 容器化
Containers leverage modern Linux kernel primitives such as control groups (cgroups) and namespaces to provide similar resource allocation and isolation features as those provided by virtual machines with much less overhead and much greater portability. Application developers will need to become comfortable packaging applications as container images to take full advantage of the features of modern cloud infrastructure.
容器利用现代Linux内核原语,如控制组(control groups, cgroups)和名称空间,提供与虚拟机提供的资源分配和隔离特性类似的资源分配和隔离特性,开销小得多,可移植性强得多。应用程序开发人员需要将应用程序轻松地打包为容器映像,以便充分利用现代云基础设施的特性。

From Orchestration to Choreography 从编制到编排
Not only must service delivery, data modeling, and governance be decentralized, but also service integration. Enterprise integration of services has traditionally been accomplished via the enterprise service bus (ESB). The ESB becomes the owner of all routing, transformation, policy, security, and other decisions governing the interaction between services. We call this orchestration, analogous to the conductor who determines the course of the music performed by an orchestra during its performance. ESBs and orchestration make for very simple and pleasing architecture diagrams, but their simplicity is deceiving. Often hiding within the ESB is a tangled web of complexity. Managing this complexity becomes a full-time occupation,
and working with it becomes a continual bottleneck for the application development team. Just as we saw with a federated data model,a federated integration solution like the ESB becomes a monolithic hazard to speed.
不仅服务交付、数据建模和治理必须分散,而且服务集成也必须拆解。传统上,服务的企业集成是通过企业服务总线(ESB)完成的。ESB成为控制服务之间交互的所有路由、转换、策略、安全性和其他决策的所有者。我们称之为管弦乐编排,类似于指挥家在管弦乐队的演奏中决定音乐的进程。ESB和业务流程可以生成非常简单体系结构图,但它们的简单性具有欺骗性。通常隐藏在ESB中的是一个错综复杂的web。管理这种复杂性成为一项全职工作,使用它成为应用程序开发团队的持续瓶颈。正如我们在领域数据模型中看到的那样,ESB这样的集成解决方案会对速度造成巨大的危害。

Cloud-native architectures, such as microservices, tend to prefer choreography, akin to dancers in a ballet. Rather than placing the smarts in the integration mechanism, they are placed in the endpoints, akin to the dumb pipes and smart filters of the Unix architecture. When circumstances on stage differ from the original plan,there’s no conductor present to tell the dancers what to do. Instead,they simply adapt. In the same way, services adapt to changing circumstances in their environment via patterns such as client-side load balancing and circuit breakers .
云原生架构,如微服务,更倾向于编排,类似于芭蕾舞中的舞者。它们不是将智能放在集成机制中,而是放在端点中,类似于Unix体系结构中的管道和过滤器。当舞台上的情况与最初的计划不同时,没有指挥在场告诉舞者该做什么。相反,他们只是适应。同样,服务通过客户端负载均衡和断路器等模式来适应环境中的变化。

Choreography simply acknowledges and exposes the essential complexity of the system. Once again this shift is in support of the autonomy required to enable the speed sought from cloud-native architectures. Teams are able to adapt to their changing circumstances without the difficult overhead of coordinating with other teams, and avoid the overhead of coordinating changes with a centrally-managed ESB.
编排只是承认并公开了系统的本质复杂性。这种转变再次支持实现从云原生架构中寻求的速度所需的自主性。团队能够适应其不断变化的环境,而无需与其他团队协调的困难开销,并避免使用集中管理的ESB协调更改的开销。

总结
Culturally the overall theme is one of decentralization and autonomy: DevOps Decentralization of skill sets into cross-functional teams. Continuous delivery Decentralization of the release schedule and process. Autonomy Decentralization of decision making.
文化的主题是权力下放和自治: DevOps 将技能集分散到跨职能的团队中。 持续交付 发布计划和过程的分散化。 自治 决策的分散化。

We codify this decentralization into two primary team structures: Business capability teams
Cross-functional teams that make their own decisions about design, process, and release schedule Platform operations teams
Teams that provide the cross-functional teams with the platform they need to operate.
我们将这种分散化整理成两个主要的团队结构: 业务能力的团队
跨功能团队对设计、流程和发布计划做出自己的决定 平台运营团队
为跨职能团队提供他们需要操作的平台的团队。

And technically, we also decentralize control: Monoliths to microservices
Control of individual business capabilities is distributed to individual autonomous services. Bounded contexts
Control of internally consistent subsets of the business domain model is distributed to microservices. Containerization
Control of application packaging is distributed to business capability teams. Choreography
Control of service integration is distributed to the service endpoints.

从技术上讲,我们也分散控制: 拆分单体应用为microservices
对单个业务功能的控制被分发到各个自治服务。 界定上下文
业务域模型内部一致子集的控制被分发给微服务。 容器化
应用程序打包的控制被分发给业务能力团队。 编排
服务集成的控制被分发到服务端点。
项目管理
2019-02-25 17:50:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
模板方法模式
定义1个操作中的流程骨架,而流程实现延伸到子类中去,使得子类可不改变一个流程结构,即可重新定义改流程的某些特定步骤
角色 父类角色:提供模板:定义操作的所有流程 子类角色:为模板某些方法提供具体的实现
父类角色:PrintTemplate package com.zhiwei.template; public abstract class PrintTemplate { //定义公用方法的抽象方法,由子类实现(可变方案) public abstract void printHead(); //打印头部 public abstract void printBody(); //打印正文 public abstract void printTail(); //打印尾部 //创建模板方法,实现完整的功能,不变的有模板内部实现,可变的子类实现 public void print(){ this.initializePrinter(); this.printHead(); this.printBody(); this.printTail(); } //打印机在开始工作的时候必须进行连接,可以使不变的部分,不需要子类去实现 public void initializePrinter(){ System.out.println("电脑正在连接打印机......"); } }
子类角色: package com.zhiwei.template; /** * 模板方法的具体实现类: * 作用:根据自定义的需求,实现父类相关的抽象方法实现对应的功能 * @author Yang ZhiWei * */ public class PrintScheme01 extends PrintTemplate { @Override public void printHead() { //实现抽象方法,达到个性化定制的目的,被父类模板方法回调使用 System.out.println("正在按照第01种方案打印文件头部.........."); } @Override public void printBody() { System.out.println("正在按照第01种方案打印文件正文.........."); } @Override public void printTail() { System.out.println("正在按照第01种方案打印文件尾部.........."); } } package com.zhiwei.template; public class PrintScheme02 extends PrintTemplate{ @Override public void printHead() { //实现抽象方法,达到个性化定制的目的 System.out.println("正在按照第02种方案打印文件头部.........."); } @Override public void printBody() { System.out.println("正在按照第02种方案打印文件正文.........."); } @Override public void printTail() { System.out.println("正在按照第02种方案打印文件尾部.........."); } }
测试代码: package com.zhiwei.template; public class TemplateTest { public static void main(String[] args) { //方案1 PrintTemplate printTemplate = new PrintScheme01(); printTemplate.print(); System.out.println("-------------------"); //方案2 printTemplate = new PrintScheme02(); printTemplate.print(); } }
通俗解释: 创建PPT文件
使用ms-PowePoint模板,PPT模板为用户创建完整的PPT文件提前做了一些样式设置等工作(父类的实现方法),而用户根据需要将自己的想法和内容填入PPT模板(子类对父类的抽象方法的实现),最后形成完整的PPT文件,完成PPT文件的创建过程。用户自己修改PPT模板、填入内容等操作都被PowerPoint(父类角色)给限定,不同的人可根据同一个PPT模板创建不同的PPT文件。
项目管理
2019-02-23 11:00:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
代理模式
虚拟代理 :为创建开销大的对象提供代理服务,真正的对象在创建前和创建中时由虚拟代理负责处理(缓冲)
保护代理 :保护目标对象,为其他对象提供一个代理以控制对这个目标对象的访问
动态代理 :运行时动态创建代理类的对象,将方法调用转发到指定类
角色 抽象主题角色:通过接口或抽象类声明真实角色实现的接口,提供给客户端 真实主题角色:实现抽象主题角色,定义真实角色所要实现的业务逻辑,供代理角色调用 代理对象:实现抽象角色,提供真实角色的代理(面向客户端)
抽象主题角色: AbstractSubject package com.zhiwei.proxy; public interface AbstractSubject { void operation(); }
真实主题角色:RealSubject package com.zhiwei.proxy; public class RealSubject implements AbstractSubject { private String name = "zhangsan"; public void operation() { System.out.println("被代理目标对象执行方法:operation"); } /** * 目标对象自有方法 */ public void show(){ System.out.println(name); } }
代理对象: package com.zhiwei.proxy; public class ProxyObject implements AbstractSubject { private AbstractSubject innerRealSubject = null; public ProxyObject(){ //默认被代理对象 innerRealSubject = new RealSubject(); } public ProxyObject(AbstractSubject realSubject){ this.innerRealSubject = realSubject; } /** * 抽象出题角色定义的方法,作为被代理的方法 * 调用被代理对象的内部处理,这里可以作为代理对象的方法扩展 * * 保护目标对象:例如提供可以实现类似AOP的功能 */ @Override public void operation() { proxyMethod01(); innerRealSubject.operation(); proxyMethod01(); } /** * 代理对象的功能扩充 */ public void proxyMethod01(){ System.out.println("代理对象的自定义方法:proxyMethod01"); } public void proxyMethod02(){ System.out.println("代理对象的自定义方法:proxyMethod02"); } }
测试代码: //代理对象通过实体对象初始化 RealSubject realSubject = new RealSubject(); ProxyObject proxyObject = new ProxyObject(realSubject); proxyObject.operation(); System.out.println("---------------------------------"); //代理对象通过默认的实体对象代理 ProxyObject po = new ProxyObject(); po.operation();
代理模式分析
保护目标对象: 若目标对象涉及到很多属性,且目标对象提供接口给 特定 对象调用,使用代理对象可通过仅代理目标对象需提供的方法,这样客户端通过代理对象就不能访问目标对象私有接口,从而实现目标对象的保护。
扩展功能:因代理模式是通过代理目标对象来处理实际的业务逻辑,代理对象管理目标对象的调用流程,可通过代理模式在目标方法的执行过程添加某些特定的功能,例如日记监控,Spring AOP。
通俗理解:
例如:摩拜共享单车流程 目标对象:人 代理对象:摩拜APP(摩拜公司), 流程:去骑摩拜单车前,扫码,APP帮助我们处理前期的工作:是否注册为膜拜用户 -> APP个人余额 -> 单车是否故障 -> 是否被别人预约 -> 打开电子锁 -> 骑走单车 -> 单车用完 ->关掉电子锁 -> 摩拜确认是否上锁 -> 扣款,积累信用分等操作。
正向代理服务器:客户端发送请求给目标服务器,目标服务器代理客户端请求,请求真正的目标服务器,并将响应结果返回给客户端(常用的翻墙技术:VPN)
反向代理服务器:客户端发动请求给目标服务器,目标服务器并不会真正处理客户端请求,而是将客户的请求转发给内部的服务器集群处理,然后内部服务器将结果--> 反向代理服务器--> 客户端(典型应用:Nginx)
项目管理
2019-02-20 10:10:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
0.准备
有一个Exynos 4412的开发板,正好趁着放假,想移植一个新的uboot和一个新的linux内核,说干就干,顺便把遇到的问题记录下来
uboot版本为2020.01,下载地址:
ftp://ftp.denx.de/pub/u-boot/
kernel版本为5.4.9,下载地址:
https://mirrors.edge.kernel.org/pub/linux/kernel/v5.x/
交叉编译器下载地址:
https://www.linaro.org/downloads/
1.交叉编译器的安装
在/usr/local/下面创建一个arm的文件夹,把交叉编译工具解压到arm文件夹下。
将该路径添加到环境变量中。

执行source命令
查看交叉编译器是否安装成功
查看交叉编译器版本
2.编译uboot
解压uboot源码,在Makefile文件中修改如下内容
首先配置一个config,确认编译环境

编译完成,编译环境没有问题,下面进行Exynos 4412开发板相关的配置,编译Exynos 4412开发板用的uboot
3.uboot的配置
创建板级目录和板级文件
修改目录下的相应文件


修改Makefile文件
修改Kconfig文件
在configs文件夹下创建defconfig文件
修改其内容
在arch/arm/mach-exynos/Kconfig文件中添加如下内容

4.添加设备树
在arch/arm/dts下添加与开发板对应的设备树文件
5.添加三星加密方式
将CodeSign4SecureBoot文件夹和sdfuse_q文件夹拷贝到uboot目录下
修改Makefile文件,支持sdfuse_q编译,添加如下内容
注意:如果执行了make distclean 则需要重新拷贝CodeSign4SecureBoot
编写执行脚本

执行脚本编译
uboot编译完成,后面进行相关驱动的移植。
项目管理
2020-02-28 21:10:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
相信在平常编程过程中,给函数或变量起名称是一件令人头痛的事情。如果起的名称不能表达代码表达的逻辑,则别人看此代码会比较难受了。代码首先要给人看,其次才是机器运行。所以给函数或变量命名就非常重要了。 本篇文章主要介绍在Java(Java web)开发过程中给类,函数等的命名。在给函数或者变量命名是一个首要原则就是要尽力的表达代码想要表达的意思。下面会给出一些具体的建议
给类命名
Java中给类命名要遵循首字母大小,驼峰式命名。类名要表达具体的业务对象。在Web开发过程,经常需要建立VO,DTO,BO,Entity等对象(这些对象的含义可以参考阿里巴巴编程规范,里面有具体的介绍)。在给这些领域对象命名时,最好带上VO,DTO等后缀。
函数命令
在给函数命名时,取一个动词很关键,因为一般函数就是执行一个具体的逻辑。在《代码整洁之道》这本书中,作者告诉我们一个函数最好只做一件事情,不要让函数变得很复杂,如果函数复杂,那么如果想要重构就会变得比较困难。同时函数最好不要对于三个参数。
下面是一些常用的动词以及表达的含义,在平常的编程中可以尽量使用下面这些动词
动词选取
类别 单词 添加/插入/插入/创建/初始化/加载 add/append/insert/create/initialize/load
删除/销毁 delete, remove, destroy, drop
打开/开始/启动 open,start
关闭/停止 close, stop
获取、读取、查找、查询 get,fetch,acquire,read,search,find,query
设置,重置,放入,写入,释放,刷新 set,reset,put,write,release,refresh
发送,推送 send,push
接收, 拉取 receive,pull
提交,撤销,取消 submit,cancel
收集,采集,选取,选择 collect,pick,select
提取,解析 sub,extract, parse
编码,解码 encode,decode
填充,打包,压缩 fill,pack,compress
清空,拆包,解压 flush,clear,unpack,decompress
增加,减少 increase,decrease, reduce
分隔,拼接
过滤,校验,检测
split,join,concat
filter, valid,check
动词决定了函数的动作,名词描述了函数具体操作的对象,名词的选取也要选择常用的,不要选择生僻的。 比如:capacity表示集合容量,size表示实际元素个数,length表示字符串长度。不要用size描述字符串长度 再比如,建造者模式用build做函数名,不要用create。
名词表
类别 单词 容量,大小,长度 capacity,size,length
实例,上下文 instance,context
配置 config,settings
头部,前面,前一个,第一个 header,front,previous, first
尾部,后面,后一个,最后一个 tail,back,next,last
区间,区域,某一部分,范围,规模 range,interval,region,area,section,scope,scale
缓存,缓冲,会话 cache,buffer,session
本地,局部,全局 local,global
成员,元素 member,element
菜单,列表
源,目标
menu, list
source,destination,target
处理采用上面的动词以及名词,还有其他一些注意的地方,如下
多条件查询的函数名词谨慎使用介词by
如下命名 public List getByName(String name) 如果某天需要增加一个查询条件,需要增加电话号码的查询条件,则可能此函数命名方式变成 public List getByNameAndMobile(String name, String mobile); 如果以后还要增加查询条件,则会变成噩梦,比较好的方式是将查询条件分装成查询的领域对象,如下: public List getStudents(StudentCondition searchParam);
返回布尔值的方法尽量以is或are开头
比如 isButtonEnabled 或 areButtonEnable
get使用要合理
get表示获取,似乎有返回值的函数都可以用这个动词。但是get比较适合描述获取某个属性,比如POJO(领域对象)中的get方法。但是如果是其他的,建议采用其他动词。比如从服务器获取用户列表,用 fetchUsers ; 获取某个返回的质数, caculatePrime
不要包含函数参数的信息
如用一个 id 和 token 找用户的方法,应该叫 findUser(userId, token) 而不是 findUserByUserIdAndToken(userId, token) 。 这个跟谨慎使用介词by有些类型
项目管理
2020-02-15 17:29:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
第一季-思想篇 [ ] 第1期 个人性格 [ ] 第2期 求知欲 [ ] 第3期 人和领导力对拓展性的影响 [ ] 第4期 个人的经验 [ ] 第5期 可劲学开源佩格 [ ] 第6期 反障碍 [ ] 第7期 双活工程师 [x] 第8期 为圆满的人生作准备 [ ] 第9期 时间管理 [ ] 第10期 硬核之哈希函数 [ ] 第11期 硬核之复盘管理 [ ] 第12期 硬核之十年自学编程
  Hi,大家好!我是Lucky。今天给小伙伴播报来自一本好书中其中一个章节--为圆满的人生作准备。
  我们每个人都守着一扇自内开启的“改变之门”。除了自己,没有人能为你开门,只要你愿意敞开心灵,抛却旧有观念,把良好准则化为习惯,成功圆满就在掌握之中。
  人的品德基本上是由习惯组成的。俗谚说: 思想决定行动,行动决定习惯,习惯决定品德,品德决定命运。 习惯对我们的生活有绝大的影响,因为它是一贯的。在不知不觉中,经年累月影响着我们的品德,暴露出我们的本性,左右着我们的成败。教育家曼恩(HoraceMann)曾说:“习惯就仿佛一条缆绳,我们每日为它缠上一股新索,不要多久就会变得牢不可破。”这句话的后半段我不敢苟同,我相信习惯可以养成,也可以打破。只是绝非一蹴而就,而是需要长期的努力及无比的毅力。
  习惯是具有极大的引力,只是许多人不加注意或不肯承认罢了。要革除因循苟且、缺乏耐心、吹毛求疵或自私自利等不良习性,缺乏意志力,不能大刀阔斧的改革,便难以竟全功。
向旧习惯说再见
  本书将习惯定义为“知识"、“技巧”与“欲望”三者的混合体。知识是理论性的观念,指点我们“做什么”及“为何做”。技巧是指“如何做”。欲望则是“想做”,表示我们有付诸行动的愿望。要培养一种习惯,这三项要素缺一不可。
  前面曾提到,要提升自我必须先从观念着手。观念一旦改变,对外界的看法自然不同,而且又会回过头来影响自我,形成一个良性循环。所以我们应该从知识、技巧与欲望三方面努力,突破旧有观念的束缚,使个人与人际关系都能更上一层楼。 改变习惯的过程可能很不好受,毕竟习以为常的事务比较予人安全感。但为追求一生的幸福与成功,暂时牺牲眼前的安适或近利,也是值得的。经过一番努力与牺牲所换来的果实,将更为甜美。
依赖→独立→互赖
所谓成长的三个层次,分别为依赖、独立、互赖。 依赖:围绕着“你”这个观念
你照顾我;你为我的成败得失负责;事情若有差错,我便怪罪于你。 独立:着眼于“我”的观念
我可以自立,我为自己负责,我可以自由选择。 互赖:从“我们”的观念出发
我们可以自主、合作、集思广益,共同开创伟大前程。
产品与产能的跷跷板定理
  对“效率”所下的定义是一“产品与产能必须平衡”。伊索寓言中有则鹅生金蛋的故事,正足以说明这个常遭人忽视的原则。
  这则故事是说,一个农夫无意间发现一只会生金蛋的鹅,不久便成了富翁。可是财富却使他变得更贪婪更急躁,每天一个金蛋已无法满足他,于是农夫异想天开的把鹅宰杀,企图将鹅肚子里的金蛋全部取出来。谁知打开一看,鹅腹里并没有金蛋,鹅却死了,再也生不出金蛋。
  这则寓言是效率观念一个很好的例证。一般人往往自金蛋的角度来衡量效率,也就是产品愈多,效率愈高。可是上面的故事却告诉我们,效率包括两个要素,一是产品(金蛋),也就是你希望获得的结果;一是产能(鹅),也就是你借以达到目标的资产或本领。
  仅重视金蛋,无视于鹅的人,结果会连产金蛋的资产本身都保不住。反之,“重鹅轻蛋”的人,最后可能养不活自己,更不用说鹅了。因此,产品与产能必须平衡,才能达到真正的效率。
个人的效率观

团队的效率观

  好了,现在就读到这里。小伙伴们应该想知道这本书吧。普京建议俄罗斯公民阅读这本书《成功人士的七个习惯》,它改变人的思维方式和行为方式。作者是史蒂芬‧柯维《经济学人》杂志推举为最具前瞻的管理思想家。
项目管理
2020-02-08 12:50:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
1.在线流程图
https://www.processon.com/diagrams
项目管理
2020-01-22 15:41:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
仍在整理中,敬请期待。。。
项目管理
2020-01-12 22:51:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
前言
在还没毕业的去面试的时候,被问到动态代理与策略模式有什么区别,当时想了想也没想出来什么合适的回答,最会草草越过,之后面试完毕进行了相关的查阅,还是懵懵懂懂的,到现在debug SpringMVC源码的时候,遇到 initHandlerAdapters 初始化的后,一直纠结此处是使用了责任链模式还是策略模式,然后就想到了之前遇到的动态代理和装饰者模式。
项目管理
2020-01-11 14:39:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
The WordPress , Rewrite API is like a mythical creature. Not many know it exists, and few still know how to tame it. In this article, we’ll cover the basics of the Rewrite API and how to make it work for you.
The Rewrite API includes three commonly used functions (and a fourth function that doesn’t get much love). add_rewrite_rule add_rewrite_tag add_rewrite_endpoint add_feed – Not incredibly useful, generally, but it could be!
How WordPress Permalinks Work
When you request a page on a WordPress site, assuming it’s using pretty permalinks, the rules in htaccess file (or webconfig, if you’re into windows) first test to see if the request file and or directory exists on the server. In other words, if someone request yoursite.com/some-folder/ and you’ve created some-folder in the root of your site (via FTP or whatever else), Apache will serve that directory. Same deal for static files. NginX works a similar way, but does so with a try_files directive: try_files $uri $uri/ index.php .
If the folder or file doesn’t exist, the server directs the request to index.php, which is, of course, where the magic happens. From here WordPress loads . During this process, WordPress tries to match the request’s url (the stuff after yourdomain.com ) with a series of rewrite rules , which are just regular expressions . If it finds a match, WP will translate the URI into a database query, render the correct template file and serve up the page.
When you add a rewrite rule, you’re augmenting the built in rewrite rules with your own. There’s all kinds of stuff you can do with this. Like creating custom shortlinks or giving your WordPress site an API.
Adding a Rewrite Rule
add_rewrite_rule is the thing to add your own custom rules. It tags three arguments: the regex, the index.php + query string URL you to rewrite the regex matching URI to, and the priority. Generally $priority is always set to “top” to push your rewrite rules before the built in WP rules.
Here’s an example. We’re going to rewrite yoursite.com/p/some_number to a post that has the ID, some_number. We’ll hook into init to add the rule. The first argument says, “match p followed by a slash, then a series of one or more numbers (and maybe another slash).” The second argument tells wordpress to rewrite the rule to index.php?p=the_group_of_numbers. $matches[1] refers to the first group of parenthesis inside the rewrite regex (the first argument).
Before this will work for you, however, you’ll need to flush your rewrite rules. You can do this pro grammatically, with an activation hook in a plugin, or manually by visiting the Settings > Permalinks page in your WordPress admin and hitting save.
Here’s how to do it with an activation hook: The reason the above rewrite rule works is because WordPress recognizes what the p query string key means — it knows to make it to a post id. What if you want to create something like a URL endpoint for a form submission? None of the built in query variables would do for this, so we need to role our own. Here’s the rewrite rule: If you flush your rewrite rules and visit yoursite.com/submit/ , it’s going to show the blog/index page. WordPress doesn’t know what to do with the form query variable. And if you try to grab the variable with get_query_var , it’s not going to work. Why? WP did not recognize the the form variable, so it stripped it out.
There’s one of two ways you can add a query variable. We’ll cover one here, and one in the next section of this article. To add the variable you add a filter to query_vars and push form onto the query variable array. Catching Custom Query Variables
You can get a query variable with the get_query_var function. Like this. get_query_var can be used any time after the query is set up — any time after init . It’s safe to use inside a template, in other words. This is a fairly common practice for me: hook into template redirect, catch a custom query variable, do stuff inside the WordPress environment, then exit(); to stop the theme from loading. Adding Rewrite Tags
add_rewrite_tag lets you add custom tags similar to %postname% or any of the permalink structure tags . It can be used in place of filtering query_vars .
In other words, instead of this: Is a complete unit. No need to filter query_vars to make sure WordPress recognizes the form variable. That said, is this the right approach? Rewrite tags are more helpful if you’re going to use them as part of, say, a custom permlink structure for a custom post type. In our example, they probalby aren’t necessary. add_rewrite_tag is also misleading. It sort of implies that, with registration, you somehow tell WordPress to know how to replace the tag with the correct value. WP does not do that. You’ll have to do that manually, but it is possible .
Adding Rewrite Endpoints
Rewrite endpoints are things that get tacked out the end of URLs on your site. Trackbacks are a good example: any post on a WordPress side has the endpoint /trackback/ that alters the behavior of the page to, you guessed it, add pingbacks/trackbacks.
We’ll add an endpoint to posts to create an “api”. Whenever you visit yoursite.com/some-permalink/json/ WP will spit out a nice json version of the post. add_rewrite_endpoint tags two arguments: (1) the endpoint itself and (2) where you want the endpoint to live ( $place ). $place is going to be one of the many EP_* constants (view them here ). If you want to place an endpoind in more than one place, you can combine EP constants with the bitwise OR (the pipe: |).
Like our other rewrites, we’ll hook into init to add our endpoint. add_rewrite_endpoint takes care of adding the rewrite rules and adding the query variable for us! Here’s the code we’ll use to catch our /json/ endpoint. If you visit yoursite.com/some-permalink/json/ (after flushing rewrite rules, of course) it’s not going to work, but yoursite.com/some-permalink/json/asdf will! That’s because add_rewrite_endpoint is going to set the json query variable equal to the stuff that comes after the /json/ endpoint. If it’s an empty string (as it is with yoursite.com/some-permalink/json/ ) php will evaluate it as false.
if( is_singular() && get_query_var( ‘json’ ) !== false ) won’t work either as it will stop all permalinks from loading. Instead, we need to hook into request and give our json query variable a value if it’s set. Now if you visit yoursite.com/some-permalink/json/ , it will work. Next up we just need to enhance our handler function (hooked into template_redirect a bit). $post->post_title, 'content' => $post->post_content ); header('Content-Type: text/plain'); echo json_encode( $out ); exit(); } }
Now if you visit yoursite.com/some-permalink/json/ a nice JSON will be there for consumption.
Adding Custom Feeds
The little used (or talked about) add_feed function is used to — well, to add feeds to wordpress. Like yoursite.com/feed/rss . add_feed takes to arguments: the feed name and the function you wish to fire when the feed is loaded. Like our other rewrite, writes, we’ll hook into init . Since we’re into JSON in this article, let’s make a feed that displays our list of posts as a JSON string. The function pmg_rewrite_json_feed is going to receive one argument: whether or not this is a comments feed. Since we don’t really need this, we’ll leave it out. Our hooked function is what you’d expect: get some posts, turn them into a JSON. $p->post_title, 'content' => $p->post_content ); } header('Content-Type: text/plain'); echo json_encode( $out ); }
Demystifying the WordPress Rewrite API
Hopefully this article cleared some things up for you regard the rewrite API. It’s very powerful, and allows you to use WordPress in ways that are well outside its usual scope. All of this code is available here .
项目管理
2020-01-09 12:56:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
公众号带参数的二维码,通过公众号接口开发,实现公众号粉丝统计,粉丝批量打标签分组或修改备注名,关注取关数据都能统计到,数据每天实时更新。会编程的都可以实现效果,也可以直接用微号帮平台渠道二维码生成实现。
1、渠道二维码生成
渠道二维码生成功能,微号帮支持自定义扫码回复、粉丝打标签分组、修改备注名,粉丝关注数据统计。
2、粉丝关注统计数据
通过带参数的二维码关注的粉丝都能统计到,数据每天更新,如果设置了打标签分组,还可以查看分组的粉丝明细。
3、不同二维码回复不同消息
微号帮平台通过带参数的二维码,每个二维码为一个渠道,每个二维码支持不同的消息类型设置,文本信息、图文消息、卡券、小程序、图片、音频、视频都可以设置成扫码回复消息。
4、带参数的二维码设置
关注二维码支持公众号粉丝打标签分组或修改备注名,默认渠道名称即标签分组,也可以修改成别标签分组。
项目管理
2019-12-05 15:05:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
一、了解Java NIO
NIO(Non-Blocking I/O,java中,也称为New I/O),是一种同步非阻塞的I/O模型,也是I/O多路复用的基础,已经被越来越多地应用到大型应用服务器,是解决高并发、I/O处理问题的有效方式。
1. 传统的BIO
BIO(Blocking I/O)即同步阻塞I/O,在NIO出现之前主要使用BIO及新建线程的方式来解决并发请求,但这样很容易因线程瓶颈而造成限制。下面是BIO的经典编程模型(主要代码): { ExecutorService executor = Excutors.newFixedThreadPollExecutor(100);//线程池 ServerSocket serverSocket = new ServerSocket(); serverSocket.bind(8088); while(!Thread.currentThread.isInturrupted()){//当前线程未中断 Socket socket = serverSocket.accept(); executor.submit(new ConnectIOnHandler(socket));//为新的连接创建新的线程 } class ConnectIOnHandler extends Thread{ private Socket socket; public ConnectIOnHandler(Socket socket){ this.socket = socket; } public void run(){ while(!Thread.currentThread.isInturrupted()&&!socket.isClosed()){死循环处理读写事件 String someThing = socket.read()....//读取数据 if(someThing!=null){ ......//处理数据 socket.write()....//写数据 } } } }
之所已使用多线程,因为accept()、read()、write()三个函数都是同步阻塞的,当一个连接存在的时候,系统是阻塞的,所以利用多线程让cpu处理更多的申请。多线程的本质: 利用cpu的多核特性 当I/O阻塞系统,但cpu空闲的时候,可以利用多线程使用cpu资源
现在的多线程一般都使用线程池,可以让线程的创建和回收成本相对较低。在活动连接数不是特别高(小于单机1000)的情况下,这种模型是比较不错的,可以让每一个连接专注于自己的I/O并且编程模型简单,也不用过多考虑系统的过载、限流等问题。线程池本身就是一个天然的漏斗,可以缓冲一些系统处理不了的连接或请求。但当处理百万级的连接时,使用这种模型肯定是不实际的,让cpu去创建这么多的线程是不可能的。
2. 优秀的NIO
‘优秀’是当今很流行的一个词,可以十分恰当的形容NIO在java中的重要性。从JDK1.4开始,Java提供了一系列改进的输入/输出处理的新特性,被统称为NIO(即New I/O)。新增了许多用于处理输入输出的类,这些类都被放在java.nio包及子包下,并且对原java.io包中的很多类进行改写,新增了满足NIO的功能。 NIO采用内存映射文件的方式来处理输入输出,NIO将文件或文件的一段区域映射到内存中,这样就可以像访问内存一样访问文件了。NIO与原来的IO有同样的作用和目的,但是使用的方式完全不同, NIO支持面向缓冲区(Buffer)的、基于通道(Channel)的IO操作。NIO将以更加高效的方式进行文件的读写操作。
NIO中的三个重要组件:
2. 1 缓冲区Buffer
缓冲区有直接缓冲区和非直接缓冲区之分(关于两者的区别可以看 这里 ),它实际上也是一段内存空间。在NIO库中,所有数据都是用缓冲区处理的。在读取数据时,它是直接读到缓冲区中的; 在写入数据时,它也是写入到缓冲区中的。流程如下图:

2. 2 通道Channel
Channel(通道)表示到实体如硬件设备、文件、网络套接字或可以执行一个或多个不同I/O操作的程序组件的开放的连接。
Channel和传统IO中的Stream很相似。主要区别为: 通道是双向的,通过一个Channel既可以进行读,也可以进行写;而Stream只能进行单向操作,通过一个Stream只能进行读或者写,比如InputStream只能进行读取操作,OutputStream只能进行写操作; 通道是一个对象,通过它可以读取和写入数据,当然了所有数据都通过Buffer对象来处理。我们永远不会将字节直接写入通道中,相反是将数据写入包含一个或者多个字节的缓冲区。同样不会直接从通道中读取字节,而是将数据从通道读入缓冲区,再从缓冲区获取这个字节。
2.3 选择器Selector
Selector类是NIO的核心类,Selector(选择器)选择器提供了选择已经就绪的任务的能力。 Selector会不断的轮询注册在上面的所有channel,如果某个channel为读写等事件做好准备,那么就处于就绪状态,通过Selector可以不断轮询发现出就绪的channel,进行后续的IO操作。 一个Selector能够同时轮询多个channel。这样,一个单独的线程就可以管理多个channel,从而管理多个网络连接。这样就不用为每一个连接都创建一个线程,同时也避免了多线程之间上下文切换导致的开销。
与Selector有关的一个关键类是SelectionKey,一个SelectionKey表示一个到达的事件,这2个类构成了服务端处理业务的关键逻辑。关于SelectionKey的详细介绍可以参考这篇 博文
3. NIO编程
客户端代码: public class Client { ByteBuffer writeBuffer = ByteBuffer.allocate(1024); ByteBuffer readBuffer = ByteBuffer.allocate(1024); public void start() throws IOException{ //打开socket通道 SocketChannel sc = SocketChannel.open(); sc.configureBlocking(false); sc.connect(new InetSocketAddress("localhost",3400)); //创建选择器 Selector selector = Selector.open(); //将channel注册到selector中 sc.register(selector, SelectionKey.OP_CONNECT); Scanner scanner = new Scanner(System.in); while (true){ selector.select(); Set keys = selector.selectedKeys(); System.out.println("keys:"+keys.size()); Iterator iterator = keys.iterator(); while (iterator.hasNext()){ SelectionKey key = iterator.next(); iterator.remove(); //判断此通道上是否在进行连接操作 if (key.isConnectable()){ sc.finishConnect(); //注册写操作 sc.register(selector,SelectionKey.OP_WRITE); System.out.println("server connected..."); break; }else if (key.isWritable()){ System.out.println("please input message:"); String message = scanner.nextLine(); writeBuffer.clear(); writeBuffer.put(message.getBytes()); //将缓冲区各标志复位,因为向里面put了数据标志被改变要想从中读取数据发向服务器,就要复位 writeBuffer.flip(); sc.write(writeBuffer); //注册写操作,每个chanel只能注册一个操作,最后注册的一个生效 //如果你对不止一种事件感兴趣,那么可以用“位或”操作符将常量连接起来 //int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE; //使用interest集合 sc.register(selector,SelectionKey.OP_WRITE | SelectionKey.OP_READ); }else if(key.isReadable()){ System.out.print("receive message:"); SocketChannel client = (SocketChannel) key.channel(); //将缓冲区清空以备下次读取 readBuffer.clear(); int num = client.read(readBuffer); System.out.println(new String(readBuffer.array(),0, num)); //注册写操作,下一次写 sc.register(selector, SelectionKey.OP_WRITE); } } } } public static void main(String[] args) throws Exception { new Client().start(); } }
服务端代码: /** * nio是面向缓冲区的 * bio是面向流的 * @author zmrwego * @descreption * @create 2018-10-15 **/ public class Server { private Selector selector; private ByteBuffer readBuffer = ByteBuffer.allocate(1024);//调整缓冲区大小为1024字节 private ByteBuffer sendBuffer = ByteBuffer.allocate(1024); String str; public void start() throws IOException{ //打开服务器套接字通道 ServerSocketChannel ssc = ServerSocketChannel.open(); ssc.configureBlocking(false); //服务器配置为非阻塞 即异步IO ssc.bind(new InetSocketAddress(3400)); //绑定本地端口 //创建选择器 selector = Selector.open(); ssc.register(selector, SelectionKey.OP_ACCEPT);//ssc注册到selector准备连接 //无限判断当前线程状态,如果没有中断,就一直执行while内容。 while(! Thread.currentThread().isInterrupted()){ selector.select(); //select()方法返回的值表示有多少个 Channel 可操作 Set keys = selector.selectedKeys(); Iterator keyIterator = keys.iterator(); while (keyIterator.hasNext()){//处理客户端连接 SelectionKey key = keyIterator.next(); if (!key.isValid()){ continue; } if (key.isAcceptable()){ accept(key); } if(key.isReadable()){ read(key); } if (key.isWritable()){ write(key); } keyIterator.remove(); //移除当前的key } } } private void read(SelectionKey key) throws IOException{ SocketChannel socketChannel = (SocketChannel) key.channel(); this.readBuffer.clear();//清除缓冲区,准备接受新数据 int numRead; try{ numRead = socketChannel.read(this.readBuffer); }catch (IOException e){ key.cancel(); socketChannel.close(); return; } str = new String(readBuffer.array(),0,numRead); System.out.println(str); socketChannel.register(selector,SelectionKey.OP_WRITE); } private void write(SelectionKey key) throws IOException, ClosedChannelException{ SocketChannel channel = (SocketChannel) key.channel(); System.out.println("write:"+str); sendBuffer.clear(); sendBuffer.put(str.getBytes()); sendBuffer.flip();//反转,由写变为读 channel.write(sendBuffer); //注册读操作 下一次进行读 channel.register(selector,SelectionKey.OP_READ); } private void accept(SelectionKey key) throws IOException { ServerSocketChannel ssc = (ServerSocketChannel) key.channel(); SocketChannel clientChannel = ssc.accept(); clientChannel.configureBlocking(false); clientChannel.register(selector, SelectionKey.OP_READ); System.out.println("a new client connected "+clientChannel.getRemoteAddress()); } public static void main(String[] args) throws Exception { System.out.println("sever start..."); new Server().start(); } }
二、详细了解Java NIO
前言 JDK 1.4 后, Java 提供了一个全新的 IO API ,即 Java New IO 本文 全面 & 详细解析 Java New IO ,希望你们会喜欢
目录
储备知识:Java IO
1. 定义 即 Java New IO 是1个全新的、 JDK 1.4 后提供的 IO API
2. 作用 提供了与标准 IO 不同的 IO 工作方式 可替代 标准 Java IO 的 IO API
3. 新特性
对比于 Java IO , NIO 具备的新特性如下:
4. 核心组件
Java NIO 的核心组件 包括: 通道( Channel ) 缓冲区( Buffer ) 选择器( Selectors )
下面将详细介绍:
5. 具体使用
5.1 基于通道 & 缓冲数据
具体步骤如下: // 1. 获取数据源 和 目标传输地的输入输出流(此处以数据源 = 文件为例) FileInputStream fin = new FileInputStream(infile); FileOutputStream fout = new FileOutputStream(outfile); // 2. 获取数据源的输入输出通道 FileChannel fcin = fin.getChannel(); FileChannel fcout = fout.getChannel(); // 3. 创建 缓冲区 对象:Buffer(共有2种方法) // 方法1:使用allocate()静态方法 ByteBuffer buff = ByteBuffer.allocate(256); // 上述方法创建1个容量为256字节的ByteBuffer // 注:若发现创建的缓冲区容量太小,则重新创建一个大小合适的缓冲区 // 方法2:通过包装一个已有的数组来创建 // 注:通过包装的方法创建的缓冲区保留了被包装数组内保存的数据 ByteBuffer buff = ByteBuffer.wrap(byteArray); // 额外:若需将1个字符串存入ByteBuffer,则如下 String sendString="你好,服务器. "; ByteBuffer sendBuff = ByteBuffer.wrap(sendString.getBytes("UTF-16")); // 4. 从通道读取数据 & 写入到缓冲区 // 注:若 以读取到该通道数据的末尾,则返回-1 fcin.read(buff); // 5. 传出数据准备:将缓存区的写模式 转换->> 读模式 buff.flip(); // 6. 从 Buffer 中读取数据 & 传出数据到通道 fcout.write(buff); // 7. 重置缓冲区 // 目的:重用现在的缓冲区,即 不必为了每次读写都创建新的缓冲区,在再次读取之前要重置缓冲区 // 注:不会改变缓冲区的数据,只是重置缓冲区的主要索引值 buff.clear();
5.2 基于选择器(Selecter)
具体步骤如下: // 1. 创建Selector对象 Selector sel = Selector.open(); // 2. 向Selector对象绑定通道 // a. 创建可选择通道,并配置为非阻塞模式 ServerSocketChannel server = ServerSocketChannel.open(); server.configureBlocking(false); // b. 绑定通道到指定端口 ServerSocket socket = server.socket(); InetSocketAddress address = new InetSocketAddress(port); socket.bind(address); // c. 向Selector中注册感兴趣的事件 server.register(sel, SelectionKey.OP_ACCEPT); return sel; // 3. 处理事件 try { while(true) { // 该调用会阻塞,直到至少有一个事件就绪、准备发生 selector.select(); // 一旦上述方法返回,线程就可以处理这些事件 Set keys = selector.selectedKeys(); Iterator iter = keys.iterator(); while (iter.hasNext()) { SelectionKey key = (SelectionKey) iter.next(); iter.remove(); process(key); } } } catch (IOException e) { e.printStackTrace(); }
6. 文件复制实例讲解 实例说明:实现文件复制功能 实现方式:通道 FileChannel 、 缓冲区 ByteBuffer import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; public class Test { public static void main(String[] args) throws IOException { // 设置输入源 & 输出地 = 文件 String infile = "C:\\copy.sql"; String outfile = "C:\\copy.txt"; // 1. 获取数据源 和 目标传输地的输入输出流(此处以数据源 = 文件为例) FileInputStream fin = new FileInputStream(infile); FileOutputStream fout = new FileOutputStream(outfile); // 2. 获取数据源的输入输出通道 FileChannel fcin = fin.getChannel(); FileChannel fcout = fout.getChannel(); // 3. 创建缓冲区对象 ByteBuffer buff = ByteBuffer.allocate(1024); while (true) { // 4. 从通道读取数据 & 写入到缓冲区 // 注:若 以读取到该通道数据的末尾,则返回-1 int r = fcin.read(buff); if (r == -1) { break; } // 5. 传出数据准备:调用flip()方法 buff.flip(); // 6. 从 Buffer 中读取数据 & 传出数据到通道 fcout.write(buff); // 7. 重置缓冲区 buff.clear(); } } }
7. 与Java IO的区别
项目管理
2019-12-05 10:26:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
1.远程调用
1.1、什么是远程调用 一个项目调用另一个项目模块(功能) 调用过程:模拟浏览器过程
1、打开浏览器窗口
2、确定访问路径(请求方式:get)
3、访问(执行),并获得结果
4、显示内容
5、关闭页面
1.2、调用方式 RPC
自定义数据格式的远程调用方式。更偏向于底层,通信速度快,效率高
---常见框架:dubbo Http
采用Http协议远程调用方式,规定了数据传输的格式,缺点是消息封装臃肿,现在热门的Rest风格,就可以通过Http协议来实现。
---常见框架:HTTPClient、RestTemplate
1.3、区别
区别 http rpc 速度 较慢 快
难度 灵活性
简单 灵活,跨平台、跨语言
复杂
1、创建客户端,打开一个浏览器
2、创建GET请求实例,相当于输入一个请求地址
3、发送请求
4、判断状态码200
5、获得响应内容
6、释放资源
2.基于HTTP协议远程调用
2.1对比 HttpClient更偏向于底层,学习时原理。相当于模拟浏览器。但操作比较繁琐。 RestTemplate对整个请求进行简化 实际开发中RestTemplate整合HttpClient
2.2 RestTemplate 语法 //1 创建核心类 RestTemplate restTemplate = new RestTemplate(); //get请求 ResponseEntity<返回值类型> entity = restTemplate.getForEntity('请求路径', 返回值类型.class ); //post请求 ResponseEntity<返回值类型> entity = restTemplate.postForEntity('请求路径',请求参数对象,返回值类型.class); //put请求 restTemplate.put('请求路径', 请求参数); //delete请求 restTemplate.delete('请求路径')
2.3 配置类的使用 步骤一:编写配置类,将需要new的对象交于spring进行管理 @Configuration //配置类,spring boot会自动扫描 public class 类{ @Bean //spring将管理此方法创建的对象 public RestTemplate restTemplate(){ return new RestTemplate(); } } 步骤二:在任意位置,通过@Resource进行注入 public class AdminClient{ @Resource //因为配置类已经构建对象,此处自动注入 public RestTemplate restTemplate; //..使用即可 }
项目管理
2019-12-04 21:44:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
VO(View Object):视图层,用于展示层,它的作用是把某个指定页面(或组件)的所有数据封装起来。
DTO(Data Transfer Object):数据传输对象,这个概念来源于J2EE的设计模式,原来的目的时为了EJB的分布式应用提供粗粒度的数据实体,以减少分布式条用的次数,从而提高分布式调用的性能和降低网络负载,但在这里,我泛指用于展示层与服务层之间的数据传输对象。
DO(Domain Object):领域对象,就是从现实世界中抽象出来的有形或无形的业务实体。
PO(Persistent Object):持久化对象,他跟持久层(通常是关系型数据库)的数据结构形成一一对应的映射关系,如果持久层是关系型数据库,那么,数据表中的每个字段(或若干个)就对应PO的一个(或若干个)属性。
模型:
下面以一个时序图建立简单模型来描述上述对象在三层架构应用中的位置
用户发出请求(可能是填写表单),表单的数据在展示层被匹配为VO。
展示层把VO转换为服务层对应方法所要求的DTO,传输给服务层。
服务层首先根据DTO的数据构造(或重建)一个DO,调用DO的业务方法完成具体业务。
服务层把DO转换为持久层对应的PO(可以使用ORM工具,也可以不用),调用持久层的持久化方法,把PO传递给它,完成持久化操作。
对于一个逆向操作,如读取数据,也是用类似的方式转换和传递,略。
VO与DTO的区别
1.大家可能会有个疑问(在笔者参与的项目中,很多程序员也有相同的疑惑):既然DTO是展示层与服务层之间传递数据的对象,为什么还需要一个VO呢?对!对于绝大部分的应用场景来说,DTO和VO的属性值基本是一致的,而且他们通常都是POJO,因此没必要多此一举,但不要忘记这是实现层面的思维,对于设计层面来说,概念上还是应该存在VO和DTO,因为两者有着本质的区别,DTO代表服务层需要接收的数据和返回的数据,而VO代表展示层需要显示的数据。
2.用一个例子来说明可能会比较容易理解:例如服务层有一个getUser的方法返回一个系统用户,其中有一个属性是gender(性别),对于服务层来说,它只从语义上定义:1-男性,2-女性,0-未指定,而对于展示层来说,它可能需要用“帅哥”代表男性,用“美女”代表女性,用“秘密”代表未指定。说到这里,可能你还会反驳,在服务层直接就返回“帅哥美女”不就行了吗?对于大部分应用来说,这不是问题,但设想一下,如果需求允许客户可以定制风格,而不同风格对于“性别”的表现方式不一样,又或者这个服务同时供多个客户端使用(不同门户),而不同的客户端对于表现层的要求有所不同,那么,问题就来了。再者,回到设计层面上分析,从职责单一原则来看,服务层只负责业务,与具体的表现形式无关,因此,它返回的DTO,不应该出现与表现形式的耦合。
3.理论归理论,这到底还是分析设计层面的思维,是否在实现层面必须这样做呢?一刀切的做法往往会得不偿失,下面我马上会分析应用中如何做出正确的选择。
VO与DTO的应用
上面只是用了一个简单的例子来说明VO与DTO在概念上的区别,本节将会告诉你如何在应用中做出正确的选择。
在以下才场景中,我们可以考虑把VO与DTO二合为一(注意:是实现层面):
当需求非常清晰稳定,而且客户端很明确只有一个的时候,没有必要把VO和DTO区分开来,这时候VO可以退隐,用一个DTO即可,为什么是VO退隐而不是DTO?回到设计层面,服务层的职责依然不因该与展示层耦合,所以,对于前面的例子,你很容易理解,DTO对于“性别”来说,依然不能用“帅哥美女”,这个转换应该依赖与页面的脚本(如javaScript)或其他机制(JSTL,EL,CSS)。
即使客户端可以进行定制,或者存在多个不同的客户端,如果客户端能够用某种技术(脚本或其他机制)实现转换,同样可以让VO隐退。
以下场景需要优先考虑VO,DTO并存:
上述场景的反面场景
因为某种技术原因,比如某个框架(如Flex)提供自动把POJO转换为UI中某些FieId时,可以考虑在实现层面定义出VO,这个这个权衡完全取决于使用框架的自动转换能力带来的开发和维护效率提升与设计多一个VO所多做的事情带来的开发和维护效率的下降之间的对比。
如果页面出现一个“大视图”,而组成这个大视图的所有数据需要调用多个服务,返回多个DTO来组装(当然,这同样可以通过服务层提供一次性返回一个大视图的DTO来取代,但在服务层提供一个这样的方法是否合适,需要在设计层面进行权衡)。
DTO与DO的区别
首先是概念上的区别,DTO是展示层和服务层之间的数据传输对象(可以认为是两者之间的协议),而DO是对现实世界各种业务角色的抽象,这就引出了两者在数据上的区别,例如UserInfo和User(对于DTO和DO的命名规则,请参见笔者前面的一篇博文),对于一个getUser方法来说,本质上它永远不应该返回用户的密码,因此UserInfo至少比User少一个password的数据。而在领域驱动设计中,正如第一篇系列文章所说,DO不是简单的POJO,它具有领域业务逻辑。
DTO与DO的应用
1.从上一节的例子中,细心的读者可能会发现问题:既然getUser方法返回的UserInfo不应该包含password,那么就不应该存在password这个属性定义,但如果同时有一个createUser的方法,传入的UserInfo需要包含用户的password,怎么办?在设计层面,展示层向服务层传递的DTO与服务层返回给展示层的DTO在概念上是不同的,但在实现层面,我们通常很少会这样做(定义两个UserInfo,甚至更多),因为这样做并不见得很明智,我们完全可以设计一个完全兼容的DTO,在服务层接收数据的时候,不该由展示层设置的属性(如订单的总价应该由其单价、数量、折扣等决定),无论展示层是否设置,服务层都一概忽略,而在服务层返回数据时,不该返回的数据(如用户密码),就不设置对应的属性。
2.对于DO来说,还有一点需要说明:为什么不在服务层中直接返回DO呢?这样可以省去DTO的编码和转换工作,原因如下:
两者在本质上的区别可能导致彼此并不一一对应,一个DTO可能对应多个DO,反之亦然,甚至两者存在多对多的关系。
DO具有一些不应该让展示层知道的数据。
DO具有业务方法,如果直接把DO传递给展示层,展示层的代码就可以绕过服务层直接调用它不应该访问的操作,对于基于AOP拦截服务层来进行访问控制的机制来说,这问题尤为突出,而在展示层调用DO的业务方法也会因为事务的问题,让事务难以控制。
对于某些ORM框架(如Hibernate)来说,通常会使用“延迟加载”技术,如果直接把DO暴露给展示层,对于大部分情况,展示层不在事务范围之内(Open session in view在大部分情况下不是一种值得推崇的设计),如果其尝试在Session关闭的情况下获取一个未加载的关联对象,会出现运行时异常(对于Hibernate来说,就是LazyInitiliaztionException)。
从设计层面来说,展示层依赖于服务层,服务层依赖于领域层,如果把DO暴露出去,就会导致展示层直接依赖于领域层,这虽然依然是单向依赖,但这种跨层依赖会导致不必要的耦合。
对于DTO来说,也有一点必须进行说明,就是DTO应该是一个“扁平的二维对象”,举个例子来说明:如果User会关联若干个其他实体(例如Address、Account、Region等),那么getUser()返回的UserInfo,是否就需要把其关联的对象的DTO都一并返回呢?如果这样的话,必然导致数据传输量的大增,对于分布式应用来说,由于涉及数据在网络上的传输、序列化和反序列化,这种设计更不可接受。如果getUser除了要返回User的基本信息外,还需要返回一个AccountId、AccountName、RegionId、RegionName,那么,请把这些属性定义到UserInfo中,把一个“立体”的对象树“压扁”成一个“扁平的二维对象”,笔者目前参与的项目是一个分布式系统,该系统不管三七二十一,把一个对象的所有关联对象都转换为相同结构的DTO对象树并返回,导致性能非常的慢。
DO与PO的区别
DO和PO在绝大部分情况下是一一对应的,PO是只含有get/set方法的POJO,但某些场景还是能反映出两者在概念上存在本质的区别:
DO在某些场景下不需要进行显式的持久化,例如利用策略模式设计的商品折扣策略,会衍生出折扣策略的接口和不同折扣策略实现类,这些折扣策略实现类可以算是DO,但它们只驻留在静态内存,不需要持久化到持久层,因此,这类DO是不存在对应的PO的。
同样的道理,某些场景下,PO也没有对应的DO,例如老师Teacher和学生Student存在多对多的关系,在关系数据库中,这种关系需要表现为一个中间表,也就对应有一个TeacherAndStudentPO的PO,但这个PO在业务领域没有任何现实的意义,它完全不能与任何DO对应上。这里要特别声明,并不是所有多对多关系都没有业务含义,这跟具体业务场景有关,例如:两个PO之间的关系会影响具体业务,并且这种关系存在多种类型,那么这种多对多关系也应该表现为一个DO,又如:“角色”与“资源”之间存在多对多关系,而这种关系很明显会表现为一个DO——“权限”。
某些情况下,为了某种持久化策略或者性能的考虑,一个PO可能对应多个DO,反之亦然。例如客户Customer有其联系信息Contacts,这里是两个一对一关系的DO,但可能出于性能的考虑(极端情况,权作举例),为了减少数据库的连接查询操作,把Customer和Contacts两个DO数据合并到一张数据表中。反过来,如果一本图书Book,有一个属性是封面cover,但该属性是一副图片的二进制数据,而某些查询操作不希望把cover一并加载,从而减轻磁盘IO开销,同时假设ORM框架不支持属性级别的延迟加载,那么就需要考虑把cover独立到一张数据表中去,这样就形成一个DO对应对个PO的情况。
PO的某些属性值对于DO没有任何意义,这些属性值可能是为了解决某些持久化策略而存在的数据,例如为了实现“乐观锁”,PO存在一个version的属性,这个version对于DO来说是没有任何业务意义的,它不应该在DO中存在。同理,DO中也可能存在不需要持久化的属性。
DO与PO的应用
由于ORM框架的功能非常强大而大行其道,而且JavaEE也推出了JPA规范,现在的业务应用开发,基本上不需要区分DO与PO,PO完全可以通过JPA,Hibernate Annotations/hbm隐藏在DO之中。虽然如此,但有些问题我们还必须注意:
对于DO中不需要持久化的属性,需要通过ORM显式的声明,如:在JPA中,可以利用@Transient声明。
对于PO中为了某种持久化策略而存在的属性,例如version,由于DO、PO合并了,必须在DO中声明,但由于这个属性对DO是没有任何业务意义的,需要让该属性对外隐藏起来,最常见的做法是把该属性的get/set方法私有化,甚至不提供get/set方法,但对于Hibernate来说,这需要特别注意,由于Hibernate从数据库读取数据转换为DO时,是利用反射机制先调用DO的空参数构造函数构造DO实例,然后再利用JavaBean的规范反射出set方法来为每个属性设值,如果不显式声明set方法,或把set方法设置为private,都会导致Hibernate无法初始化DO,从而出现运行时异常,可行的做法是把属性的set方法设置为protected。
对于一个DO对应多个PO,或者一个PO对应多个DO的场景,以及属性级别的延迟加载,Hibernate都提供了很好的支持,请参考Hibnate的相关资料。
项目管理
2019-11-27 00:37:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
https://blog.csdn.net/youanyyou/article/details/82142014
项目管理
2019-11-18 17:52:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
https://blog.csdn.net/litianxiang_kaola/article/details/86646947
项目管理
2019-11-18 17:47:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
阿姆达尔定律 = Amdahl's Law,本篇重点是介绍该定律。
背景
系统架构中对系统性能设计的解决方案之一:“阿姆达尔方案”。书中还有个完全看不懂的公式,理解起来脑仁痛(不懂...)但发现很有价值,便系统的学习一下。 例题试先做一下,带着疑问点再看定律。
例题 :假设某一功能的处理时间为整体系统运行时间的60%,若使该功能的处理速度提高至原来的5倍,则根据阿姆达尔定律,整个系统的处理速度可提高至原来的多少倍?
定理公式
优化前系统总耗时To(old),优化后系统总耗时Tn(new),加速比S=To/Tn。 Speedup = timeOld / timeNew
详细公式: S = 1 / ((1-C) + C/S)
S(speedup),加速比。
C,原系统中能够改进的部分占总部分的比例,也可以说能够改进部分运行时间占总系统运行时间的比例,比如:75%。
S,改进后系统的提升比例,比如:5倍。
举例分解
上面的例子细化一下:程序A中子程序B的运行时间占A的60%,子程序B优化提升了5倍速度,那么程序A将多少?
程序A初始假设速度可为1,如下:
1/((1-0.6)+0.6/5) = 1/(0.4+0.12) = 1/0.52 = 1.923
Amdahl
阿姆达尔 Gene Amdahl,出生于1922年11月16日,出生在美国南达科他州的弗德鲁,是著名的企业家,创办多家公司。Amdahl曾经是研究并行处理系统的,1967年Amdahl推导出了固定负载情况下描述并行处理效果的加速公式。
P是并行率。若P=1,获取到无穷加速比;
定律的理解 本篇要说的是它的一种变形运用。
定义:系统中对某一部件采用更快执行方式所能获得的系统性能改进程度,取决于这种执行方式被使用的频率,或所占总执行时间的比例。
阿姆达尔定律 实际上 定义了采取增强(加速)某部分功能处理的措施后可获得的性能改进或执行时间的加速比。简单来说: 要想显著加速整个系统,必须提升全系统大部分的速度 。可以再简单些: 优化影响最大的部分 。
建议采用这样的思路来优化系统性能,也可以直接采用公式来量化改进效果。这个公式在操作上十分的简单很实用,可以运用在整个系统分析上,也可以在模块分析上,甚至是一个方法上。
友链支持
Gene Amdahl
百度定义 Amdahl's Law
参考博客: codingforspeed.com 、 ifeve.com
作者:Owen Jia <专注技术架构>
作者博客: https://blog.shareworld.vip
项目管理
2019-10-28 16:07:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
目前 Knative 中默认支持是基于域名的转发,但域名默认格式是:"{{.Name}}.{{.Namespace}}.{{.Domain}}"(这个可以在 config-network 配置)。但对于用户来说并不能指定全域名。
另外一个问题就是基于Path 转发的能力,很多情况下我们是基于一个域名,不同服务通过访问路径进行区分,但当前的Knative 默认是不支持设置 Path 转发能力。
针对这两个问题,我们在阿里云 Knative 中提供了这样的能力,用户可以通过控制台配置域名,并基于Path和Header进行转发规则设置。
配置域名
登录 阿里云容器服务控制台 ,进入【Knative】-【组件管理】,点击 Serving 组件【详情】:
进入详情之后,选择 域名配置 ,添加自定义域名:
这里添加域名需要注意,Knative服务默认创建的域名是不允许添加的。哪些是默认创建的域名呢? 其实在Knative 中每创建一个服务都会创建对应的 VirtualService, 如 helloworld-go 对应的 VirtualService 中默认域名如下: hosts: - helloworld-go.default - helloworld-go.default.svc - helloworld-go.default.svc.cluster.local
配置路由规则
登录 阿里云容器服务控制台 ,进入【Knative】-【服务管理】,选择对应的服务。这里我们选择 helloworld-go 服务:
进入服务详情之后,选择 路由转发 页签, 创建路由规则:
接下来我们分别说明基于路径和 Header 的转发。
基于路径转发
在路由规则中设置不通的路径实现基于路径的服务转发, 这里我们在 helloworld-go 服务中,选择 hello.serverless.kuberun.com 域名并设置 /test 路径:
访问页面: http://hello.serverless.kuberun.com/test 结果如下:
基于路径与Header转发
在 helloworld-go 服务中,选择 hello.serverless.kuberun.com 域名并设置 /test 路径, 并设置Header foo:bar :
这时候如果我们再次直接访问: http://hello.serverless.kuberun.com/test ,会发现无法访问。这时候我们需要设置Header(可以通过ModHeader 插件设置请求Header)。

》》阿里云双11亿元补贴提前领,进入抽取iPhone 11 Pro

阅读原文
本文为云栖社区原创内容,未经允许不得转载。
项目管理
2019-10-28 14:36:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>> git命令总结
Study: https://www.bilibili.com/video/av55780016
often
仓库克隆 git clone git@github.com:BenCoper/BeginJava.git(仓库地址)
查看状态 git status
设置用户 git config --global user.email ysjcode@gmail.com
设置用户名 git config --global user.name BenCoper
查看日志 git log
添加暂存 git add 文件名
推送仓库 git commit -m "添加提交信息"
密钥
创建密钥 ssh-keygen -t rsa -b 4096 -C "ysjcode@gmail.com"(邮箱地址)
密钥复制 clip < ~/.ssh/id_rsa.pub
冲突解决
查看commit事件 git show ....(commitID,日志中查看)
回滚暂存 git reset (后面可接commitID,回滚单个)
推送远端 git push
更新 git pull
团队协作
创建分支 git branch 分支名称
切换分支 git checkout 分支名称
设置远端分支 git push --set-upstrean orgin 分支名称
切换第二分支 git checkout -b 分支名称2
合并分支到master git merge 分支名称(1或2)
其他
忽略.idea文件 git文件内添加 .idea/ 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.
联系方式; ysjcode@gmail.com
项目管理
2019-10-25 22:33:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
出现Unsupported major.minor version 52.0,其实就是编译版本和Tomcat启动版本不符所导致的。
使用jdk1.8编译后,在tomcat6上运行会出现此编译错误。
将配置更改一致即可解决此问题。
参考:
https://www.cnblogs.com/jpfss/p/9036645.html
项目管理
2019-10-21 14:52:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
今天遇见了一个奇怪的问题,在IDE中run unit test,全部cases都成功了,但是后来通过mvn test运行case确保错了。在寻求原因的同时也找到了对应的解决方法。
Run Unit Test和Maven test的区别 差异1:在IDE中通过选中单元测试路径,点击右键选择run test和点击maven中的test是有区别的。在Maven执行测试的过程中,是不允许测试cases访问其他项目的测试类和其他项目的resources下文件的。也就是说,在a/src/test/java下的测试用例,是不能引用b/src/test/java中的类的,同时也不允许访问b/src/test/resources下的资源的。但是在IDE中的Run Unit Test几乎是没有这样的限制的。 差异2:Maven强制要求src/test/java下不能存在resource的文件,必须放到src/test/reources文件夹下,但是IDE却很少有对应的约束。
这些约束就是导致IDE下Run Unit Test是成功的,但是在Maven中失败的原因。
因此测者提醒,提交单元测试代码之前,一定要在本地mvn test一次脚本。
解决的办法
在maven插件配置:(surefire2.14以下版本)
  org.apache.maven.plugins maven-surefire-plugin 2.12 always
重点加入configureation的配置部分
在maven插件配置:(surefire2.14及其以上版本) org.apache.maven.plugins maven-surefire-plugin 2.19.1 false 1
在2.14以上的版本中,forkMode配置项已经废弃了。
另一个可能有效的方法
有时候在webapp项目中进行测试的时候,需要WEB-INF文件夹放在Class Path中,配置如下: org.apache.maven.plugins maven-surefire-plugin 2.19.1 false 1 -Xmx1024m -XX:MaxPermSize=256m -Dfile.encoding=UTF-8 -Xverify:none ${basedir}/src/main/webapp/‌WEB-INF/
关注测者,关注测试 关注我,关注测试 FROM: https://blog.csdn.net/crisschan
项目管理
2019-10-21 13:53:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
摘要
在 详解http报文 相关文章中我们介绍了http协议是如何工作的,那么构建一个真实的网站还需要引入组件呢?一些常见的名词到底是什么含义呢? 什么叫正向代理,什么叫反向代理 服务代理与负载均衡的差别 有了nginx,为啥还需要LVS 都有哪些负载均衡的方式
服务端演进
在前面文章中我们介绍过最简单的一种客户端-服务端响应模式,如下
这是http服务最简单的一种形式,服务端就一层web服务器。
现在我们服务端变复杂了,用户数增加了,并发量增加了。对我们服务端要求增加了 服务能力:一台服务器满足不了这么多的http的请求了。我们需要增加机器了,进行服务扩容了 安全防护:开始有人对我们的服务进行网络攻击了,需要保护服务端服务器,限制ip地址 网站升级: 网站上线后,需要提供7*24小时无间断服务了,发布新的版本,需要保证网站的可用。
代理服务
为了解决这些问题,我们需要引入 中间层 也就是代理,在客户端和服务端中间插入一个中间环节,代理服务。代理,狭义上讲就是不生产内容,只是转发上下游的请求和响应。
代理服务按照是否匿名可以分为 匿名代理: 外部不知道真实机器,只知道代理服务器 透明代理: 外界知道代理,也知道真实服务器
按照靠近客户端还是服务端,分为 正向代理: 代理客户端,代表着客户端向服务器端发送请求 反向代理: 代理服务端,代表着服务器向客户端发送请求。
http协议对代理的支持
因为http协议最开始并没有考虑代理服务,设计的协议只是针对客户端-服务器模式。根据我们通常的架构标准,http协议层是不用关心使用者是如何使用的,代理服务这种中间产物自然不用考虑。服务端有获取客户端ip的需求,所以Squid这个缓存代理软件最先引入 X-Forwarded-For 头字段,用来表示 客户端的真实 IP。
格式如下,从客户端到各个代理服务,记录下每一层的转发 X-Forwarded-For: client, proxy1, proxy2
这个需求是如此的普世,所以慢慢变成了标准,被各个代理服务广泛使用,所以后来被写入到 RFC 7239 标准之中了
代理协议
HTTP 协议本身对代理服务并没有什么说明,所以就衍生出了代理协议,代理协议是haproxy的作者Willy Tarreau于2010年开发和设计的一个Internet协议,通过为tcp添加一个很小的头信息,来方便的传递客户端信息(协议栈、源IP、目的IP、源端口、目的端口等),在网络情况复杂又需要获取客户IP时非常有用。 多层NAT网络 TCP代理(四层)或多层tcp代理 https反向代理http(某些情况下由于Keep-alive导致不是每次请求都传递x-forword-for) https通信加密,不允许修改原始报文
另外由于每一层代理服务都需要解析http header 头 X-Forwarded-For ,然后追加自己的地址,所以这个成本也比较高。所以代理协议也变成了 刚需 ,虽然是haproxy提出来的,但是也被各大代理服务器支持了,如nginx、apache、squid。代理协议格式 PROXY TCP4/TCP6 客户端ip 应答方ip 请求方端口号 应答方端口号 \r\n
这样请求方解析第一行就可以拿到客户端ip,不用再去处理http报文了。
负载均衡
负载均衡,其实就是分发请求。根据OSI七层协议
负载均衡分成两种 4层负载均衡,即工作在第四层传输层,利用ip地址端口进行请求转发,因为没有其他操作,所以效率比较高 七层负载均衡,即工作在第七层应用层,根据HTTP请求头,URL信息转发特定的主机。效率相对低一点。
nginx是七层负载均衡,LVS是七层负载均衡。
所以小型网站,nginx就足够,当流量足够大时,负载均衡成为瓶颈了,就可以在前面引入了LVS一层。
关于具体的负载均衡算法, 参考这边文章 ,这里不再赘述
安全防护
前面我们提到过安全防护也是代理服务的一大重要功能。为了应对外部攻击,需要引入网络防火墙,WAF(Web Application Firewall)。工作在OSI 第七层,主要是对http报文进行更细致的审核,也就是各种filter。 比如 IP 黑白名单 DDOS攻击 各种注入
当服务的安全性要求没那么高时,或者对公司业务发展的ROI没那么高时,我们通常就在nginx层面配置一些规则即可。需求升级时,我们就要引入专门的模型,比如 ModSecurity1 。需求再升级时,引入外部云厂商提供的WAF服务。
最终架构形式
http服务端架构演进和我们单应用架构演进有异曲同工之处。在业务不复杂的时候,可以使用单体模块搞定(比如Nginx),当请求量增加,需求升级时,需要引入中间层来解决。当某个模块要求增加时,需要解耦出单独的模块来处理。
所以整体上看,一个中型的服务端架构如下图。
参考
https://juejin.im/post/5ccaaf0af265da035e213490
https://www.cnblogs.com/xybaby/p/7867735.html
关注公众号【方丈的寺院】,第一时间收到文章的更新,与方丈一起开始技术修行之路
相关阅读 详解http报文
详解http报文(2)-web容器是如何解析http报文的
> 本文由博客一文多发平台 OpenWrite 发布!
项目管理
2019-10-20 20:45:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
现在二维码种类比较多,为了突出二维码的个性及吸引客户,很多朋友都喜欢在二维码上插入图片。想要每个二维码都与众不同,但是有的时候需要批量插入图片数量有限,如果制作的二维码比较多的话,就没图片可以插入了。这时我们可以利用条码打印软件中的脚本编程功能在二维码中循环批量插入以下29张图片,具体操作如下:

1.在条码打印软件新建标签绘制二维码对象之后,点击软件左侧的”插入位图图片按钮”,在二维码上绘制一个二维图对象,双击位图,在图形属性-数据源中,点击”+”号按钮,弹出”数据管理窗口”,点击浏览,弹出”打开”窗口,在打开窗口中,选择要插入的图片,点击打开,在点击添加。
在数据源中我们可以看到,图片路径已经被添加上了,点击”修改”按钮,在数据对象管理对话框中,我们可以把序列号和图片的后缀名去掉,点击编辑。
2.文件夹所有图片名称是有序列号和后缀名组成的,点击”+”号按钮,数据对象类型选择”序列生成”,开始字符串为0,点击添加
3.添上固定的后缀名,再点击”+”号按钮,数据对象类型选择”手动输入”,在下面的状态框中,输入.jpg,点击添加
选中左侧的序列号,在右侧的处理方法中,点击”+”号按钮,处理方法类型选择”脚本编程”,在脚本编程中把默认的strReturn = "123";修改成strReturn = strReturn%29+1;,
在点击”+”号按钮,处理方法类型选择”丢弃”,位置为右端,长度为2,点击添加。
设置好之后,可以在二维码下方绘制一个普通文本,双击普通文本,在图形属性-数据源中,点击”修改”按钮,数据对象类型选择”数据引用” ,引用图片的ID,具体操作可以参考:条码打印软件引用其他对象内容的方法。我们可以更直观的看到图片路径是否跟上图一致。
之后可以点击软件上方工具栏中的”打印预览”,看下预览效果。
友情提示:在二维码上循环批量插入图片使用脚本编程的时候,需要在数据对象中选择序列生成,然后在处理方法中,处理方法类型中选择脚本编程,不是选择.JPG,然后再设置脚本编程。
项目管理
2019-10-14 11:24:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
概述 目前阿里云官方对于微消息队列 MQTT提供了很多语言的参考示例,但是在实际的使用中发现很多用户在使用Android Sample的时候总是会遇到问题,无法正常调试使用。本文主要介绍Android Sample的使用。
具体操作步骤
1、accessKey,secretKey获取参考链接: 阿里云常见参数获取位置
2、serverUri、instanceId参数获取:
3、clientId参数的格式为:GroupId@@@DeviceName,GroupId需要在控制台预创建,DeviceName可以任意设置,确保不要重复即可。
4、Topic获取
具体参数的含义可以参考链接: 微消息队列 MQTT名词解释
5、Demo下载地址: lmq-andriod-demo
6、Android Config文件配置格式参考( 注意: serverUri前面需要加上 tcp://) package mqtt.demo; public class Config { public static final String serverUri = "tcp://post-cn-0pp13ag1111.mqtt.aliyuncs.com"; public static final String clientId = "GID_MQTTDemo@@@********"; public static final String instanceId = "post-cn-0pp13ag1111"; public static final String accessKey = "********"; public static final String secretKey = "********"; public static final String topic = "********"; }
7、Android测试运行日志
8、连接查询
参考链接
MQTT 快速入门
如何使用MQTT.fx连接微服务消息队列

阅读原文
本文为云栖社区原创内容,未经允许不得转载。
项目管理
2019-09-27 14:06:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
1 背景概述
在软件行业飞速发展的今天,我们可以将软件公司分大体分为两类,一类是使用框架进行开发的软件公司,另一类是套装软件产品的提供商,前者公司多数定位是项目类公司,后者则可以称为产品类公司。但做产品与做项目有哪些区别,大多数的人面对这个问题还是较为模糊的,甚至简单认为两者是没有区别的,均是程序开发而已。但事实并非如此,做产品与做项目两者之间既存在本质的区别,也存在着紧密的联系,今天笔者在这里将自己理解与大家分享。
2 定义及周期
2.1 项目定义
项目: 是指在一定的约束条件下(主要是限定时间、限定资源),具有明确目标的工作任务。
软件项目: 是指为企业开发或者部署实施一套专用的系统,或在特定的行业领域做一些系统之间的集成,在进入项目之前必须与用户进行具体的交流和讨论,了解清楚用户心目中的产品或项目预期是什么样子,然后招投标、签订合同、实施交付。
2.2 项目周期
软件项目的生命周期是软件的产生直到报废的过程,包括项目的启动、需求调研、功能设计、业务开发、项目测试、项目验收交付给用户,项目结项后项目生存周期结束。随着时间的推移以及发展,为满足当前发展的需求项目通常会重新定义开发。软件项目的生命周期图如下:
2.3 产品定义
产品: 是指能够提供给市场,被人们使用和消费,并能满足人们某种需求的任何东西,包括有形的物品、无形的服务、组织、观念或它们的组合。
软件产品: 是指向用户提供的计算机软件、信息系统、套装软件或在提供计算机信息系统集成、应用服务等技术服务时提供的软件,是通用的产品应用于某一行业领域而不是像软件项目一样为某一需求或者单位定制开发。
2.4 产品周期
软件产品的生存周期类似于人的成长,从出生(产品构思),到成长(产品的版本更新),到去世(产品中止)的过程。产品不存在完成的说法,因为产品是不断更新的,直到被新产品替代,生存周期才结束。软件产品的生命周期图如下:
3 区别与联系
3.1 两者区别
做产品与做项目的区别,大多数人可能都认为两者是相同的,均是使用不同的编程语言进行项目/产品的开发,但其实做产品与做项目两者是存在本质性区别的,下面我们通过两者的出发点,品质要求,时间成本,任务分工,团队构成以及最终的结果导向来分别阐述两者的区别。
3.1.1 驱动因素 项目的驱动因素: 做项目侧重于时间驱动,因为时间就是成本,要压缩成本就要压缩时间,在功能上力求操作敏捷、易用、友好,如果在项目时间紧迫的情况下,至少要能保证每个功能都好用、不出现BUG。 做项目是以客户的需求为驱动,按照客户的需求进行定制开发,对于不明确的需求要第一时间与客户进行沟通,不可存在需求不明确的现象。 产品的驱动因素: 做产品侧重于功能驱动,做产品的时间相对来说比较充足,所以要以开发出有竞争力、受广大客户欢迎的产品为原则,功能响应速度要快,操作要简便、界面要美观。 做产品是为了满足某一应用市场而针对性进行一套装软件或一个产品的开发,对于产品的性能以及快速迭代扩展的要求更高,产品的需求也并不像软件项目一样完全明确,存在着后期根据需求、迭代升级的情况。
3.1.2 质量要求 项目的质量要求: 做项目的第一准则是客户的需求,项目的开发人员需要依据客户的需求进行定制开发,并且项目需要保证功能适用于当前客户的使用习惯,性能稳定。 项目的质量更加侧重于某一客户的具体需求,保证交付的软件项目程序可运行、维护,需求功能可实现。 产品的质量要求: 产品的质量要求更加侧重于某一行业领域的应用场景,某一款产品需要适应或囊括当前行业可能涉及的需求,所能匹配的应用性更为广泛,并且对产品逻辑、代码运维性的要求更高。 做产品的性能必须尽量优化,因为产品为提升竞争力就必须比同类产品更好用,更敏捷,而且产品是一个不断完善升级的过程,对代码的框架以及维护性都具有更高的要求。
3.1.3 时间投入 项目的时间投入: 做项目的时间投入一般是根据项目的需求,进行评估。通常是从项目启动、需求调研、功能设计、业务开发、测试运行、验收交付为一个周期。 项目有明确时间约束,什么时候开始,什么时候结束,每个节点都需要一目了然。通常以项目的验收单作为分项的里程碑及整体验收单作为项目的交付证明。 产品的时间投入: 做产品的时间相对来说比较长,产品通常更加关注的是整个产品的规划、开发、推广、维护等。 产品时间一般来说可以明确开始时间却不能明确真正的结束时间,因为产品是一直在进行迭代完善的过程,通常会通过不同的产品版本来区分维护、优化、升级。
3.1.4 工作区分 项目的工作区分: 做项目通常将工作按照业务功能模块划分,明确每个模块的负责人,各负责人梳理负责功能的需求,把握当前工作进度。 做项目的核心人物是项目经理,项目经理需要与客户经常沟通交互,之后由项目经理统一进行任务分工与安排,不定时的进行代码review、项目内部沟通会议以及项目进度的推进把控。 产品的工作区分: 做产品更加关注的是整个产品的生命周期,明确每个功能的负责人,将重点放在产品的功能完善中。 做产品的核心人物是产品经理,产品经理负责把控产品的整体研发方向,带领研发团队进行产品研发,并且在产品研发过程中注重考虑产品的扩展性、兼容性、安全性、推广性。
3.1.5 团队构成 项目的团队构成:
做项目团队一般由项目经理、架构设计师、需求分析员、软件工程师、测试工程师构成。 产品的团队构成:
做产品团队一般由产品经理、交互设计师、项目管理人员、开发人员、运维人员构成。
3.1.6 结果导向 项目的结果导向: 项目的结果是以项目的验收单为基准,项目主要是满足客户特定的需求,体现个性化。 项目多数是针对某一企业或客户的,功能相对特殊化、单一化、比较有针对性,项目在产品开发周期中可做为产品的雏形。 产品的结果导向: 做产品最终的结果是给用户使用的,成功的产品将会被大面积普及应用。 产品是面向大众的,更侧重市场的需要,有广泛性。产品相对而言有比较固定的价格。
3.2 内在联系
做产品与做项目有区别的同时也存在着联系,很多时候两者之间是没有明显的界限的,项目中包含产品、产品应用于项目。下面我们通过两者间的共同目标、使用相同的开发语言、具有重合的应用场景、实施与维护、均可以为公司获取经济利益等方面来分别阐述。
3.2.1 相互融合 当前的软件项目中,做项目可以作为做产品的原型,做产品也可以应用于项目之中。两者之间是没有明显界限的,项目中包含产品,产品应用于项目。 当前的项目存在于传统企业中,而当前互联网厂商以及金融企业(如:银行、证券等)内部将应用系统统称为产品,该项目为自己内部开发,或者只有少部分的资源是外部进行开发。 在软件开发商中如:电商企业对于企业开发的网上商城等应用系统,将项目作为产品来开发,统称为电商平台产品。
3.2.2 共同目标 做项目与做产品两者的目标是相同的,均是为了更好的为客户服务,满足客户的需求,帮助客户进行信息化平台的建设。 无论是做项目还是做产品两者的终极目的均是为了公司获取经济利益,支撑公司的运营与发展、让公司股东、员工都从其中受益。
3.2.3 开发语言 无论是做项目还是做产品均是由开发人员根据不同的需求进行开发/研发的,同一系列的项目与产品是使用相同的开发语言进行开发的。 在做项目与做产品的过程中均是由需求调研、产品/功能设计、程序开发、调整、完善、软件测试流程组成。 无论是做项目还是做产品均是由项目/产品的负责人进行工作划分,都要明确负责人,细分至事、人、天,保证最终目标顺利完成。
3.2.4 应用场景 在项目中包含产品同时,产品应用于项目之中,但做项目中不一定会百分百有产品,或者说不是所有项目都能(会)孵化产品。 无论是做项目还是做产品均要应用于XXX客户平台中心,将项目/产品部署于客户提供的服务器中,为客户更好的应用。
3.2.5 实施维护 无论是做项目还是做产品在交付客户之后均需要开发/研发人员进入客户现场对客户方的技术人员/使用人员进行培训。 无论是做项目还是做产品都需要交付客户相应的维护文档以及使用手册,对当前的交接人员进行实施交付。 无论是做项目还是做产品都需要开发/研发人员进行实施维护,对项目/产品进行为期一年(可根据实施情况)的维护。
3.2.6 经济收益 无论是做项目与做产品都可以为对应的参与人员都会获得相应的收益,产品项目/产品的开发人员不仅可以获取相应的奖金,也可以获取对应的个人能力的提升。 无论是好的项目还是好的产品交付客户以后都可以为公司带来品牌效益,可以通过客户之间的相互介绍为公司带来更多的项目/产品的收益。
4 能力的要求
沈阳数通畅联定位是SOA集成产品和技术解决方案提供商,但是现阶段很多集成项目都是由数通畅联自己来交付。项目经理是项目交付的核心人物,产品经理是产品开发的核心人员,有些公司的产品产品经理不开发,主要工作是通过产品市场来分析进行功能设计、项目经理不写代码只做沟通管理、项目推进等工作。但在数通畅联项目经理和产品经理都有代码开发工作,比如:项目经理负责整个项目设计、评审以及通用功能的开发,产品经理则负责产品整体升级、核心功能开发。
在沈阳数通畅联:项目经理跟产品经理角色在公司里都非常重要,但二者能力模型是略有不同,通常来说产品经理比项目经理高一个级别,产品经理必须要经历项目经理,比如说:初级产品经理跟中级项目经理级别相当,但不是所有的项目经理都一定能成为产品经理,也不是产品经理一定比项目经理更厉害或者说更重要,不能一概而论。下面我们将分别对项目经理、产品经理的工作侧重点进行对比介绍:
4.1 项目经理
4.1.1 沟通交流 作为项目经理80%的时间在沟通,剩余20%的时间在准备沟通,不仅是与客户沟通、领导沟通,与项目组成员也需要时常沟通保证项目的进度。 在与客户沟通的时候,更多是倾听对方的需求,不是马上给出答复,分析之后再给出答复。 在与领导或者同事沟通时,有问题自己思考之后,准确描述问题,快速暴漏问题,这样才能辅助你解决问题。
4.1.2 项目推进 项目经理需要每周撰写周总结,说明当周的工作内容,下周计划内容以及后续计划安排。每天早上询问当天的工作内容将可能遇见的技术点进行讲解,如果没有整体时间进行工作事项确认时,可以分项进行,将工作分解分项进行。 在项目的前期需要明确开发规范,强调开发规范,命名规则。前期每天、后期每周三、五进行代码review。 根据每位员工的个人能力的不同,分别进行任务分配,将功能分工明确至事、人、天。这样可以进一步的保证当前任务是可考核的进而保证项目的进度是可控的。
4.1.3 促进验收 项目的最终目的就是为了验收,项目经理需要时刻保持验收的心态,积极促进项目的验收,不是将当前工作全都做完才能提出验收,而是项目进入验收阶段与客户沟通项目相关的验收事宜。 项目经理需要引导客户结款的意识,不要客户说什么就是什么,要明确与钱挂钩(常用话术:这个在需求阶段并没有提出,而且整体的流程是需要梳理明确的,这个会涉及到商务的事情,需要和商务具体来谈)。
4.2 产品经理
4.2.1 整体把握 作为产品经理需要能够站对市场前景、应用场景、方案组合、盈利模式都有清晰的认知,能够从用户的角度去思考,深度挖掘用户的深层次的需求,善于抽象、归纳把用户需求转换为功能需求。 明确产品最终要在项目中使用,产品设计要能够满足多方的需求,如:满足开发人员的需求:开发快、易部署、易调试、易扩展;满足客户:界面美观、操作简单、流程合理、稳定坚挺不宕机;满足运维人员:操作简单、日志层次清晰、易定位、能够快速恢复、调优等等。 产品经理不但需要知道哪些需求能满足产品的目标,还要深刻的分析哪些需求应该做,哪些需求不应该做,这些需求优先级是什么,应该如何开展、进度规划,以及相关资源协调(美工人员、开发人员、测试人员)等。
4.2.2 产品研发 俗话说的好“打铁需要自身过硬”,作为产品经理对于技术能力的要求高于项目经理,对于代码有较高的敏感度,能够从全局来出发把控产品的可扩展性、可维护性等。 作为产品经理是整个团队的技术依靠,负责解决产品研发过程中的技术攻关以及“疑难杂症”,对新技术知识的要能够快速学习能力、掌握、运用。 在产品研发的过程难免会有迭代完善的过程,通常程序出错是常见的事情,产品经理要能够对于产品运行机制了然于心、代码排错能力要技高一筹。
4.2.3 技术支撑 产品经理需要对相关技术人员(内部、外部)提供技术支持,包括:产品培训、技术支持、BUG修复等。 产品经理需要对同行产品以及解决方案熟悉了解,能够充当售前协作市场人员进行产品宣讲、方案介绍等。 产品经理需要对前沿技术有高度敏感性、对技术来龙去脉、发展趋势有准确认知,能够作为公司内部技术人员的布道者,推进公司技术持续前进。
5 心得与体会
笔者作为数通畅联的一名技术人员,也曾担任过几个项目的项目经理,最近也参与对公司产品的完善调整、产品升级工作,故此将做项目与做产品的区别进行总结与大家分享。笔者认为项目是满足特定人群或者使用者需求,更偏向于个性化;而产品是满足特定应用场景或者特定行业领域的需求,更加具有兼容性。不管是项目经理、还是产品经理都要时刻提醒自己是负责人,尽职尽力,团队的事情就是自己的事情,拒绝鸵鸟心态,逃避不能解决问题,需要勇于面对问题,积极解决问题。
无论是做项目经理还是做产品经理自身的能力的提升是必不可少的,做事要目标驱动,明确任务的优先级,紧急有重要的事情优先做,重要不紧急的一直做,然后做紧急但不重要的,不重要也不紧急的最后做。贵在坚持,没有量的积累无法实现质的飞越,从0到70分很容易,从70到80分只要稍微努力,从80到90分就很困难了,从90到95分便更加困难,最终能达到95-99分更是寥寥无几。在职场中的生存就是“逆水行舟,不进则退”,面对日益激烈的竞争环境只有不断的提升自身的稀缺性,努力向前航行才可以到达成功的远方。
项目管理
2019-09-26 11:04:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
我在2017年5月加入饿了么的交易部门,先后负责搜索、订单、超时、赔付、条约、交付、金额计算以及评价等系统,后期开始做些整体系统升级的工作。
这篇文章成型于交易系统重构一期之后,主要是反思其过程中做决策的思路,我没有使用「架构」这个词语,是因为它给人的感受充满权利和神秘感,谈论「架构」让人有一种正在进行责任重大的决策或者深度技术分析的感觉。
如毕玄在系统设计的套路这篇文章里所提: 回顾了下自己做过的几个系统的设计,发现现在自己在做系统设计的时候确实是会按照一个套路去做,这个套路就是:系统设计的目的->系统设计的目标->围绕目标的核心设计->围绕核心设计形成的设计原则->各子系统,模块的详细设计
在进行系统设计时,摸清楚目的,并形成可衡量的目标是第一步。
"Soft" ware Software拆开来分别是soft ware,即灵活的产品 -- 鲍勃大叔
重构前的交易系统第一版的代码可以追溯到8年前,这期间也经历过拆解重构,17年我来到时,主要系统是这样:


这套系统驮着业务从百万级订单跑到了千万级订单,从压测表现来看,它可以再支撑业务多翻几倍的量,也就是说如果没有啥变化,它可以继续稳定运行着,但如果发生点变化呢,答案可能就不这么肯定了。
在我入职的这两年里,系统承载的业务迭增变化:从单一的餐饮外卖到与新零售及品牌餐饮三方并行,又从到家模式衍生至到店,随之而来的是业务持续不断的差异化定制,还有并行上线的要求。另一面,随着公司组织架构变化,有的项目需要三地协同推进才能完成,沟通协作成本翻倍提升。几方面结合起来,导致开发没有精力对大部分系统的演进都进行完善的规划。
几个月前,业务提了一个简单的需求:对交易的评价做自动审核并进行相应的处罚。当时评价核心“域模型”是这样的:

设计自身的优劣这里暂不进行讨论,只是举例说明为了满足这个诉求,会涉及多个评价子模块要改动,开发评估下来的工作量远远超出了预期,业务方对此不满意,类似的冲突在其他系统里也经常出现。但实际上,团队里没人偷懒,和之前一样努力工作,只是不管投入了多少个人时间,救了多少次火,加了多少次班,产出始终上不去,因为开发大部分时间都在系统的修修补补上,而不是真正完成实际的新功能,一直在拆东墙补西墙,周而往复。
为什么会导致这样的结果,我想应该是因为大部分系统已经演变到很难响应需求的变更了,业务认为的小小变更,对开发来说都是系统的一次大手术,但系统本不应该往这个方向发展的,它和hardware有着巨大的区别就在于:变更对软件来说应该是简单灵活的。
所以我们思考设计的核心目标:**“采用好的软件架构来节省项目构建和维护的人力成本,让每一次变更都短小简单,易于实施,并且避免缺陷,用最小的成本,最大程度地满足功能性和灵活性的要求”。
Source code is the design
提到软件设计,大家脑袋里可能会想到一幅幅结构清晰的架构图,认为关于软件架构的所有奥秘都隐藏在图里了,但经历过一些项目后发现,这往往是不够的。Jack Reeves在1992年发表了一篇论文《源代码即设计》,他在文中提出一个观点: 高层结构的设计不是完整的软件设计,它只是细节设计的一个结构框架。在严格地验证高层设计方面,我们的能力是非常有限的。详细设计最终会对高层设计造成的影响至少和其他的因素一样多(或者应该允许这种影响)。对设计的各个方面进行改进,是一个应该贯穿整个设计周期的过程
在踩过一些坑之后,这种强调详细设计重要性的观点在我看来很实在接地气,简单来说:“自顶向下的设计通常是不靠谱的,编码即是设计过程的一部分”,个人认为:系统设计应该是从下到上,随着抽象层次的提升,不断演化而得到良好的高层设计。
编程范式
从下向上,那就应该从编码开始审视,饿了么交易系统最开始是由Python编写,Python足够灵活,可以非常快速的产出mvp的系统版本,这也和当时的公司发展状态相关: 产品迭代迅速,新项目的压力很大。
最近这次重构,顺应集团趋势,我们使用Java来进行编写,不过在这之前有一个小插曲:17年底,因为预估到当前系统框架在单量到达下一个量级时会遇到瓶颈,所以针对一些新业务逐渐开始使用Go语言编写,但在这个过程里,经常会听到一些言论:用go来写业务不舒服。为什么会不舒服?大致是因为没有框架,没有泛型,没有try catch,确实,在解决业务问题的这个大的上下文中,go语言不是最优的选择,但语法简单,可以极大程度的避免普通程序员出错的概率。
那么Python呢,任何事物都有双刃剑,虽然Python具有强表达力,但是灵活性也把很多人惯坏了,代码写的糙,动态语言写太多坑也多,容易出错,在大项目上的工程管理和维护上有一定劣势,所以rails作者提到:“灵活性被过分高估——约束才是解放”也有一定道理
为避免引起语言战,这里不过多讨论,只是想引出:我从C++写到Go,又从Python写到Java,在这个过程里体会到--编程范式也许是学习任何一门编程语言时要理解的最重要的术语,简单来说它是程序员看待程序应该具有的观点,但却容易被忽视。交易老系统的代码,不管是针对什么业务逻辑,几乎都是OPP一杆到底,类似的代码在系统里随处可见。
我们好像完全遗忘了OOP,这项古老的技艺被淡化了,我这里不是说一定要OOP就是完美的,准确来说我是“面向问题”范式的拥趸者,比如,Java从骨子里就是要OOP,但是业务流程不一定需要OOP。一些交易业务就是第一步怎么样,第二步怎么样,采取OPP的范式就是好的解法。这时,弄很复杂的类设计有时并不必要,反而还会带来麻烦
此外,同一个问题还可以拆解为不同的层次,不同的层次可以使用各自适合的方式。比如高层的可以OOP,具体到某个执行逻辑里可以用FP,比如:针对订单的金额计算,我们用Go写了一版FP的底层计算服务,性能高、语法简单以及出错少等是语言附带的优点,核心还是因为该类问题自身适合。
然而,当面向整个交易领域时,针对繁复多样的业务场景,合理运用OOP的设计思想已经被证明确实可以支撑起复杂庞大的软件设计,所以我们作出第一个决策:采用以OOP为主的“混合”范式。
原则和模式 The difference between a bad programmer and a good one is whether he considers his code or his
data structures more important. Bad programmers worry about the code. Good programmers worry about data structures and their relationships. -- Linus Torvalds
不管是采用哪种编程范式、编程语言,构造出来的基础模块就像盖楼的砖头,如果砖头质量不好,最终大楼也不会牢固,引用里的一大段话,relationships才是我最想强调的:我理解它是指类之间的交互关系,“关系”的好坏通常等价于软件设计的优劣,设计不好的软件结构大都有些共同特征: 僵化性:难以对软件进行改动,一般会引发连锁改动,比如下单时增加一个新的营销类型,订单中心和相关上下游都要感知到并去做改动 脆弱性:简单的改动会引发其他意想不到的问题,甚至概念完全不相关 牢固性:设计中有对其他系统有用的部分,但是拆出来的风险和成本很高,比如订单中心针对外卖场景的支付能力并不能支持会员卡等虚拟商品的支付需求 不必要的复杂性:这个通常是指过度设计 晦涩性:随时间演化,模块难以理解,代码越来越难读懂,比如购物车阶段的核心代码已经长成了一个近千行的大函数 ...
采取合适的范式后,我们需要向上抽一个层次,来关注代码之上的逻辑,多年软件工程的发展沉淀下来了一些基本原则和模式,并被证明可以指导我们如何把数据和函数封装起来,然后再把它们组织起来成为程序。
SOLID
有人将这些原则重新排列下顺序,将首字母组成SOLID,分别是:SRP、OCP、LSP、ISP、DIP。这里针对其中几个原则来举些例子。
SRP(单一职责):这个原则很简单,即任何一个软件模块都应该只对一类用户负责,所以代码和数据应该因为和某一类用户关系紧密而被组织到一起。实际上我们大部分的工作就是在发现职责,然后拆开他们。
我认为该原则的核心在于用户的定义,18年去听Qcon时,听到俞军的分享,其中一段正好可以拿来诠释什么是用户,俞军说:“用户不是人,是需求的集合”。在我们重构的过程中,曾经对交易系统里的交付环节有过争论,目前饿了么支持商家自配和平台托管以及选择配送(比如跑腿),这几类配送的算价方式,配送逻辑,和使用场景都不一样,所以我们基于此做了拆解,一开始大家都认同这种分解方式。
但后来商户群体调整了,新零售商户和餐饮商户进行分拆,对应着业务方的运营方式也开始出现差异,导致在每个配送方式下也有了不同诉求,伴随这些变化,最后我们选择做了第二次拆解。
对于单一职责,这里有个小tips:大家如果实在不好分析的话,可以多观察那些因为分支合并而产生冲突的代码,因为这很可能是因为针对不同需求,大家同时改了同一个模块。
DIP(依赖倒置):有人说依赖反转是OOP和OPP的分水岭,因为在过程化设计里所创建的依赖关系,策略是依赖于细节的--也就是高层依赖于底层,但这通常会让策略因为细节改变而受到影响,举个例子:在外卖场景下,一旦用户因为某些原因收不到餐了,商户会赔代金券安抚用户,此时OPP可以这样做:

而过一阵子,因为代金券通常不能跨店使用,平台想让用户继续复购,就想通过赔付通用红包来挽留,这个时候就需要改动老的代码,通过增加对红包赔付逻辑的依赖,才可以来满足诉求。
但如果换个方式,采用DIP的话,问题也许可以被更优雅的解决了:

当然这个示例是简化后的版本,实际工作里还有很多更加复杂的场景存在,但本质都是一样:采用OOP倒置了策略对细节的依赖,使细节依赖于抽象,并且常常是客户拥有服务接口,这个过程中的核心是需要我们做好抽象。
OCP(开闭原则):如果仔细分析,会发现这个原则其实是我们一开始定的系统设计的目标,也是其他原则最终想达成的目的,比如:通过SRP,把每个业务线的模块拆解出来,将变动隔离,但是平台还要做一定的抽象,将核心业务流程沉淀下来,并开放出去每个业务线自己定义,这时候就又会应用到DIP。
其他的几个原则就不举例子了,当然除了SOLID,还有其他类型的原则,比如IoC:用外卖交易平台举例子,商户向用户卖饭,一手交钱一手交货,所以,基本上来说用户和商户必需强耦合(必需见面)。这个时候,饿了么平台出来做担保,用户把钱先垫到平台,平台让商家接单然后出餐,用户收到餐后,平台再把钱打给商家。这就是反转控制,买卖双方把对对方的直接依赖和控制,反转到了让对方来依赖一个标准的交易模型的接口。
可以发现只要总结规律,总会出现这样或那样的原则,但每个的原则的使用都不是一劳永逸的--需要不断根据实际的需求变化做代码调整,原则也不是万金油,不能无条件使用,否则会因为过分遵循也会带来不必要的复杂性,比如经常见到一些使用了工厂模式的代码,里面一个new其实就是违反了DIP,所以适度即可。
演进到模式
这里的模式就是我们常说的设计模式,用演进这个词,是因为我觉得模式不是起点,而是设计的终点。《设计模式》这本书的内容不是作者的发明创造,而是其从大量实际的系统里提取出来的,它们大都是早已存在并已经广泛使用的做法,只不过没有被系统的梳理。换句话说,只要遵循前面叙述的某些原则,这些模式完全可能会自然在系统代码中体现出来,在《敏捷软件开发》这本书里,就特意有一个章节,描述了一段代码随着调整慢慢演进到了观察者模式的过程。
拥有模式固然是好的,比如搜索系统里,通过Template Method模式,定义一套完整的搜索参数解析模版,只需要增加配置就可以定制不同的查询诉求。这里最想强调的是不要设计模式驱动编程,拿交易系统里的状态机来举例子(状态机简直太常见了,简单如家里使用的台灯,都有一个开和关的状态,只是交易场景下会更加复杂),在餐饮外卖交易有如下的状态流转模型:

实现这样的一个有限状态机,最直接的方式是使用嵌套switch/case语句,简略的代码比如: public class Order { // States public static final int ACCEPT = 5; public static final int SETTLED = 9; .. // Events public static final int ARRIVED = 1; // 订单送达 public void event(int event) { switch (state) { case ACCEPT: switch (event) { case ARRIVED: state = SETTLED; //to do action break case } } } }
因为是简写了流程,所以上面的代码看起来还是挺能接受的,但是对于订单状态这么复杂的状态机,这个switch/case语句会无限膨胀,可读性很差,另一个问题是状态的逻辑和动作没有拆开,《设计模式》提供了一个State 模式,具体做法是这样:

这个模式确实分离了状态机的动作和逻辑,但是随着状态的增加,不断增加State的类会让系统变得异常复杂,而且对OCP的支持也不好:对切换状态这个场景,新增类会引起状态切换类的修改,最不能忍受的是这个方式会把整个状态机的逻辑隐藏在零散的代码里。
旧版的交易系统就使用的是解释迁移表来实现的,简化版本是这样的: # 完结订单 add_transition(trigger=ARRIVED, src=ACCEPT, dest=SETTLED, on_start=_set_order_settled_at, set_state=_set_state_with_record, // 变更状态 on_end=_push_to_transcore) ... # 引擎 def event_fire(event, current_state): for transition in transitions: if transition.on_start == current_state && transition.trigger == event: transition.on_start() current_state = transition.dest transition.on_end()
这个版本非常容易理解,状态逻辑集中在一起,也没有和动作耦合起来,扩展性也比较强,唯一缺点的话是遍历的时间,但也可以通过字典表来优化,但它总体带来的好处更加明显。
不过随着业务发展,交易系统需要同时支持多套状态机,意味着会出现多个迁移表,而且还有根据业务做扩展定制的需求,这套解决方案会导致代码编写变得复杂起来,我们在重构时采用了二级编排+流程引擎的方式来优化了这个问题,只是不在我们讨论的范围内,这里只想强调第二个决策:代码上要灵活通过设计原则分析问题,再通过合适的设计模式解决问题,不能设计模式驱动编程,比如有时候一个全局变量就可以替代所谓的单例模式。
丰富的领域含义 一旦你想解说美,而不提拥有这种特质的东西,那么就完全无法解释清楚了
用个不那么贴切的说法,如果前面说的是针对静态问题的策略,现在我们需要讨论面对动态问题的解决办法:即使没有风,人们也不会觉得一片树叶是稳定的,所以人们定义稳定的时候和变更的频繁度无关,而是和变更需要的成本有关,因为吹一口气,树叶就会随之摇摆了。我们除了要写好当前代码,让其足够清晰合理,还要能写好应对需求变化的“树叶”代码。
面向业务变化的设计首先就是要理解业务的核心问题,进而进行拆解划分为各个子领域,DDD--也就是领域驱动设计,已经被证明是一个很好的切入点。这里不是把它当作技术来学习,而是作为指导开发的方法论,成为第三个决策,并且我个人仍处在初级阶段,所以只说一些理解深刻的点。
通用语言
设计良好的架构在行为上对系统还有一个最重要的作用:就是明确的显式的反映系统设计的意图,简单来说,在你拉下某些服务的代码的时候,大概扫一眼就可以觉得:嗯,这个“看起来” 就像一个交易系统的应用。我们不能嘴上在谈论业务逻辑,手上却敲出另一份模样的代码,简单来说,不能见人说人话,见鬼说鬼话。可以对比一下这两类分包的方式,哪一个更容易理解:


发现领域通用语言的目的之一是可以通过抓住领域内涵来应该需求变更,这个需要很多客观条件,比如团队里有一个领域专家。但没有的时候,我们也可以向内求解,**我有次看到一位在丁香园工作的程序员朋友,购买了一大批医学的书籍,不用去问,我就猜他一定是成了DDD的教徒。
针对这个点,我们这次重构时还做了些让“源代码即设计”的工作:领域元素可视化,当系统领域内的一些概念已经和产品达成一致之后,便增加约定好的注解,代码编译时便可以扫描并收集起来发送给前端,用于画图。
回到前面提到的评价域模型,后来在和产品多次沟通后意识到,产品没有希望评价这么多种类,对它来说商品也好、骑手也好,都属于被评价的对象,从领域模型来看,之前的设计更多是面对场景,而不是面对行为,所以合理的域模型应该是:

限界上下文
这个在我们平时开发过程中会很常见。拿用户系统举例:一个User的Object,如果是从用户自身的视角来看,就可以登陆、登出,修改昵称;如果是从其他普通用户来看,就只能看看昵称之类的;如果从后台管理员来看,就可以注销或者踢出登陆。这时就需要界定一个Scope,来说明现在的User到底是哪个Scope,这其实就是DDD中限界上下文的理念。
限界上下文可以很好的隔离相同事物的不同内涵,通过严格规范可以进入上下文的对象模型,从而保护业务抽象行为的一致性,回到交易领域,饿了么是最开始支持超级会员玩法的,为了支持对应的结算诉求,需要接入交易系统来完成这个业务,我们通过分解问题域来降低复杂度,这个时候就对应切割为会员域和交易域,为了保护超会卡在进入交易领域的时候,不扰乱交易内部的业务逻辑,我们做了一次映射:

切分
当所有代码完成之后,随着程序增长,会有越来越多的人参与进来,为了方便协作,就必须把这些代码划分成一些方便个人或者团队维护的组。根据软件变更速度不同,可以把上文提到的代码化为几个组件: Extension:扩展包,这里存放着前面提到的业务定制包,面向对象的思想,最核心的贡献在于通过多态,允许插件化的切换一段程序的逻辑,其实软件开发技术发展的历史就是一个想法设法方便的增加插件,从而创建一个可扩展,可维护的系统架构的过程。 Domain: 领域包,存放着具备领域通用语言的核心业务包,它最为稳定。 Business:业务包,存放着具体的业务逻辑,它和Domain包的区别在于,可能Domain包会提供一个people.run()的方法,他会用这个方法去跑着送外卖,或者去健身。 Infra: 基础设置包,存放这对数据库及各种中间件的依赖,他们都属于业务逻辑之外的细节。
然后是分层依赖,Martin Flower已经提供了一套经典的分层封装的模式,拿简化的订单模块举例:

然而如果有的同学避免做各种类型的转换,不想严格遵守分层依赖,觉得一些查询(这里指Query,Query != Read)可以直接绕过领域层,这样就变成了CQRS模式:

但是最理想的还是下面这种方式,领域层作为核心业务逻辑,不应该依赖基础设施的细节,通过这种方式,代码的可测性也会提升上去

单体程序的组件拆分完毕后,再向上一层,我们开始关注四个核心服务:Booking被分拆为Cart、Buy、Calculate,Eos被分拆为Procee、Query、Timeout,Blink一部分和商户订单相关的功能被分拆到Process、Query,和物流交付的部分单独成一块Delivery,最后交易的核心服务拆解成下图:


到目前,算上这个切分的方式,加起来一共就四个决策,其实也没必要分序列,它们核心都是围绕着软件灵活性这个目标,从程序范式到组件编写,最后再到分层,我们主动选择或避开的一些教条限制,所以业务架构从某种意义上来讲,也是在某种领域中限制程序员的一些行为,让他往我们所希望的规范方向编码。从而达到整个系统的灵活可靠。
"No Silver Bullet" “个体和交互胜过过程和工具”,敏捷宣言第一条
目前系统架构是什么样子并不重要,因为它可能会随着时间还会拆解成其他模样,重要的是,我们要认识到对于如何建造一个灵活的交易系统——没有银弹。
如果仔细观察的话,会发现当前系统里仍有很多问题等着被解决。比如一些横跨型变更:系统链路里会因为某个服务的接口增加了字段,而导致上下游跟着一起改。更为尴尬的是,本来我们拆分服务就是为了解耦合,但有时还会出现服务发布依赖的现象。系统演进是一场持久的战争,“个体和交互胜过过程和工具”,人才是胜利的核心因素。
过去的两年里,我们没有停止过思考和实践,经常可以看到交易团队内部成员的争执,小到一个接口字段变更,大到领域之间的边界,大家为拿到一个合理的技术方案做了很多讨论,这让我想起《禅与摩托车维修艺术》里所提到的良质,有人点评说:关于良质,程序员可能有这样的经历——写出了一段绝妙的代码,你会觉得“不是你写出了代码,这段代码一直存在,而你发现了它”。
本文作者:盛赫,花名白茶,就职于阿里本地生活中台研发部,多年交易系统建设开发经验,目前转入营销领域继续探索。
参考书籍
《软件设计的哲学》--John Ousterhout
《禅与摩托维修艺术》--Robert M.Pirsig
《领域驱动设计》--Eric Evans
《敏捷软件开发》--Uncle Bob
《架构整洁之道》--Uncle Bob
《极客与团队》--Brian W.FItzapatrick
原文链接
本文为云栖社区原创内容,未经允许不得转载。
项目管理
2019-09-20 15:25:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
录制音频是我们日常生活中很容易用到的工具,有时候用电脑看视频时,听到喜欢的音乐想要录制下来。现在我们都知道网络上下载听歌很多都要版权,会让你冲钱,所以录制音乐是可以保存歌曲的一个方法,录制视频音乐的方法?用什么软件?跟着小编一起来录制吧!
首先我们需要安装软件,可以在自己的电脑上下载迅捷录音软件,安装到我们的电脑上,方法非常简单,只需要在迅捷官网上点击下载,安装即可。安装好以后我们来了解具体的录制方法。

1、双击打开自己电脑上的迅捷录音软件,打开后软件里有很多功能可以用,我们点击“格式选项”这个工具,选择左边的倒三角,把格式选择为MP3格式。
2、然后设置“声音来源”,这里我们选择“仅系统声音”,这个选项的意思是只录制我们电脑发出的声音,就是只录制我们电脑视频发出的声音。
3、选择保存位置,点击“更改目录”点击打开后跳转到设置界面,把目录保存位置设置到桌面,这样我们录制完成的音频就会保存到桌面了。
4、这时候我们点击打开要录制的视频,然后开始播放视频,等待视频播放到自己要录制的音乐节点时,点击迅捷录制软件的开始录制。等待录制完成后,点击右下方的结束按钮,想要录制的音频文件就完成了。
以上就是小编为大家分享的录制视频音乐的方法?用什么软件?的解读,录制视频音乐方法很简单,只需要大家下载安装迅捷录音软件就可以轻松实现,希望可以帮助到有需要的朋友。
项目管理
2020-05-20 11:45:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
典型的OKR系统周期
常见的OKR系统周期为:
1)在年初,公司定义了一组高级战略OKR,最好是在团队的帮助下。
重要的是要理解,没有团队的投入,高层管理人员就不应孤立地制定战略性OKR。Keith R. McFarland 在他的文章标题: 您应该像构建软件一样构建战略吗? 由于组织中各个级别的人员都会进行日常折衷,从而影响公司的战略成功,因此需要设计流程来吸收组织各个方面的想法,而不仅仅是高层管理人员。
2)执行团队然后验证公司的 OKR ,并从团队中收集反馈。
3)团队使用上述双向方法开发战术OKR。
4)团队映射相互依存关系,并确保与其他团队和计划保持一致。
5)团队每周签到一次,以跟踪结果和行动。
6)对于使用季度 OKR的 公司,通常在中期OKR审查期间在季度的中途对OKR进行审查。
7)在周期结束时,您可以快速回顾一下/吸取教训并重新开始。
进行回顾的最简单方法是 开始-停止-继续 格式。在此模型中,要求每个团队成员标识团队应做的特定事情:开始做/停止做/继续做。
对上一个周期未实现的OKR进行重新评估,以便可以将它们包含在下一个季度中,如果不再需要,可以将其丢弃。
一些公司将目标视为公司和团队随时间推移而追求的"愿景",因此目标可能会从一个季度过渡到下一个季度。例如,诸如"使我们的客户满意"之类的目标是公司可以在多个季度中使用的目标,在每个战术周期中创建新的关键结果。
甚至随着时间的推移,某些关键结果本身也可能是相同的,只是更改了目标。在我所见过的所有公司中,几乎所有季度都有收入和净促销值等指标。但是,每个团队将用来改善这些指标的价值驱动力会随着时间而变化。
为什么您应该将OKR和薪酬分开
OKR是一种管理工具,而不是员工评估工具。因此,OKR框架的基本组成部分是将OKR与薪酬和晋升分开。
正如英特尔的安迪-格鲁夫(Andy Grove)写道: OKR不是作为绩效评估基础的法律文件,而应仅是用于确定个人表现如何的一项输入。
Google的Rick Klau写道: OKR不是员工评估的代名词。OKR与公司的目标以及每个员工如何为这些目标做出贡献有关。绩效评估(完全是评估员工在给定期间的绩效)应独立于其OKR。
这与显示老化迹象的传统模型有很大不同。Willis Towers Watson进行的一项 研究 表明,绩效工具的典型薪酬既不能有效地提高个人绩效,也无法对其进行奖励: 北美只有20%的雇主表示,绩效工资有效地推动了组织中更高水平的个人绩效。 公司对短期激励给予低分。只有一半的人说短期激励措施可以有效地提高个人绩效,而更少的受访者(47%)说这些激励措施可以有效地根据个人绩效来区分薪酬。
两个奖金的故事
曾经有一个组织有两个员工在同一团队中:保罗和玛丽。 保罗很聪明,专注并取得了成果。但是他受到金钱奖励的驱使,并且一直试图找出如何赚更多的钱。 玛丽也很聪明,专心,但是她为自己的成就所驱动。她相信,如果她成功了,金钱将会随之而来。
该组织使用简化的奖金公式,将目标与奖励联系起来:
奖金=ƒ(已达成目标的百分比*薪水等级)
这意味着奖金的大小是员工薪资等级和员工实现目标的百分比的函数。 然后,发生了以下情况: 保罗实现了一个轻松目标的110%,在与经理进行了数轮谈判之后,他成功地实现了目标。 玛丽达到了一个雄心勃勃的目标的80%,远远超出了公司认为可能的范围。一个真正的伸展目标。
谁应该得到更高的奖金?当然是玛丽。
但是,谁最终得到了更大的奖金?保罗
这个故事是不正当动机的经典例子。实际上,我们的奖励制度是对不当行为的奖励。
我们都是保罗和玛丽
每个人里面都有一些保罗和玛丽。您的激励系统应该对现实生活中的真实人起作用。即使您的团队充满玛丽,为什么您会拥有一个激励您不想发生的事情的系统?
如果您要创建一种文化,以制定延伸目标为准则,则应考虑放弃针对奖金和晋升的基于公式(或紧密耦合)的模型。
有什么选择?
另一种选择是采用一种系统,其中将目标的实现输入到绩效评估过程中,其中定义了奖金和晋升。在此模型中,奖金和目标是松散耦合的。
绩效评估不仅考虑实现目标的百分比,而且还考虑目标本身:难度和对业务的影响。将其视为体操难度分数:执行更困难的例程可以获得更多分数。
"但是这太主观了"
关于此模型的常见抱怨之一是它是"主观的",而基于公式的模型是"客观的"。
问题在于,在流程结束时使用公式并不能使其客观。人们认为这是客观的,因为他们所看到的只是一点数学: 世界各地的几家公司(至少有时)使用即期奖金或酌情性奖金来补偿或补充奖金政策。两者都是遵循主观规则的100%任意性; 根据谁拥有最好的谈判技巧来减少目标来计算奖金是"主观的"; 项目/资源分配是任意的。有时,组织需要有人来解决一个陷入困境的项目,这可能会在短期内损害他/她的奖金-通常由即期奖金来弥补。
与moonshots一样,我强烈建议您一开始不要采用这种模式。在组织中拥有稳定成熟的OKR功能之前,请勿更改您的薪酬模型。
那销售配额呢?
销售团队有所不同,因为结果更易于衡量。您可以将奖金附加到销售配额中,但是您应该避免使用任何奖励谈判减少配额的员工的模型。
常见的OKR错误
从最基本的错误开始,这些是我们在OKR实现中遇到的最常见的错误: 设置不可衡量的关键结果: 记住约翰-多尔的公式。每个关键结果必须是可测量的。 过多的 OKR 或主要结果: OKR并不是您所做的所有事情的清单。它代表了您的首要任务。少即是多。 将任务包括为关键结果: 关键结果不是您要做的事情。这是您所做的成功的结果。 自上而下设置OKR: OKR不级联。相信您的团队,并帮助他们了解他们如何做出贡献。 在筒仓中创建OKR: 设置OKR时,团队必须互相交谈,否则将无法达成一致。 " 一劳永逸 ": 不要将您的OKR视为新年的决议。OKR必须成为您组织文化的一部分,并且必须定期进行跟踪。 在补偿公式中包括OKR: OKR不是员工评估工具。OKR是一种管理工具。 试图盲目复制Google: 采用OKR并非只有一种方法。即使在Google内部,不同的团队也以多种方式使用OKR。了解所涉及的原理,并使您的实施适应组织的需要。
你可能还想看: 敏捷与OKR实践(如何让OKR与敏捷计划共存) Google OKR指导手册 有赞商城的OKR实践分享
OKR新手入门指南系列 OKR新手入门指南 第一部分 OKR新手入门指南 第二部分 OKR新手入门指南 第三部分 OKR新手入门指南 第四部分
作者: Felipe Castro
译者: Bob Jiang
原文链接 绩效管理 OKR 谷歌 目标系统 本文首发于 Bob Jiang的博客 ,转载请联系 Bob Jiang
项目管理
2020-05-17 08:25:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
工欲善其事,必先利其器。本文收集 IntelliJ IDEA 常用插件,持续更新中......
Alibaba Java Coding Guidelines 阿里巴巴《Java 开发手册》配套插件,可以实时检测代码中不符合手册规约的地方,助你码出高效,码出质量。
使用: 当我们违反手册规约时,该插件会自动检测并进行提示。 同时提供了一键检测所有代码规约情况和切换语言的功能。 如果你想修改某条规约的检测规则的话,可以通过设置的 Editor -> Inspections 进行修改。
Easy Code EasyCode 用于 代码自动生成 ,支持模板自定义、导入、导出,方便团队之间共享。
介绍: 基于 IntelliJ IDEA 开发的代码生成插件,支持自定义任意模板(Java,html,js,xml)。 只要是与数据库相关的代码都可以通过自定义模板来生成。支持数据库类型与 java 类型映射关系配置。 支持同时生成生成多张表的代码。每张表有独立的配置信息。完全的个性化定义,规则由你设置。
具体使用见: IntelliJ IDEA 插件 EasyCode(代码自动生成)
Lombok Lombok 为 Java 项目提供了非常有趣的附加功能,使用它的注解可以有效的地解决那些繁琐又重复的代码,例如 Setter、Getter、toString、equals、hashCode 以及非空判断等。
使用:
我们给一个类添加 @Getter 和 @Setter 注解, Lombok 就会为我们自动生成所有属性的 Getter 和 Setter 方法。
Free MyBatis plugin MyBatis 扩展插件,可以在 Mapper 接口的方法和 xml 实现之间自由跳转,也可以用来一键生成某些 xml 实现。
介绍: 生成 mapper xml 文件。 快速从代码跳转到 mapper 及从 mapper 返回代码。 mybatis 自动补全及语法错误提示。 集成 mybatis generator gui 界面。
使用: 我们可以通过 Mapper 接口中方法左侧的箭头直接跳转到对应的 xml 实现中去。 也可以从 xml 中 Statement 左侧的箭头直接跳转到对应的 Mapper 接口方法中去。 还可以通过 Alt+Enter 键组合直接生成新方法的 xml 实现。
MyBatis Log Plugin 有时候我们需要运行过程中产生的 SQL 语句来帮助我们排查某些问题,这款插件可以把 Mybatis 输出的 SQL 日志还原成完整的 SQL 语句,就不需要我们去手动转换了。
使用: 首先我们需要打开这款插件的窗口。 当我们调用方法,控制台输出 Mybatis 的 SQL 日志时,该插件会自动帮我们转换成对应的 SQL 语句。 有的时候我们需要转换的日志并不在自己的控制台上,这时可以使用插件的 SQL Text 功能:直接复制我们需要转换的日志,然后点击 Restore Sql 按钮即可。
RestfulToolkit 一套 Restful 服务开发辅助工具集,提供了项目中的接口概览信息,可以根据 URL 跳转到对应的接口方法中去,内置了 HTTP 请求工具,对请求方法做了一些增强功能,总之功能很强大!
介绍: 根据 URL 直接跳转到对应的方法定义 ( Ctrl \ or Ctrl Alt N ); 提供了一个 Services tree 的显示窗口; 一个简单的 http 请求工具; 在请求方法上添加了有用功能: 复制生成 URL;复制方法参数... 其他功能: java 类上添加 Convert to JSON 功能,格式化 json 数据 ( Windows: Ctrl + Enter; Mac: Command + Enter )。
使用: 可以通过右上角的 RestServices 按钮显示项目中接口的概览信息。 可以通过搜索按钮,根据 URL 搜索对应接口。 可以通过底部的 HTTP 请求工具来发起接口测试请求。 通过在接口方法上右键可以生成查询参数、请求参数、请求 URL 。 通过在实体类上右键可以直接生成实体类对应的 JSON 。
Translation 一款翻译插件,支持 Google、有道、百度翻译,对我们看源码时看注释很有帮助!
使用: 直接选中需要翻译的内容,点击右键即可找到翻译按钮。 直接使用翻译文档可以将整个文档都进行翻译。 还可以通过右上角的翻译按钮直接翻译指定内容。
GsonFormat 这款插件可以把 JSON 格式的字符串转化为实体类,当我们要根据 JSON 字符串来创建实体类的时候用起来很方便。
使用: 首先我们需要先创建一个实体类,然后在类名上右键 Generate ,之后选择 GsonFormat 。 输入我们需要转换的 JSON 字符串。 选择性更改属性名称和类型。 点击确定后直接生成实体类。
Grep Console 一款帮你分析控制台日志的插件,可以对不同级别的日志进行不同颜色的高亮显示,还可以用来按关键字搜索日志内容。
使用: 当项目打印日志的时候,可以发现不同日志级别的日志会以不同颜色来显示。 如果你需要修改配色方案的话,可以通过 Tools 打开该插件的配置菜单,然后通过配置菜单修改配色方案。 可以通过在控制台右键并使用 Grep 按钮来调出日志分析的窗口。 然后直接通过关键字来搜索即可。
Maven Helper 解决 Maven 依赖冲突的好帮手,可以快速查找项目中的依赖冲突,并予以解决!
使用: 我们可以通过 pom.xml 文件底部的依赖分析标签页查看当前项目中的所有依赖。 通过冲突按钮我们可以筛选出所有冲突的依赖。 选中有冲突的依赖,点击 Exclude 按钮可以直接排除该依赖。 同时 pom.xml 中也会对该依赖添加 标签。
Statistic 一款代码统计工具,可以用来统计当前项目中代码的行数和大小。
使用: 我们可以通过顶部菜单中的 View -> Tool Windows -> Statistic 按钮开启该功能。 此时就可以看到我们项目代码的统计情况了。
Vue.js Vue.js 支持插件,写过前端的朋友肯定用过,可以根据模板创建 .vue 文件,也可以对 Vue 相关代码进行智能提示。
使用: 启用该插件后,可以根据模板新建 .vue 文件。 当我们在标签中写入以 v- 开头的代码时,会提示 Vue 中的相关指令。
element Element-UI 支持插件,可以对 Element-UI 中的标签进行智能提示,有了它就不用盲写相关代码了!
使用: 当我们写入以 el- 开头的标签时,会提示 Element-UI 相关组件。
扫码关注微信公众号 程序员35 ,获取最新技术干货,畅聊 #程序员的35,35的程序员# 。独立站点: https://cxy35.com
项目管理
2020-05-15 18:32:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>> fdisk -l 显示硬盘分区,找到待挂载的硬盘编号 df -l 显示当前硬盘挂载情况 mount -t ext4 /dev/sdb /devdata 将硬盘挂载到devdata目录
项目管理
2020-05-14 18:19:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
你的电脑是否自己经常进入睡眠状态?小编今日推荐的 Caffeinated mac中文版 是Macos上一款系统防睡眠软件,Caffeinated Mac版是一个小巧但强大的应用程序,它可以让你轻松地覆盖你的节能设置,让你的Mac电脑保持清醒。
地址: https://www.macw.com/mac/583.html
项目管理
2020-05-13 19:08:00