注解优化,grpc开发

This commit is contained in:
暮光:城中城
2019-04-02 20:22:18 +08:00
parent f5e6a5998d
commit 7caebe9eaf
48 changed files with 13154 additions and 283 deletions

View File

@@ -0,0 +1,368 @@
package com.zyplayer.doc.grpc.controller;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.protobuf.ByteString;
import com.nxest.grpc.client.GrpcChannelFactory;
import com.nxest.grpc.server.GrpcService;
import com.zyplayer.doc.core.exception.ConfirmException;
import com.zyplayer.doc.core.json.DocResponseJson;
import com.zyplayer.doc.grpc.controller.po.ColumnInfo;
import com.zyplayer.doc.grpc.controller.po.GrpcDocInfo;
import com.zyplayer.doc.grpc.controller.po.GrpcServiceAndColumn;
import com.zyplayer.doc.grpc.controller.po.MethodParam;
import com.zyplayer.doc.grpc.framework.config.SpringContextUtil;
import com.zyplayer.doc.grpc.framework.consts.Const;
import io.grpc.Channel;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* grpc文档控制器
*
* @author 暮光:城中城
* @since 2019年3月31日
*/
@RestController
@RequestMapping("/zyplayer-doc-grpc")
public class GrpcDocController {
@Resource
GrpcChannelFactory grpcChannelFactory;
private static Map<String, ColumnInfo> allColumnsMap = new HashMap<>();
private static Map<String, Object> allBlockingStubMap = new HashMap<>();
/**
* 查找所有文档
*
* @author 暮光:城中城
* @since 2019年3月31日
*/
@RequestMapping("/service")
public DocResponseJson service() {
List<Object> grpcServiceList = SpringContextUtil.getBeanWithAnnotation(GrpcService.class);
if (grpcServiceList == null || grpcServiceList.size() <= 0) {
return DocResponseJson.ok();
}
List<GrpcDocInfo> grpcDocInfoList = new LinkedList<>();
for (Object service : grpcServiceList) {
List<GrpcDocInfo> infoList = this.getServiceInfoByJar(service.getClass());
if (infoList != null) {
grpcDocInfoList.addAll(infoList);
}
}
// 找所有的参数列表
Map<String, ColumnInfo> columnsMap = new HashMap<>();
for (GrpcDocInfo grpcDocInfo : grpcDocInfoList) {
this.setColumnsInfo(grpcDocInfo.getParamType(), columnsMap);
this.setColumnsInfo(grpcDocInfo.getResultType(), columnsMap);
}
GrpcServiceAndColumn grpcServiceAndColumn = new GrpcServiceAndColumn();
grpcServiceAndColumn.setServiceList(grpcDocInfoList);
grpcServiceAndColumn.setColumnMap(columnsMap);
return DocResponseJson.ok(grpcServiceAndColumn);
}
/**
* 执行grpc请求
*
* @author 暮光:城中城
* @since 2019年3月31日
*/
@RequestMapping("/execute")
public DocResponseJson execute(String service, String params) throws Exception {
List<GrpcDocInfo> grpcDocInfoList = this.getServiceInfoByJar(Class.forName(service));
JSONObject executeResult = null;
if (grpcDocInfoList != null && grpcDocInfoList.size() > 0) {
JSONObject paramMap = JSON.parseObject(params);
executeResult = this.executeFunction(grpcDocInfoList.get(0), paramMap);
}
return DocResponseJson.ok(executeResult);
}
/**
* 设置字段信息到map
*
* @author 暮光:城中城
* @since 2019年3月31日
*/
private void setColumnsInfo(String typeName, Map<String, ColumnInfo> columnsMap) {
if (!columnsMap.containsKey(typeName)) {
if (allColumnsMap.containsKey(typeName)) {
columnsMap.put(typeName, allColumnsMap.get(typeName));
} else {
ColumnInfo columnInfo = this.findColumnInfo(typeName);
columnsMap.put(typeName, columnInfo);
allColumnsMap.put(typeName, columnInfo);
}
}
}
/**
* 执行grpc方法
*
* @author 暮光:城中城
* @since 2019年3月31日
*/
private JSONObject executeFunction(GrpcDocInfo grpcDocInfo, JSONObject paramMap) throws Exception {
Object newBuilder = this.createParamBuilder(grpcDocInfo.getParamType(), paramMap);
if (newBuilder == null) {
throw new ConfirmException("参数组装失败");
}
// 创建参数对象
Method build = newBuilder.getClass().getMethod("build");
Object request = build.invoke(newBuilder);
System.out.println(request.toString());
// 为创建过则创建
Object blockingStub = allBlockingStubMap.get(grpcDocInfo.getService());
if (blockingStub == null) {
// 找到父类
Class<?> serviceClass = Class.forName(grpcDocInfo.getService());
String serviceSuperName = serviceClass.getSuperclass().getName();
String superClassName = serviceSuperName.substring(0, serviceSuperName.indexOf("$"));
// 注册
Class<?> superClass = Class.forName(superClassName);
Method newBlockingStubMethod = superClass.getMethod("newBlockingStub", Channel.class);
blockingStub = newBlockingStubMethod.invoke(null, grpcChannelFactory.createChannel());
allBlockingStubMap.put(grpcDocInfo.getService(), blockingStub);
}
Method sayHello = blockingStub.getClass().getMethod(grpcDocInfo.getMethod(), Class.forName(grpcDocInfo.getParamType()));
// 执行请求
Object response = sayHello.invoke(blockingStub, request);
List<ColumnInfo> columnInfos = this.findClassColumns(response.getClass());
return this.protobufToJson(response, columnInfos);
}
/**
* grpc对象转json字符串
*
* @author 暮光:城中城
* @since 2019年3月31日
*/
private JSONObject protobufToJson(Object response, List<ColumnInfo> columnInfos) throws Exception {
JSONObject jsonObject = new JSONObject();
for (ColumnInfo columnInfo : columnInfos) {
Method getMethod = response.getClass().getMethod("get" + this.toUpperCaseFirstOne(columnInfo.getName()));
Object paramValue = getMethod.invoke(response);
if (columnInfo.getParam() != null && columnInfo.getParam().size() > 0) {
JSONObject jsonObjectSub = this.protobufToJson(paramValue, columnInfo.getParam());
jsonObject.put(columnInfo.getName(), jsonObjectSub);
} else {
if ("com.google.protobuf.ByteString".equals(columnInfo.getType())) {
ByteString byteString = (ByteString) paramValue;
jsonObject.put(columnInfo.getName(), byteString.toStringUtf8());
} else {
jsonObject.put(columnInfo.getName(), paramValue);
}
}
}
return jsonObject;
}
/**
* 创建参数的builder对象
*
* @author 暮光:城中城
* @since 2019年3月31日
*/
private Object createParamBuilder(String paramType, JSONObject paramMap) {
try {
Class<?> aClassSub = Class.forName(paramType);
Method newBuilderMethod = aClassSub.getMethod("newBuilder");
Object newBuilder = newBuilderMethod.invoke(aClassSub);
List<MethodParam> functionTemp = this.getSetterFunction(newBuilder.getClass());
for (MethodParam paramTemp : functionTemp) {
Class<?> baseTypeClass = Const.BASE_TYPE.get(paramTemp.getType());
if (baseTypeClass != null) {
Method setNameSub = newBuilder.getClass().getMethod(paramTemp.getSetterName(), baseTypeClass);
Object paramObjTemp;
// 特殊类型参数处理
if ("com.google.protobuf.ByteString".equals(paramTemp.getType())) {
String stringValue = paramMap.getString(paramTemp.getName());
paramObjTemp = ByteString.copyFrom(stringValue, "UTF-8");
} else {
paramObjTemp = paramMap.getObject(paramTemp.getName(), baseTypeClass);
}
// 不为空则设置参数值
if (paramObjTemp != null) {
newBuilder = setNameSub.invoke(newBuilder, paramObjTemp);
}
} else {
Class<?> typeClassSub = Class.forName(paramTemp.getType());
Object newBuilderSub = this.createParamBuilder(paramTemp.getType(), paramMap.getJSONObject(paramTemp.getName()));
if (newBuilderSub == null) {
return null;
}
Method build = newBuilderSub.getClass().getMethod("build");
Object request = build.invoke(newBuilderSub);
Method setNameSub = newBuilder.getClass().getMethod(paramTemp.getSetterName(), typeClassSub);
newBuilder = setNameSub.invoke(newBuilder, request);
}
}
return newBuilder;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 首字母小写
*
* @author 暮光:城中城
* @since 2019年3月31日
*/
private String toLowerCaseFirstOne(String str) {
if (Character.isLowerCase(str.charAt(0))) {
return str;
} else {
return Character.toLowerCase(str.charAt(0)) + str.substring(1);
}
}
/**
* 首字母大写
*
* @author 暮光:城中城
* @since 2019年3月31日
*/
private String toUpperCaseFirstOne(String str) {
if (Character.isUpperCase(str.charAt(0))) {
return str;
} else {
return Character.toUpperCase(str.charAt(0)) + str.substring(1);
}
}
/**
* 找到所有的setter方法
*
* @author 暮光:城中城
* @since 2019年3月31日
*/
private List<MethodParam> getSetterFunction(Class clazz) {
List<MethodParam> result = new LinkedList<>();
Method[] methods = clazz.getDeclaredMethods();
StringBuilder nameSb = new StringBuilder();
for (Method method : methods) {
String methodName = method.getName();
if (!methodName.startsWith("set") || methodName.endsWith("Bytes")
|| methodName.equals("setField") || methodName.equals("setRepeatedField")
|| methodName.equals("setUnknownFields")) {
continue;
}
Type[] parameterTypes = method.getGenericParameterTypes();
if (parameterTypes.length == 1) {
String typeName = parameterTypes[0].getTypeName();
if (typeName.endsWith("$Builder")) {
continue;
}
MethodParam param = new MethodParam();
param.setSetterName(methodName);
param.setType(typeName);
String paramName = methodName.substring(3);
param.setName(this.toLowerCaseFirstOne(paramName));
result.add(param);
}
nameSb.append(methodName).append(",");
}
//System.out.println(nameSb);
return result;
}
/**
* 找到所有的字段信息
*
* @author 暮光:城中城
* @since 2019年3月31日
*/
private ColumnInfo findColumnInfo(String paramType) {
ColumnInfo columnInfo = new ColumnInfo();
try {
String paramName = paramType.substring(paramType.lastIndexOf(".") + 1);
columnInfo.setName(this.toLowerCaseFirstOne(paramName));
columnInfo.setType(paramType);
List<ColumnInfo> columnInfos = this.findClassColumns(Class.forName(paramType));
columnInfo.setParam(columnInfos);
} catch (Exception e) {
e.printStackTrace();
}
return columnInfo;
}
/**
* 找到所有的字段信息
*
* @author 暮光:城中城
* @since 2019年3月31日
*/
private List<ColumnInfo> findClassColumns(Class clazz) throws Exception {
Method getMoney = clazz.getMethod("newBuilder");
Object newBuilder = getMoney.invoke(clazz);
List<MethodParam> paramList = this.getSetterFunction(newBuilder.getClass());
List<ColumnInfo> subInfoList = new LinkedList<>();
for (MethodParam param : paramList) {
ColumnInfo info = new ColumnInfo();
info.setType(param.getType());
info.setName(param.getName());
if (!Const.BASE_TYPE.containsKey(param.getType())) {
List<ColumnInfo> classColumn = this.findClassColumns(Class.forName(param.getType()));
info.setParam(classColumn);
}
subInfoList.add(info);
}
return subInfoList;
}
/**
* 找到服务,组装方法、参数和返回值等
*
* @author 暮光:城中城
* @since 2019年3月31日
*/
private List<GrpcDocInfo> getServiceInfoByJar(Class clazz) {
List<GrpcDocInfo> providerList = new LinkedList<>();
try {
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
String methodName = method.getName();
String resultType = "";
String paramType = "";
Type[] parameterTypes = method.getGenericParameterTypes();
for (int i = 0; i < parameterTypes.length; i++) {
String typeName = parameterTypes[i].getTypeName();
if (i == 0) {
paramType = parameterTypes[i].getTypeName();
} else if (typeName.matches("io.grpc.stub.StreamObserver<.+>")) {
Pattern pattern = Pattern.compile("io.grpc.stub.StreamObserver<(.+)>");
Matcher matcher = pattern.matcher(typeName);
if (matcher.find()) {
resultType = matcher.group(1);
}
}
}
GrpcDocInfo grpcDocInfo = new GrpcDocInfo();
grpcDocInfo.setMethod(methodName);
grpcDocInfo.setService(clazz.getName());
grpcDocInfo.setParamType(paramType);
grpcDocInfo.setResultType(resultType);
providerList.add(grpcDocInfo);
}
} catch (Exception e) {
return null;
}
return providerList;
}
}

View File

@@ -0,0 +1,48 @@
package com.zyplayer.doc.grpc.controller.po;
import java.util.List;
/**
* grpc字段信息
*
* @author 暮光:城中城
* @since 2019年3月31日
*/
public class ColumnInfo {
private String name;
private String type;
private String desc;
private List<ColumnInfo> param;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public List<ColumnInfo> getParam() {
return param;
}
public void setParam(List<ColumnInfo> param) {
this.param = param;
}
}

View File

@@ -0,0 +1,73 @@
package com.zyplayer.doc.grpc.controller.po;
/**
* 请求参数对象
*
* @author 暮光:城中城
* @since 2019年2月10日
*/
public class GrpcDocInfo {
private String method;
private String service;
private String explain;
private String result;
private String paramType;
private String resultType;
private Integer version;
public Integer getVersion() {
return version;
}
public void setVersion(Integer version) {
this.version = version;
}
public String getService() {
return service;
}
public void setService(String service) {
this.service = service;
}
public String getExplain() {
return explain;
}
public void setExplain(String explain) {
this.explain = explain;
}
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
}
public String getResultType() {
return resultType;
}
public void setResultType(String resultType) {
this.resultType = resultType;
}
public String getParamType() {
return paramType;
}
public void setParamType(String paramType) {
this.paramType = paramType;
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
}

View File

@@ -0,0 +1,31 @@
package com.zyplayer.doc.grpc.controller.po;
import java.util.List;
import java.util.Map;
/**
* 服务和字段信息
*
* @author 暮光:城中城
* @since 2019年2月10日
*/
public class GrpcServiceAndColumn {
private List<GrpcDocInfo> serviceList;
private Map<String, ColumnInfo> columnMap;
public List<GrpcDocInfo> getServiceList() {
return serviceList;
}
public void setServiceList(List<GrpcDocInfo> serviceList) {
this.serviceList = serviceList;
}
public Map<String, ColumnInfo> getColumnMap() {
return columnMap;
}
public void setColumnMap(Map<String, ColumnInfo> columnMap) {
this.columnMap = columnMap;
}
}

View File

@@ -0,0 +1,37 @@
package com.zyplayer.doc.grpc.controller.po;
/**
* grpc方法信息
*
* @author 暮光:城中城
* @since 2019年3月31日
*/
public class MethodParam {
private String name;
private String type;
private String setterName;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getSetterName() {
return setterName;
}
public void setSetterName(String setterName) {
this.setterName = setterName;
}
}

View File

@@ -0,0 +1,22 @@
package com.zyplayer.doc.grpc.framework.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import java.lang.annotation.*;
/**
* 开启grpc的注解
*
* @author 暮光:城中城
* @since 2019年3月31日
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@ComponentScan(basePackages = {
"com.zyplayer.doc.grpc",
})
public @interface EnableDocGrpc {
}

View File

@@ -0,0 +1,46 @@
package com.zyplayer.doc.grpc.framework.config;
import com.nxest.grpc.client.*;
import com.nxest.grpc.client.configure.GrpcClientProperties;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.Resource;
/**
* grpc配置
*
* @author 暮光:城中城
* @since 2019年3月31日
*/
@Configuration
@AutoConfigureAfter(GrpcServerConfiguration.class)
public class GrpcClientConfiguration {
@Resource
private GrpcProperties grpcProperties;
@Bean
public GrpcClientProperties clientProperties() {
return grpcProperties.getClient();
}
@Bean
public GrpcChannelFactory channelFactory() {
return new AddressChannelFactory(clientProperties());
}
@Bean
public GrpcClientInterceptorDiscoverer clientInterceptorDiscoverer() {
return new AnnotationClientInterceptorDiscoverer();
}
@Bean
@ConditionalOnClass(GrpcClient.class)
public GrpcClientBeanPostProcessor grpcClientBeanPostProcessor() {
return new GrpcClientBeanPostProcessor(channelFactory(), clientInterceptorDiscoverer());
}
}

View File

@@ -0,0 +1,34 @@
package com.zyplayer.doc.grpc.framework.config;
import com.nxest.grpc.client.configure.GrpcClientProperties;
import com.nxest.grpc.server.configure.GrpcServerProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* grpc配置文件
*
* @author 暮光:城中城
* @since 2019年3月31日
*/
@Configuration
@ConfigurationProperties(prefix = "grpc")
public class GrpcProperties {
/**
* Grpc server properties
*/
private final GrpcServerProperties server = new GrpcServerProperties();
/**
* Grpc client properties
*/
private final GrpcClientProperties client = new GrpcClientProperties();
public GrpcServerProperties getServer() {
return server;
}
public GrpcClientProperties getClient() {
return client;
}
}

View File

@@ -0,0 +1,35 @@
package com.zyplayer.doc.grpc.framework.config;
import com.nxest.grpc.server.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.Resource;
/**
* grpc服务
*
* @author 暮光:城中城
* @since 2019年3月31日
*/
@Configuration
public class GrpcServerConfiguration {
@Resource
private GrpcProperties grpcProperties;
@Bean
public GrpcServiceDiscoverer serviceDiscoverer() {
return new AnnotationGrpcServiceDiscoverer();
}
@Bean
public GrpcServerFactory severFactory() {
return new NettyGrpcServerFactory(serviceDiscoverer(), grpcProperties.getServer());
}
@Bean(name = "grpcServer", initMethod = "start", destroyMethod = "destroy")
public GrpcServer serverRunner() {
return severFactory().createServer();
}
}

View File

@@ -0,0 +1,55 @@
package com.zyplayer.doc.grpc.framework.config;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import java.lang.annotation.Annotation;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* context工具类
*
* @author 暮光:城中城
* @since 2019年3月31日
*/
@Component
public class SpringContextUtil implements ApplicationContextAware {
public static ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
public static ApplicationContext getApplicationContext() {
return context;
}
public static <T> T getBean(Class<T> clz) {
return context.getBean(clz);
}
public static Object getBean(String string) {
return getApplicationContext().getBean(string);
}
/**
* 获取类
*
* @param annotationType annotation
* @return 类对象
*/
public static List<Object> getBeanWithAnnotation(Class<? extends Annotation> annotationType) {
if (context == null) {
return null;
}
Map<String, Object> beansWithAnnotation = context.getBeansWithAnnotation(annotationType);
return new LinkedList<>(beansWithAnnotation.values());
}
}

View File

@@ -0,0 +1,35 @@
package com.zyplayer.doc.grpc.framework.consts;
import com.google.protobuf.ByteString;
import java.math.BigDecimal;
import java.util.*;
/**
* @author 暮光:城中城
* @since 2019年3月31日
*/
public class Const {
/** 所有基础类型 */
public static final Map<String, Class<?>> BASE_TYPE;
static {
BASE_TYPE = new HashMap<>();
BASE_TYPE.put(String.class.getName(), String.class);
BASE_TYPE.put(Integer.class.getName(), Integer.class);
BASE_TYPE.put(Long.class.getName(), Long.class);
BASE_TYPE.put(Double.class.getName(), Double.class);
BASE_TYPE.put(Date.class.getName(), Date.class);
BASE_TYPE.put(Byte.class.getName(), Byte.class);
BASE_TYPE.put(Float.class.getName(), Float.class);
BASE_TYPE.put(BigDecimal.class.getName(), BigDecimal.class);
BASE_TYPE.put(ByteString.class.getName(), ByteString.class);
BASE_TYPE.put(char.class.getName(), char.class);
BASE_TYPE.put(int.class.getName(), int.class);
BASE_TYPE.put(long.class.getName(), long.class);
BASE_TYPE.put(double.class.getName(), double.class);
BASE_TYPE.put(byte.class.getName(), byte.class);
BASE_TYPE.put(float.class.getName(), float.class);
}
}

View File

@@ -0,0 +1,34 @@
//package com.zyplayer.doc.grpc.service;
//
//
//import com.google.protobuf.Empty;
//import com.google.protobuf.Timestamp;
//import com.nxest.grpc.server.GrpcService;
//import com.zyplayer.doc.grpc.proto.HelloRequest;
//import com.zyplayer.doc.grpc.proto.HelloResponse;
//import com.zyplayer.doc.grpc.proto.TimeResponse;
//import com.zyplayer.doc.grpc.proto.ZyplayerGreeterGrpc;
//import io.grpc.stub.StreamObserver;
//
///**
// * grpc服务测试类需要mvn compile项目
// * @author 暮光:城中城
// * @since 2019年3月31日
// */
//@GrpcService
//public class HelloWorldService extends ZyplayerGreeterGrpc.ZyplayerGreeterImplBase {
//
// @Override
// public void sayHello(HelloRequest request, StreamObserver<HelloResponse> responseObserver) {
// HelloResponse reply = HelloResponse.newBuilder().setMessage("Hello " + request.getName()).build();
// responseObserver.onNext(reply);
// responseObserver.onCompleted();
// }
//
// @Override
// public void sayTime(Empty request, StreamObserver<TimeResponse> responseObserver) {
// TimeResponse reply = TimeResponse.newBuilder().setTime(Timestamp.newBuilder().build()).build();
// responseObserver.onNext(reply);
// responseObserver.onCompleted();
// }
//}

View File

@@ -0,0 +1,40 @@
//package com.zyplayer.doc.grpc.service;
//
//
//import com.google.protobuf.ByteString;
//import com.nxest.grpc.server.GrpcService;
//import com.zyplayer.doc.grpc.proto.BaseMsg;
//import com.zyplayer.doc.grpc.proto.ChatMsg;
//import com.zyplayer.doc.grpc.proto.User;
//import com.zyplayer.doc.grpc.proto.ZyplayerChatGrpc;
//import io.grpc.stub.StreamObserver;
//
///**
// * grpc服务测试类需要mvn compile项目
// * @author 暮光:城中城
// * @since 2019年3月31日
// */
//@GrpcService
//public class ZyplayerChatService extends ZyplayerChatGrpc.ZyplayerChatImplBase {
//
// @Override
// public void sendText(ChatMsg request, StreamObserver<ChatMsg> responseObserver) {
// User user = null;
// try {
// user = User.newBuilder().setCookies(ByteString.copyFrom("xxx", "utf-8")).build();
// } catch (Exception e) {
// e.printStackTrace();
// }
// BaseMsg baseMsg = BaseMsg.newBuilder().setCmd(1).setUser(user).build();
// ChatMsg reply = ChatMsg.newBuilder().setToken("sendText").setIP("xx.xx.xx.xx").setBaseMsg(baseMsg).build();
// responseObserver.onNext(reply);
// responseObserver.onCompleted();
// }
//
// @Override
// public void sendImage(ChatMsg request, StreamObserver<ChatMsg> responseObserver) {
// ChatMsg reply = ChatMsg.newBuilder().setToken("sendImage").setIP("xx.xx.xx.xx").build();
// responseObserver.onNext(reply);
// responseObserver.onCompleted();
// }
//}