Start
RPC
Remote Procedure Call 远程过程调用
- 主要是基于TCP/IP协议,长连接,四个核心的组件:
Client
,Server
,Client Stub
,Server Stub
- 选择RPC框架时的关注点:
I/O
: 同步/异步,长/短连接- 线程调度模型: 单/多线程,线程池,线程调度算法的性能
- 序列化方式: 可读 (eg: xml,json),二进制(eg: fastjson,jdk自带的序列化)
- 多语言支持
- 服务治理(服务发现,监控)
- 流行的RPC框架:
Dubbo
(阿里)/Dubbox
(当当): 基于Java开发,只支持Java的客户端和服务端Motan
(新浪): 基于Java开发Thrift
(Facebook -> Apache): 跨语言的RPC框架,无服务治理Grpc
(谷歌): 基于HTTP2.0协议(基于二进制的HTTP协议升级版本),跨语言的RPC框架(应用主要面向移动端)
- 各RPC框架对比:
Dubbo
- 一款分布式服务框架
- 高性能和透明化的RPC远程服务调用方案
- SOA服务治理方案
- 提供了三大核心能力:
- 面向接口的远程方法调用
- 智能容错和负载均衡
- 服务自动注册和发现
- 基于Java开发,只支持Java的客户端和服务端
- 可以和Spring框架无缝集成
- 角色:
- Provider: 暴露服务的服务提供方
- Consumer: 调用远程服务的服务消费方
- Registry: 服务注册与发现的注册中心
- Monitor: 统计服务的调用次数和调用时间的监控中心
- Container: 服务运行容器
- 调用关系
- Container负责启动,加载,运行Provider
- Provider 启动时向注册中心注册自己提供的服务
- Consumer 启动时向注册中心订阅自己所需的服务
- Registry 返回服务提供者地址列表给Consumer (如果有变更,Registry将基于长连接推送变更数据给Consumer)
- Consumer 从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用
- Provider & Consumer 在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到 Monitor
- 注:
- Provider & Consumer & Registory 之间使用的是长连接
- Provier & Consumer 之间使用非阻塞IO(NIO)通讯
- Dubbo支持的注册中心有: Multicast,Zookeeper,Redis,Simple,下面的示例均使用Zookeeper
Dubbo Quick Start | dubbo-demo
HelloWorld
dependency
pom.xml
<!-- Dubbo (include spring)-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.6.2</version>
</dependency>
<!-- Curator (include zookeeper) -->
<!-- Note: need to change zookeeper version,the beta version zookeeper has issues -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.0.1</version>
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.6</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
Provider
resources/demo-provider.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://dubbo.apache.org/schema/dubbo" xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"> <dubbo:application name="demoProvider"/> <dubbo:registry address="zookeeper://localhost:2181"/> <dubbo:protocol name="dubbo" port="20880"/> <dubbo:service interface="com.cj.dubbo.service.DemoService" ref="demoService"/> <bean id="demoService" class="com.cj.dubbo.service.DemoServiceImpl"/> </beans>
Service interface
package com.cj.dubbo.service; public interface DemoService { String sayHello(String name); }
Service Impl
package com.cj.dubbo.service; public class DemoServiceImpl implements DemoService { @Override public String sayHello(String name) { return "Hello "+name; } }
Consumer
resources/demo-consumer.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://dubbo.apache.org/schema/dubbo" xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"> <dubbo:application name="demoConsumer"/> <dubbo:registry address="zookeeper://192.168.99.100:2181"/> <dubbo:reference id="demoService" check="false" interface="com.cj.dubbo.service.DemoService"/> </beans>
Service interface
package com.cj.dubbo.service; public interface DemoService { String sayHello(String name); }
Test
package com.cj.dubbo;
import java.io.IOException;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.cj.dubbo.consumer.HelloConsumerService;
import com.cj.dubbo.service.DemoService;
import com.cj.dubbo.service.HelloService;
public class DemoDubboTest {
@Test
public void runDemoProvider() throws IOException {
System.setProperty("java.net.preferIPv4Stack", "true");
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"demo-provider.xml"});
context.start();
System.out.println("Provider started.");
System.in.read(); // press any key to exit
}
@Test
public void runDemoConsumer() {
// System.setProperty("java.net.preferIPv4Stack", "true");
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"demo-consumer.xml"});
context.start();
DemoService demoService = (DemoService) context.getBean("demoService"); // obtain proxy object for remote invocation
String hello = demoService.sayHello("world"); // execute remote invocation
System.out.println(hello); // show the result
}
}
Check zookeeper
[zk: zk01:2181(CONNECTED) 9] ls /dubbo
[com.cj.dubbo.service.DemoService]
[zk: zk01:2181(CONNECTED) 10] ls /dubbo/com.cj.dubbo.service.DemoService
[consumers, configurators, routers, providers]
Demo
Provider (SpringBoot)
pom.xml
<!-- springboot --> <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> <!-- springboot dubbo --> <!-- <dependency> <groupId>com.alibaba.spring.boot</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>2.0.0</version> </dependency> --> <!-- Dubbo (include spring) --> <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo</artifactId> <version>2.6.2</version> </dependency> <!-- Curator (include zookeeper) --> <!-- Note: need to change zookeeper version,the beta version zookeeper has issues --> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>4.0.1</version> <exclusions> <exclusion> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> <version>3.4.6</version> <exclusions> <exclusion> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> </exclusion> <exclusion> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> </exclusion> </exclusions> </dependency>
Configure
- resources/log4j.properties
###set log levels### log4j.rootLogger=warn, stdout ###output to the console### log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.Target=System.out log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=[%d{dd/MM/yy hh:mm:ss:sss z}] %t %5p %c{2}: %m%n
resources/provider.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://dubbo.apache.org/schema/dubbo" xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"> <dubbo:application name="helloProvider"/> <dubbo:registry address="zookeeper://localhost:2181"/> <dubbo:protocol name="dubbo" port="20881"/> <dubbo:service interface="com.cj.dubbo.service.HelloService" ref="helloService"/> </beans>
Config: import the
provider.xml
package com.cj.dubbo.config; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ImportResource; @Configuration @ImportResource(locations={"classpath:provider.xml"}) public class ProviderConfig { }
- resources/log4j.properties
Service
- interface
package com.cj.dubbo.service; public interface HelloService { public String sayHello(String name); }
implement
package com.cj.dubbo.provider; import org.springframework.stereotype.Component; import com.cj.dubbo.service.HelloService; @Component("helloService") public class HelloServiceImpl implements HelloService { @Override public String sayHello(String name) { return "Hello, " + name + " (from Spring Boot)"; } }
- interface
Start
package com.cj.dubbo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class HelloProviderApplication { public static void main(String[] args) { SpringApplication.run(HelloProviderApplication.class,args); System.out.println("Start Application"); } }
Consumer (Spring)
pom.xml
<!-- Curator(include Zookeeper) --> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>4.0.1</version> </dependency> <!-- Dubbo (include Spring) --> <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo</artifactId> <version>2.6.2</version> </dependency> <!-- Junit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency>
Configure: resource/hello-consumer.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://dubbo.apache.org/schema/dubbo" xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"> <dubbo:application name="helloConsumer"/> <dubbo:registry address="zookeeper://localhost:2181"/> <dubbo:reference id="helloService" check="false" interface="com.cj.dubbo.service.HelloService"/> <bean id="helloConsumerService" class="com.cj.dubbo.consumer.HelloConsumerService"> <property name="helloService" ref="helloService"/> </bean> </beans>
Service
- interface (same with Provider service interface)
package com.cj.dubbo.service; public interface HelloService { public String sayHello(String name); }
Call the provided Service
package com.cj.dubbo.consumer; import com.cj.dubbo.service.HelloService; public class HelloConsumerService { private HelloService helloService; public HelloService getHelloService() { return helloService; } public void setHelloService(HelloService helloService) { this.helloService = helloService; } public void printSay(String name) { if(helloService==null) System.out.println("Can't get helloService!"); System.out.println(helloService.sayHello(name)); } }
- interface (same with Provider service interface)
Test
public class DemoDubboTest { @Test public void runHelloConsumer() { // System.setProperty("java.net.preferIPv4Stack", "true"); ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"hello-consumer.xml"}); context.start(); HelloService helloService = (HelloService) context.getBean("helloService"); // obtain proxy object for remote invocation String hello=helloService.sayHello("Girl"); // execute remote invocation System.out.println(hello); // show the result System.out.println("---- Consume HelloService ----"); HelloConsumerService consumer=context.getBean(HelloConsumerService.class); consumer.printSay("Boy"); } }
Check zookeeper
[zk: zk01:2181(CONNECTED) 9] ls /dubbo [com.cj.dubbo.service.DemoService, com.cj.dubbo.service.HelloService] [zk: zk01:2181(CONNECTED) 18] ls /dubbo/com.cj.dubbo.service.HelloService [consumers, configurators, routers, providers] [zk: zk01:2181(CONNECTED) 19] ls /dubbo/com.cj.dubbo.service.HelloService/providers [dubbo%3A%2F%2F192.168.31.78%3A20881%2Fcom.cj.dubbo.service.HelloService%3Fanyhost%3Dtrue%26application%3DhelloProvider%26dubbo%3D2.6.2%26generic%3Dfalse%26interface%3Dcom.cj.dubbo.service.HelloService%26methods%3DsayHello%26pid%3D1359%26side%3Dprovider%26timestamp%3D1549812635667] [zk: zk01:2181(CONNECTED) 20] ls /dubbo/com.cj.dubbo.service.HelloService/consumers []