17年读书篇

人的成长就像是不断打怪升级,不断的跟讨厌厌恶的懒,惰,思想狭隘这些本性做斗争,做斗争需要手段,最快速的就是读书.
读书就像是吃药,但不能乱吃,对症的药要不断的吃。同样,经典的书也要反复阅读,所以根据自身情况选择经典很重要!

家里一堆书,挑三拣四选了当前适合自己的”经典”!

17年读书策略

 好读书,读好书,重复读(除非书特别好,否则今年不再买书)
 技术类:
1,《java并发编程实战》:Java并发经典,人手一本不用多说了
2,《深入理解elasticsearch》:搜索引擎必读经典
3,《改善既有代码的设计》:很厚,但最有价值就是前面几章的洗脑篇
4,《大型网站技术架构-核心原理与案例分析》
    《大型分布式网站架构设计与实践》
   《大型网站系统与java中间件实践》: 淘宝出品的三本,大型互联网站的科普入门书
5,修炼java开发技术:在架构中体验设计模式与算法之美 :入门级架构设计模式经典
6,《go并发编程》:go语言郝大的经典之作
管理类:
 1 《创业维艰 》:作者亲身经历的事迹,讲创业最好的书,创业就是两个词,苦逼与挣扎
2 《格鲁夫给经理人的第一课》:中层管理者必读经典
3 《卓有成效的管理者 》 :老大极力推荐的经典!针对知识型工作者 ,效能,贡献,决策 ,落实。
其他:
《格非江南三部曲:人面桃花+春尽江南+山河入梦》:茅盾文学奖经典
《柳叶纷飞》:随着作者的人生起伏,体验邻里乡间最真实的感觉
《三体》:一定要拜读一下刘慈欣的宇宙史诗级的科幻小说17年底来打标签。。
2017.1.10

Jenkins-构建一个新工程(2)

这一篇,主要记录如何通过通过Jenkins构件一个工程。

一、配置JDK和MAVEN依赖

上记中,我们安装的只是Jenkins中的插件,为了保证maven构建能够进行,我们需要安装我我们需要对应版本的JDK版本和MAVEN。

点击进入:

Manage Jenkins(Jenkins管理)-Global Tool Configuration(工具配置)

1.1 JDK配置

提供的4种途径的方式,可以给我们安装。这里我选择通过oracle来安装。这种方式我们需要提供給Jenkins自己的oracle用户密码(可以去oracle自己注册),用来下载用。
图片描述
1.2 Maven安装
Maven也是一样,我们可新增maven按钮,通过Apache来安装,设置如下:
图片描述

二、创建一个Maven工程

首页-> create new jobs 选择 Maven project(Maven工程)
并且,输入給这个新工程取名。
图片描述

进入到工程的基本信息编辑页面,会出现了如下的几大模块编辑栏:

General 设置基本内容

Source Code Management 设置工程代码获取方式和管理

Build Triggers 选择工程构建触发的条件

Build Environment 选择工程build及构建的环境配置

Pre Steps 构建前需要执行的工作

Build 正式进行构建,设置pom.xml文件。

Post Steps 设置构建完成后的动作,我们用来作后续工程部署等批处理设置

Build Settings 主要用于设置Email的通知

Post-build Actions 设置前面的 Build到Build Settings的动作执行完后的后续工作。

下面,我们一步步来介绍下 完成一个工程构建主要设置的部分。

部分1、Maven project name 栏设置

主要填写Maven project name(工程名)、Descript(工程描述信息)

部分2、Source Code Management 栏设置

输入源码的获取地址,由于我的插件中演示的是svn,那么如下输入:

Repository depth: Jenkins在执行checkout、update代码时,所取出的代码的范围。默认我们选择infinity就可以。

Check-out Strategy:svn第一次更新代码和以后同步代码的步骤,和策略。默认选项一般就可以。

图片描述

部分3、Build Triggers 栏设置

默认选项为:

Build whenever a SNAPSHOT dependency is buil 意思就是svn更新触发构建

这里建议选择:

Poll SCM 相当于新建一个计划任务,定时检查版本库,发现有新的提交就触发构建。其配置的意思呵linux的crontab差不多,如下:
我配置的每十分钟进行一次构建。

图片描述

部分4、Build 栏设置

如果对mvn构建参数有需求,或者需要设置pom.xml的文件位置,可以在这里设置。

默认: pom.xml的文件就在工程目录下。

部分5、完成

点 save 完成对一个一个工程的设置。

三、操作构建

切换到Jenkins的主页上,将出现了我新加的工程。如下图:

图片描述

3.1 启动构建

点击上图的的对应工程project name.进入到进入到Jeson_test_project这个工程的操作模式下,如下图:

图片描述

3.2 点击Build Now,表示开始构建

第一次根据你服务器的网络质量好坏,时间较长,因为Jenkins需要安装JDK,Maven ,并且还需要安装很多包文件。

Build History 显示出我构建的一个历史信息,其中闪烁不断的图标,表示正在构建的任务。
图片描述

点击 正在构建的任务,在右侧又会出现很多的执行选项,如下图:

图片描述
点击Console Output,就可以看到正在构建的任务终端输出,如下图:

图片描述

这一记,大家能知道如何进行一个简单的工程任务构建。我们确保工程的构建成功(如果没有报错,构建成功后,闪烁的图标会又红色变化为蓝色)

Jenkins-持续集成平台(1)

