一、前言
作为微服务治理生态体系内的重要框架 dubbo ,从出身到现在历经了十多年的市场检验而依旧火热,除了其自身优秀的设计,高性能的 RPC 性能,以及依托于 spring cloud-alibaba的这个背后强劲的开源团队支撑,在众多的微服务架构选型中,成为很多架构设计者们不可或缺的选择之一。
二、dubbo适用场景
关于dubbo的具体技术点,本篇不做过多介绍,网上的参考资料较多,下面谈几点使用dubbo的技术选型场景,提供参考:
1、内部单体应用微服务化
很多创业型公司,一开始喜欢采用单体服务,随着业务量增大,单体架构很难支撑随之而来的业务并发量,为了减少单体应用向微服务转型的难度,dubbo是一个不错的选择,相比 springcloud 或者springcloud-alibaba,dubbo可以说在极少依赖过多微服务组件的基础上就可以完成微服务化的过渡。
2、应用服务更多面向内部服务间调用
如果你所在的系统或平台,在大多数情况下,需要面对的是微服务之间的调用,而很少对外提供服务接口,dubbo肯定是首选。我们知道 springcloud 走的是http协议,相对dubbo来说,在同样的网络环境下,dubbo底层使用的是 netty ,从传输性能上来说更加高效。
3、对服务管理趋于精细化
dubbo发展到当前的3.X版本,经过漫长的迭代和优化,目前来说已经提供了一整套完善的服务治理方案,细化到对某个服务接口参数调优都有着成熟的解决方案,即存在很多人力可控的因素,这一点来说,对比 Spring cloud来说,有着很强的优势。
三、dubbo微服务治理过程中的一个难题
下面是一张常见的使用dubbo进行微服务治理的内部业务架构图,从这幅图至少可以得出下面几点信息:
- 系统中的应用都是 Java应用,dubbo对Java应用的支撑性本身就很好;
- 各个应用之间将自身的服务通过dubbo注册到注册中心;
- 各应用之间通过dubbo统一从注册中心调用其他应用的服务;
从上面的解读来看,相信对大多数同学来说,使用Java应用采用dubbo进行服务治理也就是上面的模式,而且用的也顺手,但是如果出现了下面的需求该怎么办呢?
下面这张图表达的业务场景是:随着平台规模的做大,比如引入了大数据应用,或者其他周边的计算类应用,但这些新的非Java类的应用也希望从现有的服务注册中心里面调用服务接口,这就给dubbo服务治理带来了一个毕竟难处理的问题,我们知道dubbo走的不是http,而是有一套自己的 协议栈 ,而且在使用dubbo的时候,通常的做法是别的应用引入服务提供者 jar 进行调用;
因此当出现下面这样的跨语言调用时,目前来看,一个毕竟常见的解决方案就是,服务提供方提供 rest 接口,即http接口,这样带来的问题就是,服务提供方势必要提供两套服务解决方案;
相信这也是不少同学在使用dubbo过程中可能会遇到的场景之一,但是好在dubbo在2.7X之后,提供了基于rest协议的实现,就这点来说,在这个场景下,可以说带来了极大的便利,简而言之,开发者无需提供两套服务接口,而是基于dubbo一个框架的基础上,通过配置不同的协议来完成一个服务接口,支撑多种外部协议的调用;
接下来,通过实际的案例演示下基于 dubbo的2.7.X的版本,整合springboot完成一个rest应用开发和调用的案例;
四、与springboot的整合使用
完成的工程目录结构如下
- common-service,公共服务接口,实体类的模块;
- consumer-demo,服务消费方应用;
- provider_demo,服务提供方应用;
1、公共 pom 依赖
该模块用于统一定义和管理各类组件的版本号,可以根据需要酌情引用或补充其他的依赖;
<properties> | |
<spring-boot.version>.3.1.RELEASE</spring-boot.version> | |
<dubbo.version>.7.5</dubbo.version> | |
</properties> | |
<dependencyManagement> | |
<dependencies> | |
<!-- Spring Boot --> | |
<dependency> | |
<groupId>org.springframework.boot</groupId> | |
<artifactId>spring-boot-dependencies</artifactId> | |
<version>${spring-boot.version}</version> | |
<type>pom</type> | |
<scope>import</scope> | |
</dependency> | |
<dependency> | |
<groupId>org.apache.dubbo</groupId> | |
<artifactId>dubbo-dependencies-bom</artifactId> | |
<version>${dubbo.version}</version> | |
<type>pom</type> | |
<scope>import</scope> | |
</dependency> | |
<dependency> | |
<groupId>org.apache.dubbo</groupId> | |
<artifactId>dubbo</artifactId> | |
<version>${dubbo.version}</version> | |
<exclusions> | |
<exclusion> | |
<groupId>org.springframework</groupId> | |
<artifactId>spring</artifactId> | |
</exclusion> | |
<exclusion> | |
<groupId>javax.servlet</groupId> | |
<artifactId>servlet-api</artifactId> | |
</exclusion> | |
<exclusion> | |
<groupId> log4j </groupId> | |
<artifactId>logj</artifactId> | |
</exclusion> | |
</exclusions> | |
</dependency> | |
</dependencies> | |
</dependencyManagement> |
2、common-service 模块
基础pom依赖,这里只需要引入lombok即可;
<dependency> | |
<groupId>org.projectlombok</groupId> | |
<artifactId>lombok</artifactId> | |
<version>.18.8</version> | |
</dependency> |
定义一个实体类 User ,注意这里一定要实现 序列化 接口,高版本的dubbo默认的序列化协议对这里有严格的要求;
Constructor | |
public class User implements Serializable { | |
private static final long serialVersionUID = 8728327146677888239L; | |
private String userId; | |
private String userName; | |
private String address; | |
} |
定义服务接口 UserService ,并定义2个方法
import com.congge.entity.User; | |
public interface UserService { | |
User getByUserId(String userId); | |
String sayHello(String name); | |
} |
使用 maven 命令或idea自带命令,将 common-service 编译并安装到本地仓库;
3、provider-demo 模块
该模块主要结构如下
pom依赖
<dependencies> | |
<dependency> | |
<groupId>com.congge</groupId> | |
<artifactId>common-service</artifactId> | |
<version>.0-SNAPSHOT</version> | |
</dependency> | |
<dependency> | |
<groupId>org.apache.dubbo</groupId> | |
<artifactId>dubbo-spring-boot-starter</artifactId> | |
<version>.7.5</version> | |
</dependency> | |
<dependency> | |
<groupId>org.jboss.resteasy</groupId> | |
<artifactId>resteasy-jaxrs</artifactId> | |
<version>.0.19.Final</version> | |
</dependency> | |
<dependency> | |
<groupId>javax.validation</groupId> | |
<artifactId>validation-api</artifactId> | |
<version>.1.0.Final</version> | |
</dependency> | |
<dependency> | |
<groupId>org.springframework.boot</groupId> | |
<artifactId>spring-boot-starter</artifactId> | |
</dependency> | |
<dependency> | |
<groupId>org.springframework.boot</groupId> | |
<artifactId>spring-boot-starter-web</artifactId> | |
</dependency> | |
<dependency> | |
<groupId>org.apache.dubbo</groupId> | |
<artifactId>dubbo</artifactId> | |
</dependency> | |
<dependency> | |
<groupId>org.apache.dubbo</groupId> | |
<artifactId>dubbo-dependencies-zookeeper</artifactId> | |
<version>.7.5</version> | |
<type>pom</type> | |
<exclusions> | |
<exclusion> | |
<groupId>org.slfj</groupId> | |
<artifactId>slfj-log4j12</artifactId> | |
</exclusion> | |
</exclusions> | |
</dependency> | |
<dependency> | |
<groupId>org.apache.dubbo</groupId> | |
<artifactId>dubbo-rpc-http</artifactId> | |
<version>.7.5</version> | |
</dependency> | |
<dependency> | |
<groupId>org.apache.dubbo</groupId> | |
<artifactId>dubbo-metadata-report-zookeeper</artifactId> | |
<version>.7.5</version> | |
</dependency> | |
</dependencies> |
核心配置文件
spring.application.name=dubbo-provider | |
server.port= | |
dubbo.scan.base-packages=com.congge.service | |
dubbo.application.name=dubbo-provide | |
dubbo.registry.address=zookeeper:// 127.0.0.1 :2181 | |
dubbo.protocols.p.id=dubbo-one | |
dubbo.protocols.p.name=dubbo | |
dubbo.protocols.p.port=20881 | |
dubbo.protocols.p.host=0.0.0.0 | |
dubbo.protocols.p.id=rest-two | |
dubbo.protocols.p.name=rest | |
dubbo.protocols.p.port=8082 | |
dubbo.protocols.p.host=0.0.0.0 |
服务实现类
注意,类上面的 Service 注解是dubbo的,同时,里面的 protocol 同时支持 dubbo协议和rest协议,这也是dubbo 2.7.X之后的新特性;
而使用 Path 注解,就很像我们在一个Controller接口类上面定义的请求路径;
import com.congge.entity.User; | |
import org.apache.dubbo.common.URL; | |
import org.apache.dubbo.config. annotation .Service; | |
import org.apache.dubbo.rpc.RpcContext; | |
import org.apache.dubbo.rpc.protocol.rest.support.ContentType; | |
import javax .ws.rs.GET; | |
import javax.ws.rs.Path; | |
import javax.ws.rs.Produces; | |
import javax.ws.rs.QueryParam; | |
public class ProviderRestService implements UserService { | |
public User getByUserId( String userId) { | |
return new User(userId,"zhangsan","杭州"); | |
} | |
public String sayHello( String name) { | |
System.out.println("执行了rest服务" + name); | |
URL url = RpcContext.getContext().getUrl(); | |
return String.format("%s: %s, Hello, %s", url.getProtocol(), url.getPort(), name); | |
} | |
} |
启动类
import org.springframework.boot.SpringApplication; | |
import org.springframework.boot.autoconfigure.SpringBootApplication; | |
public class ProviderApp { | |
public static void main(String[] args) { | |
SpringApplication.run(ProviderApp.class, args); | |
} | |
} |
4、consumer-demo 模块
pom依赖和provider里面保持一致即可
核心配置文件
spring: | |
application: | |
name: dubbo-consumer | |
server: | |
port: | |
dubbo: | |
registry: | |
address: zookeeper://.0.0.1:2181 |
接口层 MyUserController
这里为了演示方便,定义了一个 控制器 ,方便后面的测试,这里写了两种方式的接口,即通过默认的dubbo协议调用方式,以及采用rest接口方式调用,所以注入了 RestTemplate ,相信有过微服务开发经验的同学能看懂;
import com.congge.entity.User; | |
import com.congge.service.UserService; | |
import org.apache.dubbo.config.annotation.Reference; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.web.bind.annotation.GetMapping; | |
import org.springframework.web.bind.annotation.RestController; | |
import org.springframework.web.client.RestTemplate; | |
public class MyUserController { | |
private RestTemplate restTemplate; | |
= "rest") | (version|
private UserService demoService; | |
// | |
"/getUser") | (|
public Object getUserInfo(){ | |
String result = demoService.sayHello("hello provider"); | |
return result; | |
} | |
// | |
"/getUserById") | (|
public Object getUserById(){ | |
User dbUser = demoService.getByUserId(""); | |
return dbUser; | |
} | |
// | |
"/getByRest") | (|
public Object getByRest(){ | |
User forObject = restTemplate.getForObject("#;, User.class); | |
return forObject; | |
} | |
} | |
启动类
import org.springframework.boot.SpringApplication; | |
import org.springframework.boot.autoconfigure.SpringBootApplication; | |
public class ConsumerApp { | |
public static void main(String[] args) { | |
SpringApplication.run(ConsumerApp.class); | |
} | |
} |
4、接口测试
4.1 启动zookeeper服务
4.2 启动provider服务
4.3 启动consumer服务
4.4 接口测试
模拟正常的dubbo接口调用,调用接口: ,浏览器执行调用
模拟rest接口调用,调用接口: ,执行调用观察效果
通过上述的案例演示,我们了解了如何使用dubbo的rest协议开发一个服务接口,可以这么讲,有了这种rest协议之后,对于服务提供端来说,同样的服务实现类既可以作为dubbo服务暴露出去,同样可以rest的方式被调用,这对于跨平台,跨语言的应用来说,无疑是一种很好的选择;
但是上面演示的方式仅仅是其中一种,更详细的dubbo rest 开发可以参考 dubbo rest开发规范说明 ,本篇到这里就结束了,感谢观看!