流程需求介绍:在一个购物系统的模块中,之前的前后台连接用的是httpClient跨域技术,web连向后台管理、购物车或者订单等后端。具体细分其中的层:收集好的用户请求经web前端处理后,传递到后台的controller—service—mybatis,从数据库取到数据最后原路返回。
微服务做法是,将业务层服务单独处理成一个独立项目,也就是微服务,微服务做成分布式,优点:好定位bug,独立发布,容易扩展。
1.1微服务实现的第一种方式
1.2微服务实现的第二种方式(重点说Dubbo)
接下去值得我们思考的是,原先在http请求发送中省去的那几个步骤是如何在dubbo中实现的,这个技术的出现是真的取代了原始操作吗?首先在dubbo里存在后台controller、service,介质是网络,所以后期实现后会发现这个技术虽然简洁方便,为程序员省去了时间和精力,但是由于是使用网络信号传输,所以传输会不稳定极易受到网络状况影响。
手写Dubbo:
由此拓展开了动态代理的使用:后台service作为一个接口,里面写了方法名。控制层controller先用getProxyObject调用了原service方法;
public interface CartService {
public String findCartById(Long id);
}
public class CartController {
public static void main(String[] args) {
Object proxyObject=getProxyObject
(CartService.class);
CartService cartService=(CartService)
proxyObject;
//通过代理对象调方法
cartService.findCartById(90L);
}
内部类MyInvocationHandler继承InVocationHandler,调用了Invoke方法,得到方法和参数。
//内部类
static class MyInvocationHandler implements
InvocationHandler{
//由java虚拟机自动调用invoke
public Object invoke(Object proxy,
Method method, Object[] args)
throws Throwable {
//spring框架调@before的方法,调method
System.out.println(method.getName());
System.out.println(args[0]);
//联网发送方法名,参数
return null;
}
}
最后得到类方法对象:return Proxy.newProxyInstance(classLoader,
methodInfo, myInvocationHandler);
动态代理第二版:
myDubboClient(消费者)代码:
public interface CartService {
public String findCartById(Long id);
}
package com.tedu.MyDubboClient;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.Socket;
import com.tedu.service.CartService;
public class CartController {
public static void main(String[] args) {
//1,得到动态代理对象
Object proxyObject=getProxyObject
(CartService.class);
//2,通过动态代理对象调用目标方法
CartService cartService=(CartService)
proxyObject;
cartService.findCartById(100L);
//3,调了目标方法,java虚拟机会执行invoke()
}
static class MyInvocationHandler implements
InvocationHandler {
String interfaceName;
public MyInvocationHandler(String interfaceName) {
super();
this.interfaceName = interfaceName;
}
public Object invoke(Object proxy,
Method method, Object[] args) throws Throwable {
System.out.println(method.getName());
try {
//建立和服务器连接
Socket socket=new Socket
("127.0.0.1", 9000);
//发送接口名
OutputStream outputStream=socket.getOutputStream();
ObjectOutputStream objectOutputStream=
new ObjectOutputStream(outputStream);
objectOutputStream.writeUTF(interfaceName);
//发送方法名
objectOutputStream.writeUTF(method.getName());
//发送参数类型
objectOutputStream.writeObject
(method.getParameterTypes());
//发送参数
objectOutputStream.writeObject(args);
//接收服务器结果
InputStream inputStream=socket.getInputStream();
ObjectInputStream objectInputStream=new
ObjectInputStream(inputStream);
Object serverReturnResult=objectInputStream.readObject();
System.out.println("通过网络收到"+serverReturnResult);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
static Object getProxyObject
(Class interfaceInfo)
{
//创建动态类需要类加载器和方法信息
ClassLoader classLoader=
interfaceInfo.getClassLoader();
Class[] methodInfo={interfaceInfo};
//通过动态代理对象执行目标方法时,系统自动调invoke
MyInvocationHandler myinvocatonHandler
=new MyInvocationHandler
(interfaceInfo.getName());
return Proxy.newProxyInstance
(classLoader, methodInfo,
myinvocatonHandler);
}
}
myDubboServer(提供者)代码:
public interface CartService {
public String findCartById(Long id);
}
package com.tedu.service;
public class CartServiceImpl implements CartService{
public String findCartById(Long id) {
// TODO Auto-generated method stub
return "cart";
}
}
package com.tedu.service;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;
public class MyDubboServer {
public static void main(String[] args) {
try {
//监听9000端口号
ServerSocket serverSocket=new ServerSocket(9000);
System.out.println("启动服务器");
while(true)
{
//接收用户的请求
Socket socket=serverSocket.accept();
InputStream inputStream=socket.getInputStream();
ObjectInputStream objectInputStream=
new ObjectInputStream(inputStream);
//读取接口名
String interfaceName=objectInputStream.readUTF();
//读取方法名
String methodName=objectInputStream.readUTF();
//读取参数类型
Class[] pType=(Class[])
objectInputStream.readObject();
//读取参数
Object[] p=(Object[])
objectInputStream.readObject();
//通过反射执行方法
//根据接口名interfaceName com.tedu.service.CartServier
//找到实现类com.tedu.service.CartServiceImpl
//客户端发送不同的的接口名,调不同的实现类
//map(interfacename,Impl)
Class implClassInfo=CartServiceImpl.class;
//通过反射创建对象
Object implObject=implClassInfo.newInstance();
//通过反射执行方法
Method method=implClassInfo
.getMethod(methodName, pType);
//调不同的方法
Object result=method.invoke(implObject, p);
//把执行的结果返回给客户端
OutputStream outputStream=socket.getOutputStream();
ObjectOutputStream objectOutputStream=
new ObjectOutputStream(outputStream);
objectOutputStream.writeObject(result);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
以上第二版myDubbo总结
其他知识拓展:
RPC是Dubbo底层用的框架,简称远程过程调用。
SOA面向服务的架构,同时也是一个组建模型,可以将应用的不同功能通过接口连接起来。使得功能间的互通关系实现。
Dubbo服务器也叫提供者
Dubbo客户端也叫消费者
**
Zookeeper(消息中转站)
**是一个开源式的分布协调服务,分布式应用程序可以基于它进行实现,数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master 选举、配置维护,名字服务、分布式同步、分布式锁和分布式队列
等功能。
Zookeeper的核心理念:
ZooKeeper的配置文件十分单一,每个节点的myid都是一样的,只有serice.(myid)不一样。
1.集群角色
Leader:集群同一时刻只有一个Leader,其他的都是Follower或Observer。
Zookeeper默认只有Leader和Follwer两种角色,但是为了使用Observer模式,在任何想变成的文件中加入:peerType=observer,并在所有 server 的配置文件中,配置observer 模式的 server 的那行配置追加 :observer。
2.角色分工
1)Leader被选举出来,Leader服务器为客户端提供读和写服务。
2)据网上资料理解,Follower或Observer都能提供读服务,不能提供写服务,而Observe机器不参与Leader选举过程(投票过半即选举成功),是因为 Observer 可以在不影响写性能的情况下提升集群的读性能(startLeaderElection)。
3.Session
Session是指客户端通话,在ZooKeeper 中,一个客户端连接是指客户端和 ZooKeeper 服务器之间的TCP长连接。一旦建立连接,客户端的生命周期也就生效了,通过这个连接,客户端可以通过心跳检测和服务器保持有效的会话,并向Zookeeper服务器发送请求接受响应,同时接受通知。
SessionTimeout用来设置客户端会话的超时时间,由于种种情况导致连接断开的,只要及时在集群中连接上替补服务器,那么之前的会话就不会消失。
4.数据节点
详细参考zookeeper的树形结构存储方式。leader是根节点,其他属于follwer节点,并保留内容。
持久节点:主动删除才消失
临时节点:生命周期随着会话周期的消失而失效。
5.状态信息
get命令可得到节点自身的状态信息
6.事物操作
服务器状态的操作,对于每一个事务请求Zookeeper则会为其分配全局唯一的事务ID,用ZXID表示,一个64位的数字,每一个ZXID都会对应一次更新操作,可以读出顺序。
7.事件监听器(watcher)
分布式协调服务,在某些节点上注册Watcher,在事件触发时Zookeeper服务端会将事件通知到客户端。
一、订阅模式
Push模式:
服务端主动将消息推送给用户
Pull模式:
客户端主动发送请求可以向服务端索取数据
Zookeeper采用的是推拉结合的方式。
二、命名服务
详细参考ZXID数值,根据事务变更的顺序生成,可以得到全局唯一的 ID,方便查找。
三、Master动态选举
Zookeeper的强一致性,在分布式高并发情况下成功创建的节点所在的机器就成为了唯一的Master,同时其他没有成功创建的客户端都会在该节点上注册一个子节点变更的Watcher,用于监控现在Master是否存在,一旦失效,其他的客户端就会重新选举。
四、分布式锁
Zookeeper中有客户端创建成功,那么这个用户就获得了锁,其余没有获得锁的客户端就需要到/exclusive_lock节点上注册一个子节点变更的Watcher监听,以便实时监听到lock节点的变化。
什么时候锁会被释放呢?
当前获得锁的客户端机器发生宕机或重启,那么该临时节点就会被删除,释放锁
正常执行完业务逻辑后,客户端就会主动将自己创建的临时节点删除,释放锁。
Zookeeper中的共享锁如何实现?
获得锁的Server创建一个EPHEMERAL_SEQUENTIAL 目录节点,然后调用 getChildren方法获取当前的目录节点列表中最小的目录节点是不是就是自己创建的目录节点,如果正是自己创建的,那么它就获得了这个锁,如果不是那么它就调用 exists(String path, boolean watch) 方法并监控 Zookeeper 上目录节点列表的变化,一直到自己创建的节点是列表中最小编号的目录节点,从而获得锁,释放锁很简单,只要删除前面它自己所创建的目录节点就行了。