一、什么是持续集成

1、什么是集成

指的是代码由编译、发布和测试、直到上线的一个过程

2、什么持续集成

高效的、持续性质的不断迭代代码的集成工作

3、如何高效准确的实现持续集成

必不可少的需要一套能自动化、并且可视化的平台来帮助我们。

那么总结来看,Jenkins就是一个可以帮助我们实现持续集成的平台。

二、为什么Jenkins能帮助我们进行持续集成

理由有如下几点:

1、Jenkins是一个开源的、且基于JAVA代码开发的持续集成系统,
因为含有非常丰富的插件支持所以我们可以方便的打通版本库、测试构建环境、线上环境的所有环节。并且丰富友好的通知使用者和开发、管理人员。

2、安装维护简单

安装Jenkins,不太复杂。且支持通用的平台。

3、Java 应用 常用

在企业的软件构建过程中,JAVA的应用工程稍显复杂,由于复杂构建、和代码上线、并且服务的重启。整个过程下来,消耗的时间较多,Jenkins却能很好的集成maven的编译方式,且利用自动化的插件、和自定义开发脚本的支持。所以目前广泛的应用于JAVA工程的持续集成平台。

好了,那么接下来我就来介绍,如何搭建一套快速有效的Jenkins持续集成平台。

三、Jenkins持续集成平台安装

1、前期准备

svn:一个开放源代码的版本控制系统

Centos6.4:一个较接近的版本linux系统

java:预先准备好一套JDK环境

2、安装

安装体系jenkins+svn+maven+shell(用于发布)

#安装Jenkins #

Jenkins的安装可以通过tomcat作为容器安装,由于Jenkins包就自带了servlet,所以我们只需要下载安装就可以直接启动。

1、下载war包:

官方地址下载:http://mirrors.jenkins-ci.org/war-stable/
图片描述
2、启动Jenkins

java -jar ./jenkins.war –httpPort=8080

3、测试

确认终端启动的输出日志正常,并用浏览器访问测试环境的服务,地址为:http://IP+8080

四、Jenkins安装界面配置

1、解锁服务

第一次,登录,需要进行一个解锁 ,页面也会有提示,

/root/.jenkins/secrets/initialAdminPassword,我们可以通过这个文件中查看密码,并输入。

2、进入安装界面

3、进入 Getting Started 界面
图片描述

最好能确保推荐安装的插件都有安装成功。

图片描述

4、新建admin用户

会提示我们需要新建一个用户,这个我们自己新建,记得记录好新建的用户口令就行。

确认完成后,就正式进入到了Jenkins的页面。

Welcome to Jenkins!

补充:

Jenkins的配置,和插件的安装都会放入~/.jenkins中,所以如果需要重新初始化安装,只需要清理掉~/.jenkins的文件即可。

五、插件安装

1、安装插件方式选择

如果在安装时,没有安装好对应的插件可以选择如下方式进行插件的安装:

方法一.如果服务器可以上网,那边选择在线安装最好不过了,安装流程为:
系统管理(Configure System)—-插件管理(Manage Jenkins)—选择需要的插件直接安装即可

方法二.如果服务器不能上网,那么就只能离线安装,首先去

http://updates.jenkins-ci.org/download/plugins/

