定义
一开始AB应用在同一台机器上,是本地调用
现在分到两个地方上了
server这边有很多服务A,B,HelloService
现在要把这些服务放到服务注册中心去,客户端调用的时候直接去跟服务的注册中心去就可以直接调用
注册中心用map来实现
客户端和服务端通过socket链接
- 客户端如果要调用helloService,,而服务端需要通过该字符串"HelloService"解析出该字符串代表的接口的一切信息(通过反射技术)
- socket
- 服务端需要根据客户端的不同请求,返回不同的借口类型
客户端就要接受到不同的接口类型(通过动态代理)
按照流程来写代码
1.服务端start()开启服务端服务
服务端首先需要一些内部成员:
public class ServerCenter implements Sever{
//这个用来存放服务名和对应的接口
private static HashMap<String,Class> serviceRegister = new HashMap<>();
private static int port;
public ServerCenter(int port){
this.port = port;
}
}
然后开启
public void start() throws IOException{
ServerSocket server = new ServerSocket();
//绑定端口
server.bind(new InetSocketAddress(port));
//接受客户端的请求
server.accept();
}
2.Client获取代表服务端接口的动态代理对象
服务端要告诉客户端接口的名字和 IP地址端口号()
//参数是:1.客户端请求的服务的接口,socket的地址(即ip+port)
public static <T> T getRemoteProxyObj(Class serviceInterface,InetSocketAddress addr){
/*
返回一个动态代理即可
newProxyInstance需要三个参数:
1.类加载器:需要代理哪个类,比如helloService
2.接口:需要代理的对象具备哪些功能
3.一个invocationHandler,采用匿名内部类实现
*/
return (T)Proxy.newProxyInstance(
serviceInterface.getClassLoader(),//服务类接口的类加载器
new Class<?>[] {serviceInterface},//服务类的接口(可能有很多个接口所以使用数组)
new InvocationHandler(){//匿名内部类
@Override
//只需要实现这一个方法即可
/*
三个参数:
1.proxy是代理的对象
2.method是需要代理的方法
3.args是方法参数列表
*/
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{
//1.客户端向服务端发送请求,请求某一个具体的接口
Socket socket = new Socket();
socket.connect(addr);//addr = ip + port
ObjectOutputStream output = newObjectOutputStream(socket.getOutputStream());
//发送:接口名,方法名,方法参数,参数类型
output.writeUTF(serviceInterface.getName());
output.writeUTP(method.getName());
output.writeObject(method.getParameterTypes());
output.writeObject(args);
}
}
)
}
服务端接收到客户端连接及请求,处理该请求
public void start() throws IOException{
ServerSocket server = new ServerSocket();
server.bind(new InetSocketAddress(port));
Socket socket = server.accept();
/*----------从这里开始-----------*/
ObjectInputStream input = socket.getInputStream();
//因为ObjectInputStream对发送数据的顺序严格要求,因此需要参照发送的顺序逐个接受
String serviceName = input.readUTF();//服务名
String methodName = input.readUTF();//方法名
Class[] parameterTypes = (Class[])input.readObject();//参数类型
Object[] arguments = (Object[])input.readObject();//参数值
//根据客户请求,在map(serviceRegister)找到具体接口
Class ServiceClass = serviceRegister.get(serviceName);
//根据方法名和参数类型找到方法
Method method = ServiceClass.getMethod(methodName,parameterTypes);
//执行该方法,需要类,和参数列表(反射技术)
Object result = method.invoke(ServiceClass.newInstance(),arguments);
//将方法执行完毕的返回值,传给客户端
ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
output.writeObject(result);
}
客户端接受服务端处理后的返回值
public static <T> T getRemoteProxyObj(Class serviceInterface,InetSocketAddress addr){
/*
1.类加载器:需要代理哪个类,比如helloService
2.接口:需要代理的对象具备哪些功能
*/
return (T)Proxy.newProxyInstance(
serviceInterface.getClassLoader(),
new Class<?>[] {serviceInterface},
new InvocationHandler(){
@Override
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{
/*proxy是代理的对象
method是需要代理的方法
args是方法参数列表
*/
//客户端向服务端发送请求,请求某一个具体的接口
Socket socket = new Socket();
socket.connect(addr);//addr = ip + port
ObjectOutputStream output = newObjectOutputStream(socket.getOutputStream());
//发送:接口名,方法名,方法参数,参数类型
output.writeUTF(serviceInterface.getName());
output.writeUTP(method.getName());
output.writeObject(method.getParameterTypes());
output.writeObject(args);
/*----从这里开始-----------*/
//等待服务器处理
//接受服务器处理后的返回值
ObjectInputStream input = new ObjectInputStream(socket.getInputStream);
return input.readObject();
}
}
)
}
最后加个try…catch…finally关掉就可以了
最后的改进
这里的server只能处理一个请求,需要使用多线程来处理多个客户端请求
用线程池,连接池中存在多个连接对象,每一个连接对象都可以处理一个客户请求
代码
可以直接看github,有具体测试步骤
server:
package src;
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(Runtime.getRuntime().availableProcessors());
//这个用来存放服务名和对应的接口
private static HashMap<String,Class> serviceRegister = new HashMap<>();
private static int port;
private static boolean isRunning = false;
public Server(int port){
this.port = port;
}
public void start() {//while(true){start();}
//start ->线程对象
ServerSocket server = null ;
try {
server = new ServerSocket();
server.bind(new InetSocketAddress(port));
} catch (IOException e1) {
e1.printStackTrace();
}
isRunning = true ; //服务已经启动
while(true) {
//具体的服务内容:接收客户端请求,处理请求,并返回结果
//100 :1 1 1 ...1 -->如果想让多个 客户端请求并发执行
//-> 多线程
System.out.println("start server....");
//客户端每次请求一次连接(发出一次请求),则服务端 从连接池中
//获取一个线程对象去处理
Socket socket = null ;
try {
socket = server.accept();// 等待客户端连接
} catch (IOException e) {
e.printStackTrace();
}
//启动线程 去处理客户请求
executor.execute(new ServiceTask(socket) );
}
}
public void stop() {
isRunning = false;
executor.shutdown();
}
public void register(Class service,Class serviceImpl) {
serviceRegister.put(service.getName(), serviceImpl);
}
private static class ServiceTask implements Runnable{
private Socket socket ;
public ServiceTask() {};
public ServiceTask(Socket socket) {
this.socket = socket ;
}
@Override
public void run() {
ObjectOutputStream output = null;
ObjectInputStream input = null;
try {
input = new ObjectInputStream( socket.getInputStream());
//因为ObjectInputStream对发送数据的顺序严格要求,因此需要参照发送的顺序逐个接受
String serviceName = input.readUTF();
String methodName = input.readUTF();
Class[] parameterTypes = (Class[])input.readObject();
Object[] arguments = (Object[])input.readObject();
//根据客户请求,在map(serviceRegister)找到具体接口
Class ServiceClass = serviceRegister.get(serviceName);
Method method = ServiceClass.getMethod(methodName,parameterTypes);
//执行该方法,需要类,和参数列表
Object result = method.invoke(ServiceClass.newInstance(),arguments);
//将方法执行完毕的返回值,传给客户端
output = new ObjectOutputStream(socket.getOutputStream());
output.writeObject(result);
}catch (Exception e) {
e.printStackTrace();
}finally {
try {
if (output != null) output.close();
if (input != null) input.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
client
package src;
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 {
@SuppressWarnings("unchecked")
public static <T> T getRemoteProxyObj(Class serviceInterface,InetSocketAddress addr) {
return (T)Proxy.newProxyInstance(
serviceInterface.getClassLoader(),
new Class<?>[]{serviceInterface},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Socket socket = new Socket();
ObjectOutputStream output = null;
ObjectInputStream input = null;
try {
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();
}catch (Exception e) {
e.printStackTrace();
return null;
}finally {
if(input!=null) input.close();
if(output!=null) output.close();
}
}
});
}
}
helloServiceInterface
package src;
public interface HelloServiceInterface {
public String sayHi(String name) ;
}
helloService
package src;
public class HelloService implements HelloServiceInterface{
public String sayHi(String name) {
return "hi,"+name ;
}
}
serverTest
package src;
public class ServerTest {
public static void main(String[] args) {
//开启一个线程
new Thread(new Runnable() {
@Override
public void run() {
//服务中心
Server server = new Server(9999);
//将HelloService接口及实现类 注册到 服务中心
server.register(HelloServiceInterface.class, HelloService.class);
server.start();
}
}).start();//start()
}
}
clientTest
package src;
import java.net.InetSocketAddress;
public class ClientTest {
public static void main(String[] args) throws ClassNotFoundException {
HelloServiceInterface service = Client.getRemoteProxyObj(
Class.forName("src.HelloServiceInterface") ,
new InetSocketAddress("127.0.0.1", 9999)) ;
System.out.println( service.sayHi("zs") ) ;
}
}