(续上篇)
所需技术:spring、mybatis、druid、flyway、logback、nodejs,zookeeper,dubbo,dubbo-admin等;
说明:本编讲解一个分布式架构的整个流程,首先对上编中的demo工程作一些小小的改动,以支持接下来的实验。特别说明,zookeeper作为服务注册中心,dubbo-admin作为注册中心管理客户端,虽然都是开源软件,但本章不作安装部署讲解。
目录结构调整上图所示,红框中,作为调整内容,其它内容不变。
简要
本篇引入了阿里公司开源的分布式服务框架[dubbo][1]。本篇中,定义了provider服务提供者,zookeeper注册中心及客户端消费者三者关系。provider是数据源,基于底层的数据交互都在这里完成。provider将服务暴露给zookeeper注册中心,provider的所有服务都由zookeeper进行统一管理。client作为消费者,连接到注册中心获取相应的服务。
本文中消费者部分由nodejs编写。
一、Provider服务提供者
这里首先对服务端进行改造。提供两个接口,各一个方法。DemoService接口中提供一个方法,客户端传入String型参数,处理此参数后返回客户端。IUserService接口中提供一个根据id查询用户信息的方法。方法实现同上篇文中介绍一样不变。部分代码如下:
DemoServiceImpl.java
package com.soyann.provider.demo.impl;
import com.soyann.provider.demo.DemoService;
/**
* @ProjectName: soyann
* @FileName: com.soyann.provider.demo.impl
* @Description: (do what)
* @Copyright: Copyright(C) 2016-2017 All rights Reserved
* @Company: ShenZhen Information Technology Co.,LTD.
* @Author: neil
* @Version V1.0
* @Date: 2017/11/17
* <p>
* Modification History:
* Date Author Version Discription
* -----------------------------------------------------------------------------------
* 2017/11/17 neil 1.0 1.0
* Why & What is modified: <修改原因描述>
*/
public class DemoServiceImpl implements DemoService {
@Override
public String sayHello(String name) {
return "Hello Dubbo,Hello " + name;
}
}
UserServiceImpl.java
package com.soyann.provider.user.service.impl;
import com.soyann.provider.user.mapper.UserMapper;
import com.soyann.provider.user.model.UserEntity;
import com.soyann.provider.user.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @ProjectName: soyann
* @FileName: com.soyann.business.user.service.impl
* @Description: (do what)
* @Copyright: Copyright(C) 2016-2017 All rights Reserved
* @Company: ShenZhen Information Technology Co.,LTD.
* @Author: dell657 neil
* @Version V1.0
* @Date: 2017/10/28
* <p>
* Modification History:
* Date Author Version Discription
* -----------------------------------------------------------------------------------
* 2017/10/28 neil 1.0 1.0
* Why & What is modified: <修改原因描述>
*/
@Service("userService")
public class UserServiceImpl implements IUserService {
@Autowired(required=false)
private UserMapper userMapper;
@Override
public UserEntity getUserById(int userId) {
return userMapper.selectByPrimaryKey(userId);
}
@Override
public List<UserEntity> getAllUser() {
return userMapper.getAllUser();
}
}
spring-provider.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!--定义了提供方应用信息,用于计算依赖关系;在 dubbo-admin 或 dubbo-monitor 会显示这个名字,方便辨识-->
<dubbo:application name="soyann-provider" owner="programmer" organization="dubbo"/>
<!--使用 zookeeper 注册中心暴露服务,注意要先开启 zookeeper-->
<dubbo:registry id="zk-provider" address="zookeeper://localhost:2181"/>
<!-- 用dubbo协议在20880端口暴露服务 -->
<dubbo:protocol name="dubbo" port="20880"/>
<!--使用 dubbo 协议实现定义好的 api.demoService 接口-->
<dubbo:service interface="com.soyann.provider.demo.DemoService" ref="demoService" registry="zk-provider"
timeout="3000" version="1.0.0" group="g-demo"/>
<!--具体实现该接口的 bean-->
<bean id="demoService" class="com.soyann.provider.demo.impl.DemoServiceImpl"/>
<!--使用 dubbo 协议实现定义好的 api.userService 接口-->
<dubbo:service interface="com.soyann.provider.user.service.IUserService" ref="userService"
registry="zk-provider" timeout="3000" version="2.0.0" group="g-soyann"/>
<!--具体实现该接口的 bean-->
<bean id="userService" class="com.soyann.provider.user.service.impl.UserServiceImpl"/>
</beans>
在spring-applicationContext.xml中,引入spring-provider.xml
添加服务启动测试类TestProvider.java
package com.soyann.demo;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.io.IOException;
/**
* @ProjectName: soyann
* @FileName: com.soyann.demo
* @Description: (do what)
* @Copyright: Copyright(C) 2016-2017 All rights Reserved
* @Company: ShenZhen Information Technology Co.,LTD.
* @Author: neil
* @Version V1.0
* @Date: 2017/11/17
* <p>
* Modification History:
* Date Author Version Discription
* -----------------------------------------------------------------------------------
* 2017/11/17 neil 1.0 1.0
* Why & What is modified: <修改原因描述>
*/
public class TestProvider {
public static void main(String[] args) throws IOException {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring/spring-applicationContext.xml", "spring/spring-mybatis.xml");
System.out.println(context.getDisplayName() + ": here");
context.start();
System.out.println("服务已经启动...");
System.in.read();
}
}
运行测试类,输出以下信息:
……
18:45:48.299 [main] DEBUG ResultSet - {conn-10010, stmt-20003, rs-50003} Header: [installed_rank, version, description, type, script, checksum, installed_on, installed_by, execution_time, success]
18:45:48.299 [main] DEBUG ResultSet - {conn-10010, stmt-20003, rs-50003} closed
18:45:48.299 [main] DEBUG Statement - {conn-10010, stmt-20003} closed
18:45:48.299 [main] INFO DbMigrate - Current version of schema `soyann`: 1.0.1
18:45:48.299 [main] WARN DbMigrate - Schema `soyann` has version 1.0.1, but no migration could be resolved in the configured locations !
18:45:48.300 [main] DEBUG Statement - {conn-10010, pstmt-20004} created. SELECT RELEASE_LOCK('Flyway--294804349')
18:45:48.300 [main] DEBUG Statement - {conn-10010, pstmt-20004} Parameters : []
18:45:48.300 [main] DEBUG Statement - {conn-10010, pstmt-20004} Types : []
18:45:48.300 [main] DEBUG Statement - {conn-10010, pstmt-20004} executed. 0.414796 millis. SELECT RELEASE_LOCK('Flyway--294804349')
18:45:48.300 [main] DEBUG Statement - {conn-10010, pstmt-20004} clearParameters.
18:45:48.300 [main] INFO DbMigrate - Schema `soyann` is up to date. No migration necessary.
18:45:48.301 [main] DEBUG Connection - {conn-10010} setAutoCommit false
18:45:48.301 [main] DEBUG Connection - {conn-10010} commited
18:45:48.301 [main] DEBUG Connection - {conn-10010} setAutoCommit true
18:45:48.302 [main] DEBUG Connection - {conn-10010} pool-recycle
18:45:48.527 [main] DEBUG SqlSessionFactoryBean - Parsed configuration file: 'class path resource [mybatis/mybatis-config.xml]'
18:45:48.674 [main] DEBUG SqlSessionFactoryBean - Parsed mapper file: 'file [D:\workspace\JAVA\soyann\provider\target\classes\mybatis\mapper\sqlmap-mapping-user.xml]'
org.springframework.context.support.ClassPathXmlApplicationContext@f2a0b8e: here
服务已经启动...
这样表示服务已经发布成功!(zookeeper注册中心要先启动,启动方法略)
二、服务消费者部分
消费者部分采用node工程,这里也简单创建了一个node工程进行连接实验。目录结构如下:
这里,必须的文件有三个文件,package.json,server.js,soyannClient-route.js
package.json
{
"name": "nodeClient",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "babel src -d lib"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"cookie-parser": "^1.4.3",
"express": "^4.16.2",
"http": "^0.0.0",
"js-to-java": "^2.4.0",
"morgan": "^1.9.0",
"node-zookeeper-dubbo": "^2.2.1"
},
"devDependencies": {
"babel-cli": "^6.26.0",
"babel-preset-env": "^1.6.1"
}
}
server.js
/**
* @ProjectName: nodeClient
* @FileName:
* @Description: (服务主入口)
* @Copyright: Copyright(C) 2016-2017 All rights Reserved
* @Company: ShenZhen Information Technology Co.,LTD.
* @Author: dell657 neil
* @Version V1.0
* @Date: 2017/11/17
*
* Modification History:
* Date Author Version Discription
* -----------------------------------------------------------------------------------
* 2017/11/17 neil 1.0 1.0
* Why & What is modified: <修改原因描述>
*/
const logger = require('morgan'),
http = require('http'),
express = require('express'),
bodyParser = require('body-parser'),
debug = require('debug')('mydebug:http'),
cookieParser = require('cookie-parser');
const app = express();
//设置跨域访问
app.all('*', function (req, res, next) {
if(req.headers.origin == 'http://localhost:4200' || req.headers.origin == 'http://ng2.com'){
res.header("Access-Control-Allow-Origin",req.headers.origin); //设置跨域访问
res.header('Access-Control-Allow-Credentials', 'true');
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
res.header('Access-Control-Allow-Headers', 'Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With, Authorization');
res.header("Content-Type", "application/json");
}
next();
});
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());
app.use(function (err, req, res, next) {
if (err.name === 'StatusError') {
res.send(err.status, err.message);
} else {
next(err);
}
});
/*引用模块*/
app.use(require('./modules/soyannClient-route'));
//app.use(require('./modules/zkclient-route'));
/*监听端口*/
const port = process.env.PORT || 8090;
http.createServer(app).listen(port, function (err) {
console.log('Node服务已启动,请访问:http://localhost:' + port);
});
soyannClient-route.js
/**
* @ProjectName: nodeClient
* @FileName:
* @Description: (do what)
* @Copyright: Copyright(C) 2016-2017 All rights Reserved
* @Company: ShenZhen Information Technology Co.,LTD.
* @Author: dell657 neil
* @Version V1.0
* @Date: 2017/11/17
*
* Modification History:
* Date Author Version Discription
* -----------------------------------------------------------------------------------
* 2017/11/17 neil 1.0 1.0
* Why & What is modified: <修改原因描述>
*/
const express = require('express'),
debug = require('debug')('mydebug:http'),
zookeeperClient = require('node-zookeeper-dubbo');
const app = module.exports = express.Router();
const opt = {
application: {name: 'soyann-provider'},
register: 'localhost:2181',
dubboVer: '2.5.7',
root: 'dubbo',
dependencies: {
Demo: {
interface: 'com.soyann.provider.demo.DemoService',
version: '1.0.0',
timeout: 6000,
group: 'g-demo',
methodSignature: {
sayHello: sayHello = (name) => (java) => [ java.String(name) ]
}
},
Soyann: {
interface: 'com.soyann.provider.user.service.IUserService',
version: '2.0.0',
timeout: 6000,
group: 'g-soyann',
methodSignature: {
getUserById: getUserById = (id) => [ {'$class': 'int', '$': id} ]
}
}
}
};
opt.java = require('js-to-java');
const Dubbo = new zookeeperClient(opt);
/*传参数实例*/
app.post('/api/sayHello',(req,res)=>{
debug(req);
Dubbo.Demo
.sayHello(req.body.name)
.then(data=>res.send(data))
.catch(err=>res.send(err))
});
/*传递整形参数*/
app.post('/api/getUserById',(req,res)=>{
Dubbo.Soyann
.getUserById(Number(req.body.id))
.then(data=>res.send(data))
.catch(err=>res.send(err))
});
/*访问实例*/
app.get('/api/demo',(req,res)=>{
const result={'result':'Hello world Neil!'};
debug(result);
res.status(200).send(result);
});
这个工程中,使用了node-zookeeper-dubbo这个开源模块,使得node与java通信成为现实。
启动工程,后面不用再说了吧,本篇完结。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。