OneJane
文章69
标签83
分类12

好客租房dubbo

基于dubbo react elk实战整合开发

整体架构

  • 后端架构:SpringBoot+StringMVC+Dubbo+Mybatis+ELK+区块链
  • 前端架构:React.js+html5+百度地图+微信小程序
    enter description here

    前端

    初始化haoke-manage-web
tyarn install
tyarn start

enter description here

修改logo及版权信息

src/layouts/BasicLayout.js 全局的布局文件

<SiderMenu![itcast-haoke-manage-web](./attachments/itcast-haoke-manage-web.zip)
  logo={logo}
  Authorized={Authorized}
  theme={navTheme}
  onCollapse={this.handleMenuCollapse}
  menuData={menuData}
  isMobile={isMobile}
  {...this.props}
/>

src/components/SiderMenu/index.js

<SiderMenu
        {...props}
        flatMenuKeys={getFlatMenuKeys(menuData)}
        collapsed={isMobile ? false : collapsed}
      />

src/components/SiderMenu/SiderMenu.js

<div className={styles.logo} id="logo">
  <Link to="/">
    {/* <img src={logo} alt="logo" /> */}
    <h1>好客租房 · 管理系统</h1>
  </Link>
</div>

src/layouts/Footer.js

<Footer style={{ padding: 0 }}>
  <GlobalFooter
    copyright={
      <Fragment>
        Copyright <Icon type="copyright" /> 2018 黑马程序员 博学谷 出品
      </Fragment>
    }
  />
</Footer>

新增房源

config/router.config.js

{ path: '/', redirect: '/house/resource' },       // 进入系统默认打开房源管理
{ //房源管理
  path: '/house',
  name: 'house',
  icon: 'home',
  routes: [
    {
      path: '/house/resource',
      name: 'resource',
      component: './haoke/House/Resource'
    },
    {
      path: '/house/addResource',
      name: 'addResource',
      component: './haoke/House/AddResource'
    },
    {
      path: '/house/kanfang',
      name: 'kanfang',
      component: './haoke/House/KanFang'
    },
    {
      path: '/house/zufang',
      name: 'zufang',
      component: './haoke/House/ZuFang'
    }
  ]
},

src/pages/haoke/House/AddResource.js

@Form.create() 对页面进行了包装,包装之后,会在this.props中增加form对象,将拥有getFieldDecorator 双向绑定等功能,经过 getFieldDecorator 包装的控件,表单控件会自动添加 value (或 valuePropName 指定的其他属性) onChange (或 trigger 指定的其他属性),数据同步将被 Form 接管

  1. 你不再需要也不应该用 onChange 来做同步,但还是可以继续监听 onChange 等事件。
  2. 你不能用控件的 value defaultValue 等属性来设置表单域的值,默认值可以用getFieldDecorator 里的 initialValue,利用rule进行参数规则校验
  3. 你不应该用 setState ,可以使用 this.props.form.setFieldsValue 来动态改变表单值。

表单提交

<Button type="primary" htmlType="submit" loading={submitting}>
<Form onSubmit={this.handleSubmit} hideRequiredMark style={{ marginTop: 8 }}>        进行提交拦截
handleSubmit = e => {            通过form.validateFieldsAndScroll()对表单进行校验,通过values获取表单中输入的值。通过dispatch()调用model中定义的方法。
    const { dispatch, form } = this.props;
    e.preventDefault();
  console.log(this.state.fileList);
    form.validateFieldsAndScroll((err, values) => {
        if (!err) {
          if(values.facilities){
            values.facilities = values.facilities.join(",");
          }
          if(values.floor_1 && values.floor_2){
            values.floor = values.floor_1 + "/" + values.floor_2;
          }
          values.houseType = values.houseType_1 + "室" + values.houseType_2 + "厅"
                             + values.houseType_3 + "卫" + values.houseType_4 + "厨"
                             + values.houseType_2 + "阳台";
          delete values.floor_1;
          delete values.floor_2;
          delete values.houseType_1;
          delete values.houseType_2;
          delete values.houseType_3;
          delete values.houseType_4;
          delete values.houseType_5;
          dispatch({
                type: 'form/submitRegularForm',
                payload: values,
            });
        }
    });
};

