Springboot整合dubbo搭建基本的消費、提供和負載均衡


 

 

1.確定接口

新建一個springboot項目,什么模塊都不用選,然后里面新建entity實體類和service接口。
如下圖:

User.java如下,這里需要注意的是要實現序列化接口。

public class User implements Serializable{
    private Long id;

    private String email;

    private String nickName;

    private String password;

    private String regTime;

    private String userName;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email == null ? null : email.trim();
    }

    public String getNickName() {
        return nickName;
    }

    public void setNickName(String nickName) {
        this.nickName = nickName == null ? null : nickName.trim();
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password == null ? null : password.trim();
    }

    public String getRegTime() {
        return regTime;
    }

    public void setRegTime(String regTime) {
        this.regTime = regTime == null ? null : regTime.trim();
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName == null ? null : userName.trim();
    }
}

UserService.java如下:

public interface UserService {

    int deleteByPrimaryKey(Long id);

    int insert(User record);

    User selectByPrimaryKey(Long id);

    List<User> selectAll();

    int updateByPrimaryKey(User record);

    Map<String, Object> addMethod();
}

 

2.創建提供者

因為要實現負載均衡,這里就需要創建兩個提供者。這里使用的是dubbo傳統的配置方式:xml。現在dubbo已經支持注解方式,不過個人更偏向於xml配置。
創建兩個Springboot項目:Provider和ProviderB。Provider和ProviderB基本配置和代碼都一樣,除了一個協議端口不一樣。這里就記錄Provider的配置方式。

2.1 pom配置

因為是用xml方式,在springboot項目里就引入最干凈的dubbo依賴庫。如果你是用注解的方式,需要引入springboot-dubbo的依賴庫,該庫在dubbo基礎上做了springboot適配。

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

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>dubbo</artifactId>
			<version>2.6.4</version>
			<exclusions>
				<exclusion>
					<groupId>org.springframework</groupId>
					<artifactId>spring</artifactId>
				</exclusion>
			</exclusions>
		</dependency>

		<dependency>
			<groupId>org.apache.zookeeper</groupId>
			<artifactId>zookeeper</artifactId>
			<version>3.4.12</version>
		</dependency>
		<!--<dependency>-->
			<!--<groupId>com.github.sgroschupf</groupId>-->
			<!--<artifactId>zkclient</artifactId>-->
			<!--<version>0.1</version>-->
		<!--</dependency>-->
		<dependency>
			<groupId>org.apache.curator</groupId>
			<artifactId>curator-recipes</artifactId>
			<version>4.0.0</version>
			<exclusions>
				<exclusion>
					<groupId>org.apache.zookeeper</groupId>
					<artifactId>zookeeper</artifactId>
				</exclusion>
			</exclusions>
		</dependency>

		<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.2</version>
		</dependency>

		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid-spring-boot-starter</artifactId>
			<version>1.1.10</version>
		</dependency>
	</dependencies>

 

 

這里不需要使用引入web依賴,因為只提供接口服務。然后需要引入mybatis、mysql模塊(這兩個在初始化springboot時勾選引入)。再加上dubbo,排除掉它自帶的sping庫,防止沖突。引入zookeeper庫,zookeeper客戶端依賴庫curator-recipes(排除沖突,去除zookeeper庫),再引入druid數據庫連接池。

2.2dubbo配置文件

通過xml方式配置,在resources下新建一個dubbo文件夾,然后創建兩個文件:dubbo.properties和dubbo-provider.xml,如下圖:

dubbo.properties寫一些配置參數,

dubbo.application.name=dubbo-provider
dubbo.registry.protocol=zookeeper
dubbo.registry.address1=172.16.30.100:2181
dubbo.registry.address2=172.16.30.101:2181
dubbo.registry.address3=172.16.30.101:2181
dubbo.protocol.name=dubbo
dubbo.protocol.port=20880

 