下载需要的plugin,选择匹配的版本号,下载到本地,然后打开:系统管理()—插件管理—高级—找到”上传插件”(浏览,找到扩展名为.hpi的插件,上传之后默认直接就安装了。重启jenkins,安装的插件就可以使用了。

2、安装插件依赖解决

插件安装不上去,一定是某个地方出现问题。在Jenkins的终端日志中,可以查看到对应的错误。需要针对性的解决。

比如:在安装subversion的时候,的错误提示。
一般的情况安装对应的插件,会把需要依赖的插件安装上。如出现对应的依赖问题,就需要一一安装上对应的依赖包。如下错误中,我们就需要先安装好这两个依赖包。
mapdb-api (1.0.1.0), workflow-scm-step (1.4.3)

Failure –
java.io.IOException: Failed to dynamically deploy this plugin
at hudson.model.UpdateCenter$InstallationJob._run(UpdateCenter.java:1867)
at hudson.model.UpdateCenter$DownloadJob.run(UpdateCenter.java:1624)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
at java.util.concurrent.FutureTask.run(FutureTask.java:262)
at hudson.remoting.AtmostOneThreadExecutor$Worker.run(AtmostOneThreadExecutor.java:110)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.io.IOException: Failed to install subversion plugin
at hudson.PluginManager.dynamicLoad(PluginManager.java:834)
at hudson.PluginManager.dynamicLoad(PluginManager.java:775)
at hudson.model.UpdateCenter$InstallationJob._run(UpdateCenter.java:1863)
… 5 more
Caused by: jenkins.MissingDependencyException: One or more dependencies could not be resolved for subversion : mapdb-api (1.0.1.0), workflow-scm-step (1.4.3)
at hudson.PluginWrapper.resolvePluginDependencies(PluginWrapper.java:529)
at hudson.PluginManager.dynamicLoad(PluginManager.java:824)
… 7 more

3、查看已经安装的插件

为了更好的方便我们学习下节课内容,请同学们通过插件管理(Manage Jenkins)—已安装插件(Installed),确保安装好了如下的插件:

Subversion Plug-in Jenkins对SVN的支持。

Maven Integration plugin 用于对java代码的构建

Email Extension Plugin 邮件模块

Ant Plugin 也是对java代码构建,不过是通过ant来作。

还有一些是安装jenkins的时候推荐安装的,大家也最好安装上。

自动化运维之-ansible入门

1.缘起

因为公司没有运维人员,每次发布项目都需要手动编译,手工发布,特别浪费时间,发布几次以后就烦了,就想着全换成自动化方式打包发布,经过几个筛选终于选定用ansible来做自动化打包发布。从此节约大把的时间来听听歌看看书喝喝茶了。。。经过实践表面,在没有运维岗的时候,ansible绝对是中小创业公司项目发布的最佳选择!

2.介绍

ansible其实就是一个可以通过SSH与远程服务器进行连接交互的工具,它内置了很多模块可以让你轻松的通过 命令或者编写playbook剧本操作远程服务器,

3.安装与简单测试

  1. 安装ansible很简单,我的系统是centsos7.2,直接敲,yum install ansible -y 即可
    windows系统可以考虑安装个cygwin,来模拟进行linux操作,在cygwin的虚拟linux上安装ansible与远程服务器进行交互。
    安装好以后ansible --version进行查看
    如下:
    [root@localhost ~]# ansible --version
    ansible 2.3.1.0
    config file = /etc/ansible/ansible.cfg
    configured module search path = Default w/o overrides
    python version = 2.7.5 (default, Nov 6 2016, 00:28:07) [GCC 4.8.5 20150623 (Red Hat 4.8.5-11)]
  2. 安装好ansible以后,建议配置ssh免密码登录远程服务器,不会操作的可以自行百度
  3. ansible有个host配置,yum安装以后,在/etc/ansible/hosts里可以可以添加远程服务器主机的ip地址,或者一组ip服务器地址,例如我们的服务是分布式部署,有三台服务,配置如下
#[]里是服务器组的name,后续每行跟着的是服务器的ip地址
[yrServer] 
192.168.9.50
192.168.9.51
192.168.9.52
  1. 命令行测试
    ansible yrServer -m ping
    服务器返回

    [root@localhost ~]# ansible all -m ping
    192.168.9.50 | SUCCESS => {
    "changed": false,
    "ping": "pong"
    }
    192.168.9.51 | SUCCESS => {
    "changed": false,
    "ping": "pong"
    }
    192.168.9.52 | SUCCESS => {
    "changed": false,
    "ping": "pong"
    }

    表示三台服务器都已经ping通,其中参数里的changed的false代表,主机没有任何改动
    ping后面pong标识已经ping通

ansible可以通过类似上面的命令行与主机进行交互,其实通过ansible进行远程主机操作,就是把ansible脚本转换层shell脚本与命令在远程主机上进行操作,但是自动化运维是一组命令跟脚本的组合,所以就需要学习一下ansible的杀手锏playbook了

4.Playbook

playbook是一组ansible命令的组合,我们可以称他为剧本,你完全可以理解成playbook就是拍戏的那个剧本,演员只要照着剧本演戏,就能完成拍戏任务,我们把我们想做的操作编排成剧本,ansible就可以编排有序的执行任务,并给与我们反馈。Playbooks 可用于声明配置,更强大的地方在于,在 playbooks 中可以编排有序的执行过程,甚至于做到在多组机器间,来回有序的执行特别指定的步骤.并且可以同步或异步的发起任务.

4.1 Playbook示例

playbook的格式是YAML,语法比较简单,
playbook 由一个或多个 ‘plays’ 组成.它的内容是一个以 ‘plays’ 为元素的列表.
“plays” 算是一个体育方面的类比,你可以通过多个 plays 告诉你的系统做不同的事情,不仅是定义一种特定的状态或模型.你可以在不同时间运行不同的 plays.
这里有一个从github源码demo里找到的playbook示例,其中仅包含一个 play: 仔细看注视

---#必须---开头
- hosts: webservers#hosts里定义的组名或者直接服务器ip
  vars:#参数
    http_port: 80
    max_clients: 200
  remote_user: root #执行的账户名
  tasks:#执行的任务
  - name: ensure apache is at the latest version#任务名称是安装最新版本的apache
    yum: pkg=httpd state=latest#执行的命令是ansible yum模块安装httpd
  - name: write the apache config file#任务名称是修改apache的config配置文件
    template: src=/srv/httpd.j2 dest=/etc/httpd.conf  #采用模版文件,从从src复制到dest,名字最终为httpd.conf
    notify:#事件通知,一有修改配置文件,就通知的名字为restart apache,会执行handlers相同名字的service进行执行重启
    - restart apache
  - name: ensure apache is running #任务名称
    service: name=httpd state=started #任务操作是:启动httpd
  handlers:
    - name: restart apache #任务名称
      service: name=httpd state=restarted #任务操作是重启httpd服务

执行ansible playbook
ansible-playbook demo.yml
就可以查看结果

今日感想

今天周日,与吉大的一位学长因为私活原因在中关村从上午一直厮混到下午,都是IT男,聊的难得的投缘,从程序开发聊到创业,从大环境聊到行业选择,聊人生里的得失,聊未来规划,天马行空的想法真是给我太多触动与感慨,虽然职业生涯只差一年,自己也一向自诩刻苦努力,但学长的基本功与视野都远超于我,备受感触,归根结底还是努力不够,懈怠太多,得过且过,人在奋进的过程中就得多折腾,让自己在不舒服的过程中徐徐渐进的成长!

后台架构设计

一. 基础概念

  1. service : 提供具体业务逻辑功能的单元
    • 对外提供可被其他service/webapp调用的接口
    • 内部封装了业务逻辑代码和访问数据层(DB,缓存)的功能
    • 其他模块只能根据对外暴露的接口调用,不能直接调用服务内部的方法
    • 只要保证接口不变,更改服务内部实现并不影响其他模块的功能
    • service无法直接运行,需要加入到server中才能运行
  2. server : 将多个service打包成为一个可部署运行的单元
    • 工程内有接入逻辑(例如:暴露哪些RPC接口,对客户端暴露哪些接口等)
    • 提供service运行所需的依赖资源(线程池,db配置,缓存配置,日志配置等)
    • 工程可将多个service里的业务逻辑打包成为一个独立的jar
    • 工程内有java main方法,可直接运行为java进程,对外提供功能
    • service 为业务逻辑单元,server 为部署运行单元
  3. webapp : 提供web页面访问的功能单元
    • 满足javaEE标准的webapp工程
    • 工程内只存放页面显示逻辑和数据接口,以及简单数据拼装功能
    • 工程内没有任何具体业务逻辑,所有业务功能需要调用service接口完成
    • 仅为页面展示使用
  4. 关系图

    关系图

    • server内部可以关联多个service,作为一个java进程启动运行
    • A service提供的接口可以被内部的B service Impl调用,同时也可以被外部(其他webapp/server)以RPC形式调用
    • webapp不包含业务逻辑,需要调用service接口完成业务功能

二. 单机版架构设计

  1. 整体架构图

    整体架构图

  2. 接入层
    • Android/Iphone客户端HttpApi和SocketConn直接连接weizhu-all-server
    • 管理后台web页面,手机客户端WebView访问H5页面以及大文件(例如图片视频等)上传接口通过nginx根据访问路径转发到相应的webapp上
    • webapp中包含页面展示和数据接口,不包含业务逻辑.业务逻辑需要RPC调用后方服务完成业务功能
    • 静态文件(如图片/视频/音频等)访问通过nginx读取硬盘文件
  3. 逻辑层
    • 单机版架构逻辑层只有weizhu-all-server一个server
    • weizhu-all-server负责处理整体业务逻辑功能
    • weizhu-all-server包含所有微助相关服务并作为一个独立java进程部署运行
  4. 存储层
    • 存储层目前只包含DB(MySQL)和缓存(Redis),以及硬盘文件系统
    • 存储层负责将业务数据保存,保证数据安全高可用
    • 后续随着业务发展,可能会使用不同的数据存储工具(例如全文索引存储,分布式存储等)

三. 分布式版架构设计

  1. 整体架构图

    整体架构图

  2. 接入层
    • Android/Iphone客户端Http短连接Api请求(HttpApi)访问 weizhu-api-server
    • Android客户端tcp长连接(SocketConn)访问 weizhu-conn-server
    • 管理后台web页面,手机客户端WebView访问H5页面以及大文件(例如图片视频等)上传接口,通过nginx根据访问路径转发到相应的webapp上
    • webapp中包含页面展示和数据接口,不包含业务逻辑.业务逻辑需要RPC调用后方服务完成业务功能
    • 静态文件(如图片/视频/音频等)访问通过nginx读取硬盘文件
  3. 逻辑层
    • weizhu-common-login-server 负责公共业务逻辑处理,例如:管理员信息,用户登录,用户会话信息等
    • weizhu-company-proxy-server 负责将某个公司的访问请求转发到对应的公司逻辑server上。内部没有具体逻辑,只负责转发
    • weizhu-company-login-server 负责某个公司的具体业务逻辑。例如:用户信息,发现课件逻辑,im逻辑,社区逻辑等
    • weizhu-push-server 负责消息推送相关逻辑
  4. 存储层
    • 每个server都有相对独立的DB(MySQL)和缓存(Redis)
    • 存储层负责将业务数据保存,保证数据安全高可用
    • 续随着业务发展,可能会使用不同的数据存储工具(例如全文索引存储,分布式存储等)

四. 重要功能结构设计

Service RPC 设计

  • UserService为例
  • 服务调用端设计
    1. 整体结构图

      整体架构图

    2. 类说明
      • UserService为服务定义接口,放在weizhu_server/common/proto工程的com.weizhu.proto包下
      • ServiceInvoker接口将User服务的所有接口调用抽象为一个invoke()方法
      • RpcServiceInovker类实现了ServiceInvoker接口,其内部关联了RpcInvoker接口
      • RpcInvoker接口将多个服务的调用抽象为一个invoke()方法
      • AutoSwitchRpcClient类实现了RpcInvoker接口,并关联了多个RpcClient类。该类主要负责多个RpcClient的管理,例如:创建连接,重建连接,调用调度等
      • RpcClient类同样实现了RpcInvoker接口。该类主要负责一个服务端地址的通讯,以及具体数据的发送和接收。
      • NioSocketChannel类为netty网络框架负责具体网络通讯功能
    3. 创建流程
      • 根据配置文件里server的地址(remoteHost/remotePort)(可为多个)创建AutoSwitchRpcClient实例
      • AutoSwitchRpcClient构造时内部会对应多个远程地址创建多个RpcClient
      • 利用ServiceStub.createServiceInvoker方法和AutoSwitchRpcClient实例,创建出RpcServiceInovker实例
      • 利用ServiceStub.createServiceApi方法和UserService接口,以及RpcServiceInovker实例可以使用java反射创建出UserService被调实例
    4. 调用流程
      • 调用者获取到UserService被调实例后,调用getUser()方法
      • UserService被调实例使用java反射,将请求转发到RpcServiceInovker的invoke()方法中
      • RpcServiceInovker将服务名和请求数据编码后,调用内部的AutoSwitchRpcClient实例的invoke()方法
      • AutoSwitchRpcClient实例检查内部的RpcClient挑选一个已经建立连接的RpcClient实例发送请求.
      • AutoSwitchRpcClient如果没有实例已经连接成功,则挑选一个RpcClient等待其连接成功后再发送请求.如果最终连接失败,则返回网络错误信息
      • RpcClient实例将请求包编码为二进制数据,发送到netty网络框架中具体负责网络通讯的NioSocketChannel实例中
  • 服务实现端设计
    1. 整体结构图

      整体架构图

    2. 类说明
      • NioSocketChannel类为netty网络框架负责具体网络通讯功能
      • RpcInvoker接口将多个服务的rpc调用抽象为一个invoke()方法
      • ServiceRpcInvoker类实现了RpcInvoker接口,其内部关联了多个ServiceInvoker实例,根据服务名可以将请求转发到对应的实例上。
      • ServiceInvoker接口将某个服务的所有接口调用抽象为一个invoke()方法
      • LocalServiceInvoker类实现了ServiceInvoker接口,其内部关联了UserService实例,可以将请求转发到具体的UserService接口上
      • UserService为服务定义接口,放在weizhu_server/common/proto工程的com.weizhu.proto包下
      • UserServiceImpl类实现了UserService接口,负责具体的业务逻辑处理
    3. 创建流程
      • 根据配置文件和所依赖资源,创建UserServiceImpl实例
      • 利用ServiceStub.createServiceInvoker方法和UserServiceImpl实例,创建LocalServiceInvoker实例
      • 利用ServiceStub.createRpcInvoker方法和LocalServiceInvoker实例,创建ServiceRpcInvoker实例
    4. 调用流程
      • 当接收到网络数据后,NioSocketChannel将数据分包解码后,调用ServiceRpcInvoker实例的invoke()方法
      • ServiceRpcInvoker实例根据服务名将请求转发到对应的LocalServiceInvoker实例的invoke()方法
      • LocalServiceInvoker实例根据方法名和java反射将请求转发到UserServiceImpl实例的具体方法
      • UserServiceImpl调用具体的业务方法,处理业务逻辑

接入设计 (HttpApi, SocketConn, webapp页面, 文件上传)

  • Http短连接Api调用接入设计(HttpApi)
    1. 整体结构图

      整体架构图

    2. 处理流程
      • weizhu-api-server使用jetty接收http短链接接口请求
      • 从请求包中获取到session_key,调用SessionService接口校验身份和用户信息
      • 组装请求头,发起请求到业务服务
  • Tcp长连接接入设计(SocketConn)
    1. 整体结构图

      整体架构图

    2. 处理流程
      • App登录后建立并保持tcp连接到weizhu-conn-server
      • tcp连接建立完成后发送session_key并调用SessionService接口校验身份和用户信息
      • 如果身份校验失败,返回错误信息并断掉tcp连接
      • 校验身份成功后,调用PushService获取离线推送消息,并发送到App端
      • tcp连接可以发送api请求
      • 业务服务可以调用PushService发送推送消息
      • PushService 将消息推送到weizhu-conn-server,最终通过tcp连接下发到客户端
  • Webapp页面接入设计
    1. 整体结构图

      整体架构图

    2. 处理流程
      • App打开webview发起页面请求
      • nginx接收到请求后,根据访问路径转发到具体的webapp上.手机端webview访问的是 weizhu-mobile-webapp
      • weizhu-mobile-webapp内的UserSessionFilter从http cookie中获取session_key,调用SessionService接口校验身份和用户信息
      • 校验身份成功后,如果是页面请求正常处理;如果是servlet接口请求从后方的业务服务中获取数据并返回
  • 文件上传处理
    1. 整体结构图

      整体架构图

    2. 处理流程
      • App发起文件上传
      • nginx根据请求访问路径转发到weizhu-upload-webapp
      • weizhu-upload-webapp内的UserSessionFilter从http cookie中获取session_key,调用SessionService接口校验身份和用户信息
      • 校验身份成功后,UploadServlet调用UploadService处理,并保存到磁盘文件系统上
      • App访问静态资源时,通过nginx直接访问磁盘文件系统

网络电话 (WebRTC) 设计

  1. 整体结构图

    整体架构图

  2. 使用WebRTC技术, google开源。app 端需要安装sdk,服务端部署turnserver
  3. 处理流程
    • 通话主叫端发送offer请求
    • offer请求经过weizhu-server通道,发送到通话被叫端
    • 通话被叫端发送answer请求到主叫端
    • 主叫端接收answer请求后,完成链路建立
    • 链路建立后,webrtc会根据turnserver确定网络环境
      • 同一内网中可直接连接
      • 不同网络间可根据网络情况进行路由器打洞直连,或者经过turnserver通讯
      • 网络发生变化时,可以自动切换
    • 发起点对点通话

springboot基础三:mybatis操作数据库

​前言

前两篇文章介绍了springboot是什么东西,以及默认启动跟自定义配置,这一篇就开始正式的跟springboot开发项目使用相关了。
众所周知我们一个项目启动后,分析好需求以后就开始做数据库设计,数据库设计好了,程序就要开始操作数据库进行程序开发了,根据前两篇文章搭建好了springboot的开发项目,配置好了一些简单配置,然后跟着我学习一下springboot配置mybatis操作数据库吧!

配置

  1. 引入pom
 <!-- Spring Boot Mybatis 依赖 -->
    <dependency>
      <groupId>org.mybatis.spring.boot</groupId>
      <artifactId>mybatis-spring-boot-starter</artifactId>
      <version>1.2.0</version>
    </dependency>

    <!-- MySQL 连接驱动依赖 -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.41</version>
   </dependency>
  1. 在application.properties或者对应环境的application配置文件里配置数据库链接信息
##数据库
spring.datasource.url=jdbc:mysql://localhost:3306/wishpay?useUnicode=true&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driverClassName=com.mysql.jdbc.Driver
#重连
spring.datasource.time-between-eviction-runs-millis=1800000
spring.datasource.num-tests-per-eviction-run=3
#最小生命时间
spring.datasource.min-evictable-idle-time-millis=1800000
spring.datasource.validation-query=SELECT 1
##mybatis相关配置
mybatis.mapper-locations=classpath:mapper/*.xml
#mybatis.type-aliases-package=com.wish.pay.web.dao
mybatis.configuration.mapUnderscoreToCamelCase=true
mybatis.configuration.useColumnLabel=true
  1. 在启动类Application里加入MapperScan用来指定对应的mapperDao接口
@SpringBootApplication
@ComponentScan("com.wish.action")
//用于扫描dao包下的mapper接口文件
@MapperScan("com.wish.action.dao")
public class Application { 
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
  1. 在resources资源目录下建立mapper文件夹用来存放mybatis的mapper文件
  2. 测试
    • 数据库新建一个User表有两个字段id与name,新建User类,有id与name两个属性
    • 新建UserMapper.xml文件放入resources的mapper文件夹下
      <mapper namespace="org.spring.springboot.dao.CityDao">
      <resultMap id="BaseResultMap" type="com.wish.action.doman.User">
          <result column="id" property="id" />
          <result column="name" property="name" />
      </resultMap>
      
      <parameterMap id="User" type="com.wish.action.doman.User"/>
      
      <sql id="Base_Column_List">
          id, name
      </sql>
      <select id="findByName" resultMap="BaseResultMap" parameterType="java.lang.String">
          select
          <include refid="Base_Column_List" />
          from user
          where name = #{name}
      </select>
      </mapper>
      
    • 在dao包下建立UserDao类
      /**
      * User DAO 接口类
      */
      public interface UserDao {
      /**
       * 根据用户名字,查询用户信息
       * @param name 用户名
       */
      User findByName(@Param("name") String name);
      }
      
    • 在service新建UserServer接口定义findUserByName方法,在实现类UserServiceImpl里实现以下方法
      //实现里进行实现
      @Autowired
      private UserDao userDao;
        /**
       * 根据用户姓名查询用户信息
       * @param name
       */
      public User findUserByName(String name);
          return userDao.findByName(name);
      }
      
    • 在controller包里面新建UserRestController 类
      @RestController
      public class UserRestController {
          @Autowired
          private UserService userService;
          @RequestMapping(value = "/api/user", method = RequestMethod.GET)
          public User Test(@RequestParam(value = "name", required = true) String name) {
              return userService.findUserByName(name);
          }
      }
      
    • 通过访问,发现从数据库可以查询到用户信息,至此springboot配置mybatis信息并且编写一个从mysql数据库根据用户名读取用户信息的例子完毕!

