配置管理平台Apollo搭建指南

Acheron注:

Apollo 的官方文档写得非常友好全面,官方地址:https://github.com/ctripcorp/apollo/wiki

这里只是记录一下我在服务器上搭建的过程。

一、环境要求

  • Java: 1.8+
    • 检查:java -version
  • MySql:5.6.5+
    • 检查:SHOW VARIABLES WHERE Variable_name = 'version';

二、部署步骤

2.1 导入数据库生成脚本

官方提供的数据库脚本有两个,在scripts/sql下,apolloportaldb.sqlapolloconfigdb.sql

// 创建:ApolloPortalDB
source /your_local_path/sql/apolloportaldb.sql
// 创建:ApolloConfigDB
source /your_local_path/sql/apolloconfigdb.sql
// 检查是否导入成功:
select `Id`, `Key`, `Value`, `Comment` from `ApolloPortalDB`.`ServerConfig` limit 1;
select `Id`, `Key`, `Value`, `Comment` from `ApolloConfigDB`.`ServerConfig` limit 1;

2.2 Apollo自身的一些配置

2.2.1 配置ApolloPortalDB.ServerConfig

  • 1.apollo.portal.envs – 可支持的环境列表:

默认值是dev,多个以逗号分隔即可(大小写不敏感),如:

DEV,FAT,UAT,PRO

注意:只在数据库添加环境是不起作用的,需要配合修改scripts/build.sh,添加新增环境对应的meta server地址。

  • organizations – 部门列表:
[{"orgId":"TEST1","orgName":"样例部门1"},{"orgId":"TEST2","orgName":"样例部门2"}]
  • wiki.address: portal上“帮助”链接的地址,默认是Apollo github的wiki首页。

####2.2.2 配置ApolloConfigDB.ServerConfig

  • 1.eureka.service.url – Eureka服务Url,如有多个,用逗号分隔(注意不要忘了/eureka/后缀):
http://1.1.1.1:8080/eureka/,http://2.2.2.2:8080/eureka/

2.3 配置数据库连接信息

  • vim scripts/build.sh
#apollo config db info
apollo_config_db_url=jdbc:mysql://localhost:3306/ApolloConfigDB?characterEncoding=utf8
apollo_config_db_username=用户名
apollo_config_db_password=密码(如果没有密码,留空即可)

# apollo portal db info
apollo_portal_db_url=jdbc:mysql://localhost:3306/ApolloPortalDB?characterEncoding=utf8
apollo_portal_db_username=用户名
apollo_portal_db_password=密码(如果没有密码,留空即可)

2.4 配置各环境meta service地址

  • vim scripts/build.sh:修改各环境meta service服务地址。 如果某个环境不需要,也可以直接删除对应的配置项
dev_meta=http://localhost:8080
fat_meta=http://localhost:8080
uat_meta=http://localhost:8080
pro_meta=http://localhost:8080

2.5 执行编译、打包

./build.sh 

2.6 部署运行

2.6.1 部署apollo-configservice

apollo-configservice/target/目录下的apollo-configservice-x.x.x-github.zip上传到服务器上,解压后执行scripts/startup.sh即可。如需停止服务,执行scripts/shutdown.sh.

cd apollo-configservice/target/
scp apollo-configservice-x.x.x-github.zip 209:/opt/apollo/config
ssh 209
cd /opt/apollo/config
unzip apollo-configservice-x.x.x-github.zip
运行:./scripts/startup.sh
停止:./scripts/shutdown.sh

注:如要调整服务的监听端口,可以修改startup.sh中的SERVER_PORT。另外apollo-configservice同时承担meta server职责,如果要修改端口,注意要同时修改scripts/build.sh中的meta server url信息以及ApolloConfigDB.ServerConfig表中的eureka.service.url配置项。

2.6.2 部署apollo-adminservice

同上:

apollo-adminservice/target/目录下的apollo-adminservice-x.x.x-github.zip上传到服务器上,解压后执行scripts/startup.sh即可。如需停止服务,执行scripts/shutdown.sh.

注:如要调整服务的监听端口,可以修改startup.sh中的SERVER_PORT

2.6.3 部署apollo-portal

同上:

apollo-portal/target/目录下的apollo-portal-x.x.x-github.zip上传到服务器上,解压后执行scripts/startup.sh即可。如需停止服务,执行scripts/shutdown.sh.