dubbo-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:application name="${dubbo.application.name}"/>

    <!-- 注冊中心暴露服務地址 -->
    <dubbo:registry protocol="${dubbo.registry.protocol}" address="${dubbo.registry.address1},${dubbo.registry.address2},${dubbo.registry.address3}"/>

    <!-- 暴露服務 -->
    <dubbo:protocol name="${dubbo.protocol.name}" port="${dubbo.protocol.port}"/>

    <dubbo:service interface="com.steven.xmldubbo.service.UserService"
                   ref="userServiceImpl" retries="0" timeout="6000" loadbalance="random"/>
</beans>

 

這里需要配置一個參數loadbalance="random",用來設定負載均衡策略。這里設置是Random,隨機的。
還有其他三種策略,參考官網:http://dubbo.apache.org/zh-cn/docs/user/demos/loadbalance.html

2.3 application.properties

這里只要配置數據庫訪問相關的參數就行。這里也貼一下吧

spring.datasource.name=datasource
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
#監控統計攔截的filters
spring.datasource.druid.filters=stat
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=true&useUnicode=true&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=root
#配置初始化大小/最小/最大
spring.datasource.druid.initial-size=1
spring.datasource.druid.min-idle=1
spring.datasource.druid.max-active=20
#獲取連接等待超時時間
spring.datasource.druid.max-wait=60000
#間隔多久進行一次檢測,檢測需要關閉的空閑連接
spring.datasource.druid.time-between-eviction-runs-millis=60000
#一個連接在池中最小生存的時間
spring.datasource.druid.min-evictable-idle-time-millis=300000
spring.datasource.druid.validation-query=SELECT 1
spring.datasource.druid.test-while-idle=true
spring.datasource.druid.test-on-borrow=false
spring.datasource.druid.test-on-return=false
#打開PSCache,並指定每個連接上PSCache的大小。oracle設為true,mysql設為false。分庫分表較多推薦設置為false
spring.datasource.druid.pool-prepared-statements=false
spring.datasource.druid.max-pool-prepared-statement-per-connection-size=20

#映射方式 配置下面這個就行了
#pojo類所在包路徑
mybatis.type-aliases-package=com.steven.xmldubbo.entity