自己动手试试吧!

springboot基础二:配置文件设置

概述

springboot为什么开发效率那么快,很大一部分是因为提供了自动化配置,相比之前的框架各种xml,各种properties文件,这种显示方式的声明是不需要的,采用默认配置就可以最轻量化的跑起一个应用程序,这一篇就讲讲springboot的配置

默认配置

springboot配置文件默认读取依赖包中resources目录下的application.properties文件,这里面配置了一些springboot的默认配置,如下:

 ===================================================================
# COMMON SPRING BOOT PROPERTIES
# This sample file is provided as a guideline. Do NOT copy it in its
# entirety to your own application.               ^^^
# ===================================================================
# ----------------------------------------
# CORE PROPERTIES
# ----------------------------------------
# SPRING CONFIG (ConfigFileApplicationListener)
spring.config.name= # config file name (default to 'application')
spring.config.location= # location of config file
# PROFILES
spring.profiles= # comma list of active profiles
# APPLICATION SETTINGS (SpringApplication)
spring.main.sources=
spring.main.web-environment= # detect by default
spring.main.show-banner=true
spring.main....= # see class for all properties
# LOGGING
logging.path=/var/logs
logging.file=myapp.log
logging.config=
# IDENTITY (ContextIdApplicationContextInitializer)
spring.application.name=
spring.application.index=
其余略。。。。。。。。。。。。。。。。。。。。。。。。。。。。