自动完成

const estateMap = new Map([
  ['中远两湾城','1001|上海市,上海市,普陀区,远景路97弄'],
  ['上海康城','1002|上海市,上海市,闵行区,莘松路958弄'],
  ['保利西子湾','1003|上海市,上海市,松江区,广富林路1188弄'],
  ['万科城市花园','1004|上海市,上海市,闵行区,七莘路3333弄2区-15区'],
  ['上海阳城','1005|上海市,上海市,闵行区,罗锦路888弄']
]);
<AutoComplete
  style={{ width: '100%' }}
  dataSource={this.state.estateDataSource}
  placeholder="搜索楼盘"
  onSelect={(value, option)=>{
    let v = estateMap.get(value);
    this.setState({
      estateAddress: v.substring(v.indexOf('|')+1),
      estateId : v.substring(0,v.indexOf('|'))
    });
  }}
  onSearch={this.handleSearch}
  filterOption={(inputValue, option) => option.props.children.toUpperCase().indexOf(inputValue.toUpperCase()) !== -1}
/>
通过onSearch进行动态设置数据源,这里使用的数据是静态数据
handleSearch = (value)=>{
  let arr = new Array();
  if(value.length > 0 ){
    estateMap.forEach((v, k) => {
      if(k.startsWith(value)){
        arr.push(k);
      }
    });
  }
  this.setState({
    estateDataSource: arr
  });
} ;
通过onSelect设置 选中楼盘后,在楼盘地址中填写地址数据
onSelect={(value, option)=>{
  let v = estateMap.get(value);
  this.setState({
    estateAddress: v.substring(v.indexOf('|')+1),
    estateId : v.substring(0,v.indexOf('|'))
  });
}}

图片上传

父组件通过属性的方式进行引用子组件,自组件在bind方法中改变this的引用为父组件
<FormItem {...formItemLayout} label="上传室内图">
    <PicturesWall handleFileList={this.handleFileList.bind(this)}/>
</FormItem>
父组件中获取数据
handleFileList = (obj)=>{
  console.log(obj, "图片列表");
}

src/pages/haoke/Utils/PicturesWall.js

在子组件中通过this.props获取父组件方法传入的函数,进行调用,即可把数据传递到父组件中
handleChange = ({ fileList }) => {
  this.setState({ fileList });
  this.props.handleFileList(this.state.fileList);
}
<Upload
    action="1111111"
    listType="picture-card"
    fileList={fileList}
    onPreview={this.handlePreview}
    onChange={this.handleChange}
>
    {fileList.length >= 5 ? null : uploadButton}
</Upload>

后端

