如果etcd采用v3结构存储,则etcd只有v3.5以上的版本才支持restful 接口
@FeignClient(name = "etcdApiService")
public interface EtcdApi {
@RequestMapping(value = "/v3/kv/put", method = RequestMethod.POST, consumes = "application/json")
String notify(@RequestBody EtcdPutParamInfo paramInfo);
}
public class EtcdPutParamInfo {
private String key;
private String value;
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
@Service
public class etcd EtcdNotifier {
private static final Logger logger = LoggerFactory.getLogger(EtcdNotifier.class);
@Autowired
private EtcdApi etcdApi;
public void notify(Object conf) {
for (int i = 1; i <= 3; i++) {
boolean success = retry(conf, i);
if (success) {
break;
}
}
}
public boolean retry(Object conf, int count) {
Callable<Boolean> callable = () -> send(conf, count);
boolean success = false;
try {
success = callable.call();
} catch (Exception ex) {
logger.error(String.format("etcd notifier retry err count:%s", count), ex);
}
return success;
}
public boolean send(Object conf, int count) {
try {
String key = "myEtcdKey";
String value = Base64Util.toBase64(JsonUtil.serialize(conf));
if (StringUtils.isEmpty(value)) {
logger.info(String.format("etcd notifier deal info is empty :%s", value));
return true;
}
EtcdPutParamInfo paramInfo = new EtcdPutParamInfo();
paramInfo.setKey(key);
paramInfo.setValue(value);
logger.info(String.format("etcd notifier info :%s", JsonUtil.serialize(paramInfo)));
String ret = etcdApi.notify(paramInfo);
logger.info(String.format("etcd notifier count:%s,ret:%s", count, ret));
return true;
} catch (Exception ex) {
logger.error(String.format("etcd notifier err count:%s", count), ex);
return false;
}
}
}
public class JsonUtil {
public static final ObjectMapper MAPPER = new ObjectMapper();
private JsonUtil() {
}
public static String serialize(Object obj) {
try {
return MAPPER.writeValueAsString(obj);
} catch (JsonProcessingException var2) {
var2.printStackTrace();
return null;
}
}
public static Object deserialize(String jsonText, TypeReference type) {
try {
return MAPPER.readValue(jsonText, type);
} catch (JsonProcessingException var3) {
var3.printStackTrace();
return null;
}
}
public static <T> T deserialize(String jsonText, Class<T> beanClass) {
try {
return MAPPER.readValue(jsonText, beanClass);
} catch (Exception var3) {
var3.printStackTrace();
return null;
}
}
public static JsonNode deserialize(String jsonText) {
try {
return MAPPER.readTree(jsonText);
} catch (JsonProcessingException var2) {
var2.printStackTrace();
return null;
}
}
}
public class Base64Util {
private static final Decoder decoder = Base64.getDecoder();
private static final Encoder encoder = Base64.getEncoder();
public Base64Util() {
}
public static String toBase64(String text) {
try {
byte[] textByte = text.getBytes("UTF-8");
String encodedText = encoder.encodeToString(textByte);
return encodedText;
} catch (UnsupportedEncodingException var3) {
var3.printStackTrace();
return null;
}
}
public static String fromBase64(String encodedText) {
try {
String text = new String(decoder.decode(encodedText), "UTF-8");
return text;
} catch (UnsupportedEncodingException var2) {
var2.printStackTrace();
return null;
}
}
}
引用的pom
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
<version>2.5.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
etcdApiService.ribbon.eureka.enabled=false
etcdApiService.ribbon.listOfServers=http://192.168.0.8:2379
其他接口
Use the /v3/kv/range and /v3/kv/put services to read and write keys:
curl -L http://localhost:2379/v3/kv/put \
-X POST -d '{"key": "Zm9v", "value": "YmFy"}'
# {"header":{"cluster_id":"12585971608760269493","member_id":"13847567121247652255","revision":"2","raft_term":"3"}}
curl -L http://localhost:2379/v3/kv/range \
-X POST -d '{"key": "Zm9v"}'
# {"header":{"cluster_id":"12585971608760269493","member_id":"13847567121247652255","revision":"2","raft_term":"3"},"kvs":[{"key":"Zm9v","create_revision":"2","mod_revision":"2","version":"1","value":"YmFy"}],"count":"1"}
# get all keys prefixed with "foo"
curl -L http://localhost:2379/v3/kv/range \
-X POST -d '{"key": "Zm9v", "range_end": "Zm9w"}'
# {"header":{"cluster_id":"12585971608760269493","member_id":"13847567121247652255","revision":"2","raft_term":"3"},"kvs":[{"key":"Zm9v","create_revision":"2","mod_revision":"2","version":"1","value":"YmFy"}],"count":"1"}
Watch keys
Use the /v3/watch service to watch keys:
curl -N http://localhost:2379/v3/watch \
-X POST -d '{"create_request": {"key":"Zm9v"} }' &
# {"result":{"header":{"cluster_id":"12585971608760269493","member_id":"13847567121247652255","revision":"1","raft_term":"2"},"created":true}}
curl -L http://localhost:2379/v3/kv/put \
-X POST -d '{"key": "Zm9v", "value": "YmFy"}' >/dev/null 2>&1
# {"result":{"header":{"cluster_id":"12585971608760269493","member_id":"13847567121247652255","revision":"2","raft_term":"2"},"events":[{"kv":{"key":"Zm9v","create_revision":"2","mod_revision":"2","version":"1","value":"YmFy"}}]}}
Transactions
Issue a transaction with /v3/kv/txn:
# target CREATE
curl -L http://localhost:2379/v3/kv/txn \
-X POST \
-d '{"compare":[{"target":"CREATE","key":"Zm9v","createRevision":"2"}],"success":[{"requestPut":{"key":"Zm9v","value":"YmFy"}}]}'
# {"header":{"cluster_id":"12585971608760269493","member_id":"13847567121247652255","revision":"3","raft_term":"2"},"succeeded":true,"responses":[{"response_put":{"header":{"revision":"3"}}}]}
# target VERSION
curl -L http://localhost:2379/v3/kv/txn \
-X POST \
-d '{"compare":[{"version":"4","result":"EQUAL","target":"VERSION","key":"Zm9v"}],"success":[{"requestRange":{"key":"Zm9v"}}]}'
# {"header":{"cluster_id":"14841639068965178418","member_id":"10276657743932975437","revision":"6","raft_term":"3"},"succeeded":true,"responses":[{"response_range":{"header":{"revision":"6"},"kvs":[{"key":"Zm9v","create_revision":"2","mod_revision":"6","version":"4","value":"YmF6"}],"count":"1"}}]}
Authentication
Set up authentication with the /v3/auth service:
# create root user
curl -L http://localhost:2379/v3/auth/user/add \
-X POST -d '{"name": "root", "password": "pass"}'
# {"header":{"cluster_id":"14841639068965178418","member_id":"10276657743932975437","revision":"1","raft_term":"2"}}
# create root role
curl -L http://localhost:2379/v3/auth/role/add \
-X POST -d '{"name": "root"}'
# {"header":{"cluster_id":"14841639068965178418","member_id":"10276657743932975437","revision":"1","raft_term":"2"}}
# grant root role
curl -L http://localhost:2379/v3/auth/user/grant \
-X POST -d '{"user": "root", "role": "root"}'
# {"header":{"cluster_id":"14841639068965178418","member_id":"10276657743932975437","revision":"1","raft_term":"2"}}
# enable auth
curl -L http://localhost:2379/v3/auth/enable -X POST -d '{}'
# {"header":{"cluster_id":"14841639068965178418","member_id":"10276657743932975437","revision":"1","raft_term":"2"}}
Authenticate with etcd for an authentication token using /v3/auth/authenticate:
# get the auth token for the root user
curl -L http://localhost:2379/v3/auth/authenticate \
-X POST -d '{"name": "root", "password": "pass"}'
# {"header":{"cluster_id":"14841639068965178418","member_id":"10276657743932975437","revision":"1","raft_term":"2"},"token":"sssvIpwfnLAcWAQH.9"}
Set the Authorization header to the authentication token to fetch a key using authentication credentials:
curl -L http://localhost:2379/v3/kv/put \
-H 'Authorization : sssvIpwfnLAcWAQH.9' \
-X POST -d '{"key": "Zm9v", "value": "YmFy"}'
# {"header":{"cluster_id":"14841639068965178418","member_id":"10276657743932975437","revision":"2","raft_term":"2"}}