自定义配置

springboot web程序默认启用的端口是8080,如果我们想更换端口改为8888怎么办呢?
其实很简单,步骤如下:

  1. 在项目resources文件夹下新建application.properties,另外也支持yml文件,但是我们推荐使用properties这种配置文件
  2. 在application.properties配置文件里添加如下配置即可
#更改springboot内嵌tomcat服务器端口
server.port=8888

除了在我们自己新建的application.properties配置文件里修改默认配置外,我们还可以添加自定义的配置,例如有这样一个需求,我们项目里用的文件服务器是阿里的fastdfs,需要自定义fastdfs的配置,都以fdfs开头

#fastdfs tracker配置信息
fdfs.trackerServer=192.168.1.100:22122
#fastdfs storage内网组名
fdfs.intranetGruop=group1
#fastdfs nginx模块访问地址
fdfs.showPrefixUrl=192.168.9.100:80

我们的fasterdfs模块初始化链接的时候,需要读取配置文件,我们只需要在对应的配置信息类里用注解@ConfigurationProperties(prefix = “fdfs”)引入前缀为fdfs的配置文件即可

/**
 * FastdfsInfo配置信息类
 * @author fqh
 * @email fanqinghui100@126.com
 * @date 2017/6/24 22:31
 */
@Component
@ConfigurationProperties(prefix = "fdfs")
public class FastdfsInfo {
    private String trackerServer;
    private String intranetGruop;
    private String showPrefixUrl;