#xml方式
#xml文件所在路徑
mybatis.mapper-locations=classpath:mybatis/mapper/*.xml
mybatis.config-location=classpath:mybatis/mybatis-config.xml

 

2.4 mybatis相關

2.4.1 配置UserMapper.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.steven.xmldubbo.dao.UserMapper">
  <resultMap id="BaseResultMap" type="com.steven.xmldubbo.entity.User">
    <id column="id" jdbcType="BIGINT" property="id" />
    <result column="email" jdbcType="VARCHAR" property="email" />
    <result column="nick_name" jdbcType="VARCHAR" property="nickName" />
    <result column="password" jdbcType="VARCHAR" property="password" />
    <result column="reg_time" jdbcType="VARCHAR" property="regTime" />
    <result column="user_name" jdbcType="VARCHAR" property="userName" />
  </resultMap>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Long">
    delete from user
    where id = #{id,jdbcType=BIGINT}
  </delete>
  <insert id="insert" parameterType="com.steven.xmldubbo.entity.User">
    insert into user (id, email, nick_name, 
      password, reg_time, user_name
      )
    values (#{id,jdbcType=BIGINT}, #{email,jdbcType=VARCHAR}, #{nickName,jdbcType=VARCHAR}, 
      #{password,jdbcType=VARCHAR}, #{regTime,jdbcType=VARCHAR}, #{userName,jdbcType=VARCHAR}
      )
  </insert>
  <update id="updateByPrimaryKey" parameterType="com.steven.xmldubbo.entity.User">
    update user
    set email = #{email,jdbcType=VARCHAR},
      nick_name = #{nickName,jdbcType=VARCHAR},
      password = #{password,jdbcType=VARCHAR},
      reg_time = #{regTime,jdbcType=VARCHAR},
      user_name = #{userName,jdbcType=VARCHAR}
    where id = #{id,jdbcType=BIGINT}
  </update>
  <select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
    select id, email, nick_name, password, reg_time, user_name
    from user
    where id = #{id,jdbcType=BIGINT}
  </select>
  <select id="selectAll" resultMap="BaseResultMap">
    select id, email, nick_name, password, reg_time, user_name
    from user
  </select>
</mapper>

 

2.4.2 配置UserMapper接口

在項目包下創建dao文件夾,然后創建UserMapper.java

@Repository
public interface UserMapper {
    int deleteByPrimaryKey(Long id);

    int insert(User record);

    User selectByPrimaryKey(Long id);

    List<User> selectAll();

    int updateByPrimaryKey(User record);
}

 

2.4.3 實現UserService接口

包下創建service文件夾,再創建impl文件夾,然后創建UserServiceImpl,實現UserService接口。
這里需要配置項目Provider依賴Interface,然后就找得到Interface里的UserService了

@Service
public class UserServiceImpl implements UserService{

    private Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);

    @Autowired
    private UserMapper userMapper;

    @Override
    public int deleteByPrimaryKey(Long id) {
        return userMapper.deleteByPrimaryKey(id);
    }

    @Override
    public int insert(User record) {
        return userMapper.insert(record);
    }

    /**
     * 增加調用方基本信息
     * @param id
     * @return
     */
    @Override
    public User selectByPrimaryKey(Long id) {
        // 本端是否為提供端,這里會返回true
        boolean isProviderSide = RpcContext.getContext().isProviderSide();
        // 獲取調用方IP地址
        String clientIp = RpcContext.getContext().getRemoteHost();
        // 獲取當前服務配置信息,所有配置信息都將轉換為URL的參數
        String url = RpcContext.getContext().getUrl().toFullString();

        logger.info("{} {} {}", isProviderSide, clientIp, url);
        return userMapper.selectByPrimaryKey(id);
    }

    @Override
    public List<User> selectAll() {
        return userMapper.selectAll();
    }

    @Override
    public int updateByPrimaryKey(User record) {
        return userMapper.updateByPrimaryKey(record);
    }

    /**
     * 接口新增一個方法測試
     * @return
     */
    @Override
    public Map<String, Object> addMethod() {
        Map<String,Object> result = Maps.newHashMap();
        result.put("attachment", true);
        String count = RpcContext.getContext().getAttachment("count");
        result.put("count", count);
        return result;
    }
}

 

2.5 讓dubbo配置生效

在項目包下新建一個config文件夾,創建DubboConfig.java

/**
 * com.steven.xmldubbo.config
 * Created by ZhiLiSteven
 * Date 2018/10/30
 */
@Configuration
@PropertySource("classpath:dubbo/dubbo.properties")
@ImportResource({"classpath:dubbo/*.xml"})
public class DubboConfig {
}

 

2.6 實現Springboot application

因為Provider不是web項目,所以啟動方式要調整。並且為防止它啟動main之后,因為不是以web方式啟動,直接退出,需要增加一個阻塞:

@SpringBootApplication
@MapperScan("com.steven.xmldubbo.dao")
public class XmldubboApplication implements CommandLineRunner{

	private Logger logger = LoggerFactory.getLogger(XmldubboApplication.class);

	@Bean
	public CountDownLatch countDownLatch() {
		return new CountDownLatch(1);
	}

	public static void main(String[] args) throws InterruptedException {
		ApplicationContext ctx = new SpringApplicationBuilder(XmldubboApplication.class)
				.web(WebApplicationType.NONE)//非web項目
				.run(args);
//		SpringApplication.run(XmldubboApplication.class, args);

		CountDownLatch countDownLatch = ctx.getBean(CountDownLatch.class);
		countDownLatch.await();
	}