apollo-portal的默认端口是8080,和apollo-configservice一致,所以如果需要在一台机器上同时启动apollo-portal和apollo-configservice的话,需要修改apollo-portal的端口。直接修改startup.sh中的SERVER_PORT即可,如SERVER_PORT=8070

三、Java客户端使用

3.1 配置Appid

classpath:/META-INF/app.properties文件:

app.id=YOUR-APP-ID

3.2 配置Environment

  • 对于Mac/Linux,文件位置为/opt/settings/server.properties
  • 对于Windows,文件位置为C:\opt\settings\server.properties

保证settings目录文件权限:chmod 777 /opt/settings

文件内容形如:

env=DEV

3.3 配置本地缓存路径

本地缓存路径位于以下路径,所以请确保/opt/dataC:\opt\data\目录存在,且应用有读写权限。

  • Mac/Linux: /opt/data/{appId}/config-cache
  • Windows: C:\opt\data{appId}\config-cache

保证data目录文件权限:chmod 777 /opt/data

3.4 配置日志地址

保证logs目录文件权限:chmod 777 /opt/logs

3.5 Maven Dependency

<dependency>
        <groupId>com.ctrip.framework.apollo</groupId>
        <artifactId>apollo-client</artifactId>
        <version>0.7.0</version>
 </dependency>

3.6 使用

@Configuration
@EnableApolloConfig
public class AppConfig {
  
}
@ApolloConfig
private Config config;

@Test
public void testApollo(){
	String name = config.getProperty("name", "hello");
    assertEquals(name,"Acheron");
}

使用canal实现redis缓存刷新

背景

假设现有项目P1,是直连数据库(mysql)的。为了提高性能,现在要加上缓存(redis),但是另有项目P2,也对数据库有操作,如何保证当P1加上redis后,P2对数据库修改后,P1从redis中获取的数据是最新的。有两种方案:

  • 一种是P1的开发人员和P2开发人员协调,当P2对mysql有修改、增加等操作,必须清除P1使用到的相关缓存。当P1读redis时,发现缓存失效,再读mysql,获取最新数据。
  • 一种是利用阿里巴巴的canal,获得mysql-binlog,根据mysql-binlog清除相关的缓存。这种方法的好处是不需要修改原来的程序。

使用到的技术

部署配置canal

canal的部署可以参考官方文档:https://github.com/alibaba/canal/wiki/QuickStart 配置的时候要注意:

  • conf/canal.properties是canal本身的配置文件,基本上默认即可
  • conf/example/instance.properties:应用的配置参数,一般需要修改其中的数据库连接配置

使用

1.启动canal:

sh bin/startup.sh

canal 的官方例子: https://github.com/alibaba/canal/wiki/ClientExample

利用canal解析mysql-binlog,能够获取到执行的sql,修改的表数据等信息,我这里获取修改的表,根据表名去清除掉使用到这个表的相关缓存,所以这里有一个对应关系:表名--缓存key

这里表名和缓存key的对应关系,为了方便,我将其存在一个属性文件中:cache.properties,并提供一个web界面来维护这个文件。

2.配置cache.properties

在计算机上任一位置建一文件:cache.properties,在项目的application.properties中有一属性:cache.properties.path,指向cache.properties文件的位置:

cache.properties.path=/Users/acheron/cache.properties

2.配置redis

util/RedisUtil.java中配置redis相关信息,主要是以下三项:

// Redis的地址
private static String HOST = "127.0.0.1";
// Redis的端口号
private static int PORT = 6379;
// 访问密码
private static String PASS = null;

3.添加表-缓存key配置

运行项目,浏览器访问,添加一条记录,数据将会保存到cache.properties

table_name1=herohuang'skey
table_name2=user.name.haha
table_name3=hahaha,com.hero,com.herohuang*
table_name4=com.hahaha,user.name.key,test

注意cachekey的配置:

  • 如果有多个,则用英文逗号隔开
  • 如果要删除所有以com.herohuang开头的缓存,则可以com.herohuang*

源码地址:https://github.com/Ac-heron/hexo-canal

界面展示

canal1canal2

分布式跟踪工具Pinpoint技术入门

本文初衷