    //set get方法略

    @Override
    public String toString() {
        return "FastdfsInfo{" +
                "trackerServer='" + trackerServer + '\'' +
                ", intranetGruop='" + intranetGruop + '\'' +
                ", showPrefixUrl='" + showPrefixUrl + '\'' +
                '}';
    }
}

引入外部配置文件

springboot不单从application.properties文件里获取配置,还可以在程序里进行额外的自定义配置
可以在Application启动类里使用@ImportResource注解进行引入,例如我的支付项目愿支付里加载了额外的spring配置文件,就用到了这个方式

/**
 * Spring Boot应用启动类
 */
@MapperScan("com.wish.pay.web.dao")
@ComponentScan("com.wish.pay")
@SpringBootApplication
@ImportResource("classpath:spring/spring-context-pay.xml")
public class WebApplication extends SpringBootServletInitializer {
    /**
     * 无 applicationContext.xml 和 web.xml, 靠下述方式进行配置:
     * <p>
     * 1. 扫描当前package下的class设置自动注入的Bean<br/>
     * 2. 也支持用@Bean标注的类配置Bean <br/>
     * 3. 根据classpath中的三方包Class及集中的application.properties条件配置三方包,如线程池 <br/>
     * 4. 也支持用@Configuration标注的类配置三方包.
     */
    public static void main(String[] args) {
        SpringApplication.run(WebApplication.class, args);
    }
}