	@Override
	public void run(String... args) throws Exception {
		logger.info("Dubbo Provider start now.................");
	}
}

 

至此,提供方搭建完成。
然后再新建一個ProviderB,其他都一樣,唯一區別就是項目名不一樣和dubbo.properties一個參數不一樣。
dubbo.protocol.port=20881
這里ProviderB改成20881
然后,在接口實現的一個方法里增加一個參數

    /**
     * 接口新增一個方法測試
     * @return
     */
    @Override
    public Map<String, Object> addMethod() {
        Map<String,Object> result = Maps.newHashMap();
        result.put("attachment", true);
        String count = RpcContext.getContext().getAttachment("count");
        result.put("count", count);
        //區別Provider和ProviderB,這里增加一個參數
        result.put("more", "providerB");
        return result;
    }

 

3.創建消費者

新建spingboot項目Consume,初始化時,勾選web就可以了。

3.1 pom依賴

然后項目依賴dubbo庫,zookeeper庫,curator-recipes這幾個,和Provider一樣。

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

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>dubbo</artifactId>
			<version>2.6.4</version>
			<exclusions>
				<exclusion>
					<groupId>org.springframework</groupId>
					<artifactId>spring</artifactId>
				</exclusion>
			</exclusions>
		</dependency>

		<dependency>
			<groupId>org.apache.zookeeper</groupId>
			<artifactId>zookeeper</artifactId>
			<version>3.4.12</version>
		</dependency>
		<!--<dependency>-->
		<!--<groupId>com.github.sgroschupf</groupId>-->
		<!--<artifactId>zkclient</artifactId>-->
		<!--<version>0.1</version>-->
		<!--</dependency>-->
		<!-- https://mvnrepository.com/artifact/org.apache.curator/curator-recipes -->
		<dependency>
			<groupId>org.apache.curator</groupId>
			<artifactId>curator-recipes</artifactId>
			<version>4.0.0</version>
			<exclusions>
				<exclusion>
					<groupId>org.apache.zookeeper</groupId>
					<artifactId>zookeeper</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
	</dependencies>

 

項目結構如下圖:

3.2 dubbo配置

配置和Provider差不多,在resources下新建dubbo文件夾,然后新建dubbo.properties和dubbo-consumer.xml

3.2.1 dubbo.properties
dubbo.application.name=dubbo-consumer
dubbo.registry.protocol=zookeeper
dubbo.registry.address1=172.16.30.100:2181
dubbo.registry.address2=172.16.30.101:2181
dubbo.registry.address3=172.16.30.102:2181

 

3.2.2 dubbo-consumer.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:application name="${dubbo.application.name}"/>

    <!-- 注冊中心暴露服務地址 -->
    <!-- <dubbo:registry address="multicast://224.5.6.7:1234" /> -->

    <dubbo:registry protocol="${dubbo.registry.protocol}" address="${dubbo.registry.address1},${dubbo.registry.address2},${dubbo.registry.address3}"/>

    <dubbo:reference interface="com.steven.xmldubbo.service.UserService"
                   id="userService" retries="0" timeout="6000"/>
</beans>

 

3.2.3 dubbo配置生效

項目包下創建config文件夾,創建DubboConfig.java

/**
 * com.steven.xmldubbo.config
 * Created by ZhiLiSteven
 * Date 2018/10/30
 */
@Configuration
@PropertySource("classpath:dubbo/dubbo.properties")
@ImportResource({"classpath:dubbo/*.xml"})
public class DubboConfig {
}

 

3.3 web創建

創建一個UserController.java

/**
 * com.steven.xmldubbo.web
 * Created by ZhiLiSteven
 * Date 2018/10/30
 */