因为工作需要,这周研究了一下APM(Application Performance Management)相关技术,看了些许资料,对比了各种技术的特点,最终选择Pinpoint作为性能监控工具,本文的主要目的是记录本人部署Pinpoint的过程。

Pinpoint是什么

简单的说,Pinpoint是一款对Java编写的大规模分布式系统的APM工具,有些人也喜欢称呼这类工具为调用链系统分布式跟踪系统。我们知道,前端向后台发起一个查询请求,后台服务可能要调用多个服务,每个服务可能又会调用其它服务,最终将结果返回,汇总到页面上。如果某个环节发生异常,工程师很难准确定位这个问题到底是由哪个服务调用造成的,Pinpoint等相关工具的作用就是追踪每个请求的完整调用链路,收集调用链路上每个服务的性能数据,方便工程师能够快速定位问题。

同类工具

为什么要用Pinpoint

最重要的原因,对代码的零侵入,运用JavaAgent字节码增强技术,只需要加启动参数即可。

Pinpoint架构图

pinpoint-architecture

(图片出处:官网)架构说明:

  • Pinpoint-Collector:收集各种性能数据
  • Pinpoint-Agent:和自己运行的应用关联起来的探针
  • Pinpoint-Web:将收集到的数据显示成WEB网页形式
  • HBase Storage:收集到的数据存到HBase中

开始部署

前提条件

  • maven 3.2.x+
  • git
  • java 6+

0. 开始

1. 部署Hbase

Pinpoint以Hbase作为数据的存储。

export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home
  • 编辑hbase-site.xml:
<configuration>
    <property>
        <name>hbase.rootdir</name>
        <value>file:///Users/acheron/Tmp/hbase</value>
    </property>
    <property>
        <name>hbase.zookeeper.property.dataDir</name>
        <value>/Users/acheron/Tmp/zookeeper</value>
    </property>
    <property>
        <name>hbase.zookeeper.property.clientPort</name>
        <value>2182</value>
  </property>
</configuration>

这样配置是本地单实例模式启动,上面配置分别表示:数据存放地点、zookeeper数据存放地点、zookeeper端口号(默认2181)。当然这个文件可以不配置,那么会采取默认值

启动hbase的时候,hbase用得是自带的zk,在hbase的配置里可见 export HBASE_MANAGES_ZK=true;

  • 进入hbase/bin目录启动hbase:./start-hbase.sh
  • jps命令查看Hbase是否启动成功,如果启动成功的会看到”HMaster”的进程
  • 初始化pinpoint需要的表:./hbase shell hbase-create.hbase (这里的hbase-create.hbase在源码pinpoint/hbase/scripts/hbase-create.hbase)
  • 访问页面测试是否成功:http://localhost:16010/master-status ,如果成功在页面的tables标签下能看到导入的表。
  • 也可以用命令来查看是否导入表成功,进入hbase,输入”status ‘detailed'”可以查看初始化的表
./hbase shell
status 'detailed'

2. 部署Pinpoint-collector

  • 将pinpoint-collector-1.6.0-SNAPSHOT.war放到tomcat-collector-8086/webapps/下,并重命名为ROOT.war
  • 启动tomcat,配置ROOT/WEB-INF/classes/hbase.properties:
hbase.client.host=localhost
hbase.client.port=2181