配置文件多环境设置

一般公司里项目都有开发环境,测试环境,预上线环境,跟生成环境,每个环境里的一些配置值都是不同的,例如数据库之类的配置等。
springboot对多环境设置这块可谓是特别简单的,例如我们设置2个环境,开发环境dev与测试环境test,除了默认的application.properties配置文件外只需要新建2个配置文件application-dev.properties与application-test.properties,然后新建的每个配置文件里设置各自的属性,在默认的application.properties里只需要添加一行即可

#指定springboot默认的开发环境为dev
spring.profiles.active=dev

可以参考我github上愿支付的配置

结尾

关于springboot配置这块应该就这么多内容,很简单吧,敲敲代码练习一下吧

springboot基础一:HelloWorld

缘起

springboot是一个封装springMvc等相关功能的开发框架,如果要开展新项目,建议使用springboot,一些常用功能都进行了封装,省去了我们自己去做框架架构,另外springboot的开发效率还是很高的,有几个朋友还没有接触过springboot,恰巧最近有时间,所以做一个springboot的快速入门教程,几个系列,从spring基础到springboot整合mybatis,redis,dubbo,elasticsearch等,希望一起进步

HelloWorld

  1. 新建一个maven工程
  2. pom文件里加入springboot的parent
<!-- Spring Boot 启动父依赖 -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.4.RELEASE</version>
 </parent>
  1. 依赖里加入springboot web依赖
 <!-- Spring Boot web依赖 -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
  1. 新建xxx.xxx.web 包,并在包下建立Application启动类
/**
 * Spring Boot应用启动类
 * Created by fqh on 17/6/24.
 */
 //springBootApplication注解用来标注此类是springboot启动类
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
  1. 在xxx.xxx.web包下面建一个HelloWorldController类
    注意:未来所有web的Controller类必须与Application这个springboot启动类同包目录或者在子包目录里。
/**
 *  启动访问类rest
 *  Created by fqh on 17/6/24.
 */
@RestController
public class HelloWorldController {
    @RequestMapping("/")
    public String sayHello() {
        return "Hello,World!";
    }
}
  1. 运行Application
    在java开发工具里直接右键Application,选择run运行即可启动项目
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.5.4.RELEASE) 
..............................
............中间这些信息略.....................
2017-06-24 12:20:38.810  INFO 59808 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2017-06-24 12:20:38.914  INFO 59808 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
2017-06-24 12:20:38.925  INFO 59808 --- [           main] com.wish.action.web.Application          : Started Application in 7.244 seconds (JVM running for 8.982)

可以从启动里看到,web项目已经启动,启动是用内嵌的Tomcat服务器,端口是默认的8080

  1. 测试
    在浏览器里输入:http://localhost:8080/
    浏览器打印 Hello,World!
    很简单吧。继续跟着学习吧!