@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("user")
    public Map<String, Object> user(String id) {
        Map<String, Object> result = new HashMap<>();
        try {
            result.put("user", userService.selectByPrimaryKey(Long.parseLong(id)));
            result.put("type", "200");
            result.put("content", "success");
        } catch (Exception e) {
            e.printStackTrace();
            result.put("type", "500");
            result.put("content", e.getMessage());
        }
        return result;
    }

    @GetMapping("allUsers")
    public Map<String, Object> allUsers() {
        Map<String, Object> result = new HashMap<>();
        try {
            result.put("users", userService.selectAll());
            result.put("type", "200");
            result.put("content", "success");
        } catch (Exception e) {
            e.printStackTrace();
            result.put("type", "500");
            result.put("content", e.getMessage());
        }
        return result;
    }

    /**
     * 回聲測試
     * @return
     */
    @GetMapping("echoTest")
    public Map<String, Object> echoTest() {
        // 所有服務自動實現 EchoService 接口
        EchoService echoService = (EchoService) userService;
        // 回聲測試可用性
        String status = (String) echoService.$echo("OK");
        Map<String, Object> result = Maps.newHashMap();
        result.put("status", status);
        return result;
    }

    /**
     * 獲取上下文信息
     */
    @GetMapping("rpcContext")
    public Map<String, Object> rpcContext() {
        // 要先請求一次
        userService.selectAll();
        // 本端是否為消費端
        boolean isConsumerSide = RpcContext.getContext().isConsumerSide();
        // 獲取最后一次調用的提供方IP地址
        String serverIp = RpcContext.getContext().getRemoteHost();
        // 獲取當前服務配置信息,所有配置信息都將轉換為URL的參數
        String application = RpcContext.getContext().getUrl().getParameter("application");

        Map<String, Object> result = Maps.newHashMap();
        result.put("isConsumerSide", isConsumerSide);
        result.put("serverIp", serverIp);
        result.put("application", application);

        return result;
    }

    private int count = 0;
    /**
     * 隱式參數
     *
     */
    @GetMapping("attachment")
    public Map<String, Object> attachment() {
        if (++count%2 != 0) {
            // 隱式傳參,后面的遠程調用都會隱式將這些參數發送到服務器端,類似cookie,用於框架集成,不建議常規業務使用
            RpcContext.getContext().setAttachment("count", count+"");
        }
        return userService.addMethod();
    }
}

 

然后application.properties 設定一個端口
server.port=8882

至此,消費方也創建好了。

4.啟動測試

啟動兩個提供者Provider,ProviderB項目,然后啟動Consume項目

調用http://localhost:8882/allUsers
{“type”:“200”,“users”:[{“id”:9,“email”:“bb”,“nickName”:“bb123456”,“password”:“bb@126.com”,“regTime”:“2018年10月24日 下午02時03分53秒”,“userName”:“bb2”},{“id”:10,“email”:“cc”,“nickName”:“cc123456”,“password”:“cc@126.com”,“regTime”:“2018年10月24日 下午02時03分53秒”,“userName”:“cc3”},{“id”:11,“email”:“steven@126.com”,“nickName”:“steven”,“password”:“123456”,“regTime”:“2018年10月24日 下午02時06分57秒”,“userName”:“steven”},{“id”:12,“email”:“Liz@126.com”,“nickName”:“Liz”,“password”:“123321”,“regTime”:“2018年10月24日 下午02時06分57秒”,“userName”:“Liz”},{“id”:13,“email”:“HanHan@126.com”,“nickName”:“HanHan”,“password”:“654321”,“regTime”:“2018年10月24日 下午02時06分57秒”,“userName”:“HanHan”},{“id”:15,“email”:“YaoYao@126.com”,“nickName”:“YaoYao”,“password”:“654321”,“regTime”:“2018-01-01”,“userName”:“YaoYao”}],“content”:“success”}

調用http://localhost:8882/attachment(該接口測試負載均衡)
分別得到兩種類型結果:
{“attachment”:true,“count”:“1”}

{“attachment”:true,“more”:“providerB”,“count”:null}

這兩個結果隨機出現。

至此,搭建測試完成!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM