protobuf 个人理解就是一种数据格式,只不过在序列化后的大小、序列化、反序列化方面更优秀一些。
demo 是用spring boot做了一个server,一个test。server暴露出来的服务是rest的,数据格式就是protobuf。
直接上代码
common:
proto数据结构文件
syntax = "proto3";
// package comp.protobuf.pb;
option java_package = "com.protobuf.model";
option java_outer_classname = "UserProto";
message User {
int64 id = 1;
string name = 2;
message PhoneNumber {
string number = 1;
}
repeated PhoneNumber phone = 4;
}
生成对应语言的类,我这直接用java 的runtime,也可以直接再cmd中操作
package com.protobuf;
import java.io.IOException;
public class GenerateClass {
public static void main(String[] args) {
String protoFile = "user.proto";//
String strCmd = "D:/software/protobuf-3.3.0/protoc.exe --java_out=./src/main/java ./src/main/resources/"+ protoFile;
try {
Runtime.getRuntime().exec(strCmd);
} catch (IOException e) {
e.printStackTrace();
}//通过执行cmd命令调用protoc.exe程序
}
}
生成的java类文件 UserProto.java
2. server port:8080
application, controller
package com.protobuf;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.protobuf.model.UserProto;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/**
* This example demonstrates serving up REST payloads encoded using
* <a href="https://developers.google.com/protocol-buffers/">Google Protocol Buffers</a>.
*/
@SpringBootApplication
public class ProtoServerApplication {
public static void main(String[] args) {
SpringApplication.run(ProtoServerApplication.class, args);
}
@Bean
ProtobufHttpMessageConverter protobufHttpMessageConverter() {
return new ProtobufHttpMessageConverter();
}
private UserProto.User user(long id, String name, Collection<String> phones) {
Collection<UserProto.User.PhoneNumber> pnList =
phones.stream().map(e -> UserProto.User.PhoneNumber.newBuilder().setNumber(e).build()).collect(Collectors.toList());
return UserProto.User.newBuilder().setId(id).setName(name).addAllPhone(pnList).build();
}
@Bean
UserRepository userRepository() {
Map<Long, UserProto.User> userM = new ConcurrentHashMap<>();
Arrays.asList(
user(1, "Chris", Arrays.asList("1566666","15777777")),
user(2, "Josh", Arrays.asList("157777777777","158888888888")),
user(3, "Matt", Arrays.asList("1566666","159999999999999")),
user(4, "Russ", Arrays.asList("1599999999","15733337"))
).forEach(c -> userM.put(c.getId(), c));
//UserRepository 只有一个抽象方法,可以认为是一个函数接口,所以可以用lambda表达式 表示。
return userM::get;
}
}
interface UserRepository {
UserProto.User findById(long id);
}
@RestController
class UserRestController {
@Autowired
private UserRepository userRepository;
@RequestMapping("/user/{id}")
UserProto.User user(@PathVariable Integer id) {
return userRepository.findById(id);
}
@RequestMapping("/map/{id}")
Map<String,String> map(@PathVariable Integer id) {
Map<String,String> m = new HashMap<String,String>();
m.put("name", "yonyou");
m.put("www","yonyou.com");
return m;
}
}
3. test port:8181
application, controller
package com.protobuf;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import com.protobuf.model.UserProto;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/**
* This example demonstrates serving up REST payloads encoded using
* <a href="https://developers.google.com/protocol-buffers/">Google Protocol Buffers</a>.
*/
@SpringBootApplication
public class ProtobufTestApplication {
public static void main(String[] args) {
SpringApplication.run(ProtobufTestApplication.class, args);
}
@Bean
RestTemplate restTemplate() {
RestTemplate rt = new RestTemplate();
rt.getMessageConverters().add(new ProtobufHttpMessageConverter());
return rt;
}
}
@RestController
class UserRestController {
@Autowired
private RestTemplate restTemplate;
@RequestMapping("/user/{id}")
UserProto.User user(@PathVariable Integer id) {
UserProto.User obj = this.restTemplate.getForObject("http://127.0.0.1:8080/user/" + id,
UserProto.User.class);
return obj;
}
@RequestMapping("/user2/{id}")
String user2(@PathVariable Integer id) {
UserProto.User obj = this.restTemplate.getForObject("http://127.0.0.1:8080/user/" + id,
UserProto.User.class);
return obj.toString();
}
@RequestMapping("/map/{id}")
Map<String,String> map(@PathVariable Integer id) {
Map m = this.restTemplate.getForObject("http://127.0.0.1:8080/map/" + id, Map.class);
return m;
}
}
还有一个本地测试的 Test.java
package com.protobuf;
import java.io.IOException;
import com.protobuf.model.UserProto;
import com.protobuf.model.UserProto.User;
public class Test {
public static void main(String[] args) throws IOException {
//模拟将对象转成byte[],方便传输
UserProto.User.Builder builder = UserProto.User.newBuilder();
builder.setId(1);
builder.setName("ant");
builder.addPhone(UserProto.User.PhoneNumber.newBuilder().setNumber("15000000"));
builder.addPhone(UserProto.User.PhoneNumber.newBuilder().setNumber("15111111"));
UserProto.User user = builder.build();
System.out.println("before :"+ user.toString());
System.out.println("===========Person Byte==========");
for(byte b : user.toByteArray()){
System.out.print(b);
}
System.out.println();
System.out.println(user.toByteString());
System.out.println("================================");
//模拟接收Byte[],反序列化成Person类
byte[] byteArray =user.toByteArray();
User p2 = user.parseFrom(byteArray);
System.out.println("after :" +p2.toString());
}
}
4. 测试
server 直接访问
通过test访问server
报错是因为我后台代码返回给前台的是protobuf的数据格式,前台浏览器解析不了。
换个方式,就可以了,因为返回的是string
注意代码中的resttemplate
@Bean
RestTemplate restTemplate() {
RestTemplate rt = new RestTemplate();
rt.getMessageConverters().add(new ProtobufHttpMessageConverter());
return rt;
}
若是将rt.getMessageConverters().add(new ProtobufHttpMessageConverter()); 注释掉测试,会发现test后台不能解析server返回的结果
报错
ProtobufHttpMessageConverter 是spring 4.1 新增加的支持protobuf格式的数据转换功能。
代码下载:
http://download.youkuaiyun.com/detail/stonexmx/9889649