This commit is contained in:
tanghc
2020-07-28 18:21:01 +08:00
parent ee30f18d53
commit 0fea955db9
165 changed files with 918 additions and 4838 deletions

View File

@@ -30,20 +30,3 @@
- 修改sop-website下的application-dev.properties相关配置
- 运行WebsiteServerApplication.java
- 访问http://localhost:8083
## 基本配置
`sop-gateway`下的application-dev.properties配置各项配置说明如下
```properties
# 忽略验证设置true则所有接口不会进行签名校验默认false
sop.api-config.ignore-validate=false
# 是否对结果进行合并默认true
sop.api-config.merge-result=true
# 显示返回sign默认true
sop.api-config.show-return-sign=true
# 是否开启限流功能默认true
sop.api-config.open-limit=true
# 请求超时时间默认5分钟即允许在5分钟内重复请求默认300
sop.api-config.timeout-seconds=300
```

View File

@@ -1,21 +1,29 @@
# 新增接口
以story服务为例新增一个获取故事内容接口
假设要对下面这个接口提供开放能力。
- 在controller下新建一个类StoryDemoController.java
- 加上`@RestController`注解
```java
@RestController
public class StoryDemoController {
@RequestMapping("/story/get")
public StoryResult getStory() {
StoryResult result = new StoryResult();
result.setId(1L);
result.setName("海底小纵队");
return result;
}
}
```
- 新增一个接口
只需要在方法上新增一个`@Open`注解,指定接口名即可
```java
@ApiMapping(value = "story.demo.get")
// 添加一个@Open注解
@Open("story.demo.get")
@RequestMapping("/story/get")
public StoryResult getStory() {
StoryResult result = new StoryResult();
result.setId(1L);
@@ -24,13 +32,9 @@ public StoryResult getStory() {
}
```
这里的`@ApiMapping`注解作用同`@RequestMapping`注解,可以理解为是它的扩展
如果要加上版本号,指定`version`参数:`@Open(value = "story.demo.get", version = "2.0")`
value就是接口名对应客户端的`method`参数
如果要加上版本号,指定`version`参数:`@ApiMapping(value = "story.demo.get", version = "2.0")`
- 重启story服务这样接口就可以使用了。
- 重启服务,这样接口就可以使用了。
## 绑定业务参数
@@ -43,7 +47,8 @@ value就是接口名对应客户端的`method`参数
其中biz_content部分是我们想要的在方法上申明一个对象对应biz_content中的内容即可完成参数绑定并且对参数进行JSR-303校验。
```java
@ApiMapping(value = "goods.add")
@Open("goods.add")
@RequestMapping("/goods/add")
public Object addGoods(GoodsParam param) {
return param;
}
@@ -58,40 +63,14 @@ public class GoodsParam {
一般情况下,只需要获取业务参数即可,如果想要获取更多的参数,可在后面跟一个`HttpServletRequest`对象。
```java
@ApiMapping(value = "goods.add")
@Open("goods.add")
@RequestMapping("/goods/add")
public Object addGoods(GoodsParam param, HttpServletRequest request) {
System.out.println(request.getParameter("method"));
return param;
}
```
- 方式2
```java
@ApiMapping(value = "story.get", version = "2.2")
public Story getStory22(OpenContext<Story> openContext) {
// 业务参数
Story bizObject = openContext.getBizObject();
// 获取appid更多方法查看OpenContext类
String appId = openContext.getAppId();
System.out.println(appId);
return bizObject;
}
```
另一种方式OpenContext泛型参数填bizObject类调用openContext.getBizObject()可直接获得对象
此方式等价于:
```java
@ApiMapping(value = "story.get", version = "2.2")
public Story getStory22(Story bizObject) {
OpenContext openContext = ServiceContext.getCurrentContext().getOpenContext();
String appId = openContext.getAppId();
System.out.println(appId);
return bizObject;
}
```
## 接口命名
@@ -122,13 +101,13 @@ public class StoryDemoTest extends TestBase {
// 公共请求参数
Map<String, String> params = new HashMap<String, String>();
params.put("app_id", appId);
// 这里对应@ApiMapping.value属性
// 这里对应@Open.value属性
params.put("method", "story.demo.get");
params.put("format", "json");
params.put("charset", "utf-8");
params.put("sign_type", "RSA2");
params.put("timestamp", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
// 这里对应@ApiMapping.version属性
// 这里对应@Open.version属性
params.put("version", "1.0");
// 业务参数
@@ -166,47 +145,3 @@ public class StoryDemoTest extends TestBase {
{"story_demo_get_response":{"msg":"Success","code":"10000","name":"白雪公主","id":1},"sign":"YMbxTPdovi6htcn1K3USTS6/Tbg6MOAMigG6x/kG0kQFCYH8ljvxXzcY86UT056nUG3OXxnj0xkw07eV6E03HMlu7bn3/jrT3PCcV3YguhA92aWz720x2xJWdfXY13OUPS9VOCC9zIVxu6EBD+PoZ7ojYChYvOfCR5I8bR/oOc0ZLjK63PWTBdf0eFS4sybXzRf81uNLMROsMhmBDDy0Fhml3ml77qzWBIpsmq5ECZ+89rMPbkNhAUcnFAe7ik7xZIL6WcUhAOhKVa8ZQK1GMjoGnAbGRed1FbuOHZGubgffg4/vMqrY10Bcy6h9jt/zK5w9L3HVgK3aPgQlfP16Gg=="}
```
## 开放现有接口
如果想把现有项目中的接口开放出去,提供给客户调用,具体操作如下:
- 将现有项目接入到SOP前往`项目接入到SOP`文档页查看
- 在现有接口方法上加上一个注解`@ApiAbility`,如下面这个接口
```java
// 具备开放平台能力
@ApiAbility
@RequestMapping("getStory2")
public Story getStory2_0() {
Story story = new Story();
story.setId(1);
story.setName("海底小纵队(默认版本号)");
return story;
}
```
- 启动程序
这种情况下,老接口依然能正常访问,同时开放平台也能访问进来。
**注意** 此时的开放接口对应的接口名为:类@RequestMapping.value + "." + 方法@RequestMapping.value
举个列子:
```java
@RequestMapping("goods")
public class MyController {
@ApiAbility
@RequestMapping("listGoods")
public Object fun() {
}
}
```
fun接口对应的路径为`/goods/listGoods`
那么对应开放平台的接口名会转换成:`goods.listGoods`客户端的method参数要填`goods.listGoods`
当然也可以直接把@RequestMapping替换成`@ApiMapping`并指定接口名,这样的话不能兼容以前的访问形式。