enter description here
后台系统服务采用RPC+微服务的架构思想,RPC采用dubbo架构作为服务治理框架,对外接口采用RESTFul+GraphQL接口方式。

  • 单一应用架构
    当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。此时,用于简化增删改查
    工作量的数据访问框架(ORM)是关键。
  • 垂直应用架构
    当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,将应用拆成互不相干的几个应用,以提升效率。此
    时,用于加速前端页面开发的Web框架(MVC)是关键。
  • 分布式服务架构
    当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中
    心,使前端应用能更快速的响应多变的市场需求。此时,用于提高业务复用及整合的分布式服务框架(RPC)是关
    键。
  • 流动计算架构
    当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管
    理集群容量,提高集群利用率。此时,用于提高机器利用率的资源调度和治理中心(SOA)是关键。
    enter description here
    调用关系说明
  1. 服务容器负责启动,加载,运行服务提供者。
  2. 服务提供者在启动时,向注册中心注册自己提供的服务。
  3. 服务消费者在启动时,向注册中心订阅自己所需的服务。
  4. 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
  5. 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
  6. 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
    流程说明:
  • 服务提供者启动时: 向 /dubbo/com.foo.BarService/providers 目录下写入自己的 URL 地址
  • 服务消费者启动时: 订阅 /dubbo/com.foo.BarService/providers 目录下的提供者 URL 地址。并向/dubbo/com.foo.BarService/consumers 目录下写入自己的 URL 地址
  • 监控中心启动时: 订阅 /dubbo/com.foo.BarService 目录下的所有提供者和消费者 URL 地址。
    支持以下功能:
  • 当提供者出现断电等异常停机时,注册中心能自动删除提供者信息
  • 当注册中心重启时,能自动恢复注册数据,以及订阅请求
  • 当会话过期时,能自动恢复注册数据,以及订阅请求
  • 当设置 <dubbo:registry check=”false” /> 时,记录失败注册和订阅请求,后台定时重试
  • 可通过 <dubbo:registry username=”admin” password=”1234” /> 设置 zookeeper 登录信息
  • 可通过 <dubbo:registry group=”dubbo” /> 设置 zookeeper 的根节点,不设置将使用无根树
  • 支持 * 号通配符 <dubbo:reference group=”*” version=”*” /> ,可订阅服务的所有分组和所有版本的提供者

zk安装

apt-get install --reinstall systemd  -y  
apt-get install -y docker.io
systemctl start docker
docker create --name zk -p 2181:2181 zookeeper:3.5
docker start zk    可以通过ZooInspector连接查看

yum install -y yum-utils device-mapper-persistent-data lvm2
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
yum makecache fast
yum list docker-ce --showduplicates | sort -r
yum install -y docker-ce-18.09.5
systemctl restart docker
docker create --name zk -p 2181:2181 zookeeper:3.5
docker start zk 

ZooInspector

执行ZooInspector\build\start.bat
enter description here
查看zk信息

服务提供方

dubbo/pom.xml

<!--添加SpringBoot parent支持-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.0.RELEASE</version>
    </parent>
    <dependencies>
        <!--添加SpringBoot测试-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--添加dubbo的springboot依赖-->
        <dependency>
            <groupId>com.alibaba.boot</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>0.2.0</version>
        </dependency>

        <!--添加dubbo依赖-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>dubbo</artifactId>
            <version>2.6.4</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <!--添加springboot的maven插件-->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

dubbo/dubbo-service/pom.xml

    <dependencies>
        <!--添加springboot依赖,非web项目-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.4.13</version>
        </dependency>
        <dependency>
            <groupId>com.github.sgroschupf</groupId>
            <artifactId>zkclient</artifactId>
            <version>0.1</version>
        </dependency>
    </dependencies>

com/onejane/dubbo/pojo/User.java

// 使用dubbo要求传输的对象必须实现序列化接口
public class User implements java.io.Serializable {

    private static final long serialVersionUID = -7341603933521593227L;

    private Long id;

    private String username;

    private String password;

    private Integer age;

}

com/onejane/dubbo/service/UserService.java

public interface UserService {
    List<User> queryAll();
}

com/onejane/dubbo/service/impl/UserServiceImpl.java

@Service(version = "${dubbo.service.version}") //声明这是一个dubbo服务
public class UserServiceImpl implements UserService {
   public List<User> queryAll() {
      List<User> list = new ArrayList<User>();
      for (int i = 0; i < 10; i++) {
         User user = new User();
         user.setAge(10 + i);
         user.setId(Long.valueOf(i + 1));
         user.setPassword("123456");
         user.setUsername("username_" + i);
         list.add(user);
      }
      System.out.println("---------Service 3------------");
      return list;
   }
}

application.properties

# Spring boot application
spring.application.name = dubbo-service
server.port = 9090

# Service version
dubbo.service.version = 1.0.0

# 服务的扫描包
dubbo.scan.basePackages  =com.onejane.dubbo.service

# 应用名称
dubbo.application.name = dubbo-provider-demo

# 协议以及端口
dubbo.protocol.name = dubbo
dubbo.protocol.port = 20882

# zk注册中心
dubbo.registry.address = zookeeper://192.168.3.237:2181
dubbo.registry.client = zkclient

com/onejane/dubbo/DubboProvider.java

@SpringBootApplication
public class DubboProvider {
    public static void main(String[] args) {
        new SpringApplicationBuilder(DubboProvider.class)
                .web(WebApplicationType.NONE) // 非 Web 应用
                .run(args);
    }
}

服务消费方

dubbo/dubbo-comsumer/pom.xml

 <dependencies>
        <!--添加springboot依赖,非web项目-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.4.13</version>
        </dependency>
        <dependency>
            <groupId>com.github.sgroschupf</groupId>
            <artifactId>zkclient</artifactId>
            <version>0.1</version>
        </dependency>
        <!--引入service的依赖-->
        <dependency>
            <groupId>com.onejane</groupId>
            <artifactId>dubbo-service</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

application.properties

# Spring boot application
spring.application.name = dubbo-consumer
server.port = 9091

# 应用名称
dubbo.application.name = dubbo-consumer-demo

# zk注册中心
dubbo.registry.address = zookeeper://192.168.3.237:2181
dubbo.registry.client = zkclient

com/onejane/dubbo/UserServiceTest.java

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserServiceTest {
    // 负载均衡策略 默认随机 测试时启动dubbo.protocol.port多个不同端口的userService服务,并修改打印值进行区分
    // loadbalance = "roundrobin"设置负载均衡策略
    @Reference(version = "1.0.0", loadbalance = "roundrobin")
    private UserService userService;
    @Test
    public void testQueryAll() {
        for (int i = 0; i < 100; i++) {
            System.out.println("开始调用远程服务 >>>>>" + i);
            List<User> users = this.userService.queryAll();
            for (User user : users) {
                System.out.println(user);
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

启动服务后,即可测试
enter description here

Dubbo Admin

tar zxf incubator-dubbo-ops.tar.gz -C /usr/local/
tar zxf apache-maven-3.6.0-bin.tar.gz -C /usr/local/
vim incubator-dubbo-ops/dubbo-admin-backend/src/main/resources/application.properties
dubbo.registry.address=zookeeper://192.168.3.237:2181
vim /etc/profile
如误操作导致基础命令丢失,export PATH=/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
export MAVEN_HOME=/usr/local/apache-maven-3.6.0
export JAVA_HOME=/usr/local/jdk
export PATH=$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$MAVEN_HOME/bin:$PATH
source /etc/profile
cd /usr/local/incubator-dubbo-ops && mvn clean install 
vim dubbo-admin-backend/src/main/resources/application.properties server.port=8888
mvn --projects dubbo-admin-backend spring-boot:run

http://192.168.3.237:8888
Dubbo 缺省协议不适合传送大数据量的服务,比如传文件,传视频等,除非请求量很低。
Dubbo 缺省协议dubbo:// 协议采用单一长连接和 NIO 异步通讯,适合于小数据量大并发的服务调用,以及服务消费者机器数远
大于服务提供者机器数的情况。

Transporter (传输): mina, netty, grizzy
Serialization(序列化): dubbo, hessian2, java, json
Dispatcher(分发调度): all, direct, message, execution, connection
ThreadPool(线程池): fixed, cached

  • 连接个数:单连接
  • 连接方式:长连接
  • 传输协议:TCP
  • 传输方式:NIO 异步传输
  • 序列化:Hessian 二进制序列化
  • 适用范围:传入传出参数数据包较小(建议小于100K),消费者比提供者个数多,单一消费者无法压满提供
  • 者,尽量不要用 dubbo 协议传输大文件或超大字符串。
  • 适用场景:常规远程服务方法调用
本文作者:OneJane
本文链接:https://onejane.github.io/2019/08/21/好客租房dubbo/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可