指向zookeeper的地址和端口,如果是本机,端口默认,则这里不需更改。

  • 配置上面后,重启tomcat(端口8086

3. 部署Pinpoint-web

Pinpoint-web的配置和Pinpoint-collector相似:

  • 将pinpoint-web-1.6.0-SNAPSHOT.war放到tomcat-web-8085/webapps/下,并重命名为ROOT.war
  • 启动tomcat,配置ROOT/WEB-INF/classes/hbase.properties:
hbase.client.host=localhost
hbase.client.port=2181

指向zookeeper的地址和端口,如果是本机,端口默认,则这里不需更改。

  • 配置上面后,重启tomcat(端口8085

4. 部署Pinpoint-agent

  • 新建目录:mkdir pp-agent
  • 将pinpoint-agent-1.6.0-SNAPSHOT.tar.gz 拷贝到pp-agent目录并解压
  • 配置pinpoint.config:profiler.collector.ip=127.0.0.1这是指pinpoint-collector的地址,如果是同一服务器,则不用修改。其它默认。
  • 安装pinpoint-collector启动后,自动就开启了9994,9995,9996的端口了,这里默认即可。如果有端口需求,要去pinpoint-collector的配置文件(“pinpoint-collector/webapps/ROOT/WEB-INF/classes/pinpoint-collector.properties”)中,修改这些端口。

5. 部署TestApp

  • 官方提供了一个测试用的应用,pinpoint-quickstart-testapp-null.war,将其重命名为ROOT.war,部署到tomcat-testapp-8084
  • 修改此tomat的/bin/catalina.sh,在106行左右加入启动参数:
CATALINA_OPTS="$CATALINA_OPTS -javaagent:/Users/acheron/pinpoint/pp-agent/pinpoint-bootstrap-1.6.0.jar"
CATALINA_OPTS="$CATALINA_OPTS -Dpinpoint.agentId=myapp"
CATALINA_OPTS="$CATALINA_OPTS -Dpinpoint.applicationName=MyTestPP"

第一行:pinpoint-bootstrap-1.6.0.jar的位置

第二行:这里的agentId必须唯一,标志一个jvm。

第三行:applicationName表示同一种应用:同一个应用的不同实例应该使用不同的agentId,相同的applicationName。

6. springboot包部署

如果是jar包部署,直接在启动命令加启动参数:

nohup java -javaagent:/Users/acheron/pinpoint/pp-agent/pinpoint-bootstrap-1.6.0.jar -Dpinpoint.agentId=acheron-consumer -Dpinpoint.applicationName=acheron-consumer -jar myapp.jar &

7.效果演示

hbase和各应用都启动成功之后,访问Pinpoint-web,效果:

pinpoint-page

结束语

各大APM工具,几乎都是根据google这篇经典的Dapper论文而来,一定要读一读。这里是它的源文地址:https://research.google.com/pubs/pub36356.html,感谢这位同学的翻译:http://bigbully.github.io/Dapper-translation/

参考文章

实时应用监控平台CAT安装和配置

CAT是什么

CAT是一个Java语言编写的实时监控系统,能够跟各种流行的中间件框架集成(MVC框架、RPC框架、数据库框架、缓存框架等),实现对应用各层级的系统运行状况监控。

CAT支持的监控消息类型

  • Transaction: 适合记录跨越系统边界的程序访问行为,比如远程调用,数据库调用,也适合执行时间较长的业务逻辑监控,Transaction用来记录一段代码的执行时间和次数。
  • Event :用来记录一件事发生的次数,比如记录系统异常,它和transaction相比缺少了时间的统计,开销比transaction要小。
  • Heartbeat: 表示程序内定期产生的统计信息, 如CPU%, MEM%, 连接池状态, 系统负载等。 Metric 用于记录业务指标、指标可能包含对一个指标记录次数、记录平均值、记录总和,业务指标最低统计粒度为1分钟。
  • Trace: 用于记录基本的trace信息,类似于log4j的info信息,这些信息仅用于查看一些相关信息

运行环境

  • Java 6 以上
  • Web 应用服务器,如:Apache Tomcat、JBoss Application Server、WebSphere Application Server、WebLogic Application Server(可选项,内置Netty应用服务器)
  • MySQL 数据库
  • Maven 3 以上(只编译和安装时需要)

开始安装

前提:已安装jdk、git、maven、mysql

  • 获取源码:
    git clone https://github.com/dianping/cat.git
    
  • 进入cat目录,执行maven构建命令
    cd cat
    clean install -Dmaven.test.skip=true
    

此处如果编译不成功,不能下载相关jar包,则可以手动下载,包在cat的mvn-repo分支下(https://github.com/dianping/cat/tree/mvn-repo) ,下载后拷贝到本地的maven仓库下

  git checkout mvn-repo
  cp -R * ~/.m2/repository
  • 将构建成功后的war包重命名,并拷贝到tomcat的webapp下
    mv cat-alpha-1.4.0.war cat.war
    cp cat.war tomcat/webapp
    
  • 启动tomcat,访问如下地址,后台默认登录catadmin/catadmin
    http://localhost:8080/cat/
    

创建数据库

  • 安装好mysql后,登录mysql,创建表空间
    create database cat;
    
  • 执行cat源码下的数据库脚本cat/script/Cat.sql,创建相关表
    use cat;
    source Cat.sql;
    

修改服务配置

  • /data/appdatas/cat/下,如果没有则新建此目录,确保这个目录有读写权限
    cd /data/appdatas/cat/
    
  • 将cat源码下script目录下的三个配置文件(client.xml、server.xml、datasources.xml)拷贝到上述目录
    cp /cat/script/*.xml /data/appdatas/cat/
    

*** 修改配置文件之前的几项假设:***

  • cat.war 包部署在10.8.40.26、10.8.40.27、10.8.40.28三台机器上,10.8.40.26为三台机器中的主服务器,TCP端口只能局域网内访问;
  • 数据库采用 MySQL安装在10.8.40.147上;
  • 暂不启用HDFS存储服务;
  • 暂不启用LDAP服务;

1. 修改客户端配置文件:client.xml

打开/data/appdatas/cat/client.xml客户端配置文件,

<config mode="client"
xmlns:xsi="http://www.w3.org/2001/XMLSchema"
xsi:noNamespaceSchemaLocation="config.xsd">
    <servers>
        <server ip="10.8.40.26" port="2280" http-port="8080" />
        <server ip="10.8.40.27" port="2280" http-port="8080" />
        <server ip="10.8.40.28" port="2280" http-port="8080" />
    </servers>
</config>

配置说明:

  • mode : 定义配置模式,固定值为client;–暂未使用
  • servers : 定义多个服务端信息;
  • server : 定义某个服务端信息;
  • ip : 配置服务端(cat-home)对外IP地址
  • port : 配置服务端(cat-home)对外TCP协议开启端口,固定值为2280;
  • http-port : 配置服务端(cat-home)对外HTTP协议开启端口, 如:tomcat默认是8080端口,若未指定,默认为8080

2. 修改数据库配置文件:datasources.xml

打开/data/appdatas/cat/datasources.xml数据库配置文件,

<data-sources>
    <data-source id="cat">
        <maximum-pool-size>3</maximum-pool-size>
        <connection-timeout>1s</connection-timeout>
        <idle-timeout>10m</idle-timeout>
        <statement-cache-size>1000</statement-cache-size>
        <properties>
            <driver>com.mysql.jdbc.Driver</driver>
            <url><![CDATA[jdbc:mysql://10.8.40.147:3306/cat]]></url>
            <user>root</user>
            <password>mysql</password>
            <connectionProperties>
                <![CDATA[useUnicode=true&autoReconnect=true]]>
            </connectionProperties>
        </properties>
    </data-source> 
    <data-source id="app">
        <maximum-pool-size>3</maximum-pool-size>
        <connection-timeout>1s</connection-timeout>
        <idle-timeout>10m</idle-timeout>
        <statement-cache-size>1000</statement-cache-size>
        <properties>
            <driver>com.mysql.jdbc.Driver</driver>
            <url><![CDATA[jdbc:mysql://10.8.40.147:3306/cat]]></url>
            <user>root</user>
            <password>mysql</password>
            <connectionProperties>
                <![CDATA[useUnicode=true&autoReconnect=true]]>
            </connectionProperties>
        </properties>
    </data-source>
</data-sources>

配置说明:

  • 主要修改项为:url(数据库连接地址)、user(数据库用户名)、password(数据用户登录密码)

3.修改服务端服务配置:server.xml

打开/data/appdatas/cat/server.xml服务端服务配置文件,

<config local-mode="false" hdfs-machine="false" job-machine="true" alert-machine="true">
    <storage local-base-dir="/data/appdatas/cat/bucket/" max-hdfs-storage-time="15" local-report-storage-time="7" local-logivew-storage-time="7">
        <hdfs id="logview" max-size="128M" server-uri="hdfs://10.8.40.31/user/cat" base-dir="logview"/>
        <hdfs id="dump" max-size="128M" server-uri="hdfs://10.8.40.32/user/cat" base-dir="dump"/>
        <hdfs id="remote" max-size="128M" server-uri="hdfs://10.8.40.33/user/cat" base-dir="remote"/>
    </storage>
    <console default-domain="Cat" show-cat-domain="true">
        <remote-servers>10.8.40.26:8080,10.8.40.27:8080,10.8.40.28:8080</remote-servers>
    </console>
    <ldap ldapUrl="ldap://10.8.40.21:389/DC=dianpingoa,DC=com"/>
</config>

配置说明:

  • local-mode : 定义服务是否为本地模式(开发模式),在生产环境时,设置为false,启动远程监听模式。默认为 false;
  • hdfs-machine : 定义是否启用HDFS存储方式,默认为 false;
  • job-machine : 定义当前服务是否为报告工作机(开启生成汇总报告和统计报告的任务,只需要一台服务机开启此功能),默认为 false;
  • alert-machine : 定义当前服务是否为报警机(开启各类报警监听,只需要一台服务机开启此功能),默认为 false;
  • storage : 定义数据存储配置信息
  • local-report-storage-time : 定义本地报告存放时长,单位为(天)
  • local-logivew-storage-time : 定义本地日志存放时长,单位为(天)
  • local-base-dir : 定义本地数据存储目录
  • hdfs : 定义HDFS配置信息,便于直接登录系统
  • server-uri : 定义HDFS服务地址
  • console : 定义服务控制台信息
  • remote-servers : 定义HTTP服务列表,(远程监听端同步更新服务端信息即取此值)
  • ldap : 定义LDAP配置信息
  • ldapUrl : 定义LDAP服务地址

4.修改路由配置

登入 cat系统,修改路由配置,打开浏览器,输入http://10.8.40.26:8080/cat/

配置--》全局告警配置--》客户端路由,进入配置路由界面:

<?xml version="1.0" encoding="utf-8"?>
<router-config backup-server="127.0.0.1" backup-server-port="2280">
   <default-server id="127.0.0.1" weight="0.8" port="2280" enable="true"/>
   <network-policy id="default" title="default" block="false" server-group="default_machine">
   </network-policy>
   <server-group id="default_machine" title="????">
      <group-server id="127.0.0.1"/>
   </server-group>
   <domain id="cat">
      <group id="default">
         <server id="127.0.0.1" port="2280" weight="1.0"/>
      </group>
   </domain>
</router-config>
  • 把backup-server设置为当前服务器对外IP地址,端口固定为2280;
  • default-server定义可跳转的路由地址,可以设置多个。default-server的id属性配置可路由的cat-home服务IP地址,端口固定为2280;若需要禁用路由地址,可把enable设置为false。
  • 点击“提交”按钮,保存修改的路由配置

5. 复制配置到27、28两机器

  • 拷贝 10.8.40.26机器/data/appdatas/cat/目录中client.xml、server.xml、datasources.xml三个配置文件到27、28两机器相同目录中
  • 修改server.xml配置中的 job-machine 和 alert-machine属性,都设置为false,禁用生成报告和报警功能,只开启监听功能
  • 启动27、28上的Tomcat,开启cat服务,完成服务端的配置及启动
  • 若服务端只分配一台服务器,按10.8.40.26完成安装配置即可

这里记录我安装配置的过程,更详细的文档查看官网

手把手教你实现一个网页聊天室

现在写文章都喜欢“手把手”,有一种老爸抱着三岁的儿子在路边撒尿的感觉,所以这篇的题目我也“手把手”一回,费话不说,开始撒尿:

使用到的技术

  • websocket
  • springmvc
  • maven
  • fastjson

这里使用到的主要技术是websocket,后端服务用java来写,其中springmvcmaven只是用于写java时的框架和构建工具,fastjson用于json数据格式的转换。

什么是websocket?

websocket是html5提供的一种在一个(TCP)接口进行全双工通信的技术,所谓全双工通信,简单点说就是通信的双方都可以同时向对方发送数据,类似于打电话时两方可以同时说话。

在没有websocket之前,实现聊天室这种实时的消息响应需求,一般会用长连接轮询的方式,在特定的的时间间隔(如每1秒),由客户端对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端,这种方式有很明显的缺点,客户端需要不断的向服务器发出请求,然而HTTP请求的header是非常长的,而我们需要的有用数据可能只是一个很小的值,这样会占用很多的带宽。而用websocket,浏览器和服务器只需要做一个握手的动作,两者之间就形成了一条快速通道,直接就可以互相传送数据了。

支持websocket的浏览器和服务器

websocket是html5的特性,浏览器至少要支持html5,服务器我这边用tomcat7.0.54(其它较高版本我这边不起作用!?),另外java版本要7以上:

浏览器支持 版本
Chrome 4+
Firefox 4+
Internet Explorer 10+
Opera 10+
Safari 5+
服务器支持 版本
tomcat 7.0.27+
jetty 7.0.1+
Nginx 1.3.13+
resin 4+

开始代码

websocket浏览器实现

浏览器通过javascript向服务器发起websocket请求,获取websocket连接后,浏览器和服务器就可以通过TCP连接直接交换数据了。

首先需要创建一个websocket对象:

var socket = new WebSocket('ws://localhost:8080/chatsocket');

这里的参数指定后台websocket服务的连接地址,不同于http://,websocket请求地址以ws://开头,接下来是主机的地址+端口号+后台服务名,这里的chatsocket是后面java后端所起的服务名字。其实第一次的握手连接,套接字还是以HTTP连接开始的,在HTTP握手之后才升级为TCP套接字,任意一端就都可以发送数据了。如果页面用得是JSP模板,地址和端口可以用JSP的pageContext获取,而不要直接写死:

  • 获取地址:${pageContext.request.serverName}
  • 获取端口:${pageContext.request.serverPort}
  • 获取上下文路径:${pageContext.request.contextPath}

websocket对象的方法

创建websocket对象后,它有一些相关属性、事件和方法,具体可以查看W3C的websocket API

比较重要的事件和方法主要有以下几个:

//连接成功建立的回调方法
socket.onopen = function (event) { console.log("连接服务器成功!"); };
//点击发送消息的方法,这里以json格式传送数据
socket.send(JSON.stringify({
    content: "这里是消息的内容"
    name:"发送人"
}));
//接收到消息的回调方法,一般将返回的数据append到消息页面
socket.onmessage = function (event) { 
    var message = JSON.parse(event.data);
    //dosomething...
    //发送人:message.name ,发送时间: message.date,发送内容:message.content
};

websocket服务器实现

第一步:加入信赖

java要使用websocket要加入相关信赖:

<dependency>
    <groupId>javax.websocket</groupId>
    <artifactId>javax.websocket-api</artifactId>
    <version>1.1</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-websocket</artifactId>
    <version>4.0.1.RELEASE</version>
</dependency>

注意:

  • tomcat的lib目录下有一个包websocket-api.jar,用这个包就可以了,所以部署的时候将上述的javax.websocket-api.jar删除再重新启动,否则两则重复,启动会报错。
  • spring-websocket-4.0.1.RELEASE.jar这个包的作用只是需要在websocket后端注入service所用的,否则删除这个包不影响websocket服务运行。
  • 具体spring对websocket的支持见这里

第二步:开启后端服务

新建一个java类,在这个类上加上ServerEndpoint注解,它就变成websocket服务了:

@ServerEndpoint(value = "/chatsocket",configurator = SpringConfigurator.class)
public class HudongEndpoint{

}

这里定义了两个参数:

  • value:服务的名字,前面js创建websocket时的url的服务名就是这个
  • configurator:如果要用spring注入servie,就要加上这个参数

第三步:接收消息的方法

和html5的websocket一样,后端也有相关操作方法,加上相关的注解就可以,比如:

  • @OnOpen:连接建立成功时调用的方法
  • @OnMessage:收到客户端消息后调用的方法
  • @OnClose:连接关闭调用的方法
  • @OnError:发生错误时调用的方法

这里主要说明下@OnMessage方法:

@OnMessage
public void getMessage(String message, Session session) {
    JSONObject jsonObject= JSONObject.parseObject(message);

    jsonObject.put("date", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
    for (Session openSession : session.getOpenSessions()) {
        jsonObject.put("isSelf", openSession.equals(session));
        openSession.getAsyncRemote().sendText(jsonObject.toString());
        //openSession.getBasicRemote().sendText(message);
    }
}

这个方法有两个参数:

  • message:前端发送过来的消息,json格式
  • session:这个参数可选,为与某个客户端的连接会话,需要通过它给客户端发送数据。如果要将聊天数据保存到数据库中,可以在这个方法中执行相关操作

总结

整个websocket发送消息和接收消息的过程,和http请求在使用上其实没多大区别,后台的一个ServerEndpoint类似于 springmvc的一个controller,相关的业务操作在对应的方法里就是了。这里只是简单的文本聊天,如果要加上表情和图片,可以使用富文本编辑器(Ueditor,kindeditor等),当然还可以加上计算实时在线人数,上线下线通知等功能。