微服务介绍《一》
随着用户需求个性化,产品生命周期变短,微服务架构是未来软件架构朝着灵活性,扩展性,伸缩性以及高可用性发展的必然方向。
微服务是一种软件架构风格,目标是将一个复杂的应用拆分成多个服务模块,每个模块专注单一业务功能对外提供服务,并可以独立编译及部署,同时各个各模块间互相通信彼此协作,组合为整体对外提供完整服务。
由于每个模块都独立部署,各自拥有互不干扰的内存空间,模块之间无法之间调用,所以需要借助RPC[远程过程调用协议]或HTTP协议让各个模块之间传递通信报文及交换数据,实现远程调用,整个通信管理的过程也是微服务架构重要的组成部分。
垂直应用和微服务:
mvc模式构建的垂直应用非常适合项目初期,使用其能够方便的进行开发,部署,测试,但随着业务的发展于访问量的增加,垂直应用的问题也随之暴露出来,而微服务架构可以很好的解决这些问题。
代码维护:
1.垂直应用中,大部分逻辑都部署再一个集中化,单一的环境或服务器中运行。垂直应用程序通常很大,由一个团队或多个团队维护。庞大的代码库可能给希望熟悉代码的开发人员增加学习成本。还会让应用程序开发过程中使用的开发环境工具和运行容器不堪重负,最终导致开发效率降低,可能会阻止对执行更改的尝试。
2.微服务架构将这个庞大并且复杂的应用拆分成多个逻辑简单且独立的小应用,每个小应用交由不同的团队开发或人员维护,彼此之间互不干扰,通过标准接口互相通信。
部署:
1.垂直应用需要处理一个庞大的应用程序,编译,部署需要花费很长的时间,一个小的修改就可能导致重新构建整个项目。
2.微服务架构中对其中某一个服务进行修改,只需要重新编译,部署被改动的服务模块。
资源控制:
1.垂直应用里,当请求量过大导致单台服务器无法支撑时,一般会将垂直应用部署在堕胎服务器形成服务集群,并通过反向代理实现负载均衡。集群中的每个服务必须部署完整的应用,但在实际业务需求中仅有部分功能使用频繁,但这种架构必须为不常用的功能分配计算资源。
2.微服务将提供功能的各服务拆分为多个服务模块,它具有天生的集群属性,能够轻松的根据用量部署。
稳定:
1.垂直应用中如果有一个小的问题,就可能使整个系统崩溃。
2.微服务所拆分出的各个模块中,由于模块之间的耦合度很低,当发生问题时影响范围被固定在该模块本身,整个系统依然健全。
基本工作流程如下:
1.客户端发起调用请求
2.将调用的内容序列化后通过网络发给服务器
3.服务器端接收到的调用请求,执行具体服务并获得结果
4.将结果序列化后通过网络返回给客户端
通信:当请求过大后会发现,BIO[同步阻塞]的通信方式会消耗过多的资源导致服务器变慢甚至崩溃。
序列化和反序列化:在发起网络请求前,将对象转换成二进制串便于网络传输;收到消息请求后,将二进制串反转换成对象便于后续处理。序列化与反序列化直接影响到整个RPC框架的效率和稳定性。
服务注册中心:发起服务调用时,都需要指定提供方的访问地址,如果当前服务提供方有多个或一个服务部署在多个机器上,调用时每次手动指定访问地址非常麻烦,这时就需要一个公共的注册中心去管理这些服务。
负载均衡:实施微服务的目的是为了让系统在进行横向扩展时能够拥有更多的计算资源,如果发现某一提供服务的机器负载较大,这就需要将新的需求转发到其他空闲的机器上。
服务监控:服务提供方有可能崩溃无法继续提供服务,在客户端进行调用时就需要将这些无法使用的服务排除掉。
异常处理:当服务端有异常发生导致无法返回正确的结果时,客户端并不知道该如何处理,只能等待并最终以超时结束此次远程调用请求。
Dubbo:采用zookeeper作为注册中心,RPC作为服务调用方式,致力于提供高性能和透明化的RPC远程服务调用方案。它与spring无缝集成,基于服务提供方和服务调用方角色构建简单模型,其优点是使用方便,学习成本低。
1。服务提供方发布服务到服务注册中心。
2.服务消费方从服务注册中心订阅服务
3.注册中心通知消息调用方服务已注册
4.服务消费方调用已经注册的可用服务
5.监控计数
spring cloud :基于spring boot 实现,使用HTTP的restful 风格api 作为调用方式,它所包含的多个子项目共同构建了微服务架构体系。
netflix eureka: spring cloud 的服务注册中心提供服务注册,服务发现,负载均衡等功能。
Netflix hystrix :当某个服务发生故障后,则触发熔断机制向服务调用方返回结果标识错误,而不是一直等待服务提供方返回结果,这样就不会使得线程因调用故障服务而被长时间占用不释放,避免了故障在分布式系统中的蔓延。
Netflix zuul: 代理各模块提供的服务 ,统一暴露给第三方。提供动态路由,监控,弹性,全等的边缘服务。
config server:分布式架构下多微服务会产生非常多的配置文件,分布式配置中心(config server)将所有配置文件交由git 或svn 进行统一管理,避免出错。
spring boot :spring boot 通过牺牲项目的自由度来减少配置的复杂度,约定一套规则,把这些框架都自动配置集成好,从而达到:开箱即用。
代码实例:
服务提供方:
package org.book; import java.io.IOException; public class App { public static void main(String[] args) throws IOException { Server server = new Server(); //注册服务 server.register(HelloService.class,HelloServiceImpl.class); //启动并绑定端口 server.start(8020); } }
public interface HelloService { public String hello(String name); }
public class HelloServiceImpl implements HelloService { public String hello(String name) { System.out.println("收到信息:"+name); return "你好:"+ name; } }
import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Method; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.util.HashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class Server { private static ExecutorService executor = Executors.newFixedThreadPool(10); private static final HashMap<String,Class> serviceRegistry = new HashMap<String, Class>(); public void register(Class serviceInterface,Class impl){ serviceRegistry.put(serviceInterface.getName(),impl); } public void start(int port) throws IOException { final ServerSocket server = new ServerSocket(); server.bind(new InetSocketAddress(port)); System.out.println("服务已经启动"); while (true){ executor.execute(new Runnable() { public void run() { Socket socket = null; ObjectInputStream inputStream = null; ObjectOutputStream outputStream = null; try { socket = server.accept(); //接收到服务调用请求,将码流反序列化定位具体服务 inputStream = new ObjectInputStream(socket.getInputStream()); String serviceName = inputStream.readUTF(); String methodName = inputStream.readUTF(); Class<?>[]parameterTypes = (Class<?>[]) inputStream.readObject(); Object[] arguments = (Object[]) inputStream.readObject(); //在服务注册表中根据调用的服务获取到具体的实现类 Class serviceClass = serviceRegistry.get(serviceName); if (serviceClass == null){ throw new ClassNotFoundException(serviceName +"未找到!"); } Method method = serviceClass.getMethod(methodName,parameterTypes); //调用获取结果 Object result = method.invoke(serviceClass.newInstance(),arguments); outputStream = new ObjectOutputStream(socket.getOutputStream()); outputStream.writeObject(result); } catch (Exception e) { e.printStackTrace(); } finally { try { if (socket != null) socket.close();; if (inputStream != null) inputStream.close(); if (outputStream != null) outputStream.close(); }catch (Exception e){ e.printStackTrace(); } } } }); } } }
<groupId>org.book</groupId> <artifactId>rpcInterface</artifactId> <version>1.0-SNAPSHOT</version> <name>rpcInterface</name>
服务消费方代码:
import java.net.InetSocketAddress; public class App { public static void main(String[] args) { HelloService service = org.book.Client.get(HelloService.class,new InetSocketAddress("localhost",8020)); System.out.println(service.hello("RPC")); } }
import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.net.InetSocketAddress; import java.net.Socket; public class Client <T> { @SuppressWarnings("unchecked") public static <T> T get(final Class<?> serviceInterface, final InetSocketAddress addr){ T instance = (T) Proxy.newProxyInstance(serviceInterface.getClassLoader(),new Class<?>[]{serviceInterface},new InvocationHandler(){ public Object invoke(Object proxy, Method method,Object []args) throws Throwable { Socket socket = null; ObjectOutputStream output = null; ObjectInputStream input = null; try{ // 链接服务端 socket = new Socket(); socket.connect(addr); //将调用的接口类,方法名,参数列表等序列化后发送给服务提供者 output = new ObjectOutputStream(socket.getOutputStream()); output.writeUTF(serviceInterface.getName()); output.writeUTF(method.getName()); output.writeObject(method.getParameterTypes()); output.writeObject(args); //同步阻塞等待服务器返回应答,获取应答后返回 input = new ObjectInputStream(socket.getInputStream()); return input.readObject(); }finally { if (socket != null) socket.close(); if ( input != null) input.close(); if (output != null) output.close(); } } }); return instance; } }
<dependency> <groupId>org.book</groupId> <artifactId>rpcInterface</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
<groupId>org.book</groupId> <artifactId>rpcClient</artifactId> <version>1.0-SNAPSHOT</version>
启动服务--调用服务,结果显示:
调用方控制台:你好:RPC
提供方控制台:服务已经启动 收到信息:RPC
项目展示:[rpc-server]:https://download.****.net/download/qq_35781178/10538172
项目展示:[rpc-client]: https://download.****.net/download/qq_35781178/10538179