View File

@@ -5,7 +5,8 @@
在参数中使用注解即可框架会自动进行验证。如下面一个添加商品接口它的参数是GoodsParam
```java
@ApiMapping(value = "goods.add")
@Open("goods.add")
@RequestMapping("/goods/add")
public void addGoods(GoodsParam param) {
...
}

View File

@@ -84,7 +84,7 @@ public class StoryResult {
* @return
*/
@ApiOperation(value = "获取故事信息", notes = "说明接口的详细信息,介绍,用途,注意事项等。")
@ApiMapping(value = "alipay.story.find")
@Open(value = "alipay.story.find")
public StoryResult getStory2(StoryParam story) {
log.info("获取故事信息参数, story: {}", story);
// 获取其它参数

View File

@@ -1,78 +0,0 @@
# easyopen支持
SOP对easyopen项目提供了很好的支持如果您的服务端使用了easyopen框架相关配置步骤如下
## 服务端配置
首先是服务端相关配置
- pom添加依赖
```xml
<!-- sop接入依赖 -->
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-service-common</artifactId>
<version>最新版本</version>
</dependency>
<!-- 使用nacos注册中心
版本 0.2.x.RELEASE 对应的是 Spring Boot 2.x 版本,版本 0.1.x.RELEASE 对应的是 Spring Boot 1.x 版本。
https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-alibaba-nacos-discovery
-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>0.2.2.RELEASE</version>
<exclusions>
<exclusion>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>net.oschina.durcframework</groupId>
<artifactId>easyopen</artifactId>
<version>1.16.1</version>
</dependency>
<!-- sop接入依赖 end -->
```
easyopen版本必须升级到1.16.1
- 启动类上面添加注解@EnableDiscoveryClient,将自己注册到注册中心
- 新增一个配置类继承EasyopenServiceConfiguration内容为空
```java
@Configuration
public class SopConfig extends EasyopenServiceConfiguration {
}
```
服务端配置完毕,重启服务。
## 网关端配置
接下来是网关的配置
- 打开ZuulConfig.java注释掉原本的@Configuration新增如下Configuration
```java
@Configuration
public class ZuulConfig extends EasyopenZuulConfiguration {
}
```
配置完毕,重启网关服务,可运行测试用例`EasyopenClientPostTest.java`验证
**注:** 配置完成后easyopen签名校验将会关闭改用网关端来校验网关对easyopen返回的结果不进行处理直接返回服务端的结果。
完整配置可查看sop-example/sop-easyopen项目

View File

@@ -1,6 +1,6 @@
# 自定义校验token2.5.0
# 自定义校验token
从2.5.0开始在`@ApiMapping`注解中新增了一个属性`needToken`用来告诉网关是否校验token
`@Open`注解中一个属性`needToken`用来告诉网关是否校验token
```java
/**
@@ -12,7 +12,9 @@ boolean needToken() default false;
使用方式:
```java
@ApiMapping(value = "story.token.get", needToken = true/* 设置true网关会校验token是否存在 */)
@ApiOperation(value="传递token", notes = "传递token")
@Open(value = "story.get.token", needToken = true/* 设置true网关会校验token是否存在 */)
@RequestMapping("token")
public StoryResult token(StoryParam story) {
OpenContext openContext = ServiceContext.getCurrentContext().getOpenContext();
String appAuthToken = openContext.getAppAuthToken();
@@ -42,14 +44,11 @@ public class MyConfig {
ApiConfig.getInstance().setTokenValidator(apiParam -> {
// 获取客户端传递过来的token
String token = apiParam.fetchAccessToken();
if (StringUtils.isBlank(token)) {
return false;
}
return !StringUtils.isBlank(token);
// TODO: 校验token有效性可以从redis中读取
// 返回true表示这个token真实、有效
return true;
});
});
}
}
```

View File

@@ -9,8 +9,8 @@
假设把路由a,b,c分配给了`VIP角色`那么具有VIP角色的ISV可以访问a,b,c三个路由。
默认情况下接口访问时公开的ISV都能访问。如果要设置某个接口访问权限`@ApiMapping`注解中指定permission=true。
如:`@ApiMapping(value = "permission.story.get", permission = true)`。这样该接口是需要经过授权给ISV才能访问的。
默认情况下接口访问时公开的ISV都能访问。如果要设置某个接口访问权限`@Open`注解中指定permission=true。
如:`@Open(value = "permission.story.get", permission = true)`。这样该接口是需要经过授权给ISV才能访问的。
重启服务后登录admin服务管理-路由列表界面中,操作操作列会出现一个授权按钮,点击出现授权窗口,勾选对应的角色即可完成授权。

View File

@@ -1,16 +0,0 @@
# 使用SpringCloudGateway
修改`sop-gateway/pom.xml`配置artifactId部分改成`sop-bridge-gateway`即可
```xml
<dependency>
<groupId>com.gitee.sop</groupId>
<!-- 使用zuul作为网关 -->
<!--<artifactId>sop-bridge-zuul</artifactId>-->
<!-- 使用spring cloud gateway作为网关 -->
<artifactId>sop-bridge-gateway</artifactId>
<version>对应版本</version>
</dependency>
```
修改完毕重启sop-gateway

View File

@@ -1,27 +1,10 @@
# 传统web开发
# 提供rest接口
默认情况下SOP只提供开放接口也可以同时提供restful接口即程序提供一部分的开放接口同时提供一部分restful接口
默认情况下提供restful功能是关闭的开启方式如下
- 打开sop-gateway配置文件新增一行配置
```properties
# 提供restful接口
sop.restful.enable=true
```
- 前端app请求网关(`2.4.1之后有变动`)
有些接口没有被开放但是也想要通过网关来访问SOP提供一个固定的请求格式来访问
请求格式:
**2.4.1版本之前:** `http://ip:port/rest/your_path`,其中`http://ip:port/rest/`为固定部分,后面跟微服务请求路径。
**2.4.1之后:** `http://ip:port/rest/服务id/your_path`,其中`http://ip:port/rest/`为固定部分,后面跟微服务请求路径。
**注意,`2.4.1`开始多了一个服务id作为区分这样做是为了避免各微服务之间url冲突假如两个微服务都有一个叫`/getItems`这样的接口
那么调用`http://ip:port/rest/getItems`接口网关无法做出正确的路由,虽然可以在代码上进行规范,为了防止万一,还是强行加上了,避免采坑
。可以指定`sop.restful.old-model=true`强制使用老的调用方式**
`http://ip:port/rest/服务id/your_path`,其中`http://ip:port/rest/`为固定部分,后面跟微服务请求路径。
> 可在微服务端指定一个配置:`sop.restful.prefix=xxx`。请求路径将变成:`http://ip:port/rest/xxx/your_path`
@@ -48,7 +31,6 @@ public class TraditionalWebappController {
2. 本地访问:`http://10.0.1.22:2222/food/getFoodById/?id=2`
更多例子,可查看源码类:`TraditionalWebappController.java`
由此可见,对于前端调用者来说,它把网关看做一个大服务,只访问网关提供的请求,不需要关心网关后面的路由转发。网关后面各个微服务独自管理,
微服务之间的调用可以使用dubbo或feign有了版本号的管理可以做到服务的平滑升级对用户来说都是无感知的。结合SOP-Admin提供的上下线功能

View File

@@ -61,7 +61,8 @@ public UploadFile(String name, File file) throws IOException {
* @param param
* @return
*/
@ApiMapping(value = "demo.file.upload")
@Open("file.upload")
@RequestMapping("file1")
public FileUploadVO file1(FileUploadParam param) {
System.out.println(param.getRemark());
// 获取上传的文件
@@ -95,7 +96,8 @@ public class FileUploadParam {
* @param param
* @return
*/
@ApiMapping(value = "demo.file.upload2")
@Open("file.upload2")
@RequestMapping("file2")
public FileUploadVO file2(FileUploadParam2 param, HttpServletRequest request) {
System.out.println(param.getRemark());
FileUploadVO vo = new FileUploadVO();

View File

@@ -21,8 +21,8 @@
```properties
# nacos cloud配置
#spring.cloud.nacos.discovery.server-addr=${nacos.url}
#nacos.config.server-addr=${nacos.url}
#spring.cloud.nacos.discovery.server-addr=${register.url}
#nacos.config.server-addr=${register.url}
```
添加eureka配置

View File

@@ -1,86 +0,0 @@
# 扩展其它注册中心
SOP默认使用的注册中心是[nacos](https://nacos.io/),可以扩展实现其它注册中心,其中`eureka`分支是已经扩展好的使用eureka注册中心。
现在以扩展[consul](https://www.consul.io/)为例,说下具体扩展步骤:
- 扩展注册中心监听
`sop-gateway-common`工程下找到com.gitee.sop.gatewaycommon.route包可以看到有两个类
`EurekaRegistryListener``NacosRegistryListener`
这两个类的作用是监听注册中心服务注册,从而触发事件,然后获取新注册的服务。
新建一个类:`ConsulRegistryListener`,继承`BaseRegistryListener`
实现onEvent方法具体内容可参考`EurekaRegistryListener`
```java
public class ConsulRegistryListener extends BaseRegistryListener {
/**
* 注册中心触发事件,可以从中获取服务<br>
*
* 这个方法做的事情有2个<br>
*
* 1. 找出新注册的服务调用pullRoutes方法<br>
* 2. 找出删除的服务调用removeRoutes方法<br>
*
* @param applicationEvent 事件体
*/
@Override
public void onEvent(ApplicationEvent applicationEvent) {
}
}
```
配置类中新增:
```java
@Bean
@ConditionalOnProperty("spring.cloud.consul.host")
RegistryListener registryListenerConsul() {
return new ConsulRegistryListener();
}
```
其中`@ConditionalOnProperty("spring.cloud.consul.host")`的意思是只有配置了`spring.cloud.consul.host`属性这个Bean才会被Spring注入
`sop-gateway`工程添加`Spring Cloud Consul`相关依赖配置文件新增consul配置
- 扩展admin实现
找到`sop-admin-server`工程下com.gitee.sop.adminserver.service包可以看到有两个类`RegistryServiceEurekaImpl``RegistryServiceNacosImpl`
它们实现了`com.gitee.sop.adminserver.service.RegistryService`接口因此我们要新建一个consul对应的类
新建`RegistryServiceConsulImpl`然后实现RegistryService接口中的方法具体可参考RegistryServiceEurekaImpl
```java
public class RegistryServiceConsulImpl implements RegistryService {
}
```
打开`com.gitee.sop.adminserver.config.WebConfig`
新增一条配置
```java
/**
* 当配置了registry.name=eureka生效。
*
* @return
*/
@Bean
@ConditionalOnProperty(value = "registry.name", havingValue = "consul")
RegistryService registryServiceEureka() {
return new RegistryServiceConsulImpl();
}
```
application配置文件新增一条配置
```properties
registry.name=consul
```

View File

@@ -1,21 +0,0 @@
# 原理分析之@ApiMapping注解
@ApiMapping注解的使用方式参考了Spring自带的@PostMapping注解
查看org.springframework.web.bind.annotation.PostMapping的类注释有这么一句话
> Specifically, @PostMapping is a composed annotation that acts as a shortcut for @RequestMapping(method = RequestMethod.POST).
翻译过来就是说,@PostMapping是一个组合模式的注解,可以看成是@RequestMapping(method = RequestMethod.POST)快捷方式。
如果我们自己定义个Mapping仿照@PostMapping的方式,然后作用在方法上面会不会成功呢?实践证明是可以的。
@ApiMapping注解正是仿照了@PostMapping注解,然后再添加了几个自己的属性,比如版本号字段。
那么如何才能通过path + 版本号来确定一个接口呢?
springmvc提供了RequestCondition接口来实现这个功能具体的操作可参考这篇文章[让SpringMVC支持可版本管理的Restful接口](http://www.cnblogs.com/jcli/p/springmvc_restful_version.html)
SOP对应的是`com.gitee.sop.servercommon.mapping.ApiMappingRequestCondition`这个类在com.gitee.sop.servercommon.mapping下。可以从`ApiMappingHandlerMapping`类开始解读。

View File

@@ -1,23 +0,0 @@
# 2.x升3.x注意事项
升级到3.x后`本地访问接口`方式会有不同。
```java
@ApiMapping(value = "alipay.story.get")
public StoryResult getStory(StoryParam param) {
StoryResult story = new StoryResult();
story.setId(1L);
story.setName("海底小纵队(alipay.story.get1.0), port:" + environment.getProperty("server.port") + ", param:" + param);
return story;
}
```
- 2.x版本访问方式
`http://localhost:2222/alipay.story.get/?name=Jim&version=1.0`
- 3.x版本访问方式
`http://localhost:2222/alipay.story.get/1.0/?name=Jim`
3.x版本中把版本号融合在了url中如果这个功能没有用到可以放心升级。

View File

@@ -34,7 +34,7 @@ Story bizObject = openContext.getBizObject(Story.class);
## 如何关闭签名验证
- 针对某一个接口关闭签名验证
`@ApiMapping(value = "alipay.story.get", ignoreValidate = true)`
`@Open(value = "alipay.story.get", ignoreValidate = true)`
- 针对所有接口关闭签名验证
@@ -100,7 +100,7 @@ yml添加
# https://blog.csdn.net/qq_36872046/article/details/81058045
# 路由转发超时时间毫秒默认值1000详见RibbonClientConfiguration.DEFAULT_READ_TIMEOUT。
# 如果微服务端 处理时间过长会导致ribbon read超时解决办法将这个值调大一点
ribbon.ReadTimeout: 60000
ribbon.ReadTimeout= 60000
```
## 指定了context-path拉取路由404