1. Overview
Dubbo is a high-performance, lightweight open source distributed service framework, which was early open sourced by Alibaba. It provides distributed service management functions such as service registration, discovery, invocation and load balancing, which provides great convenience for distributed development. The core concepts of dubbo include: Provider (consumer provider), Consumer (service consumer), Registry (registration center), Monitor (monitoring center), etc. The registration center is the core component of dubbo service governance. Dubbo relies on the coordination of the registration center to implement services. (Address) discovery, automated service discovery is the basis for microservices to achieve dynamic expansion and contraction, load balancing, and traffic management. Dubbo provides many registration centers: Multicast registration center, Zookeeper registration center, Redis registration center, Simple registration center, Nacos registration center, etc. This article mainly introduces the springboot service integration with dubbo, and uses nacos as the registration center for service registration.
2.dubbo core concepts
2.1 dubbo architecture diagram
Node name | Description |
---|---|
Provider | The service provider that exposes the service registers with the registration center to provide its own services |
Consumer | The service consumer that calls the remote service, from the provider address list , based on the load balancing algorithm, select the provider to call |
Monitor | Monitoring center that counts the number of calls and call time of the service |
Registry | The registration center for service registration and discovery, returns the service provider address list to consumers |
Container | Service running container is responsible for starting, loading and running service providers |
The dotted lines in the figure indicate that the service consumer (Consumer) and the service provider (Provider) send messages to the Monitor asynchronously. Consumers and Providers will store information on local disks and send information once every 1 minute on average. Monitor is optional in this architecture and needs to be configured separately, and the running status of Monitor (normal or abnormal) will not affect the normal invocation of the service.
2.2 dubbo registration center
The registration centers provided by dubbo include: Multicast registration center, Zookeeper registration center, Redis registration center, Simple registration center, Nacos registration center, etc.
Registration Center | Description |
---|---|
Multicast Registration Center | No central node is needed, as long as the broadcast address is used, they can discover each other |
Zookeeper registration center | is a tree-shaped directory service that supports Change push has high reliability and is the most used registration center in the early days of Dubbo |
Redis Registration Center | The redis registration center uses key/map structure to store data Structure, use the publish/subscribe event of redis to notify data changes |
Simple registration center | The simple registration center itself is an ordinary dubbo service, which can reduce unnecessary Necessary dependencies to make the overall communication method consistent |
Nacos Registration Center | Open sourced by Alibaba to realize dynamic service discovery, service configuration, service metadata and traffic management , supports separate and merged deployment of registration center and configuration center |
3.Case code
3.1 Service relationship diagram
dubbo-api module: This module defines the contract between the server and the consumer and describes the capabilities and behavior of the service. UserProviderAPI is a dubbo interface that defines the insert and queryById methods of the service;
dubbo-provider module: Service provider, which implements the UserProviderAPI interface. Through the @Service method in dubbo (dubbo
3.0 and later is @DubboService), the service will be registered in the registration center (Nacos) for consumers to discover and call;
dubbo-consumer module: Service consumer, which references the UserProviderAPI interface through the @Reference (dubbo 3.0 and later is @DubboReference) annotation. Dubbo will automatically handle service discovery, load balancing, etc., allowing consumers to transparently call remote services.
Dubbo Registration Center: Use Nacos as the Dubbo registration center. The registration center is one of the key components of microservice governance. It is responsible for the registration, discovery and management of services. The service provider registers its service information with the registration center at startup, and the consumer obtains the service provider’s information through the registration center, thereby realizing dynamic service invocation.
The code directory structure is as follows:
3.2 Core Code
The dubbo version used in this article is 2.7.5, the springboot version is 2.3.7, and the springcloud version is 2.2.3. The specific code is as follows:
3.2.1 dubbo-api module
1.pom file
<parent> <artifactId>dubbo</artifactId> <groupId>com.eckey.lab</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>dubbo-api</artifactId> <dependencies> <dependency> <groupId>com.eckey.lab</groupId> <artifactId>nacos-common</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies>
2.UserProviderAPI interface
import com.eckey.lab.common.Result; import com.eckey.lab.interfaces.bean.UserDTO; public interface UserProviderAPI {<!-- --> Result insert(UserDTO user); Result<UserDTO> queryById(Integer id); }
3.UserDTO entity class
import lombok.Data; import javax.validation.constraints.NotNull; import java.io.Serializable; import java.util.Date; @Data public class UserDTO implements Serializable {<!-- --> @NotNull(message = "userName cannot be empty") private String userName; @NotNull(message = "age cannot be null") private Integer age; private String nickName; @NotNull(message = "gender cannot be empty") private String gender; private Date createTime; private Date modifyTime; private static final long serialVersionUID = 1L; }
3.2.2 dubbo-provider module
1.pom file
<parent> <artifactId>dubbo</artifactId> <groupId>com.eckey.lab</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>dubbo-provider</artifactId> <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>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo</artifactId> </dependency> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.4</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.34</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.4</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> </dependency> <!--java verification framework--> <dependency> <groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> <version>1.1.0.Final</version> </dependency> <dependency> <groupId>com.eckey.lab</groupId> <artifactId>nacos-common</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>com.eckey.lab</groupId> <artifactId>dubbo-api</artifactId> <version>1.0-SNAPSHOT</version> <scope>compile</scope> </dependency> </dependencies>
2.UserProviderServiceImpl class
import com.alibaba.fastjson.JSON; import com.eckey.lab.common.Result; import com.eckey.lab.dubbo.bean.User; import com.eckey.lab.dubbo.dao.UserDao; import com.eckey.lab.interfaces.UserProviderAPI; import com.eckey.lab.interfaces.bean.UserDTO; import lombok.extern.slf4j.Slf4j; import org.apache.dubbo.config.annotation.Service; import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.util.Date; /** * @Author: ChengLiang * @CreateTime: 2023-11-13 10:11 * @Description: TODO * @Version: 1.0 */ @Slf4j @Service(version = "1.0.0", timeout = 6000) @Component public class UserProviderServiceImpl implements UserProviderAPI {<!-- --> @Resource private UserDao userDao; @Override public Result insert(UserDTO user) {<!-- --> log.info("Add user data into the database: {}", JSON.toJSONString(user)); if (user == null) {<!-- --> log.error("user is not empty"); return Result.buildDataError("user is not empty"); } try {<!-- --> user.setCreateTime(new Date()); user.setModifyTime(new Date()); User userInsert = new User(); userInsert.setUserName(user.getUserName()); userInsert.setNickName(user.getNickName()); userInsert.setGender(user.getGender()); userInsert.setAge(user.getAge()); userInsert.setCreateTime(user.getCreateTime()); userInsert.setModifyTime(user.getModifyTime()); userDao.insert(userInsert); log.info("Data added successfully: {}", JSON.toJSONString(user)); } catch (Exception e) {<!-- --> log.error("Add data exception: {}", e); return Result.buildDataError("Add data exception"); } return Result.buildResultSuccess(); } @Override public Result<UserDTO> queryById(Integer id) {<!-- --> if (id == null) {<!-- --> log.error("id cannot be empty"); return Result.buildDataError("id cannot be empty"); } final User user = userDao.selectByPrimaryKey(id); final UserDTO userDTO = new UserDTO(); BeanUtils.copyProperties(user, userDTO); log.info("The query result is: {}", JSON.toJSONString(userDTO)); return Result.buildDataSuccess(userDTO); } }
3.application.properties
server.port=9090 spring.application.name=dubbo-provider spring.cloud.nacos.server-addr=http://123.213.45.103:8848 spring.datasource.username= spring.datasource.password= spring.datasource.url=jdbc:mysql://192.168.154.10:3306/test?useUnicode=true & amp;characterEncoding=UTF-8 & amp;allowMultiQueries=true & amp;autoReconnect=true & amp;failOverReadOnly=false & amp;maxReconnects=1000 & amp;initialTimeout=30 spring.datasource.driver-class-name=com.mysql.jdbc.Driver dubbo.application.name=dubbo-provider dubbo.application.version=1.0.0 dubbo.registry.address= dubbo.protocol.name=dubbo dubbo.protocol.port=20880 dubbo.scan.base-packages=com.eckey.lab.dubbo.interfaces mybatis.mapper-locations=classpath:/mybatis/*.xml
4.UserDao class
import com.eckey.lab.dubbo.bean.User; import org.apache.ibatis.annotations.Mapper; @Mapper public interface UserDao {<!-- --> int deleteByPrimaryKey(Integer id); int insert(User record); int insertSelective(User record); User selectByPrimaryKey(Integer id); int updateByPrimaryKeySelective(User record); int updateByPrimaryKey(User record); }
5.UserDao.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.eckey.lab.dubbo.dao.UserDao"> <resultMap id="BaseResultMap" type="com.eckey.lab.dubbo.bean.User"> <id column="id" jdbcType="INTEGER" property="id" /> <result column="user_name" jdbcType="VARCHAR" property="userName" /> <result column="age" jdbcType="INTEGER" property="age" /> <result column="nick_name" jdbcType="VARCHAR" property="nickName" /> <result column="gender" jdbcType="VARCHAR" property="gender" /> <result column="create_time" jdbcType="TIMESTAMP" property="createTime" /> <result column="modify_time" jdbcType="TIMESTAMP" property="modifyTime" /> </resultMap> <sql id="Base_Column_List"> id, user_name, age, nick_name, gender, create_time, modify_time </sql> <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap"> select <include refid="Base_Column_List" /> from user where id = #{<!-- -->id,jdbcType=INTEGER} </select> <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer"> delete from user where id = #{<!-- -->id,jdbcType=INTEGER} </delete> <insert id="insert" keyColumn="id" keyProperty="id" parameterType="com.eckey.lab.dubbo.bean.User" useGeneratedKeys="true"> insert into user (user_name, age, nick_name, gender, create_time, modify_time ) values (#{<!-- -->userName,jdbcType=VARCHAR}, #{<!-- -->age,jdbcType=INTEGER}, #{<!-- -->nickName,jdbcType=VARCHAR}, #{<!-- -->gender,jdbcType=VARCHAR}, #{<!-- -->createTime,jdbcType=TIMESTAMP}, #{<!-- -->modifyTime,jdbcType=TIMESTAMP} ) </insert> <insert id="insertSelective" keyColumn="id" keyProperty="id" parameterType="com.eckey.lab.dubbo.bean.User" useGeneratedKeys="true"> insert into user <trim prefix="(" suffix=")" suffixOverrides=","> <if test="userName != null"> user_name, </if> <if test="age != null"> age, </if> <if test="nickName != null"> nick_name, </if> <if test="gender != null"> gender, </if> <if test="createTime != null"> create_time, </if> <if test="modifyTime != null"> modify_time, </if> </trim> <trim prefix="values (" suffix=")" suffixOverrides=","> <if test="userName != null"> #{<!-- -->userName,jdbcType=VARCHAR}, </if> <if test="age != null"> #{<!-- -->age,jdbcType=INTEGER}, </if> <if test="nickName != null"> #{<!-- -->nickName,jdbcType=VARCHAR}, </if> <if test="gender != null"> #{<!-- -->gender,jdbcType=VARCHAR}, </if> <if test="createTime != null"> #{<!-- -->createTime,jdbcType=TIMESTAMP}, </if> <if test="modifyTime != null"> #{<!-- -->modifyTime,jdbcType=TIMESTAMP}, </if> </trim> </insert> <update id="updateByPrimaryKeySelective" parameterType="com.eckey.lab.dubbo.bean.User"> update user <set> <if test="userName != null"> user_name = #{<!-- -->userName,jdbcType=VARCHAR}, </if> <if test="age != null"> age = #{<!-- -->age,jdbcType=INTEGER}, </if> <if test="nickName != null"> nick_name = #{<!-- -->nickName,jdbcType=VARCHAR}, </if> <if test="gender != null"> gender = #{<!-- -->gender,jdbcType=VARCHAR}, </if> <if test="createTime != null"> create_time = #{<!-- -->createTime,jdbcType=TIMESTAMP}, </if> <if test="modifyTime != null"> modify_time = #{<!-- -->modifyTime,jdbcType=TIMESTAMP}, </if> </set> where id = #{<!-- -->id,jdbcType=INTEGER} </update> <update id="updateByPrimaryKey" parameterType="com.eckey.lab.dubbo.bean.User"> update user set user_name = #{<!-- -->userName,jdbcType=VARCHAR}, age = #{<!-- -->age,jdbcType=INTEGER}, nick_name = #{<!-- -->nickName,jdbcType=VARCHAR}, gender = #{<!-- -->gender,jdbcType=VARCHAR}, create_time = #{<!-- -->createTime,jdbcType=TIMESTAMP}, modify_time = #{<!-- -->modifyTime,jdbcType=TIMESTAMP} where id = #{<!-- -->id,jdbcType=INTEGER} </update> </mapper>
3.2.3 dubbo-consumer module
The pom file is consistent with the dubbo-provider module. The core module is the DubboConsumerServiceImpl class, as follows:
1.DubboConsumerServiceImpl class
import com.alibaba.fastjson.JSON; import com.eckey.lab.common.Result; import com.eckey.lab.consumer.bean.UserVO; import com.eckey.lab.consumer.service.DubboConsumerService; import com.eckey.lab.interfaces.UserProviderAPI; import com.eckey.lab.interfaces.bean.UserDTO; import lombok.extern.slf4j.Slf4j; import org.apache.dubbo.config.annotation.Reference; import org.springframework.stereotype.Service; /** * @Author: ChengLiang * @CreateTime: 2023-11-13 10:56 * @Description: TODO * @Version: 1.0 */ @Slf4j @Service public class DubboConsumerServiceImpl implements DubboConsumerService {<!-- --> @Reference(interfaceClass = UserProviderAPI.class, version = "1.0.0") private UserProviderAPI userProviderAPI; @Override public Result insert(UserVO userVO) {<!-- --> UserDTO userDTO = new UserDTO(); userDTO.setUserName(userVO.getUserName()); userDTO.setNickName(userVO.getNickName()); userDTO.setAge(userVO.getAge()); userDTO.setGender(userVO.getGender()); userDTO.setCreateTime(userVO.getCreateTime()); userDTO.setModifyTime(userVO.getModifyTime()); log.info("userDTO:{}", JSON.toJSONString(userDTO)); return userProviderAPI.insert(userDTO); } @Override public Result<UserDTO> selectByKeyId(Integer id) {<!-- --> Result<UserDTO> userDTOResult = userProviderAPI.queryById(id); log.info("The query result is;{}", JSON.toJSONString(userDTOResult.getData())); return userDTOResult; } }
2.application.properties
server.port=9091 spring.application.name=dubbo-consumer spring.cloud.nacos.server-addr=http://123.213.45.103:8848 dubbo.application.name=dubbo-consumer dubbo.application.version=1.0.0 dubbo.registry.address=nacos://http://123.213.45.103:8848/?username=nacos & amp;password=nacos dubbo.protocol.name=dubbo dubbo.protocol.port=20881
For detailed code, please refer to the appendix of the article. In the above code, there are some precautions, as follows:
1. On the startup classes of dubbo-provider and dubbo-consumer, you need to add @EnableDubbo. @EnableDubbo integrates three annotations @EnableDubboConfig, @DubboComponentScan, and @EnableDubboLifecycle. The functions of @EnableDubbo are completed by these three annotations.
@EnableDubboConfig introduces the class DubboConfigConfigurationRegistrar to register classes related to parsing configuration to the spring container;
@DubboComponentScan introduces the class DubboComponentScanRegistrar, which is used to specify the @Service scan path;
@EnableDubboLifecycle introduces the class DubboLifecycleComponentRegistrar and registers two listeners to the spring container.
Generally, @DubboComponentScan needs to be configured to define the scan path of @Service. If @DubboComponentScan is not configured, the package path of the class annotated with @EnableDubbo is used by default.
2. The annotation @Service in UserProviderServiceImpl introduces org.apache.dubbo.config.annotation.Service;
3. In the configuration file, you need to specify the nacos login account and password, otherwise an error will be reported.
3.3 Test results
The service list on the nacos registration page is as follows:
The interface for calling service consumers is as follows:
The logs of service consumers and providers are as follows:
4. Summary
1. There are many dubbo registration centers. This article is based on nacos for demonstration. For nacos installation, please see my blog post: SpringCloud Source Code Analysis (2) – Nacos Registration Center;
2. In the dubbo framework, the interface module needs to be defined first (defining the provider’s output capabilities and consumers’ required capabilities). The service provider implements the capabilities and registers them in the registration center for consumers to consume;
3. There is a correspondence between versions between dubbo and spring cloud alibaba. For details, you can check dubbo official website or spring cloud official website for comparison.
5. References
- https://zhuanlan.zhihu.com/p/638826433
- https://blog.csdn.net/java_cpp_/article/details/128051413
- https://cn.dubbo.apache.org/zh-cn/overview/mannual/java-sdk/quick-start/api/
- https://juejin.cn/post/7159776981771354119
- https://developer.aliyun.com/article/808571
6.Appendix
https://gitee.com/Marinc/nacos/tree/master/dubbo