Springboot dubbo zookeeper 实现简单的注册后台搭建

mac2024-04-22  12

这几天工作没什么事,学习下Springboot dubbo zookeeper

:介绍下学习内容:

1:zookeeper做服务注册和管理中心 2:dubbo服务调度 3:mybatis、springboot集成 总的来说就是通过这套框架实现了一个简单的注册demo(这里不强调注册逻辑中的细节)

zookeeper安装及部署

环境:CentOS7、zookeeper:3.4.14(没有用zookeeper的最新版,因为集群部署的时候有个奇怪的坑)、docker。 现在只要是部署啊,安装啊什么的很多情况下都是docker安装了,这里废话不多,直接上zookeeper的部署流程

1:在usr/local下创建docker文件夹,再在下面创建zookeeper文件夹

/usr/local/docker/zookeeper

2:创建docker-compose.yml文件

vi docker-compose.yml #以下是配置文件 services: zoo1: image: zookeeper:3.4.14 restart: always hostname: zoo1 ports: - 2182:2181 environment: ZOO_MY_ID: 1 ZOO_SERVERS: server.1=zoo1:2888:3888 server.2=zoo2:2888:3888 server.3=zoo3:2888:3888 zoo2: image: zookeeper:3.4.14 restart: always hostname: zoo2 ports: - 2183:2181 environment: ZOO_MY_ID: 2 ZOO_SERVERS: server.1=zoo1:2888:3888 server.2=zoo2:2888:3888 server.3=zoo3:2888:3888 zoo3: image: zookeeper:3.4.14 restart: always hostname: zoo3 ports: - 2184:2181 environment: ZOO_MY_ID: 3 ZOO_SERVERS: server.1=zoo1:2888:3888 server.2=zoo2:2888:3888 server.3=zoo3:2888:3888

配置好了记住保存,千万记住wq,还有版本最好不要省去,可能会出现集群安装失败的问题!保存后启动,在当前文件夹下(与docker-compose.yml同级)

docker-compose up -d

启动后看下启动成功没有,启动成功了后随便进入一个容器去查看下集群模式启动成功没有

查看容器

docker ps

交互模式进入容器查看(这里应该有三个zookeeper的容器启动了,可以随便进入一个去查看,这里进入的是第三个)

docker exec -it zookeeper_zoo3_1 /bin/bash #然后检查启动状态 ./bin/zkServer.sh status

如果出现以下表示集群启动成功了,进入其他两个查看状态的话Mode应follower,表示我们zookeeper以及配置并且启动好了

root@zoo3:/zookeeper-3.4.14# ./bin/zkServer.sh status ZooKeeper JMX enabled by default Using config: /conf/zoo.cfg Mode: leader

向dubbo暴露服务接口

首先创建一个maven项目(官方例子是将服务接口,服务实现(provider),服务调用(consumer))三个单独分开来做的,没有一个统一的pom来管理,我多了一步是先创建了一个统一的包来管理这接口、提供、消费) 这里的demoPom就是一个最简单的一个maven项目包,在pom.xml中加入打包类型就可以了因为这个只用起到管理作用,所以src目录没有什么用了,直接删除即可

<packaging>pom</packaging>

创建完成后在包中新建module 依然是创建一个maven项目,这个模块只提供接口,版本号建议与项目的版本号相同,其他没有多余的设置,直接下一步下一步就完事儿了 创建完成后这个接口模块中的pom.xml需要添加

<packaging>jar</packaging>

注:需要去看看demoPom中的pom.xml是否自动添加了 如果没有自动添加的话,需要自己手动添加自己创建模块的artifactId 这是我本例子中的接口模块的目录结构 在api包中定义了一个interface:

package com.kj.demopom.user.api; import com.kj.demopom.user.domain.User; public interface RegistUser { int registUser(User user); //用户注册 boolean flagUser(String name); //因为数据库name字段设置了主键,所以要判断数据库有没有相同的name值 }

domain中是一个user的实体类(必须要实现序列化接口):

package com.kj.demopom.user.domain; import java.io.Serializable; public class User implements Serializable { private String name; private String password; private String old; public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getOld() { return old; } public void setOld(String old) { this.old = old; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", password='" + password + '\'' + ", old='" + old + '\'' + '}'; } }

接口和实体类定义好了,首先我们先clean打包了,用maven自带的打包工具就可以啦啦,或者 运行下就可以了,提示build success表示成功了 或者直接控制台 mvn clean install,进行build,build成功后要刷新一下maven

构建服务提供模块(provider)

还是先在再创建一个模块,这次不是创建maven项目了。创建一个Spring Initializr模块啦。 项目名这些搞完了过后选择依赖的时候可以不用选择,因为这个是提供服务的(要实现接口,方便其他服务调用),所以不是web项目,其他的依赖看你自己,也可以什么都不选择。

然后next后选一下目录然后就完成了,第一次构建可能有点慢,需要等等。

1:添加依赖

spring dubbo: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>com.alibaba.boot</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>0.2.0</version> </dependency>

mysql mybatis:

<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.0</version> </dependency>

自己的接口(接口在本地没有上传服务器,如果报错肯定是clean时没有成功):

<dependency> <groupId>com.kj</groupId> <artifactId>demoPom-user-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency>

以上依赖就是本例子用到的依赖。 2:配置文件: 全部采用yml文件配置(有些地方打了***,基本上看的出来是什么,不是骂人哈,嘻嘻)

spring: application: name: demopom-user-provider datasource: url: jdbc:mysql://localhost:3306/demoPom?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT username: root password: ******** driver-class-name: com.mysql.jdbc.Driver mybatis: mapper-locations: classpath:mapper/*.xml user: service: version: 1.0.0 dubbo: scan: basePackages: com.kj.demopom.user.provider.api.impl application: id: demopom-user-provider name: demopom-user-provider registry: id: zookeeper address: zookeeper://1**.1**.**.1**:2182?backup=1**.1**.**.1**:2183,1**.1**.**.1**:2184

配置完成后,就可以开心的写代码了。(注册中心zookeeper的主机ip和端口,一定要看准啊)。 以下是provider这个服务的结构,简单解释一下: 1、comment里面放的是常量类,放一些常量 2、mapper,就不用多说了,mybatis的接口 3、service下有两个包,第一个包里面的RegistUserImpl是实现了自己定义的接口,RegistUserFunctionApi是为了实现RegistUserImpl中的一些方法而定义的接口,RegistUserFunctionApiImpl是对这个接口的实现 上代码: StatusArgs.java

package com.kj.demopom.user.provider.api.impl.comment; public class StatusArgs { public static final int Flag_User_SUCCESS = 0; //验证成功,不存在当前name public static final int Flag_User_FAILED = 1; //验证失败,存在当前name }

RegistUserFunctionMapper.java

package com.kj.demopom.user.provider.api.impl.mapper; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Repository; @Mapper @Repository public interface RegistUserFunctionMapper { //查询数据库中存在当前name吗 int RegistFlagUser(String name); //添加用户 int RegistUserIntsert(@Param("name") String name, @Param("password") String password, @Param("old") String old); }

RegistUserImpl.java

package com.kj.demopom.user.provider.api.impl.service.apiIpml; import com.alibaba.dubbo.config.annotation.Service; import com.kj.demopom.user.api.RegistUser; import com.kj.demopom.user.domain.User; import com.kj.demopom.user.provider.api.impl.service.RegistUserFunctionApi; import com.kj.demopom.user.provider.api.impl.comment.StatusArgs; import org.springframework.beans.factory.annotation.Autowired; @Service(version = "1.0.1") public class RegistUserImpl implements RegistUser { private boolean flagBLN; //用于接收查询name结果的参数 private int registUserIntsert = 0; //判断保存用户成功的参数 @Autowired private RegistUserFunctionApi registUserFunctionApi; @Override public int registUser(User user) { flagBLN = flagUser(user.getName()); if (flagBLN){ //如果name的判断通过,即不存在当前name执行insert registUserIntsert = registUserFunctionApi.RegistUserIntsert(user); } return registUserIntsert; } @Override public boolean flagUser(String name) { int flagNum = registUserFunctionApi.RegistFlagUser(name);//查询name flagBLN = flagNum == StatusArgs.Flag_User_SUCCESS; return flagBLN; } }

RegistUserFunctionApi.java

package com.kj.demopom.user.provider.api.impl.service; import com.kj.demopom.user.domain.User; public interface RegistUserFunctionApi { public int RegistFlagUser(String name); public int RegistUserIntsert(User user); }

RegistUserFunctionApiImpl.java

package com.kj.demopom.user.provider.api.impl.service.Impl; import com.kj.demopom.user.domain.User; import com.kj.demopom.user.provider.api.impl.comment.StatusArgs; import com.kj.demopom.user.provider.api.impl.mapper.RegistUserFunctionMapper; import com.kj.demopom.user.provider.api.impl.service.RegistUserFunctionApi; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class RegistUserFunctionApiImpl implements RegistUserFunctionApi { @Autowired private RegistUserFunctionMapper registUserFunctionMapper; @Override public int RegistFlagUser(String name) { int flagUser = registUserFunctionMapper.RegistFlagUser(name); //查询name后返回值不等于0就等于1,用于判断 if (flagUser != StatusArgs.Flag_User_SUCCESS){ flagUser = StatusArgs.Flag_User_FAILED; } return flagUser; } @Override public int RegistUserIntsert(User user) { return registUserFunctionMapper.RegistUserIntsert(user.getName(),user.getPassword(),user.getOld()); } }

RegistUserFunctionMapper.xml

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.kj.demopom.user.provider.api.impl.mapper.RegistUserFunctionMapper"> <select id="RegistFlagUser" parameterType="String" resultType="int"> select count(name) from user u where u.name = #{name} </select> <insert id="RegistUserIntsert" parameterType="String" useGeneratedKeys="true" > insert into user values(#{name},#{password},#{old}) </insert> </mapper>

到此provider这个模块完成了

构建服务消费模块(consumer)

和provider不一样,这个模块是一个web项目,所以新建的时候选择依赖的时候将Sping Web勾选上 其他的基本一致, 同样需要依赖自己的接口和dubbo,在pom.xml中添加:

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>com.alibaba.boot</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>0.2.0</version> </dependency>

自己的接口:

<dependency> <groupId>com.kj</groupId> <artifactId>demoPom-user-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency>

如果一开始没有选择web的依赖,需要添加:

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>

application.yml(除了没有数据库和mybatis的配置其他基本一样):

spring: application: name: demopom-user-consumer user: service: version: 1.0.0 dubbo: scan: basePackages: com.kj.demopom.user.consumer.controller application: id: demopom-user-consumer name: demopom-user-consumer registry: id: zookeeper address: zookeeper://1**.1**.**.1**:2182?backup=1**.1**.**.1**:2183,1**.1**.**.1**:2184

这是用于测试的三个页面,第一个是失败跳转,第二个是注册页,第三个是成功的页面, 注册页面:

<!DOCTYPE html> <html lang="zh" xmlns:th="http://www.w3.org/1999/xhtml"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/user/registed" method="post"> 用户名:<input type="text" name="name"><br> 密码:<input type="text" name="password"><br> 年龄:<input type="text" name="old"><br> <button type="submit">提交</button> </form> </body> </html>

成功页面:

<!DOCTYPE html> <html lang="zh" xmlns:th="http://www.w3.org/1999/xhtml"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1 th:text="${user.name}"> </h1> </body> </html>

页面很简单注册成功转跳并显示当前注册的name参数

模块的结构: PageName:存放页面的名称的常量类 StatusArgs:存放固定参数的常量类 RegistController:处理业务逻辑,调用接口 RegistService:处理业务逻辑的接口 RegistServiceImpl:实现RegistService接口 代码: PageName.java

package com.kj.demopom.user.consumer.comment; public class PageName { public static final String PAGE_NAME_REGIST = "regist"; public static final String PAGE_NAME_REGIST_FAILED = "faild"; public static final String PAGE_NAME_REGIST_SUCCESS = "success"; }

StatusArgs.java

package com.kj.demopom.user.consumer.comment; public class StatusArgs { public static final int REGIST_USER_FAILED = 0; public static final String USER_PARAM_NAME = "user"; }

RegistController.java

package com.kj.demopom.user.consumer.controller; import com.alibaba.dubbo.config.annotation.Reference; import com.kj.demopom.user.api.RegistUser; import com.kj.demopom.user.consumer.comment.PageName; import com.kj.demopom.user.consumer.comment.StatusArgs; import com.kj.demopom.user.consumer.service.RegistService; import com.kj.demopom.user.domain.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import javax.annotation.PostConstruct; import javax.servlet.http.HttpSession; @Component @Controller public class RegistController { @Autowired private RegistService registService; @Reference(version = "1.0.1") private RegistUser registUser; @PostConstruct public void init(){ //想registService中初始化registUser, // 直接在registService中不能直接调用registUser, // 会报nullpoint 甚至启动时候报错服务找不到 registService.setRegistUser(registUser); } @GetMapping(value = "/user/regist") public String showRegist(){ return registService.showRegist(); } @PostMapping(value = "/user/registed") public String Registing(User user, HttpSession session){ int registing = registService.Registing(user); if (registing == StatusArgs.REGIST_USER_FAILED){ return PageName.PAGE_NAME_REGIST_FAILED; }else{ session.setAttribute(StatusArgs.USER_PARAM_NAME,user); return PageName.PAGE_NAME_REGIST_SUCCESS; } } }

RegistService.java

package com.kj.demopom.user.consumer.service; import com.kj.demopom.user.api.RegistUser; import com.kj.demopom.user.domain.User; public interface RegistService { String showRegist(); int Registing(User user); void setRegistUser(RegistUser registUser); }

RegistServiceImpl.java

package com.kj.demopom.user.consumer.service.Impl; import com.alibaba.dubbo.config.annotation.Reference; import com.kj.demopom.user.api.RegistUser; import com.kj.demopom.user.consumer.comment.PageName; import com.kj.demopom.user.consumer.service.RegistService; import com.kj.demopom.user.domain.User; import org.springframework.stereotype.Component; import org.springframework.stereotype.Service; @Service() @Component public class RegistServiceImpl implements RegistService { int registUser_FLAG; @Reference(version = "1.0.1") private RegistUser registUser; public void setRegistUser(RegistUser registUser) { this.registUser = registUser; } @Override public String showRegist() { return PageName.PAGE_NAME_REGIST; } @Override public int Registing(User user) { System.out.println(user.toString()); String username = user.getName(); boolean flagUser = registUser.flagUser(user.getName()); if (flagUser){ registUser_FLAG = this.registUser.registUser(user); } return registUser_FLAG; } }

说明下:在Controller中可以直接调用 @Reference(version = “1.0.1”) private RegistUser registUser; 并且得到接口实例,但是如果是在service中就不能直接得到,本人觉得业务逻辑隐藏起来是比较好的,所以借用Service来调用接口,达到隐蔽的效果,就为什么service中直接得到实例为null,目前我也不清楚,目前能想到的方法是在controller中使用初始化的方式来自动的注入进service。

效果: 启动provider后再启动 consumer,由于没有安装dubbo的admin,所以只能在zookeeper中查看服务同样随便交互进入一个zookeeper中,在bin中启动zkCli.sh 客户端

查看节点: ls / 查看节点中注册的服务: ls /节点名

进入dubbo中查看,可以发现这个服务,表名这个服务以及注册到zookeeper中了 访问 http://localhost:8080/user/regist/ 返回成功结果: 数据库: 说明结果是正确的,通过zookeeper和dubbo成功实现了接口的远程调用。 实现了服务端与消费端的通信

最新回复(0)