Protobuf学习

缘起

平常程序要进行网络通讯,数据交换的时候,我们常用的数据传递方式就是json或者xml,但是json与xml进行数据传递,在网络传输过程中是透明的(除非服务端与客户端进行加密解密处理)通过抓包工具就可以知道你服务或者接口间传递的数据格式,怎么通过一些方案来解决这个问题呢(先不考虑服务端客户端接口加密方式,因为一些小型项目完全没有必要任意接口都加密),所以这种情况下搜索了Protobuf,这个出师名门的家伙网络传输直接是二进制流,完全可以解决我们的问题

简介

Protobuf是出自谷歌的一个数据交互方式,解决谷歌内部服务间传递数据,谷歌这个全球数一数二的公司随便一个业务都是TB,PB级别的,假如采用xml进行数据交互,xml的反序列化特别慢,很消耗性能,所以数据交换性能提高哪怕1%,这效果都是特别明显的,谷歌向来不缺乏造轮子的牛人,造出了序列号与反序列化变态快,占用空间又很小的Protobuf

优缺点

  • 优点
    1. 性能好,效率高,网络传输占用空间小
    2. 代码有生成机制,数据解析类自动生成
    3. 支持向后兼容和向前兼容
    4. 支持多种编程语言(java,golang, c++,python等)
  • 缺点
    1. 二进制格式导致可读性差
    2. 应用不够广发,相关文档资源不够全面

实例

  • maven依赖
<dependency>
    <groupId>com.google.protobuf</groupId>
    <artifactId>protobuf-java</artifactId>
    <version>3.2.0</version>
</dependency>
  • 编写.proto文件Person-entity.proto
option java_outer_classname = "PersonEntity";//生成的数据访问类的类名  
message Person {  
  required int32 id = 1;  
  required string name = 2;
  optional string email = 3;
}  
  • 使用protoc.exe编译成java类
cmd命令窗口里输入以下命令,并且进行替换相关目录
protoc.exe -I=proto的输入目录 --java_out=java类输出目录 proto的输入目录包括包括proto文件
  • 测试
 public static void main(String[] args) throws IOException {
        //模拟将对象转成byte[],方便传输
        PersonEntity.Person.Builder builder = PersonEntity.Person.newBuilder();
        builder.setId(1);
        builder.setName("ant");
        builder.setEmail("ghb@soecode.com");
        PersonEntity.Person person = builder.build();
        System.out.println("before :"+ person.toString());

        System.out.println("===========Person Byte==========");
        for(byte b : person.toByteArray()){
            System.out.print(b);
        }
        System.out.println();
        System.out.println(person.toByteString());
        System.out.println("================================");

        //模拟接收Byte[],反序列化成Person类
        byte[] byteArray =person.toByteArray();
        Person p2 = Person.parseFrom(byteArray);
        System.out.println("after :" +p2.toString());
    }

Java实用技能 之 接口作为参数传递

简述

Java里的方法参数可以使用接口,实际在使用时可以将实现了接口的类传递给方法,也可以new一个接口进行传递,在接口的实现类里进行相关的业务实现,
这在实际开发里很多场景会遇到。

下面是我实际缓存项目里的实例接口,希望对各位同学有帮助!

示例代码

接口

  1. 有返回值的模版Action接口
public interface JedisAction<T> {
        T action(Jedis jedis);
}
  1. 没有返回值的模版Action接口
public interface JedisActionNoResult {
        void action(Jedis jedis);
}

方法

  1. 有返回结果的执行方法
public  T execute(JedisAction jedisAction) throws JedisException {
        Jedis jedis = null;
        boolean broken = false;
        try {
            jedis = jedisPool.getResource();//从jedis连接池里获取连接
            return jedisAction.action(jedis);
        } catch (JedisException e) {
            broken = handleJedisException(e);//出现异常,连接归还连接池
            throw e;
        } finally {
            closeResource(jedis, broken);//释放资源
        }
    }
  1. 没有返回结果的执行方法
public void execute(JedisActionNoResult jedisAction) throws JedisException {
        Jedis jedis = null;
        boolean broken = false;
        try {
            jedis = jedisPool.getResource();//从jedis连接池里获取连接资源
            jedisAction.action(jedis);
        } catch (JedisException e) {
            broken = handleJedisException(e);//出现异常,连接归还连接池
            throw e;
        } finally {
            closeResource(jedis, broken);//释放资源
        }
    }

执行

  1. 无需返回值的方法执行,用基于jedis的set举例
/**
 * 将字符串值 value 关联到 key 
 *  如果 key 已经持有其他值, SET 就覆写旧值,无视类型
 * ps:value虽然可以放入最多不超过1GB的字符串。但还是存入的越短越好
 */
public void set(final String key, final String value) {
    execute(new JedisActionNoResult() {//这里new了一个不需要结果的接口类
        @Override
        public void action(Jedis jedis) {
            jedis.set(key, value);
        }
    });
}
  1. 有返回值的方法执行,用基于jedis的get举例
/**
 * 根据key获得string类型的value
 * /
public String get(final String key) {
    return execute(new JedisAction() {//这里new了一个需要结果的接口类
        @Override
        public String action(Jedis jedis) {
            return jedis.get(key);
        }
    });
}
完毕,希望对大家有帮助!