分佈式軟件架構整合(二)

(續上篇)java

所需技術:spring、mybatis、druid、flyway、logback、nodejs,zookeeper,dubbo,dubbo-admin等;
說明:本編講解一個分佈式架構的整個流程,首先對上編中的demo工程做一些小小的改動,以支持接下來的實驗。特別說明,zookeeper做爲服務註冊中心,dubbo-admin做爲註冊中心管理客戶端,雖然都是開源軟件,但本章不做安裝部署講解。node

clipboard.png

目錄結構調整上圖所示,紅框中,做爲調整內容,其它內容不變。spring

簡要

本篇引入了阿里公司開源的分佈式服務框架[dubbo][1]。本篇中,定義了provider服務提供者,zookeeper註冊中心及客戶端消費者三者關係。provider是數據源,基於底層的數據交互都在這裏完成。provider將服務暴露給zookeeper註冊中心,provider的全部服務都由zookeeper進行統一管理。client做爲消費者,鏈接到註冊中心獲取相應的服務。
本文中消費者部分由nodejs編寫。

1、Provider服務提供者

這裏首先對服務端進行改造。提供兩個接口,各一個方法。DemoService接口中提供一個方法,客戶端傳入String型參數,處理此參數後返回客戶端。IUserService接口中提供一個根據id查詢用戶信息的方法。方法實現同上篇文中介紹同樣不變。部分代碼以下:

DemoServiceImpl.javasql

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.javaexpress

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.xmljson

<?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.xmlapi

添加服務啓動測試類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();
    }
}

運行測試類,輸出如下信息:babel

……
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註冊中心要先啓動,啓動方法略)cookie

2、服務消費者部分

消費者部分採用node工程,這裏也簡單建立了一個node工程進行鏈接實驗。目錄結構以下:

clipboard.png

這裏,必須的文件有三個文件,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://dubbo.io/

相關文章
相關標籤/搜索