分布式系统是若干个独立计算机的集合,计算机之间通过网络进行通信,为了完成共同的任务而协调工作从而组成的系统。简单来说就是利用更多的机器来处理更多的数据。
当网站流量很小时,只需要一个应用,将所有功能代码打成一个包部署在一个服务器上,以减少部署节点和成本。
缺点:
当访问量逐渐增大,将应用拆成互不相干的几个应用,以提升效率。
通过切分业务来实现各模块独立部署,降低了维护和部署的难度,性能扩展也更加方便,更具针对性。
缺点:公共模块无法重复利用,开放性的浪费。
当垂直应用越来越多,应用之间的交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使得前端应用能够快速的响应多变的市场需求。,此时,用于提高业务复用及整合分布式服务框架RPC是关键。
举例理解:
- 如果要修改界面,只需将界面单独修改后重启界面的服务器,其相关的核心业务逻辑还在其他服务器上。
- 比如订单web前端需要订单信息、用户信息、商品信息,那就直接去调这几个服务器的业务就行,包括业务之间也需要互相调用。
当服务器越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需要增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。此时,用于提高机器利用率的资源调度和治理中心是关键。
RPC:远程过程调用,它是一种技术的思想
是一种进程之间通信的方式。它允许程序调用另一个地址空间(通常是共享网络的另一台机器上)的过程或函数,而不是程序员显示编写这个远程调用的细节,即程序员无论是调用本地还是远程函数,本质编写的调用代码基本相同(类似使用Mybatis后,仅需要通过调用自己写的mapper映射的接口实现crud)。
RPC的用途
两台服务器A、B,一个应用部署在A服务器上,想要调用B服务器上的应用提供的函数(方法),由于两个应用不在一个内存空间,不能直接调用,而需要网络来表达调用的语义和传达调用的数据。为什么要用RPC呢?就是无法在一个进程内,甚至一个计算机内通过本地调用的方式完成的需求,比如不同的系统间的通讯,甚至不同的组织间的通讯,由于计算能力需要横向扩展,需要在多台机器组成的集群上部署应用。RPC就是要像调用本地的函数一样去调远程函数;
是一款高性能、轻量级的开源Java RPC框架。
它提供了三大核心能力:
- 面向接口的远程方法调用;
2.智能容错和负载均衡;
3.服务自动注册和发现。
Provider:暴露服务的服务提供方;
Consumer:调用远程服务的服务消费方;
Register:服务注册与发现的注册中心;
Monitor:统计服务的调用次数和调用时间的控制中心。
调用流程
0:服务器容器负责启动、加载、运行服务提供者;
1:服务提供者在启动时,向注册中心注册自己提供的服务;
2:服务消费者在启动时,向注册中心订阅自己所需的服务;
3:注册中心返回服务提供者地址列表给消费者,如有变更,注册中心将基于长连接推送变更数据给消费者;
4:服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选一台调用。
5:服务消费者和提供者,在内存中积累调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
zookeeper
1.将conf文件夹下面的zoo_sample.cfg复制一份改名为zoo.cfg即可;
2.修改zoo.cfg中的dataDir路径(新建一个data文件,将路径设置成此文件夹,注意目录使用“\”);
3.使用zkCli.cmd测试:
(因为zookeeper是一个树型的目录服务)
ls /:列出zookeeper根下保存的所有节点
create –e /duanxinwei 123456:创建一个duanxinwei节点,值为123456
get /duanxinwei:获取/duanxinwei节点的值
mvn clean package -Dmaven.test.skip=true
用cmd打jar包我总是报错,所以我用的idea中的maven打的jar包
java -jar dubbo-admin-0.0.1-SNAPSHOT.jar
此时控制台服务就已经跑起来了,通过locahost:7001,访问到注册中心,输入账号密码:root
基于下图实现服务提供者、消费者
提供的服务是:通过userId查询用户地址
UserService:
//用户服务
public interface UserService {
/**
* 按照用户id返回所有的收货地址
* @param userId
* @return
*/
public List<UserAddress> getUserAddressList(String userId);
}
UserServiceImpl:
public class UserServiceImpl implements UserService {
public List<UserAddress> getUserAddressList(String userId) {
// // 模拟从数据库中获取的该userId对应的地址
UserAddress address1 = new UserAddress(1, "河南省郑州巩义市宋陵大厦2F", "1", "安然", "150360313x", "Y");
UserAddress address2 = new UserAddress(2, "北京市昌平区沙河镇沙阳路", "1", "情话", "1766666395x", "N");
return Arrays.asList(address1,address2);
}
}
实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserAddress implements Serializable {
private Integer id;
private String userAddress; //用户地址
private String userId; //用户id
private String consignee; //收货人
private String phoneNum; //电话号码
private String isDefault; //是否为默认地址 Y-是 N-否
//get set
//有参构造 无参构造
}
OrderService
public interface OrderService {
/**
* 初始化订单
* @param userID
*/
public void initOrder(String userID);
}
OrderServiceImpl
public class OrderServiceImpl implements OrderService {
public UserService userService;
public void initOrder(String userID) {
//查询用户的收货地址
List<UserAddress> userAddressList = userService.getUserAddressList(userID);
System.out.println(userAddressList);
}
}
因为服务消费者要拿到提供者的方法
得将服务提供者中的实体类UserAddress和UserService复制到消费者的文件中,这样消费者代码才不会报错。
但是这样复制代码会使得重复代码太多,可以直接将重用的代码(实体类,service方法)放在单独的项目下,打成jar包。
在服务提供者和消费者项目中引入这个jar包的依赖,这样就会省略大量代码。
<dependency>
<groupId>com.duan</groupId>
<artifactId>duan-interface</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>system</scope>
<systemPath>..路径../duan-interface-1.0-SNAPSHOT.jar</systemPath>
</dependency>
在service-user-provider中引入依赖
<dependency>
<groupId>com.alibabagroupId>
<artifactId>dubboartifactId>
<version>2.6.2version>
dependency>
<dependency>
<groupId>org.apache.curatorgroupId>
<artifactId>curator-frameworkartifactId>
<version>2.12.0version>
dependency>
在resrouce文件中创建provider.xml
<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://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd
http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<dubbo:application name="service-user-provider">dubbo:application>
<dubbo:registry protocol="zookeeper" address="127.0.0.1:2181">dubbo:registry>
<dubbo:protocol name="dubbo" port="20880">dubbo:protocol>
<dubbo:service interface="com.duan.service.UserService" ref="userServiceImpl">dubbo:service>
<bean id="userServiceImpl" class="com.duan.service.impl.UserServiceImpl">bean>
beans>
编写一个ProviderApplication启动类程序,运行测试配置
public class MailApplication {
public static void main(String[] args) throws IOException {
ClassPathXmlApplicationContext applicationContext= new ClassPathXmlApplicationContext("provider.xml");
applicationContext.start();
System.in.read();
}
}
(保证此时zoopkeeper和dubbo-admin运行时,在dubbo控制首页查看提供者信息)
服务提供者的配置和测试完成。
在service-order-consumer服务消费者项目中引入依赖
<dependency>
<groupId>com.alibabagroupId>
<artifactId>dubboartifactId>
<version>2.6.2version>
dependency>
<dependency>
<groupId>org.apache.curatorgroupId>
<artifactId>curator-frameworkartifactId>
<version>2.12.0version>
dependency>
创建consumer.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd
http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<context:component-scan base-package="com.duan.service.impl"/>
<dubbo:application name="service-order-consumer">dubbo:application>
<dubbo:registry address="zookeeper://127.0.0.1:2181">dubbo:registry>
<dubbo:reference interface="com.duan.service.UserService" id="userService">dubbo:reference>
beans>
把当前OrderServiceImpl实现类中加上注解(使用spring的注解)
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
public UserService userService;
public void initOrder(String userID) {
//查询用户的收获地址
List<UserAddress> userAddressList = userService.getUserAddressList(userID);
//为了直观看到得到的数据
System.out.println("当前接收到的userId=> "+userID);
System.out.println("**********");
System.out.println("查询到的所有地址为:");
for (UserAddress userAddress : userAddressList) {
//打印远程服务地址的信息
System.out.println(userAddress.getUserAddress());
}
}
}
编写一个ConsumerApplication启动类程序,运行测试配置
public class ConsumerApplication {
public static void main(String[] args) throws IOException {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("consumer.xml");
OrderService orderService = (OrderService)applicationContext.getBean(OrderService.class);
//调用方法查询出数据
orderService.initOrder("1");
System.out.println("调用完成...");
System.in.read();
}
}
结果:
管理控制台:
此时可以看到一个提供者,一个消费者的信息监控信息。
dubbo-monitor-simple目录下cmd输入命令mvn clean package,打出jar包
将target目录下的dubbo-monitor-simple-2.0.0-assembly.tar.gz 压缩包解压至当前文件夹,复制到dubbo主目录下
进入在config文件查看properties的配置是否是本地的zookeeper。
打开解压后的 assembly.bin 文件,start.bat 启动dubbo-monitor-simple监控中心(记住要先开启zookeeper服务)
localhost:8080 ,可以看到一个监控中心。
在服务提供者和消费者的xml中配置以下内容,再次启动服务提供和消费者启动类。
<dubbo:monitor protocol="registry">dubbo:monitor>
创建spring项目后,导入依赖:
<dependency>
<groupId>com.alibaba.bootgroupId>
<artifactId>dubbo-spring-boot-starterartifactId>
<version>0.2.0version>
dependency>
配置 application.properties
dubbo.application.name=boot-user-service-provider
dubbo.registry.address=127.0.0.1:2181
dubbo.registry.protocol=zookeeper
dubbo.protocol.name=dubbo
dubbo.protocol.port=20880
#连接监控中心
dubbo.monitor.protocol=registry
把user-service-provider中的service拿到此项目中。将将此方法的返回类型改为List
@Service//dubbo的服务暴露
@Component
public class UserServiceImpl implements UserService {
public List<UserAddress> getUserAddressList(String userId) {
UserAddress address1 = new UserAddress(1, "河南省郑州巩义市宋陵大厦2F", "1", "安然", "150360313x", "Y");
UserAddress address2 = new UserAddress(2, "北京市昌平区沙河镇沙阳路", "1", "情话", "1766666395x", "N");
return Arrays.asList(address1,address2);
}
}
BootProviderApplication 启动类配置
@EnableDubbo //开启基于注解的dubbo功能
@SpringBootApplication
public class BootProviderApplication {
public static void main(String[] args) {
SpringApplication.run(BootProviderApplication.class, args);
}
}
启动注册中心,启动当前服务提供者,可以在浏览器看到一个服务提供者。
server.port=8081
dubbo.application.name=boot-order-service-consumer
dubbo.registry.address=zookeeper://127.0.0.1:2181
#连接监控中心 注册中心协议
dubbo.monitor.protocol=registry
@Service
public class OrderServiceImpl implements OrderService {
@Reference//引用远程提供者服务
UserService userService;
public List<UserAddress> initOrder(String userID) {
//查询用户的收货地址
List<UserAddress> userAddressList = userService.getUserAddressList(userID);
System.out.println("当前接收到的userId=> "+userID);
System.out.println("**********");
System.out.println("查询到的所有地址为:");
for (UserAddress userAddress : userAddressList) {
//打印远程服务地址的信息
System.out.println(userAddress.getUserAddress());
}
return userAddressList;
}
}
@Controller
public class OrderController {
@Autowired
OrderService orderService;
@RequestMapping("/initOrder")
@ResponseBody
public List<UserAddress> initOrder(@RequestParam("uid")String userId) {
return orderService.initOrder(userId);
}
}
配置完毕,此时启动zookeeper注册中心及监控。
启动springboot配置的服务提供者和消费者
在浏览器输入 localhost:7001 查看结果